View Javadoc

    1   /***************************************************************************************
    2    * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved.                 *
    3    * http://aspectwerkz.codehaus.org                                                    *
    4    * ---------------------------------------------------------------------------------- *
    5    * The software in this package is published under the terms of the LGPL license      *
    6    * a copy of which has been included with this distribution in the license.txt file.  *
    7    **************************************************************************************/
    8   package org.codehaus.aspectwerkz.reflect;
    9
    10  import java.util.List;
    11  import java.util.ArrayList;
    12  import java.util.Collections;
    13  import java.util.Iterator;
    14  import java.util.Set;
    15  import java.util.HashSet;
    16  import java.util.Map;
    17  import java.util.HashMap;
    18
    19  import org.codehaus.aspectwerkz.transform.TransformationConstants;
    20  import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
    21
    22  /***
    23   * Utility method for manipulating and managing ClassInfo hierarchies.
    24   *
    25   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
    26   * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
    27   */
    28  public class ClassInfoHelper {
    29      private static final ArrayList EMPTY_ARRAY_LIST = new ArrayList();
    30      private static final String OBJECT_CLASS_NAME = "java.lang.Object";
    31
    32      /***
    33       * Checks if a class has a certain class as super class or interface, somewhere up in the class hierarchy.
    34       *
    35       * @param classInfo      the meta-data for the class to parse
    36       * @param superclassName the name of the super class or interface
    37       * @return true if we have a parse else false
    38       */
    39      public static boolean instanceOf(final ClassInfo classInfo, final String superclassName) {
    40          return implementsInterface(classInfo, superclassName) || extendsSuperClass(classInfo, superclassName);
    41      }
    42
    43      /***
    44       * Checks if a class implements a certain inteface, somewhere up in the class hierarchy, excluding
    45       * itself.
    46       *
    47       * @param classInfo
    48       * @param interfaceName
    49       * @return true if we have a parse else false
    50       */
    51      public static boolean implementsInterface(final ClassInfo classInfo, final String interfaceName) {
    52          if ((classInfo == null) || (interfaceName == null)) {
    53              return false;
    54          } else {
    55              //TODO: we could lookup in names onlny FIRST to not trigger lazy getInterfaces() stuff
    56              ClassInfo[] interfaces = classInfo.getInterfaces();
    57              for (int i = 0; i < interfaces.length; i++) {
    58                  ClassInfo anInterface = interfaces[i];
    59                  if (interfaceName.equals(anInterface.getName())) {
    60                      return true;
    61                  } else if (ClassInfoHelper.implementsInterface(anInterface, interfaceName)) {
    62                      return true;
    63                  }
    64              }
    65              return ClassInfoHelper.implementsInterface(classInfo.getSuperclass(), interfaceName);
    66          }
    67      }
    68
    69      /***
    70       * Checks if a class has a certain class as super class, somewhere up in the class hierarchy.
    71       *
    72       * @param classInfo the meta-data for the class to parse
    73       * @param className the name of the super class
    74       * @return true if we have a parse else false
    75       */
    76      public static boolean extendsSuperClass(final ClassInfo classInfo, final String className) {
    77          if ((classInfo == null) || (className == null)) {
    78              return false;
    79              // TODO odd comparison
    80  //        } else if (classInfo.getName().equals(null)) {
    81  //            return true;
    82          } else if (className.equals(classInfo.getName())) {
    83              return true;
    84          } else {
    85              return ClassInfoHelper.extendsSuperClass(classInfo.getSuperclass(), className);
    86          }
    87      }
    88
    89      /***
    90       * Creates a method list of all the methods in the class and super classes, including package private ones.
    91       * Inherited methods are last in the list.
    92       *
    93       * @param klass the class with the methods
    94       * @return the sorted method list
    95       */
    96      public static List createMethodList(final ClassInfo klass) {
    97          if (klass == null) {
    98              return EMPTY_ARRAY_LIST;
    99          }
    100
    101         // get this klass methods
    102         List methods = new ArrayList();
    103         MethodInfo[] methodInfos = klass.getMethods();
    104         for (int i = 0; i < methodInfos.length; i++) {
    105             MethodInfo methodInfo = methodInfos[i];
    106             if (isUserDefinedMethod(methodInfo)) {
    107                 methods.add(methodInfo);
    108             }
    109         }
    110
    111         // get all the inherited methods, as long as they are user defined ones
    112
    113         ClassInfo superClass = klass.getSuperclass();
    114         if (!superClass.getName().equals(OBJECT_CLASS_NAME)) {
    115             List parentMethods = createMethodList(superClass);
    116             // merge the method list (parent discovered methods are not added if overrided in this klass)
    117             for (Iterator iterator = parentMethods.iterator(); iterator.hasNext();) {
    118                 MethodInfo parentMethod = (MethodInfo) iterator.next();
    119                 if (!methods.contains(parentMethod)) { // TODO seems to work but ? since tied to declaringTypeName
    120                     methods.add(parentMethod);
    121                 }
    122             }
    123         }
    124         return methods;
    125     }
    126
    127 //    /***
    128 //     * Creates a sorted method list of all the methods in the class and super classes, including package private ones.
    129 //     *
    130 //     * @param klass the class with the methods
    131 //     * @return the sorted method list
    132 //     */
    133 //    public static List createSortedMethodList(final ClassInfo klass) {
    134 //        final List methods = createMethodList(klass);
    135 //
    136 //        // Note: sorting is only use to maintain mixin consistency
    137 //        //Alex2Jonas - why sort should be needed ??? Collections.sort(methods, MethodComparator.getInstance(MethodComparator.METHOD_INFO));
    138 //
    139 //        return methods;
    140 //    }
    141
    142     /***
    143      * Collects the methods from all the interface and its super interfaces.
    144      *
    145      * @param interfaceClassInfo
    146      * @return list of methods declared in given class interfaces
    147      */
    148     public static List collectMethodsFromInterface(final ClassInfo interfaceClassInfo) {
    149         final List interfaceDeclaredMethods = new ArrayList();
    150         final List sortedMethodList = createMethodList(interfaceClassInfo);
    151         for (Iterator it = sortedMethodList.iterator(); it.hasNext();) {
    152             MethodInfo methodInfo = (MethodInfo) it.next();
    153             if (methodInfo.getDeclaringType().getName().equals(OBJECT_CLASS_NAME)) {
    154                 continue;
    155             }
    156             interfaceDeclaredMethods.add(methodInfo);
    157         }
    158         // grab methods from all super classes' interfaces
    159         ClassInfo superClass = interfaceClassInfo.getSuperclass();
    160         if (superClass != null && !superClass.getName().equals(OBJECT_CLASS_NAME)) {
    161             interfaceDeclaredMethods.addAll(collectMethodsFromInterfacesImplementedBy(superClass));
    162         }
    163         return interfaceDeclaredMethods;
    164     }
    165
    166     /***
    167      * Collects the methods from all the interfaces of the class and its super interfaces.
    168      *
    169      * @param classInfo
    170      * @return list of methods declared in given class interfaces
    171      */
    172     public static List collectMethodsFromInterfacesImplementedBy(final ClassInfo classInfo) {
    173         final List interfaceDeclaredMethods = new ArrayList();
    174         ClassInfo[] interfaces = classInfo.getInterfaces();
    175
    176         // grab methods from all interfaces and their super interfaces
    177         for (int i = 0; i < interfaces.length; i++) {
    178             final List sortedMethodList = createMethodList(interfaces[i]);
    179             for (Iterator it = sortedMethodList.iterator(); it.hasNext();) {
    180                 MethodInfo methodInfo = (MethodInfo) it.next();
    181                 if (methodInfo.getDeclaringType().getName().equals(OBJECT_CLASS_NAME)) {
    182                     continue;
    183                 }
    184                 interfaceDeclaredMethods.add(methodInfo);
    185             }
    186         }
    187         // grab methods from all super classes' interfaces
    188         ClassInfo superClass = classInfo.getSuperclass();
    189         if (superClass != null && !superClass.getName().equals(OBJECT_CLASS_NAME)) {
    190             interfaceDeclaredMethods.addAll(collectMethodsFromInterfacesImplementedBy(superClass));
    191         }
    192         return interfaceDeclaredMethods;
    193     }
    194
    195     /***
    196      * Creates a method list of all the methods in the class and super classes, if and only
    197      * if those are part of the given list of interfaces declared methods.
    198      *
    199      * @param klass                    the class with the methods
    200      * @param interfaceDeclaredMethods the list of interface declared methods
    201      * @return the sorted method list
    202      */
    203     public static List createInterfaceDefinedMethodList(final ClassInfo klass,
    204                                                               final List interfaceDeclaredMethods) {
    205         if (klass == null) {
    206             throw new IllegalArgumentException("class to sort method on can not be null");
    207         }
    208         // get all methods including the inherited methods
    209         List methodList = new ArrayList();
    210         for (Iterator iterator = createMethodList(klass).iterator(); iterator.hasNext();) {
    211             MethodInfo methodInfo = (MethodInfo) iterator.next();
    212             if (isDeclaredByInterface(methodInfo, interfaceDeclaredMethods)) {
    213                 methodList.add(methodInfo);
    214             }
    215         }
    216         return methodList;
    217     }
    218
    219     /***
    220      * Returns true if the method is not of on java.lang.Object and is not an AW generated one
    221      *
    222      * @param method
    223      * @return
    224      */
    225     private static boolean isUserDefinedMethod(final MethodInfo method) {
    226         if (!method.getName().startsWith(TransformationConstants.SYNTHETIC_MEMBER_PREFIX)
    227             && !method.getName().startsWith(TransformationConstants.ORIGINAL_METHOD_PREFIX)
    228             && !method.getName().startsWith(TransformationConstants.ASPECTWERKZ_PREFIX)) {
    229             return true;
    230         } else {
    231             return false;
    232         }
    233     }
    234
    235     /***
    236      * Returns true if the method is declared by one of the given method declared in an interface class
    237      *
    238      * @param method
    239      * @param interfaceDeclaredMethods
    240      * @return
    241      */
    242     private static boolean isDeclaredByInterface(final MethodInfo method, final List interfaceDeclaredMethods) {
    243         boolean match = false;
    244         for (Iterator iterator = interfaceDeclaredMethods.iterator(); iterator.hasNext();) {
    245             MethodInfo methodIt = (MethodInfo) iterator.next();
    246             if (method.getName().equals(methodIt.getName())) {
    247                 // TODO - using param type NAME should be enough - optimize
    248                 if (method.getParameterTypes().length == methodIt.getParameterTypes().length) {
    249                     boolean matchArgs = true;
    250                     for (int i = 0; i < method.getParameterTypes().length; i++) {
    251                         ClassInfo parameterType = method.getParameterTypes()[i];
    252                         if (parameterType.getName().equals(methodIt.getParameterTypes()[i].getName())) {
    253                             ;
    254                         } else {
    255                             matchArgs = false;
    256                             break;
    257                         }
    258                     }
    259                     if (matchArgs) {
    260                         match = true;
    261                         break;
    262                     }
    263                 }
    264             }
    265         }
    266         return match;
    267     }
    268
    269     /***
    270      * Collects all the interface from the given class including the one from its super class.
    271      *
    272      * @param classInfo
    273      * @return list of interface classInfo declared in given class and its hierarchy in correct order
    274      */
    275     public static List collectInterfaces(final ClassInfo classInfo) {
    276         final List interfaceList = new ArrayList();
    277         final Set interfaceNames = new HashSet();
    278         for (int i = 0; i < classInfo.getInterfaces().length; i++) {
    279             ClassInfo interfaceInfo = classInfo.getInterfaces()[i];
    280             interfaceList.add(interfaceInfo);
    281             interfaceNames.add(interfaceInfo.getName());
    282         }
    283         for (ClassInfo superClass = classInfo.getSuperclass(); superClass != null; superClass =
    284                                                                                    superClass.getSuperclass()) {
    285             for (int i = 0; i < superClass.getInterfaces().length; i++) {
    286                 ClassInfo interfaceInfo = superClass.getInterfaces()[i];
    287                 if (!interfaceNames.contains(interfaceInfo.getName())) {
    288                     interfaceList.add(interfaceInfo);
    289                     interfaceNames.add(interfaceInfo.getName());
    290                 }
    291             }
    292         }
    293         return interfaceList;
    294     }
    295
    296     /***
    297      * Checks if a set of interfaces has any clashes, meaning any methods with the same name and signature.
    298      *
    299      * @param interfacesToAdd
    300      * @param loader
    301      * @return boolean
    302      */
    303     public static boolean hasMethodClash(final Set interfacesToAdd, final ClassLoader loader) {
    304         // build up the validation structure
    305         Map methodMap = new HashMap();
    306         for (Iterator it = interfacesToAdd.iterator(); it.hasNext();) {
    307             ClassInfo classInfo = AsmClassInfo.getClassInfo((String) it.next(), loader);
    308
    309             List methods = collectMethodsFromInterface(classInfo);
    310
    311             for (Iterator it2 = methods.iterator(); it2.hasNext();) {
    312                 MethodInfo methodInfo = (MethodInfo) it2.next();
    313                 String key = methodInfo.getName() + ':' + methodInfo.getSignature();
    314                 if (!methodMap.containsKey(key)) {
    315                     methodMap.put(key, new ArrayList());
    316                 }
    317                 ((List) methodMap.get(key)).add(classInfo.getName());
    318             }
    319         }
    320
    321         // validate the structure
    322         for (Iterator it = methodMap.entrySet().iterator(); it.hasNext();) {
    323             Map.Entry entry = (Map.Entry) it.next();
    324             String key = (String) entry.getKey();
    325             List interfaceNames = (List) entry.getValue();
    326             if (interfaceNames.size() > 1) {
    327                 StringBuffer msg = new StringBuffer();
    328                 msg.append("can not add interfaces [");
    329                 for (Iterator it2 = interfaceNames.iterator(); it2.hasNext();) {
    330                     String interfaceName = (String) it2.next();
    331                     msg.append(interfaceName);
    332                     if (it2.hasNext()) {
    333                         msg.append(',');
    334                     }
    335                 }
    336                 msg.append("] since they all have method [");
    337                 msg.append(key);
    338                 msg.append(']');
    339                 System.out.println("AW::WARNING - " + msg.toString());
    340                 return true;
    341             }
    342         }
    343         return false;
    344     }
    345 }