package com.idega.util.reflect; /** * A utility class to find methods by reflection. * Title: idega Reflection utility classes * Description: * Copyright: Copyright (c) 2001-2003 * Company: idega * @author <a href="tryggvi@idega.is">Tryggvi Larusson</a> * @version 1.1 */ import java.lang.reflect.Method; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.StringTokenizer; import java.util.Vector; import com.idega.repository.data.Instantiator; import com.idega.repository.data.RefactorClassRegistry; import com.idega.repository.data.Singleton; import com.idega.repository.data.SingletonRepository; import com.idega.util.caching.CacheMap; public class MethodFinder implements Singleton { static final String separator = ":"; static final String methodString = "method"; static final int declaringClassIndex = 2; static final int returnedClassIndex = 3; static final int methodIndex = 4; static final int parameterClassStartIndex = 5; private static Instantiator instantiator = new Instantiator() { public Object getInstance() { return new MethodFinder();}}; private Map methodCache; private Map classMethodCache; private MethodFinder() { } public static MethodFinder getInstance() { return (MethodFinder) SingletonRepository.getRepository().getInstance(MethodFinder.class, instantiator); } private Map getMethodCache() { if (this.methodCache == null) { this.methodCache = new CacheMap(1000); } return this.methodCache; } protected Method getMethodFromGlobalCache(String methodIdentifier) { return (Method) getMethodCache().get(methodIdentifier); } protected void putMethodInGlobalCache(String methodIdentifier, Method method) { getMethodCache().put(methodIdentifier, method); } private Map getClassMethodCache(Class declaringClass) { if (this.classMethodCache == null) { this.classMethodCache = new HashMap(); } Map mapForClass = (Map) this.classMethodCache.get(declaringClass); if(mapForClass==null){ mapForClass = new CacheMap(150); this.classMethodCache.put(declaringClass,mapForClass); } return mapForClass; } protected Method getMethodFromClassCache(Class declaringClass,String methodIdentifier) { return (Method) getClassMethodCache(declaringClass).get(methodIdentifier); } protected void putMethodInClassCache(Class declaringClass,String methodIdentifier, Method method) { getClassMethodCache(declaringClass).put(methodIdentifier, method); } public Method getMethod(String methodIdentifier) { try { Method m = getMethodFromGlobalCache(methodIdentifier); if (m == null) { String[] idArray = getIdentifierAsArray(methodIdentifier); Class declaringClass = getDeclaringClass(idArray); m = getMethod(declaringClass, idArray); putMethodInGlobalCache(methodIdentifier, m); } return m; } catch (Exception e) { e.printStackTrace(); } return null; } public Method getMethod(String methodIdentifier, Class declaringClass) { Method m = getMethodFromClassCache(declaringClass,methodIdentifier); if(m==null){ String[] idArray = getIdentifierAsArray(methodIdentifier); m = getMethod(declaringClass, idArray); putMethodInClassCache(declaringClass,methodIdentifier,m); } return m; } /** * Does method finding and tries to correct any refactored class types * @param methodIdentifier * @param declaringClass * @param idArray * @return */ private Method getMethod(Class declaringClass, String[] idArray) { try{ return findRealMethod(declaringClass,idArray); } catch(Throwable nsme){ RefactorClassRegistry registry = RefactorClassRegistry.getInstance(); Map refactoredClassNames = registry.getRefactoredClassNames(); Iterator iterator = refactoredClassNames.keySet().iterator(); while (iterator.hasNext()) { String oldClassName = (String) iterator.next(); String newClassName = (String)refactoredClassNames.get(oldClassName); String[] newIdArray = new String[idArray.length]; for (int i = 0; i < idArray.length; i++) { String str = idArray[i]; if(str.equals(oldClassName)){ newIdArray[i]=newClassName; } else{ newIdArray[i]=idArray[i]; } } try{ return findRealMethod(declaringClass,newIdArray); } catch(ClassNotFoundException cnfe){ System.err.println("MethodFinder.getMethod() Error finding method with parameter of type : "+cnfe.getMessage()); } catch(Throwable nsme2){ System.err.println("MethodFinder.getMethod() Error finding method, message: "+nsme2.getMessage()); } } throw new NoSuchMethodError(nsme.getMessage()); } } /** * Does the Real method finding * @param methodIdentifier * @param declaringClass * @param idArray * @return */ private Method findRealMethod(Class declaringClass, String[] idArray) throws ClassNotFoundException { //try //{ //System.out.println("Declaring class: "+c.getName()); Method[] methods = getMethods(declaringClass); for (int i = 0; i < methods.length; i++) { if (methods[i].getName().equals(getMethodName(idArray))) { //System.out.println("MethodName: "+methods[i].getName()+" returnType = "+methods[i].getReturnType().getName()); if (methods[i].getReturnType().equals(getReturnTypeClass(idArray))) { Class[] classes = getArgumentClasses(idArray); Class[] methodClasses = methods[i].getParameterTypes(); boolean check = true; if (methodClasses.length > 0) { if (classes.length != methodClasses.length) { check = false; } else { for (int j = 0;(j < classes.length && check); j++) { if (!methodClasses[j].equals(classes[j])) { //System.out.print(methodIdentifier); //System.out.println(methods[i].toString()); //System.out.println("MethodClasses["+j+"]"+methodClasses[j].getName()); check = false; } } } } else { //System.out.println("Methodfinder length "+classes.length); if (classes.length > 0) { if (!classes[0].equals(java.lang.Void.TYPE)) { check = false; } } } if (check) { return methods[i]; } } } } //} catch (ClassNotFoundException e) //{ // e.printStackTrace(); //} return null; } public String getMethodIdentifierWithoutDeclaringClass(Method method) { return getMethodIdentifier(method, false); } public String getMethodIdentifier(Method method) { return getMethodIdentifier(method, true); } private String getMethodIdentifier(Method method, boolean includeDeclaringClass) { StringBuffer buf = new StringBuffer(); buf.append(separator); buf.append(methodString); buf.append(separator); buf.append(method.getModifiers()); buf.append(separator); if (includeDeclaringClass) { buf.append(method.getDeclaringClass().getName()); } else { buf.append("implied"); } buf.append(separator); buf.append(method.getReturnType().getName()); buf.append(separator); buf.append(method.getName()); buf.append(separator); Class[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length > 0) { for (int i = 0; i < parameterTypes.length; i++) { buf.append(parameterTypes[i].getName()); buf.append(separator); } } else { buf.append("void"); buf.append(separator); } return buf.toString(); } private String[] getIdentifierAsArray(String identifier) { StringTokenizer token = new StringTokenizer(identifier, separator); int size = token.countTokens(); String array[] = new String[size]; for (int i = 0; i < array.length; i++) { array[i] = token.nextToken(); } return array; } private static Class getClass(String className) throws ClassNotFoundException { if (className.equals("void")) { return java.lang.Void.TYPE; } else if (className.equals("int")) { return java.lang.Integer.TYPE; } else if (className.equals("boolean")) { return java.lang.Boolean.TYPE; } else if (className.equals("float")) { return java.lang.Float.TYPE; } else if (className.equals("double")) { return java.lang.Double.TYPE; } else if (className.equals("long")) { return java.lang.Long.TYPE; } else { return RefactorClassRegistry.forName(className); } } public String getDeclaringClassName(String[] identifierArray) { return identifierArray[declaringClassIndex]; } public Class getDeclaringClass(String[] identifierArray) throws ClassNotFoundException { return RefactorClassRegistry.forName(getDeclaringClassName(identifierArray)); } public String getReturnTypeClassName(String[] identifierArray) { return identifierArray[returnedClassIndex]; } public Class getReturnTypeClass(String[] identifierArray) throws ClassNotFoundException { return getClass(getReturnTypeClassName(identifierArray)); } public String getMethodName(String[] identifierArray) { return identifierArray[methodIndex]; } public String[] getArgumentClassNames(String[] identifierArray) { int length = identifierArray.length - parameterClassStartIndex; String[] theReturn = new String[length]; if (length != 0) { System.arraycopy(identifierArray, parameterClassStartIndex, theReturn, 0, theReturn.length); } return theReturn; } public Class[] getArgumentClasses(String methodIdentifier) throws ClassNotFoundException { return getArgumentClasses(getIdentifierAsArray(methodIdentifier)); } public Class[] getArgumentClasses(String[] identifierArray) throws ClassNotFoundException { String[] strings = getArgumentClassNames(identifierArray); Class[] theReturn = new Class[strings.length]; for (int i = 0; i < theReturn.length; i++) { theReturn[i] = getClass(strings[i]); } return theReturn; } public Method[] getMethods(Class c) { return c.getMethods(); } public String[] getMethodIdentifiersWithoutDeclaringClass(Class c) { Method[] methods = getMethods(c); String[] theReturn = new String[methods.length]; for (int i = 0; i < theReturn.length; i++) { theReturn[i] = getMethodIdentifierWithoutDeclaringClass(methods[i]); } return theReturn; } public String[] getMethodIdentifiers(Class c) { Method[] methods = getMethods(c); String[] theReturn = new String[methods.length]; for (int i = 0; i < theReturn.length; i++) { theReturn[i] = getMethodIdentifier(methods[i]); } return theReturn; } /** * Gets only the method that is named "name" in class objectClass and takes in no arguments (parameters) * @throws NoSuchMethodException if no mathing method is found. **/ public Method getMethodWithNameAndNoParameters(Class objectClass, String name) throws NoSuchMethodException { Method[] allMethods = objectClass.getMethods(); for (int i = 0; i < allMethods.length; i++) { Method methodToCheck = allMethods[i]; if (methodToCheck.getName().equals(name)) { if (methodToCheck.getParameterTypes().length == 0) { return methodToCheck; } } } throw new NoSuchMethodException("Method " + name + "() not found in " + objectClass.getName()); } /** * Gets only the method that is named "name" in class objectClass and takes in one parameter of the type parameterType * @throws NoSuchMethodException if no mathing method is found. **/ public Method getMethodWithNameAndOneParameter(Class objectClass, String name,Class parameterType) throws NoSuchMethodException { Method[] allMethods = objectClass.getMethods(); for (int i = 0; i < allMethods.length; i++) { Method methodToCheck = allMethods[i]; if (methodToCheck.getName().equals(name)) { if (methodToCheck.getParameterTypes().length == 1) { if(methodToCheck.getParameterTypes()[0].equals(parameterType)){ return methodToCheck; } } } } throw new NoSuchMethodException("Method " + name + "() not found in " + objectClass.getName()); } /** * Gets only the method that is named "name" in class objectClass and takes in parameters of the types exactly found in parameterTypes * @throws NoSuchMethodException if no mathing method is found. **/ public Method getMethodWithNameAndParameters(Class objectClass, String name,Class[] parameterTypes) throws NoSuchMethodException { int numOfParameters = (parameterTypes!=null)? parameterTypes.length : 0; Method[] allMethods = getMethodsWithName(objectClass,name,numOfParameters); for (int i = 0; i < allMethods.length; i++) { Method methodToCheck = allMethods[i]; Class[] methodParameters = methodToCheck.getParameterTypes(); if (methodParameters.length == numOfParameters) { boolean check = true; for (int j = 0; j < methodParameters.length; j++) { Class methodParam = methodParameters[j]; Class parameter = parameterTypes[j]; if(!parameter.equals(methodParam)){ check=false; } } if(check) { return methodToCheck; } } } throw new NoSuchMethodException("Method " + name + "() not found in " + objectClass.getName()); } /** * Gets all the methods that are named "name" in class objectClass * @return An array with all mathing methods or an array with length 0 if no mathing methods are found. **/ public Method[] getMethodsWithName(Class objectClass, String name) { return getMethodsWithName(objectClass, name, -1); } /** * Gets all the methods that are named "name" in class objectClass * @return An array with all mathing methods or an array with length 0 if no mathing methods are found. * @param maxNumberOfParameters The maximum number of parameters the method can include or -1 of no maximum is specified. **/ public Method[] getMethodsWithName(Class objectClass, String name, int maxNumberOfParameters) { Method[] allMethods = objectClass.getMethods(); //Method[] matchedMethods = new Method[returningSize]; Vector matchedMethodsVector = new Vector(); for (int i = 0; i < allMethods.length; i++) { Method methodToCheck = allMethods[i]; if (methodToCheck.getName().equals(name)) { if (maxNumberOfParameters < 0) { matchedMethodsVector.add(methodToCheck); } else { if (methodToCheck.getParameterTypes().length <= maxNumberOfParameters) { matchedMethodsVector.add(methodToCheck); } } } } //return matchedMethods; return (Method[]) matchedMethodsVector.toArray(new Method[0]); } private final static String openingParentheses = "("; private final static String closingParentheses = ")"; private final static String comma = ","; /** * Gets a string representation of a class method * "getMyValue(String arg)" * @param method * @return string representation of method */ public String getMethodNameWidthParameters(Method method){ String methodToString = method.getName()+openingParentheses; Class[] arguments = method.getParameterTypes(); for (int j = 0; j < arguments.length; j++) { if(j!=0){ methodToString += comma; } methodToString += arguments[j].getName(); } methodToString += closingParentheses; return methodToString; } /** * Gets a map of class methods, keyed by getMethodNameWithParameter method * lastSuperClassToReflect tells at wich superclass the reflection should stop * if null then only the methods of the base class * @param methodClass * @param lastSuperClassToReflect * @param filters * @return map of methods */ public Map getMapOfClassMethodsRecursive(Class methodClass, Class lastSuperClassToReflect,String[] filters){ Map map = new HashMap(); boolean noStopClass = lastSuperClassToReflect ==null; while(!methodClass.equals(lastSuperClassToReflect)){ Method[] methods = methodClass.getMethods(); for (int i = 0; i < filters.length; i++) { for (int j = 0; j < methods.length; j++) { String name = MethodFinder.getInstance().getMethodNameWidthParameters(methods[j]); if(name.startsWith(filters[i])){ map.put(name,methods[j]); System.out.println("Putting method for "+name); } } } if(noStopClass) { break; } methodClass = methodClass.getSuperclass(); } return map; } /** Returns an updated methodIdentifier. * Checks if the declared class, the returned class or the parameter classes are refactored and * replaces them by the new ones. * Returns the same string if no classes are refactored. * * Use this method when handling with old method identifers (e.g. stored in old builder pages). * See also MethodIdentifierCache. * * @param methodIdentifier * @return updated methodIdentifer */ public String getUpdatedMethodIdentifier(String methodIdentifier) { RefactorClassRegistry registry = RefactorClassRegistry.getInstance(); StringTokenizer firstTokenizer = new StringTokenizer(methodIdentifier, separator); StringBuffer buffer = new StringBuffer(separator); boolean somethingHasChanged = false; int counter = 0; while (firstTokenizer.hasMoreTokens()) { String token = firstTokenizer.nextToken(); if ((counter == declaringClassIndex || counter == returnedClassIndex || counter >= parameterClassStartIndex) && registry.isClassRefactored(token)) { buffer.append(registry.getRefactoredClassName(token)); somethingHasChanged = true; } else { buffer.append(token); } buffer.append(separator); counter++; } // avoid expensive creation of string if (somethingHasChanged) { return buffer.toString(); } return methodIdentifier; } /** * Test main method **/ public static void main(String[] args) { /*Method[] methods = getInstance().getMethodsWithName(MethodFinder.class,"getMethods"); for (int i = 0; i < methods.length; i++) { System.out.println("Found method: "+methods[i].toString()); }*/ /*try{ Method method = getInstance().getMethodsWithNameAndNoParameters(MethodFinder.class,"getInstance"); System.out.println("Found method: "+method.toString()); } catch(Exception e){ e.printStackTrace(); }*/ } }