How to search for Java API methods by type signature?

后端 未结 3 1008
自闭症患者
自闭症患者 2020-12-13 04:20

Are there any open-source tools available which support searching for Java methods by the set of parameter types and return type?

As an example, say I\'m looking for

3条回答
  •  一生所求
    2020-12-13 04:57

    Solution comments:

    1. Ignoring order, all permutation of parameters are tested till a match of a given function is found
    2. Primitive types are interchangeable (eg Integer.class = Integer.TYPE)
    3. Checks wider parameters
    4. Return types can be narrower
    5. Self is treated as first of the arguments to the findMethod method

    This is the output of the program:

    int[] -> Integer
        public native int java.lang.Object.hashCode()
        public static native int java.lang.reflect.Array.getLength(java.lang.Object) throws java.lang.IllegalArgumentException
        public static int java.util.Arrays.hashCode(int[])
        public static native int java.lang.System.identityHashCode(java.lang.Object)
    
    String, Character, Character -> String
        public java.lang.String java.lang.String.replace(char,char)
    
    String -> Integer
        public int java.lang.String.hashCode()
        public int java.lang.String.length()
        public static native int java.lang.reflect.Array.getLength(java.lang.Object) throws java.lang.IllegalArgumentException
        public static java.lang.Integer java.lang.Integer.decode(java.lang.String) throws java.lang.NumberFormatException
        public static java.lang.Integer java.lang.Integer.valueOf(java.lang.String) throws java.lang.NumberFormatException
        public static int java.lang.Integer.parseInt(java.lang.String) throws java.lang.NumberFormatException
        public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String)
        public static native int java.lang.System.identityHashCode(java.lang.Object)
    
    List -> Void
        public abstract void java.util.List.clear()
        public static void java.util.concurrent.locks.LockSupport.park(java.lang.Object)
        public static void java.util.Collections.reverse(java.util.List)
        public static void java.util.Collections.shuffle(java.util.List)
        public static void java.util.Collections.sort(java.util.List)
    

    The code:

    public class MethodMatcher {
    
        public static void main(String... args) throws Exception {
    
            // where to load some classes from (could be a list of classes to 
            // search from)..
            //        String pathToJar = "/usr/lib/jvm/java-6-sun-1.6.0.22/jre/lib/rt.jar";
            String pathToJar = "C:\\Program Files\\Java\\jdk1.6.0_20\\jre\\lib\\rt.jar";
    
            MethodMatcher m = new MethodMatcher(pathToJar, 
                    "java.io", "java.lang", "java.math", "java.net", 
                    "java.nio", "java.text", "java.util");
    
            // print some examples
            m.printExampleSearch(Integer.class, new int[0].getClass());
            m.printExampleSearch(String.class, String.class, Character.class, Character.class);
            m.printExampleSearch(Integer.class, String.class);
            m.printExampleSearch(Void.class, List.class);
        }
    
        public void printExampleSearch(Class returnType, Class... arguments) {
    
            for (int i = 0; i < arguments.length; i++)
                System.out.print((i == 0 ? "":", ") + arguments[i].getSimpleName());
    
            System.out.println(" -> " + returnType.getSimpleName());
    
            Set methods = findMethods(returnType, arguments);
    
            for (Method method : methods)
                System.out.println("\t" + method);
    
            System.out.println();
        }
    
    
    
        private final List klasses;
    
        public MethodMatcher(String jarFile, String... allowedPackages) 
        throws IOException, ClassNotFoundException {
    
            klasses = loadClasses(jarFile, allowedPackages);
        }
    
        /**
         * Finds a set of methods
         * @param returnType the return type
         * @param arguments the arguments (in any order)
         * @return a set of methods
         */
        public Set findMethods(Class returnType,
                Class... arguments) {
    
            Set methods = new LinkedHashSet();
    
            if (arguments.length > 0) {
                MethodFinder instance = new MethodFinder(arguments[0]);
    
                Class[] rest = new Class[arguments.length - 1];
                System.arraycopy(arguments, 1, rest, 0, rest.length);
    
                methods.addAll(instance.findInstanceMethods(returnType, rest));
            }
            else {
                for (MethodFinder k : klasses)
                    methods.addAll(k.findInstanceMethods(returnType, arguments));
            }
    
            for (MethodFinder k : klasses)
                methods.addAll(k.findStaticMethods(returnType, arguments));
    
            return methods;
        }
    
        /**
         * A method finder class
         */
        static class MethodFinder {
    
            public final Class klass;
    
            /**
             * Constructs the method finder (doh)
             * @param klass the class
             */
            public MethodFinder(Class klass) {
                this.klass = klass;
            }
    
            /**
             * Finds instance method matches
             * @param returnType the return type
             * @param arguments the arguments (in any order)
             * @return
             */
            public List findInstanceMethods(Class returnType, 
                    Class... arguments) {
    
                List matches = new LinkedList();
    
                for (Method method : klass.getMethods()) {
                    if ((method.getModifiers() & Modifier.STATIC) == 0) 
                        if (testMethod(method, returnType, arguments))
                            matches.add(method);
                }
    
                return matches;        
            }
    
            /**
             * Finds static method matches
             * @param returnType the return type
             * @param arguments the arguments (in any order)
             * @return
             */
            public List findStaticMethods(Class returnType,
                    Class... arguments) {
    
                List matches = new LinkedList();
    
                for (Method method : klass.getMethods()) 
                    if ((method.getModifiers() & Modifier.STATIC) != 0) 
                        if (testMethod(method, returnType, arguments))
                            matches.add(method);
    
                return matches;        
            }
    
            /**
             * Tests a method if it is a match
             * @param method the method to test
             * @param returnType the return type
             * @param arguments the arguments (in any order)
             * @return true if it matches
             */
            private boolean testMethod(Method method, 
                    Class returnType, 
                    Class... arguments) {
    
                boolean returnTypeIsOk = false;
                for (Class ic : getInterchangable(returnType))
                    if (ic.isAssignableFrom(method.getReturnType()))
                        returnTypeIsOk = true;
    
                if (!returnTypeIsOk)
                    return false;
    
                Class[] methodArguments = method.getParameterTypes();
    
                if (methodArguments.length != arguments.length)
                    return false;
    
                if (methodArguments.length == 0) {
                    return true;
                }
                else {
                    Permutations permutations = new Permutations(arguments);
    
                    outer: for (Class[] permutation : permutations) {
                        for (int i = 0; i < methodArguments.length; i++) {
    
                            boolean canAssign = false;
                            for (Class ic : getInterchangable(permutation[i])) 
                                if (methodArguments[i].isAssignableFrom(ic))
                                    canAssign = true;
    
                            if (!canAssign)
                                continue outer;
                        }
                        return true;
                    }
    
                    return false;
                }
            }
    
            /**
             * Returns the autoboxing types
             * @param type the type to autobox :)
             * @return a list of types that it could be
             */
            private static Class[] getInterchangable(Class type) {
    
                if (type == Boolean.class || type == Boolean.TYPE)
                    return new Class[] { Boolean.class, Boolean.TYPE };
                if (type == Character.class || type == Character.TYPE)
                    return new Class[] { Character.class, Character.TYPE };
                if (type == Short.class || type == Short.TYPE)
                    return new Class[] { Short.class, Short.TYPE };
                if (type == Integer.class || type == Integer.TYPE)
                    return new Class[] { Integer.class, Integer.TYPE };
                if (type == Float.class || type == Float.TYPE)
                    return new Class[] { Float.class, Float.TYPE };
                if (type == Double.class || type == Double.TYPE)
                    return new Class[] { Double.class, Double.TYPE };
                if (type == Void.class || type == Void.TYPE)
                    return new Class[] { Void.class, Void.TYPE };
    
                return new Class[] { type };
            }
    
    
            /**
             * Creates a permutation list of all different combinations
             */
            @SuppressWarnings("serial")
            private class Permutations extends LinkedList[]> {
    
                /**
                 * Creates a permutation list
                 * @param list the list to be permutated
                 */
                public Permutations(Class[] list) {
                    permutate(new LinkedList>(Arrays.asList(list)),
                            new LinkedList>());
                }
    
                // ugly, there is better ways of doing this...
                private void permutate(List> tail, List> choosen) {
    
                    if (tail.isEmpty()) {
                        add(choosen.toArray(new Class[0]));
                        return;
                    }
    
                    ListIterator> it = tail.listIterator();
    
                    while (it.hasNext()) {
    
                        Class current = it.next();
    
                        choosen.add(current);
                        it.remove();
    
                        permutate(new LinkedList>(tail), choosen);
    
                        choosen.remove(current);
                        it.add(current);
                    }
                }
            }
        }
    
        /**
         * A hack to read some classes from some allowed packages
         * @param jarFile the jar file to read from
         * @param allowedPackages the allowed packages
         * @return a list of MethodFinders
         * @throws IOException
         * @throws ClassNotFoundException
         */
        private static List loadClasses(
                String jarFile, 
                String... allowedPackages) throws IOException, ClassNotFoundException {
    
            List klasses = new LinkedList();
    
            JarFile file = new JarFile(jarFile);
            try {
                Enumeration enumerator = file.entries();
    
                while (enumerator.hasMoreElements()) {
    
                    String name = enumerator.nextElement().getName();
    
                    if (!name.endsWith(".class")) 
                        continue;
    
                    name = name.substring(0, name.length() - 6).replace('/', '.');
    
                    boolean allowed = false;
                    for (String pkg : allowedPackages)
                        allowed |= name.startsWith(pkg);
    
                    if (allowed)
                        klasses.add(new MethodFinder(Class.forName(name)));
                }
            } 
            finally {
                if (file != null)
                    file.close();
            }
    
            return klasses;
        }
    }
    

提交回复
热议问题