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 }