class Method {
        public CtClass declaring;
        public MethodInfo info;
        public int notmatch;

        public Method(CtClass c, MethodInfo i, int n) {
            declaring = c;
            info = i;
            notmatch = n;
        }

        /**
         * Returns true if the invoked method is static.
         */
        public boolean isStatic() {
            int acc = info.getAccessFlags();
            return (acc & AccessFlag.STATIC) != 0;
        }
    }

    public Method lookupMethod(CtClass clazz, CtClass currentClass, MethodInfo current,
                                String methodName,
                                int[] argTypes, int[] argDims,
                                String[] argClassNames)
        throws CompileError
    {
        Method maybe = null;
        // to enable the creation of a recursively called method
        if (current != null && clazz == currentClass)
            if (current.getName().equals(methodName)) {
                int res = compareSignature(current.getDescriptor(),
                                           argTypes, argDims, argClassNames);
                if (res != NO) {
                    Method r = new Method(clazz, current, res);
                    if (res == YES)
                        return r;
                    maybe = r;
                }
            }

        Method m = lookupMethod(clazz, methodName, argTypes, argDims,
                                argClassNames, maybe != null);
        if (m != null)
            return m;
        return maybe;
    }

    private Method lookupMethod(CtClass clazz, String methodName,
                               int[] argTypes, int[] argDims,
                               String[] argClassNames, boolean onlyExact)
        throws CompileError
    {
        Method maybe = null;
        ClassFile cf = clazz.getClassFile2();
        // If the class is an array type, the class file is null.
        // If so, search the super class java.lang.Object for clone() etc.
        if (cf != null) {
            List<MethodInfo> list = cf.getMethods();
            for (MethodInfo minfo:list) {
                if (minfo.getName().equals(methodName)
                    && (minfo.getAccessFlags() & AccessFlag.BRIDGE) == 0) {
                    int res = compareSignature(minfo.getDescriptor(),
                                           argTypes, argDims, argClassNames);
                    if (res != NO) {
                        Method r = new Method(clazz, minfo, res);
                        if (res == YES)
                            return r;
                        else if (maybe == null || maybe.notmatch > res)
                            maybe = r;
                    }
                }
            }
        }

        if (onlyExact)
            maybe = null;
        else
            if (maybe != null)
                return maybe;

        int mod = clazz.getModifiers();
        boolean isIntf = Modifier.isInterface(mod);
        try {
            // skip searching java.lang.Object if clazz is an interface type.
            if (!isIntf) {
                CtClass pclazz = clazz.getSuperclass();
                if (pclazz != null) {
                    Method r = lookupMethod(pclazz, methodName, argTypes,
                                            argDims, argClassNames, onlyExact);
                    if (r != null)
                        return r;
                }
            }
        }
        catch (NotFoundException e) {}

        try {
            CtClass[] ifs = clazz.getInterfaces();
            for (CtClass intf:ifs) {
                Method r = lookupMethod(intf, methodName,
                        argTypes, argDims, argClassNames,
                        onlyExact);
                if (r != null)
                    return r;
            }

            if (isIntf) {
                // finally search java.lang.Object.
                CtClass pclazz = clazz.getSuperclass();
                if (pclazz != null) {
                    Method r = lookupMethod(pclazz, methodName, argTypes,
                                            argDims, argClassNames, onlyExact);
                    if (r != null)
                        return r;
                }
            }
        }
        catch (NotFoundException e) {}

        return maybe;
    }

    private static final int YES = 0;
    private static final int NO = -1;

    /*
     * Returns YES if actual parameter types matches the given signature.
     *
     * argTypes, argDims, and argClassNames represent actual parameters.
     *
     * This method does not correctly implement the Java method dispatch
     * algorithm.
     *
     * If some of the parameter types exactly match but others are subtypes of
     * the corresponding type in the signature, this method returns the number
     * of parameter types that do not exactly match.
     */
    private int compareSignature(String desc, int[] argTypes,
                                 int[] argDims, String[] argClassNames)
        throws CompileError
    {<FILL_FUNCTION_BODY>

        int result = YES;
        int i = 1;
        int nArgs = argTypes.length;
        if (nArgs != Descriptor.numOfParameters(desc))
            return NO;

        int len = desc.length();
        for (int n = 0; i < len; ++n) {
            char c = desc.charAt(i++);
            if (c == ')')
                return (n == nArgs ? result : NO);
            else if (n >= nArgs)
                return NO;

            int dim = 0;
            while (c == '[') {
                ++dim;
                c = desc.charAt(i++);
            }

            if (argTypes[n] == NULL) {
                if (dim == 0 && c != 'L')
                    return NO;

                if (c == 'L')
                    i = desc.indexOf(';', i) + 1;
            }
            else if (argDims[n] != dim) {
                if (!(dim == 0 && c == 'L'
                      && desc.startsWith("java/lang/Object;", i)))
                    return NO;

                // if the thread reaches here, c must be 'L'.
                i = desc.indexOf(';', i) + 1;
                result++;
                if (i <= 0)
                    return NO;  // invalid descriptor?
            }
            else if (c == 'L') {        // not compare
                int j = desc.indexOf(';', i);
                if (j < 0 || argTypes[n] != CLASS)
                    return NO;

                String cname = desc.substring(i, j);
                if (!cname.equals(argClassNames[n])) {
                    CtClass clazz = lookupClassByJvmName(argClassNames[n]);
                    try {
                        if (clazz.subtypeOf(lookupClassByJvmName(cname)))
                            result++;
                        else
                            return NO;
                    }
                    catch (NotFoundException e) {
                        result++; // should be NO?
                    }
                }

                i = j + 1;
            }
            else {
                int t = descToType(c);
                int at = argTypes[n];
                if (t != at)
                    if (t == INT
                        && (at == SHORT || at == BYTE || at == CHAR))
                        result++;
                    else
                        return NO;
            }
        }

        return NO;
    
<no_super_class>