How to search for Java API methods by type signature? -
are there open-source tools available support searching java methods set of parameter types , return type?
as example, i'm looking method generate hash code array of ints. search method takes int[] parameter , returns int:
int[] -> int
yielding
java.util.arrays#hashcode(int[]) ...
or may want find method takes string, , character replace, , character replace with. search matching method:
string, char, char -> string
yielding
java.lang.string#replace(char, char) ...
ideally i'd java equivalent haskell's hoogle, supports searching functions type signature.
i'd expect tool to:
- ignore order of parameters
- include methods accept 'wider' types parameters (e.g. superclasses)
- include methods return 'narrower' types return values (e.g. subclasses)
- treat 'self' value parameter instance methods (e.g. 'string -> int' include string#hashcode)
i'm aware many ides support searching methods take or return given type, haven't yet seen tool narrow search combination of parameter types and return type.
solution comments:
- ignoring order, permutation of parameters tested till match of given function found
- primitive types interchangeable (eg
integer.class
=integer.type
) - checks wider parameters
- return types can narrower
- self treated first of arguments
findmethod
method
this output of 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 { // load classes (could list of classes // 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 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) { (int = 0; < arguments.length; i++) system.out.print((i == 0 ? "":", ") + arguments[i].getsimplename()); system.out.println(" -> " + returntype.getsimplename()); set<method> methods = findmethods(returntype, arguments); (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 set of methods * @param returntype return type * @param arguments arguments (in order) * @return 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 { (methodfinder k : klasses) methods.addall(k.findinstancemethods(returntype, arguments)); } (methodfinder k : klasses) methods.addall(k.findstaticmethods(returntype, arguments)); return methods; } /** * method finder class */ static class methodfinder { public final class<?> klass; /** * constructs method finder (doh) * @param klass class */ public methodfinder(class<?> klass) { this.klass = klass; } /** * finds instance method matches * @param returntype return type * @param arguments arguments (in order) * @return */ public list<method> findinstancemethods(class<?> returntype, class<?>... arguments) { list<method> matches = new linkedlist<method>(); (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 return type * @param arguments arguments (in order) * @return */ public list<method> findstaticmethods(class<?> returntype, class<?>... arguments) { list<method> matches = new linkedlist<method>(); (method method : klass.getmethods()) if ((method.getmodifiers() & modifier.static) != 0) if (testmethod(method, returntype, arguments)) matches.add(method); return matches; } /** * tests method if match * @param method method test * @param returntype return type * @param arguments arguments (in order) * @return true if matches */ private boolean testmethod(method method, class<?> returntype, class<?>... arguments) { boolean returntypeisok = false; (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: (class<?>[] permutation : permutations) { (int = 0; < methodarguments.length; i++) { boolean canassign = false; (class<?> ic : getinterchangable(permutation[i])) if (methodarguments[i].isassignablefrom(ic)) canassign = true; if (!canassign) continue outer; } return true; } return false; } } /** * returns autoboxing types * @param type type autobox :) * @return list of types */ 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 permutation list of different combinations */ @suppresswarnings("serial") private class permutations extends linkedlist<class<?>[]> { /** * creates permutation list * @param list list permutated */ public permutations(class<?>[] list) { permutate(new linkedlist<class<?>>(arrays.aslist(list)), new linkedlist<class<?>>()); } // ugly, there 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<?>> = 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); } } } } /** * hack read classes allowed packages * @param jarfile jar file read * @param allowedpackages allowed packages * @return 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; (string pkg : allowedpackages) allowed |= name.startswith(pkg); if (allowed) klasses.add(new methodfinder(class.forname(name))); } } { if (file != null) file.close(); } return klasses; } }
Comments
Post a Comment