How to search for Java API methods by type signature?

后端 未结 3 1005
自闭症患者
自闭症患者 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<Method> methods = findMethods(returnType, arguments);
    
            for (Method method : methods)
                System.out.println("\t" + method);
    
            System.out.println();
        }
    
    
    
        private final List<MethodFinder> 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<Method> findMethods(Class<?> returnType,
                Class<?>... arguments) {
    
            Set<Method> methods = new LinkedHashSet<Method>();
    
            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<Method> findInstanceMethods(Class<?> returnType, 
                    Class<?>... arguments) {
    
                List<Method> matches = new LinkedList<Method>();
    
                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<Method> findStaticMethods(Class<?> returnType,
                    Class<?>... arguments) {
    
                List<Method> matches = new LinkedList<Method>();
    
                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<Class<?>[]> {
    
                /**
                 * Creates a permutation list
                 * @param list the list to be permutated
                 */
                public Permutations(Class<?>[] list) {
                    permutate(new LinkedList<Class<?>>(Arrays.asList(list)),
                            new LinkedList<Class<?>>());
                }
    
                // ugly, there is better ways of doing this...
                private void permutate(List<Class<?>> tail, List<Class<?>> choosen) {
    
                    if (tail.isEmpty()) {
                        add(choosen.toArray(new Class<?>[0]));
                        return;
                    }
    
                    ListIterator<Class<?>> it = tail.listIterator();
    
                    while (it.hasNext()) {
    
                        Class<?> current = it.next();
    
                        choosen.add(current);
                        it.remove();
    
                        permutate(new LinkedList<Class<?>>(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<MethodFinder> loadClasses(
                String jarFile, 
                String... allowedPackages) throws IOException, ClassNotFoundException {
    
            List<MethodFinder> klasses = new LinkedList<MethodFinder>();
    
            JarFile file = new JarFile(jarFile);
            try {
                Enumeration<JarEntry> 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;
        }
    }
    
    0 讨论(0)
  • 2020-12-13 05:08

    Very good question although I do not understand why do you need such tool. I am sorry to say it but it seems that it takes less time to implement such tool than to write this post. Here is a code that I have just implemented. It took 182 seconds. It is a static method that takes class, return type and arguments and returns all method of the class that match the signature.

    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    public class ClassUtil {
        public static Method[] getMethodsBySignature(Class<?> clazz, Class<?> returnType, Class<?>... args) {
            List<Method> result = new ArrayList<Method>();
            for (Method m : clazz.getDeclaredMethods()) {
                if (m.getReturnType().equals(returnType)) {
                    Class<?>[] params = m.getParameterTypes();
                    if (Arrays.equals(params, args)) {
                        result.add(m);
                    }
                }
            }
            return result.toArray(new Method[result.size()]);
        }
    }
    

    You can spend another 5-10 minutes to implement method that opens jar, iterates over entries, calls Class.forName() and then calls my method. That's it!

    0 讨论(0)
  • 2020-12-13 05:20

    I just tried this on Eclipse (Helios) and it supports this. Hit Ctrl-H, go to Java tab, choose Search For -> Method radio button, Limit To -> Declarations. In the Search string enter *(int, int) int and it will return lots of results of methods that take two ints and return an int.

    0 讨论(0)
提交回复
热议问题