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