/**************************************************************************************
* Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved. *
* http://aspectwerkz.codehaus.org *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the LGPL license *
* a copy of which has been included with this distribution in the license.txt file. *
**************************************************************************************/
package org.codehaus.aspectwerkz.reflect;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;
import java.util.HashSet;
import java.util.Map;
import java.util.HashMap;
import org.codehaus.aspectwerkz.transform.TransformationConstants;
import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
/**
* Utility method for manipulating and managing ClassInfo hierarchies.
*
* @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a>
* @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
*/
public class ClassInfoHelper {
private static final ArrayList EMPTY_ARRAY_LIST = new ArrayList();
private static final String OBJECT_CLASS_NAME = "java.lang.Object";
/**
* Checks if a class has a certain class as super class or interface, somewhere up in the class hierarchy.
*
* @param classInfo the meta-data for the class to parse
* @param superclassName the name of the super class or interface
* @return true if we have a parse else false
*/
public static boolean instanceOf(final ClassInfo classInfo, final String superclassName) {
return implementsInterface(classInfo, superclassName) || extendsSuperClass(classInfo, superclassName);
}
/**
* Checks if a class implements a certain inteface, somewhere up in the class hierarchy, excluding
* itself.
*
* @param classInfo
* @param interfaceName
* @return true if we have a parse else false
*/
public static boolean implementsInterface(final ClassInfo classInfo, final String interfaceName) {
if ((classInfo == null) || (interfaceName == null)) {
return false;
} else {
//TODO: we could lookup in names onlny FIRST to not trigger lazy getInterfaces() stuff
ClassInfo[] interfaces = classInfo.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
ClassInfo anInterface = interfaces[i];
if (interfaceName.equals(anInterface.getName())) {
return true;
} else if (ClassInfoHelper.implementsInterface(anInterface, interfaceName)) {
return true;
}
}
return ClassInfoHelper.implementsInterface(classInfo.getSuperclass(), interfaceName);
}
}
/**
* Checks if a class has a certain class as super class, somewhere up in the class hierarchy.
*
* @param classInfo the meta-data for the class to parse
* @param className the name of the super class
* @return true if we have a parse else false
*/
public static boolean extendsSuperClass(final ClassInfo classInfo, final String className) {
if ((classInfo == null) || (className == null)) {
return false;
// TODO odd comparison
// } else if (classInfo.getName().equals(null)) {
// return true;
} else if (className.equals(classInfo.getName())) {
return true;
} else {
return ClassInfoHelper.extendsSuperClass(classInfo.getSuperclass(), className);
}
}
/**
* Creates a method list of all the methods in the class and super classes, including package private ones.
* Inherited methods are last in the list.
*
* @param klass the class with the methods
* @return the sorted method list
*/
public static List createMethodList(final ClassInfo klass) {
if (klass == null) {
return EMPTY_ARRAY_LIST;
}
// get this klass methods
List methods = new ArrayList();
MethodInfo[] methodInfos = klass.getMethods();
for (int i = 0; i < methodInfos.length; i++) {
MethodInfo methodInfo = methodInfos[i];
if (isUserDefinedMethod(methodInfo)) {
methods.add(methodInfo);
}
}
// get all the inherited methods, as long as they are user defined ones
ClassInfo superClass = klass.getSuperclass();
if (!superClass.getName().equals(OBJECT_CLASS_NAME)) {
List parentMethods = createMethodList(superClass);
// merge the method list (parent discovered methods are not added if overrided in this klass)
for (Iterator iterator = parentMethods.iterator(); iterator.hasNext();) {
MethodInfo parentMethod = (MethodInfo) iterator.next();
if (!methods.contains(parentMethod)) { // TODO seems to work but ? since tied to declaringTypeName
methods.add(parentMethod);
}
}
}
return methods;
}
// /**
// * Creates a sorted method list of all the methods in the class and super classes, including package private ones.
// *
// * @param klass the class with the methods
// * @return the sorted method list
// */
// public static List createSortedMethodList(final ClassInfo klass) {
// final List methods = createMethodList(klass);
//
// // Note: sorting is only use to maintain mixin consistency
// //Alex2Jonas - why sort should be needed ??? Collections.sort(methods, MethodComparator.getInstance(MethodComparator.METHOD_INFO));
//
// return methods;
// }
/**
* Collects the methods from all the interface and its super interfaces.
*
* @param interfaceClassInfo
* @return list of methods declared in given class interfaces
*/
public static List collectMethodsFromInterface(final ClassInfo interfaceClassInfo) {
final List interfaceDeclaredMethods = new ArrayList();
final List sortedMethodList = createMethodList(interfaceClassInfo);
for (Iterator it = sortedMethodList.iterator(); it.hasNext();) {
MethodInfo methodInfo = (MethodInfo) it.next();
if (methodInfo.getDeclaringType().getName().equals(OBJECT_CLASS_NAME)) {
continue;
}
interfaceDeclaredMethods.add(methodInfo);
}
// grab methods from all super classes' interfaces
ClassInfo superClass = interfaceClassInfo.getSuperclass();
if (superClass != null && !superClass.getName().equals(OBJECT_CLASS_NAME)) {
interfaceDeclaredMethods.addAll(collectMethodsFromInterfacesImplementedBy(superClass));
}
return interfaceDeclaredMethods;
}
/**
* Collects the methods from all the interfaces of the class and its super interfaces.
*
* @param classInfo
* @return list of methods declared in given class interfaces
*/
public static List collectMethodsFromInterfacesImplementedBy(final ClassInfo classInfo) {
final List interfaceDeclaredMethods = new ArrayList();
ClassInfo[] interfaces = classInfo.getInterfaces();
// grab methods from all interfaces and their super interfaces
for (int i = 0; i < interfaces.length; i++) {
final List sortedMethodList = createMethodList(interfaces[i]);
for (Iterator it = sortedMethodList.iterator(); it.hasNext();) {
MethodInfo methodInfo = (MethodInfo) it.next();
if (methodInfo.getDeclaringType().getName().equals(OBJECT_CLASS_NAME)) {
continue;
}
interfaceDeclaredMethods.add(methodInfo);
}
}
// grab methods from all super classes' interfaces
ClassInfo superClass = classInfo.getSuperclass();
if (superClass != null && !superClass.getName().equals(OBJECT_CLASS_NAME)) {
interfaceDeclaredMethods.addAll(collectMethodsFromInterfacesImplementedBy(superClass));
}
return interfaceDeclaredMethods;
}
/**
* Creates a method list of all the methods in the class and super classes, if and only
* if those are part of the given list of interfaces declared methods.
*
* @param klass the class with the methods
* @param interfaceDeclaredMethods the list of interface declared methods
* @return the sorted method list
*/
public static List createInterfaceDefinedMethodList(final ClassInfo klass,
final List interfaceDeclaredMethods) {
if (klass == null) {
throw new IllegalArgumentException("class to sort method on can not be null");
}
// get all methods including the inherited methods
List methodList = new ArrayList();
for (Iterator iterator = createMethodList(klass).iterator(); iterator.hasNext();) {
MethodInfo methodInfo = (MethodInfo) iterator.next();
if (isDeclaredByInterface(methodInfo, interfaceDeclaredMethods)) {
methodList.add(methodInfo);
}
}
return methodList;
}
/**
* Returns true if the method is not of on java.lang.Object and is not an AW generated one
*
* @param method
* @return
*/
private static boolean isUserDefinedMethod(final MethodInfo method) {
if (!method.getName().startsWith(TransformationConstants.SYNTHETIC_MEMBER_PREFIX)
&& !method.getName().startsWith(TransformationConstants.ORIGINAL_METHOD_PREFIX)
&& !method.getName().startsWith(TransformationConstants.ASPECTWERKZ_PREFIX)) {
return true;
} else {
return false;
}
}
/**
* Returns true if the method is declared by one of the given method declared in an interface class
*
* @param method
* @param interfaceDeclaredMethods
* @return
*/
private static boolean isDeclaredByInterface(final MethodInfo method, final List interfaceDeclaredMethods) {
boolean match = false;
for (Iterator iterator = interfaceDeclaredMethods.iterator(); iterator.hasNext();) {
MethodInfo methodIt = (MethodInfo) iterator.next();
if (method.getName().equals(methodIt.getName())) {
// TODO - using param type NAME should be enough - optimize
if (method.getParameterTypes().length == methodIt.getParameterTypes().length) {
boolean matchArgs = true;
for (int i = 0; i < method.getParameterTypes().length; i++) {
ClassInfo parameterType = method.getParameterTypes()[i];
if (parameterType.getName().equals(methodIt.getParameterTypes()[i].getName())) {
;
} else {
matchArgs = false;
break;
}
}
if (matchArgs) {
match = true;
break;
}
}
}
}
return match;
}
/**
* Collects all the interface from the given class including the one from its super class.
*
* @param classInfo
* @return list of interface classInfo declared in given class and its hierarchy in correct order
*/
public static List collectInterfaces(final ClassInfo classInfo) {
final List interfaceList = new ArrayList();
final Set interfaceNames = new HashSet();
for (int i = 0; i < classInfo.getInterfaces().length; i++) {
ClassInfo interfaceInfo = classInfo.getInterfaces()[i];
interfaceList.add(interfaceInfo);
interfaceNames.add(interfaceInfo.getName());
}
for (ClassInfo superClass = classInfo.getSuperclass(); superClass != null; superClass =
superClass.getSuperclass()) {
for (int i = 0; i < superClass.getInterfaces().length; i++) {
ClassInfo interfaceInfo = superClass.getInterfaces()[i];
if (!interfaceNames.contains(interfaceInfo.getName())) {
interfaceList.add(interfaceInfo);
interfaceNames.add(interfaceInfo.getName());
}
}
}
return interfaceList;
}
/**
* Checks if a set of interfaces has any clashes, meaning any methods with the same name and signature.
*
* @param interfacesToAdd
* @param loader
* @return boolean
*/
public static boolean hasMethodClash(final Set interfacesToAdd, final ClassLoader loader) {
// build up the validation structure
Map methodMap = new HashMap();
for (Iterator it = interfacesToAdd.iterator(); it.hasNext();) {
ClassInfo classInfo = AsmClassInfo.getClassInfo((String) it.next(), loader);
List methods = collectMethodsFromInterface(classInfo);
for (Iterator it2 = methods.iterator(); it2.hasNext();) {
MethodInfo methodInfo = (MethodInfo) it2.next();
String key = methodInfo.getName() + ':' + methodInfo.getSignature();
if (!methodMap.containsKey(key)) {
methodMap.put(key, new ArrayList());
}
((List) methodMap.get(key)).add(classInfo.getName());
}
}
// validate the structure
for (Iterator it = methodMap.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
String key = (String) entry.getKey();
List interfaceNames = (List) entry.getValue();
if (interfaceNames.size() > 1) {
StringBuffer msg = new StringBuffer();
msg.append("can not add interfaces [");
for (Iterator it2 = interfaceNames.iterator(); it2.hasNext();) {
String interfaceName = (String) it2.next();
msg.append(interfaceName);
if (it2.hasNext()) {
msg.append(',');
}
}
msg.append("] since they all have method [");
msg.append(key);
msg.append(']');
System.out.println("AW::WARNING - " + msg.toString());
return true;
}
}
return false;
}
}