如何通过类型签名搜索Java API方法? [英] How to search for Java API methods by type signature?
问题描述
是否有任何可用的开源工具支持通过参数类型和返回类型搜索Java方法?
Are there any open-source tools available which support searching for Java methods by the set of parameter types and return type?
举个例子,说我是寻找一种为int数组生成哈希码的方法。我搜索一个接受int []参数并返回int的方法:
As an example, say I'm looking for a method to generate a hash code for an array of ints. I search for a method which takes an int[] parameter and returns an int:
int[] -> int
yielding
java.util.Arrays#hashCode(int[])
...
或者我可能想要找到一个方法,它接受一个字符串,并替换字符,并将字符替换为。所以我搜索匹配方法:
Or I may want to find a method which takes a String, and character to replace, and the character to replace it with. So I search for a matching method:
String, char, char -> String
yielding
java.lang.String#replace(char, char)
...
理想情况下,我喜欢Java等同于Haskell的 Hoogle ,它支持按类型签名搜索功能。
Ideally I'd like a Java equivalent to Haskell's Hoogle, which supports searching for functions by type signature.
我希望该工具能够:
- 忽略参数的顺序
- 包括接受'更宽'类型作为参数的方法(例如超类)
- 包含将'narrower'类型作为返回值返回的方法(例如子类)
- 将'self'值视为实例方法的参数(例如'String - > int'将包含String#hashCode)
- ignore the order of parameters
- include methods which accept 'wider' types as parameters (e.g. superclasses)
- include methods which return 'narrower' types as return values (e.g. subclasses)
- treat the 'self' value as a parameter to instance methods (e.g. 'String -> int' would include String#hashCode)
我知道许多IDE支持搜索获取或返回给定类型的方法,但尚未看到通过参数类型组合缩小搜索范围的工具和返回类型。
I'm aware that many IDEs support searching for methods which take or return a given type, but haven't yet seen a tool to narrow the search by the combination of parameter types and return type.
推荐答案
解决方案评论:
Solution comments:
- 忽略顺序,测试参数的所有排列直到找到给定函数的匹配
- 原始类型是可互换的(例如
Integer.class
=Integer.TYPE
) - 检查更宽的参数
- 返回类型可以更窄
- 自我被视为<$ c的参数的第一个 $ c> findMethod 方法
- Ignoring order, all permutation of parameters are tested till a match of a given function is found
- Primitive types are interchangeable (eg
Integer.class
=Integer.TYPE
) - Checks wider parameters
- Return types can be narrower
- 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;
}
}
这篇关于如何通过类型签名搜索Java API方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!