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
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
80
81
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
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
112
113 ClassInfo superClass = klass.getSuperclass();
114 if (!superClass.getName().equals(OBJECT_CLASS_NAME)) {
115 List parentMethods = createMethodList(superClass);
116
117 for (Iterator iterator = parentMethods.iterator(); iterator.hasNext();) {
118 MethodInfo parentMethod = (MethodInfo) iterator.next();
119 if (!methods.contains(parentMethod)) {
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
134
135
136
137
138
139
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
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
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
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
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
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
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
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 }