/************************************************************************************** * Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved. * * http://aspectwerkz.codehaus.org * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the LGPL license * * a copy of which has been included with this distribution in the license.txt file. * **************************************************************************************/ package org.codehaus.aspectwerkz.reflect; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Iterator; import org.codehaus.aspectwerkz.exception.WrappedRuntimeException; import org.codehaus.aspectwerkz.transform.TransformationConstants; /** * Helper class with utility methods for working with the java.lang.reflect.* package. * * @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a> */ public class ReflectHelper { private final static Method OBJECT_EQUALS; private final static Method OBJECT_HASH_CODE; private final static Method OBJECT_GET_CLASS; private final static Method OBJECT_TO_STRING; private final static Method OBJECT_CLONE; private final static Method OBJECT_WAIT_1; private final static Method OBJECT_WAIT_2; private final static Method OBJECT_WAIT_3; private final static Method OBJECT_NOTIFY; private final static Method OBJECT_NOTIFY_ALL; private final static Method OBJECT_FINALIZE; static { Class clazz = Object.class; try { OBJECT_EQUALS = clazz.getDeclaredMethod("equals", new Class[]{clazz}); OBJECT_HASH_CODE = clazz.getDeclaredMethod("hashCode", new Class[]{}); OBJECT_GET_CLASS = clazz.getDeclaredMethod("getClass", new Class[]{}); OBJECT_CLONE = clazz.getDeclaredMethod("clone", new Class[]{}); OBJECT_TO_STRING = clazz.getDeclaredMethod("toString", new Class[]{}); OBJECT_WAIT_1 = clazz.getDeclaredMethod("wait", new Class[]{}); OBJECT_WAIT_2 = clazz.getDeclaredMethod("wait", new Class[]{long.class}); OBJECT_WAIT_3 = clazz.getDeclaredMethod("wait", new Class[]{long.class, int.class}); OBJECT_NOTIFY = clazz.getDeclaredMethod("notify", new Class[]{}); OBJECT_NOTIFY_ALL = clazz.getDeclaredMethod("notifyAll", new Class[]{}); OBJECT_FINALIZE = clazz.getDeclaredMethod("finalize", new Class[]{}); } catch (NoSuchMethodException e) { throw new WrappedRuntimeException(e); } } // /** // * Creates a sorted method list of all the methods in the class and super classes, including package private ones. // * // * @param klass the class with the methods // * @return the sorted method list // */ // public static List createSortedMethodList(final Class klass) { // if (klass == null) { // throw new IllegalArgumentException("class to sort method on can not be null"); // } // // // get all public methods including the inherited methods // java.lang.reflect.Method[] methods = klass.getMethods(); // java.lang.reflect.Method[] privateMethods = klass.getDeclaredMethods(); // List methodList = new ArrayList(methods.length); // for (int i = 0; i < methods.length; i++) { // Method method = methods[i]; // if (ReflectHelper.isUserDefinedMethod(method)) { // methodList.add(method); // } // } // // lookup in declared method to add "package private" method (which can be Pointcut with signatures) // for (int i = 0; i < privateMethods.length; i++) { // Method method = privateMethods[i]; // if (ReflectHelper.isUserDefinedMethod(method) && !methodList.contains(method)) { // methodList.add(method); // } // } // // Collections.sort(methodList, MethodComparator.getInstance(MethodComparator.NORMAL_METHOD)); // return methodList; // } // /** // * Creates a sorted method list of all the methods in the class and super classes, if and only // * if those are part of the given list of interfaces declared method // * // * @param klass the class with the methods // * @param interfaceDeclaredMethods the list of interface declared methods // * @return the sorted method list // */ // public static List createInterfaceDefinedSortedMethodList(final Class klass, List interfaceDeclaredMethods) { // if (klass == null) { // throw new IllegalArgumentException("class to sort method on can not be null"); // } // // // get all public methods including the inherited methods // java.lang.reflect.Method[] methods = klass.getMethods(); // java.lang.reflect.Method[] privateMethods = klass.getDeclaredMethods(); // List methodList = new ArrayList(methods.length); // for (int i = 0; i < methods.length; i++) { // Method method = methods[i]; // if (ReflectHelper.isUserDefinedMethod(method) && isDeclaredByInterface(method, interfaceDeclaredMethods)) { // methodList.add(method); // } // } // // lookup in declared method to add "package private" method (which can be Pointcut with signatures) // for (int i = 0; i < privateMethods.length; i++) { // Method method = privateMethods[i]; // if (ReflectHelper.isUserDefinedMethod(method) && isDeclaredByInterface(method, interfaceDeclaredMethods) // && !methodList.contains(method)) { // methodList.add(method); // } // } // // Collections.sort(methodList, MethodComparator.getInstance(MethodComparator.NORMAL_METHOD)); // return methodList; // } /** * Returns true if the method is declared by one of the given method declared in an interface class * * @param method * @param interfaceDeclaredMethods * @return */ private static boolean isDeclaredByInterface(Method method, List interfaceDeclaredMethods) { boolean match = false; for (Iterator iterator = interfaceDeclaredMethods.iterator(); iterator.hasNext();) { Method methodIt = (Method) iterator.next(); if (method.getName().equals(methodIt.getName())) { if (method.getParameterTypes().length == methodIt.getParameterTypes().length) { boolean matchArgs = true; for (int i = 0; i < method.getParameterTypes().length; i++) { // BAD ! will lead to nested loading while system not ready // => if introduced method has target class in its signature weaving will not occur // properly // ?? should we use ASMInfo ? Class parameterType = method.getParameterTypes()[i]; if (parameterType.getName().equals(methodIt.getParameterTypes()[i].getName())) { ; } else { matchArgs = false; break; } } if (matchArgs) { match = true; break; } } } } return match; } /** * Returns true if the method is not of on java.lang.Object and is not an AW generated one * * @param method * @return */ private static boolean isUserDefinedMethod(final Method method) { if (!method.equals(OBJECT_EQUALS) && !method.equals(OBJECT_HASH_CODE) && !method.equals(OBJECT_GET_CLASS) && !method.equals(OBJECT_TO_STRING) && !method.equals(OBJECT_CLONE) && !method.equals(OBJECT_WAIT_1) && !method.equals(OBJECT_WAIT_2) && !method.equals(OBJECT_WAIT_3) && !method.equals(OBJECT_NOTIFY) && !method.equals(OBJECT_NOTIFY_ALL) && !method.getName().startsWith(TransformationConstants.SYNTHETIC_MEMBER_PREFIX) && !method.getName().startsWith(TransformationConstants.ORIGINAL_METHOD_PREFIX) && !method.getName().startsWith(TransformationConstants.ASPECTWERKZ_PREFIX)) { return true; } else { return false; } } /** * Converts modifiers represented in a string array to an int. * * @param modifiers the modifiers as strings * @return the modifiers as an int */ public static int getModifiersAsInt(final String[] modifiers) { int accessFlags = 0; for (int i = 0; i < modifiers.length; i++) { if (modifiers[i].equals("abstract")) { accessFlags |= Modifier.ABSTRACT; } else if (modifiers[i].equals("final")) { accessFlags |= Modifier.FINAL; } else if (modifiers[i].equals("interface")) { accessFlags |= Modifier.INTERFACE; } else if (modifiers[i].equals("native")) { accessFlags |= Modifier.NATIVE; } else if (modifiers[i].equals("private")) { accessFlags |= Modifier.PRIVATE; } else if (modifiers[i].equals("protected")) { accessFlags |= Modifier.PROTECTED; } else if (modifiers[i].equals("public")) { accessFlags |= Modifier.PUBLIC; } else if (modifiers[i].equals("static")) { accessFlags |= Modifier.STATIC; } else if (modifiers[i].equals("strict")) { accessFlags |= Modifier.STRICT; } else if (modifiers[i].equals("synchronized")) { accessFlags |= Modifier.SYNCHRONIZED; } else if (modifiers[i].equals("transient")) { accessFlags |= Modifier.TRANSIENT; } else if (modifiers[i].equals("volatile")) { accessFlags |= Modifier.VOLATILE; } } return accessFlags; } /** * Calculate the hash for a class. * * @param klass the class * @return the hash */ public static int calculateHash(final Class klass) { return klass.getName().hashCode(); } /** * Calculate the hash for a method. * * @param method the method * @return the hash */ public static int calculateHash(final java.lang.reflect.Method method) { int hash = 17; hash = (37 * hash) + method.getName().hashCode(); for (int i = 0; i < method.getParameterTypes().length; i++) { Class type = method.getParameterTypes()[i]; hash = (37 * hash) + type.getName().hashCode(); } return hash; } /** * Calculate the hash for a constructor. * * @param constructor the constructor * @return the hash */ public static int calculateHash(final Constructor constructor) { int hash = 17; hash = (37 * hash) + TransformationConstants.INIT_METHOD_NAME.hashCode(); for (int i = 0; i < constructor.getParameterTypes().length; i++) { Class type = constructor.getParameterTypes()[i]; hash = (37 * hash) + type.getName().replace('/', '.').hashCode(); } return hash; } /** * Calculate the hash for a field. * * @param field the field * @return the hash */ public static int calculateHash(final Field field) { int hash = 17; hash = (37 * hash) + field.getName().hashCode(); Class type = field.getType(); hash = (37 * hash) + type.getName().hashCode(); return hash; } /** * Checks if the class is a of a primitive type, if so create and return the class for the type else return null. * * @param className * @return the class for the primitive type or null */ public static Class getPrimitiveClass(final String className) { if (className.equals("void")) { return void.class; } else if (className.equals("long")) { return long.class; } else if (className.equals("int")) { return int.class; } else if (className.equals("short")) { return short.class; } else if (className.equals("double")) { return double.class; } else if (className.equals("float")) { return float.class; } else if (className.equals("byte")) { return byte.class; } else if (className.equals("boolean")) { return boolean.class; } else if (className.equals("char")) { return char.class; } else { return null; } } /** * Returns JVM type signature for given class. * * @param cl * @return */ public static String getClassSignature(Class cl) { StringBuffer sbuf = new StringBuffer(); while (cl.isArray()) { sbuf.append('['); cl = cl.getComponentType(); } if (cl.isPrimitive()) { if (cl == Integer.TYPE) { sbuf.append('I'); } else if (cl == Byte.TYPE) { sbuf.append('B'); } else if (cl == Long.TYPE) { sbuf.append('J'); } else if (cl == Float.TYPE) { sbuf.append('F'); } else if (cl == Double.TYPE) { sbuf.append('D'); } else if (cl == Short.TYPE) { sbuf.append('S'); } else if (cl == Character.TYPE) { sbuf.append('C'); } else if (cl == Boolean.TYPE) { sbuf.append('Z'); } else if (cl == Void.TYPE) { sbuf.append('V'); } else { throw new InternalError(); } } else { sbuf.append('L' + cl.getName().replace('.', '/') + ';'); } return sbuf.toString(); } /** * Returns JVM type signature for a constructor. * * @param constructor * @return */ public static String getConstructorSignature(final Constructor constructor) { return getMethodSignature(constructor.getParameterTypes(), Void.TYPE); } /** * Returns JVM type signature for a field. * * @param field * @return */ public static String getFieldSignature(final Field field) { return getClassSignature(field.getType()); } /** * Returns JVM type signature for a method. * * @param method * @return */ public static String getMethodSignature(final Method method) { return getMethodSignature(method.getParameterTypes(), method.getReturnType()); } /** * Returns JVM type signature for given list of parameters and return type. * * @param paramTypes * @param retType * @return */ private static String getMethodSignature(Class[] paramTypes, Class retType) { StringBuffer sbuf = new StringBuffer(); sbuf.append('('); for (int i = 0; i < paramTypes.length; i++) { sbuf.append(getClassSignature(paramTypes[i])); } sbuf.append(')'); sbuf.append(getClassSignature(retType)); return sbuf.toString(); } }