001: /**************************************************************************************
002: * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved. *
003: * http://aspectwerkz.codehaus.org *
004: * ---------------------------------------------------------------------------------- *
005: * The software in this package is published under the terms of the LGPL license *
006: * a copy of which has been included with this distribution in the license.txt file. *
007: **************************************************************************************/package org.codehaus.aspectwerkz.reflect;
008:
009: import java.util.List;
010: import java.util.ArrayList;
011: import java.util.Collections;
012: import java.util.Iterator;
013: import java.util.Set;
014: import java.util.HashSet;
015: import java.util.Map;
016: import java.util.HashMap;
017:
018: import org.codehaus.aspectwerkz.transform.TransformationConstants;
019: import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
020:
021: /**
022: * Utility method for manipulating and managing ClassInfo hierarchies.
023: *
024: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
025: * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
026: */
027: public class ClassInfoHelper {
028: private static final ArrayList EMPTY_ARRAY_LIST = new ArrayList();
029: private static final String OBJECT_CLASS_NAME = "java.lang.Object";
030:
031: /**
032: * Checks if a class has a certain class as super class or interface, somewhere up in the class hierarchy.
033: *
034: * @param classInfo the meta-data for the class to parse
035: * @param superclassName the name of the super class or interface
036: * @return true if we have a parse else false
037: */
038: public static boolean instanceOf(final ClassInfo classInfo,
039: final String super className) {
040: return implements Interface(classInfo, super className)
041: || extendsSuperClass(classInfo, super className);
042: }
043:
044: /**
045: * Checks if a class implements a certain inteface, somewhere up in the class hierarchy, excluding
046: * itself.
047: *
048: * @param classInfo
049: * @param interfaceName
050: * @return true if we have a parse else false
051: */
052: public static boolean implements Interface(
053: final ClassInfo classInfo, final String interfaceName) {
054: if ((classInfo == null) || (interfaceName == null)) {
055: return false;
056: } else {
057: //TODO: we could lookup in names onlny FIRST to not trigger lazy getInterfaces() stuff
058: ClassInfo[] interfaces = classInfo.getInterfaces();
059: for (int i = 0; i < interfaces.length; i++) {
060: ClassInfo anInterface = interfaces[i];
061: if (interfaceName.equals(anInterface.getName())) {
062: return true;
063: } else if (ClassInfoHelper.implements Interface(
064: anInterface, interfaceName)) {
065: return true;
066: }
067: }
068: return ClassInfoHelper.implements Interface(classInfo
069: .getSuperclass(), interfaceName);
070: }
071: }
072:
073: /**
074: * Checks if a class has a certain class as super class, somewhere up in the class hierarchy.
075: *
076: * @param classInfo the meta-data for the class to parse
077: * @param className the name of the super class
078: * @return true if we have a parse else false
079: */
080: public static boolean extendsSuperClass(final ClassInfo classInfo,
081: final String className) {
082: if ((classInfo == null) || (className == null)) {
083: return false;
084: // TODO odd comparison
085: // } else if (classInfo.getName().equals(null)) {
086: // return true;
087: } else if (className.equals(classInfo.getName())) {
088: return true;
089: } else {
090: return ClassInfoHelper.extendsSuperClass(classInfo
091: .getSuperclass(), className);
092: }
093: }
094:
095: /**
096: * Creates a method list of all the methods in the class and super classes, including package private ones.
097: * Inherited methods are last in the list.
098: *
099: * @param klass the class with the methods
100: * @return the sorted method list
101: */
102: public static List createMethodList(final ClassInfo klass) {
103: if (klass == null) {
104: return EMPTY_ARRAY_LIST;
105: }
106:
107: // get this klass methods
108: List methods = new ArrayList();
109: MethodInfo[] methodInfos = klass.getMethods();
110: for (int i = 0; i < methodInfos.length; i++) {
111: MethodInfo methodInfo = methodInfos[i];
112: if (isUserDefinedMethod(methodInfo)) {
113: methods.add(methodInfo);
114: }
115: }
116:
117: // get all the inherited methods, as long as they are user defined ones
118:
119: ClassInfo super Class = klass.getSuperclass();
120: if (!super Class.getName().equals(OBJECT_CLASS_NAME)) {
121: List parentMethods = createMethodList(super Class);
122: // merge the method list (parent discovered methods are not added if overrided in this klass)
123: for (Iterator iterator = parentMethods.iterator(); iterator
124: .hasNext();) {
125: MethodInfo parentMethod = (MethodInfo) iterator.next();
126: if (!methods.contains(parentMethod)) { // TODO seems to work but ? since tied to declaringTypeName
127: methods.add(parentMethod);
128: }
129: }
130: }
131: return methods;
132: }
133:
134: // /**
135: // * Creates a sorted method list of all the methods in the class and super classes, including package private ones.
136: // *
137: // * @param klass the class with the methods
138: // * @return the sorted method list
139: // */
140: // public static List createSortedMethodList(final ClassInfo klass) {
141: // final List methods = createMethodList(klass);
142: //
143: // // Note: sorting is only use to maintain mixin consistency
144: // //Alex2Jonas - why sort should be needed ??? Collections.sort(methods, MethodComparator.getInstance(MethodComparator.METHOD_INFO));
145: //
146: // return methods;
147: // }
148:
149: /**
150: * Collects the methods from all the interface and its super interfaces.
151: *
152: * @param interfaceClassInfo
153: * @return list of methods declared in given class interfaces
154: */
155: public static List collectMethodsFromInterface(
156: final ClassInfo interfaceClassInfo) {
157: final List interfaceDeclaredMethods = new ArrayList();
158: final List sortedMethodList = createMethodList(interfaceClassInfo);
159: for (Iterator it = sortedMethodList.iterator(); it.hasNext();) {
160: MethodInfo methodInfo = (MethodInfo) it.next();
161: if (methodInfo.getDeclaringType().getName().equals(
162: OBJECT_CLASS_NAME)) {
163: continue;
164: }
165: interfaceDeclaredMethods.add(methodInfo);
166: }
167: // grab methods from all super classes' interfaces
168: ClassInfo super Class = interfaceClassInfo.getSuperclass();
169: if (super Class != null
170: && !super Class.getName().equals(OBJECT_CLASS_NAME)) {
171: interfaceDeclaredMethods
172: .addAll(collectMethodsFromInterfacesImplementedBy(super Class));
173: }
174: return interfaceDeclaredMethods;
175: }
176:
177: /**
178: * Collects the methods from all the interfaces of the class and its super interfaces.
179: *
180: * @param classInfo
181: * @return list of methods declared in given class interfaces
182: */
183: public static List collectMethodsFromInterfacesImplementedBy(
184: final ClassInfo classInfo) {
185: final List interfaceDeclaredMethods = new ArrayList();
186: ClassInfo[] interfaces = classInfo.getInterfaces();
187:
188: // grab methods from all interfaces and their super interfaces
189: for (int i = 0; i < interfaces.length; i++) {
190: final List sortedMethodList = createMethodList(interfaces[i]);
191: for (Iterator it = sortedMethodList.iterator(); it
192: .hasNext();) {
193: MethodInfo methodInfo = (MethodInfo) it.next();
194: if (methodInfo.getDeclaringType().getName().equals(
195: OBJECT_CLASS_NAME)) {
196: continue;
197: }
198: interfaceDeclaredMethods.add(methodInfo);
199: }
200: }
201: // grab methods from all super classes' interfaces
202: ClassInfo super Class = classInfo.getSuperclass();
203: if (super Class != null
204: && !super Class.getName().equals(OBJECT_CLASS_NAME)) {
205: interfaceDeclaredMethods
206: .addAll(collectMethodsFromInterfacesImplementedBy(super Class));
207: }
208: return interfaceDeclaredMethods;
209: }
210:
211: /**
212: * Creates a method list of all the methods in the class and super classes, if and only
213: * if those are part of the given list of interfaces declared methods.
214: *
215: * @param klass the class with the methods
216: * @param interfaceDeclaredMethods the list of interface declared methods
217: * @return the sorted method list
218: */
219: public static List createInterfaceDefinedMethodList(
220: final ClassInfo klass, final List interfaceDeclaredMethods) {
221: if (klass == null) {
222: throw new IllegalArgumentException(
223: "class to sort method on can not be null");
224: }
225: // get all methods including the inherited methods
226: List methodList = new ArrayList();
227: for (Iterator iterator = createMethodList(klass).iterator(); iterator
228: .hasNext();) {
229: MethodInfo methodInfo = (MethodInfo) iterator.next();
230: if (isDeclaredByInterface(methodInfo,
231: interfaceDeclaredMethods)) {
232: methodList.add(methodInfo);
233: }
234: }
235: return methodList;
236: }
237:
238: /**
239: * Returns true if the method is not of on java.lang.Object and is not an AW generated one
240: *
241: * @param method
242: * @return
243: */
244: private static boolean isUserDefinedMethod(final MethodInfo method) {
245: if (!method.getName().startsWith(
246: TransformationConstants.SYNTHETIC_MEMBER_PREFIX)
247: && !method.getName().startsWith(
248: TransformationConstants.ORIGINAL_METHOD_PREFIX)
249: && !method.getName().startsWith(
250: TransformationConstants.ASPECTWERKZ_PREFIX)) {
251: return true;
252: } else {
253: return false;
254: }
255: }
256:
257: /**
258: * Returns true if the method is declared by one of the given method declared in an interface class
259: *
260: * @param method
261: * @param interfaceDeclaredMethods
262: * @return
263: */
264: private static boolean isDeclaredByInterface(
265: final MethodInfo method, final List interfaceDeclaredMethods) {
266: boolean match = false;
267: for (Iterator iterator = interfaceDeclaredMethods.iterator(); iterator
268: .hasNext();) {
269: MethodInfo methodIt = (MethodInfo) iterator.next();
270: if (method.getName().equals(methodIt.getName())) {
271: // TODO - using param type NAME should be enough - optimize
272: if (method.getParameterTypes().length == methodIt
273: .getParameterTypes().length) {
274: boolean matchArgs = true;
275: for (int i = 0; i < method.getParameterTypes().length; i++) {
276: ClassInfo parameterType = method
277: .getParameterTypes()[i];
278: if (parameterType.getName().equals(
279: methodIt.getParameterTypes()[i]
280: .getName())) {
281: ;
282: } else {
283: matchArgs = false;
284: break;
285: }
286: }
287: if (matchArgs) {
288: match = true;
289: break;
290: }
291: }
292: }
293: }
294: return match;
295: }
296:
297: /**
298: * Collects all the interface from the given class including the one from its super class.
299: *
300: * @param classInfo
301: * @return list of interface classInfo declared in given class and its hierarchy in correct order
302: */
303: public static List collectInterfaces(final ClassInfo classInfo) {
304: final List interfaceList = new ArrayList();
305: final Set interfaceNames = new HashSet();
306: for (int i = 0; i < classInfo.getInterfaces().length; i++) {
307: ClassInfo interfaceInfo = classInfo.getInterfaces()[i];
308: interfaceList.add(interfaceInfo);
309: interfaceNames.add(interfaceInfo.getName());
310: }
311: for (ClassInfo super Class = classInfo.getSuperclass(); super Class != null; super Class = super Class
312: .getSuperclass()) {
313: for (int i = 0; i < super Class.getInterfaces().length; i++) {
314: ClassInfo interfaceInfo = super Class.getInterfaces()[i];
315: if (!interfaceNames.contains(interfaceInfo.getName())) {
316: interfaceList.add(interfaceInfo);
317: interfaceNames.add(interfaceInfo.getName());
318: }
319: }
320: }
321: return interfaceList;
322: }
323:
324: /**
325: * Checks if a set of interfaces has any clashes, meaning any methods with the same name and signature.
326: *
327: * @param interfacesToAdd
328: * @param loader
329: * @return boolean
330: */
331: public static boolean hasMethodClash(final Set interfacesToAdd,
332: final ClassLoader loader) {
333: // build up the validation structure
334: Map methodMap = new HashMap();
335: for (Iterator it = interfacesToAdd.iterator(); it.hasNext();) {
336: ClassInfo classInfo = AsmClassInfo.getClassInfo((String) it
337: .next(), loader);
338:
339: List methods = collectMethodsFromInterface(classInfo);
340:
341: for (Iterator it2 = methods.iterator(); it2.hasNext();) {
342: MethodInfo methodInfo = (MethodInfo) it2.next();
343: String key = methodInfo.getName() + ':'
344: + methodInfo.getSignature();
345: if (!methodMap.containsKey(key)) {
346: methodMap.put(key, new ArrayList());
347: }
348: ((List) methodMap.get(key)).add(classInfo.getName());
349: }
350: }
351:
352: // validate the structure
353: for (Iterator it = methodMap.entrySet().iterator(); it
354: .hasNext();) {
355: Map.Entry entry = (Map.Entry) it.next();
356: String key = (String) entry.getKey();
357: List interfaceNames = (List) entry.getValue();
358: if (interfaceNames.size() > 1) {
359: StringBuffer msg = new StringBuffer();
360: msg.append("can not add interfaces [");
361: for (Iterator it2 = interfaceNames.iterator(); it2
362: .hasNext();) {
363: String interfaceName = (String) it2.next();
364: msg.append(interfaceName);
365: if (it2.hasNext()) {
366: msg.append(',');
367: }
368: }
369: msg.append("] since they all have method [");
370: msg.append(key);
371: msg.append(']');
372: System.out.println("AW::WARNING - " + msg.toString());
373: return true;
374: }
375: }
376: return false;
377: }
378: }
|