/************************************************************************************** * 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.impl.java; import gnu.trove.TIntObjectHashMap; import org.codehaus.aspectwerkz.reflect.ClassInfo; import org.codehaus.aspectwerkz.reflect.ConstructorInfo; import org.codehaus.aspectwerkz.reflect.FieldInfo; import org.codehaus.aspectwerkz.reflect.MethodInfo; import org.codehaus.aspectwerkz.reflect.ReflectHelper; import org.codehaus.aspectwerkz.reflect.StaticInitializationInfo; import org.codehaus.aspectwerkz.reflect.StaticInitializationInfoImpl; import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo; import org.codehaus.aspectwerkz.transform.TransformationConstants; import org.codehaus.backport175.reader.bytecode.AnnotationElement; import org.codehaus.backport175.reader.bytecode.AnnotationReader; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * Implementation of the ClassInfo interface for java.lang.reflect.*. * * @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a> */ public class JavaClassInfo implements ClassInfo { /** * The class. */ // TODO might be safer to wrap this member in a weak ref private final Class m_class; /** * The name of the class. */ private String m_name; /** * The signature of the class. */ private String m_signature; /** * Is the class an interface. */ private boolean m_isInterface = false; /** * Is the class a primitive type. */ private boolean m_isPrimitive = false; /** * Is the class of type array. */ private boolean m_isArray = false; /** * A list with the <code>ConstructorInfo</code> instances. */ private final TIntObjectHashMap m_constructors = new TIntObjectHashMap(); /** * A list with the <code>MethodInfo</code> instances. */ private final TIntObjectHashMap m_methods = new TIntObjectHashMap(); /** * A list with the <code>FieldInfo</code> instances. */ private final TIntObjectHashMap m_fields = new TIntObjectHashMap(); /** * A list with the interfaces. */ private ClassInfo[] m_interfaces = null; /** * The super class. */ private ClassInfo m_superClass = null; /** * The component type if array type. */ private ClassInfo m_componentType = null; /** * The class info repository. */ private final JavaClassInfoRepository m_classInfoRepository; /** * Lazy, the static initializer info or null if not present */ private StaticInitializationInfo m_staticInitializer = null; /** * Creates a new class meta data instance. * * @param klass */ JavaClassInfo(final Class klass) { if (klass == null) { throw new IllegalArgumentException("class can not be null"); } m_class = klass; m_signature = ReflectHelper.getClassSignature(klass); m_classInfoRepository = JavaClassInfoRepository.getRepository(klass.getClassLoader()); m_isInterface = klass.isInterface(); if (klass.isPrimitive()) { m_name = klass.getName(); m_isPrimitive = true; } else if (klass.getComponentType() != null) { m_name = convertJavaArrayTypeNameToHumanTypeName(klass.getName()); m_isArray = true; m_interfaces = new ClassInfo[0]; } else { m_name = klass.getName(); Method[] methods = m_class.getDeclaredMethods(); for (int i = 0; i < methods.length; i++) { Method method = methods[i]; m_methods.put(ReflectHelper.calculateHash(method), new JavaMethodInfo(method, this)); } Constructor[] constructors = m_class.getDeclaredConstructors(); for (int i = 0; i < constructors.length; i++) { Constructor constructor = constructors[i]; m_constructors.put( ReflectHelper.calculateHash(constructor), new JavaConstructorInfo( constructor, this ) ); } Field[] fields = m_class.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { if (fields[i].getName().startsWith(TransformationConstants.ASPECTWERKZ_PREFIX)) { continue; } Field field = fields[i]; m_fields.put(ReflectHelper.calculateHash(field), new JavaFieldInfo(field, this)); } } m_classInfoRepository.addClassInfo(this); } /** * Returns the class info for a specific class. * * @return the class info */ public static ClassInfo getClassInfo(final Class clazz) { JavaClassInfoRepository repository = JavaClassInfoRepository.getRepository(clazz.getClassLoader()); ClassInfo classInfo = repository.getClassInfo(clazz.getName()); if (classInfo == null) { classInfo = new JavaClassInfo(clazz); } return classInfo; } /** * Returns the annotations. * * @return the annotations */ public AnnotationElement.Annotation[] getAnnotations() { return getAnnotationReader().getAnnotationElements(); } /** * Returns the name of the class. * * @return the name of the class */ public String getName() { return m_name.replace('/', '.'); } /** * Checks if the class has a static initalizer. * * @return */ public boolean hasStaticInitializer() { ClassInfo classInfo = AsmClassInfo.getClassInfo(getName(), getClassLoader()); return classInfo.hasStaticInitializer(); } /** * Returns the static initializer info of the current underlying class if any. * * @see org.codehaus.aspectwerkz.reflect.ClassInfo#staticInitializer() */ public StaticInitializationInfo staticInitializer() { if (hasStaticInitializer() && m_staticInitializer == null) { m_staticInitializer = new StaticInitializationInfoImpl(this); } return m_staticInitializer; } /** * Returns the signature for the element. * * @return the signature for the element */ public String getSignature() { return m_signature; } /** * Returns the class modifiers. * * @return the class modifiers */ public int getModifiers() { return m_class.getModifiers(); } /** * Returns the class loader that loaded this class. * * @return the class loader */ public ClassLoader getClassLoader() { return m_class.getClassLoader(); } /** * Returns a constructor info by its hash. * * @param hash * @return */ public ConstructorInfo getConstructor(final int hash) { ConstructorInfo constructor = (ConstructorInfo) m_constructors.get(hash); if (constructor == null && getSuperclass() != null) { constructor = getSuperclass().getConstructor(hash); } return constructor; } /** * Returns a list with all the constructors info. * * @return the constructors info */ public ConstructorInfo[] getConstructors() { Object[] values = m_constructors.getValues(); ConstructorInfo[] methodInfos = new ConstructorInfo[values.length]; for (int i = 0; i < values.length; i++) { methodInfos[i] = (ConstructorInfo) values[i]; } return methodInfos; } /** * Returns a method info by its hash. * * @param hash * @return */ public MethodInfo getMethod(final int hash) { MethodInfo method = (MethodInfo) m_methods.get(hash); if (method == null) { for (int i = 0; i < getInterfaces().length; i++) { method = getInterfaces()[i].getMethod(hash); if (method != null) { break; } } } if (method == null && getSuperclass() != null) { method = getSuperclass().getMethod(hash); } return method; } /** * Returns a list with all the methods info. * * @return the methods info */ public MethodInfo[] getMethods() { Object[] values = m_methods.getValues(); MethodInfo[] methodInfos = new MethodInfo[values.length]; for (int i = 0; i < values.length; i++) { methodInfos[i] = (MethodInfo) values[i]; } return methodInfos; } /** * Returns a field info by its hash. * * @param hash * @return */ public FieldInfo getField(final int hash) { FieldInfo field = (FieldInfo) m_fields.get(hash); if (field == null && getSuperclass() != null) { field = getSuperclass().getField(hash); } if (field == null) { // Trying to find constants in Interfaces ClassInfo[] interfaces = getInterfaces(); for (int i = 0; i < interfaces.length; i++) { ClassInfo ifc = interfaces[i]; field = ifc.getField(hash); if (field != null) break; } } return field; } /** * Returns a list with all the field info. * * @return the field info */ public FieldInfo[] getFields() { Object[] values = m_fields.getValues(); FieldInfo[] fieldInfos = new FieldInfo[values.length]; for (int i = 0; i < values.length; i++) { fieldInfos[i] = (FieldInfo) values[i]; } return fieldInfos; } /** * Returns the interfaces. * * @return the interfaces */ public ClassInfo[] getInterfaces() { if (m_interfaces == null) { Class[] interfaces = m_class.getInterfaces(); m_interfaces = new ClassInfo[interfaces.length]; for (int i = 0; i < interfaces.length; i++) { Class anInterface = interfaces[i]; ClassInfo classInfo = JavaClassInfo.getClassInfo(anInterface); m_interfaces[i] = classInfo; if (!m_classInfoRepository.hasClassInfo(anInterface.getName())) { m_classInfoRepository.addClassInfo(classInfo); } } } return m_interfaces; } /** * Returns the super class. * * @return the super class */ public ClassInfo getSuperclass() { if (m_superClass == null) { Class superclass = m_class.getSuperclass(); if (superclass != null) { if (m_classInfoRepository.hasClassInfo(superclass.getName())) { m_superClass = m_classInfoRepository.getClassInfo(superclass.getName()); } else { m_superClass = JavaClassInfo.getClassInfo(superclass); m_classInfoRepository.addClassInfo(m_superClass); } } } return m_superClass; } /** * Returns the component type if array type else null. * * @return the component type */ public ClassInfo getComponentType() { if (isArray() && (m_componentType == null)) { Class componentType = m_class.getComponentType(); if (m_classInfoRepository.hasClassInfo(componentType.getName())) { m_componentType = m_classInfoRepository.getClassInfo(componentType.getName()); } else { m_componentType = JavaClassInfo.getClassInfo(componentType); m_classInfoRepository.addClassInfo(m_componentType); } } return m_componentType; } /** * Is the class an interface. * * @return */ public boolean isInterface() { return m_isInterface; } /** * Is the class a primitive type. * * @return */ public boolean isPrimitive() { return m_isPrimitive; } /** * Is the class an array type. * * @return */ public boolean isArray() { return m_isArray; } /** * Converts an internal Java array type name ([Lblabla) to the a the format used by the expression matcher * (blabla[]) * * @param typeName is type name * @return */ public static String convertJavaArrayTypeNameToHumanTypeName(final String typeName) { int index = typeName.lastIndexOf('['); if (index != -1) { StringBuffer arrayType = new StringBuffer(); if (typeName.endsWith("I")) { arrayType.append("int"); } else if (typeName.endsWith("J")) { arrayType.append("long"); } else if (typeName.endsWith("S")) { arrayType.append("short"); } else if (typeName.endsWith("F")) { arrayType.append("float"); } else if (typeName.endsWith("D")) { arrayType.append("double"); } else if (typeName.endsWith("Z")) { arrayType.append("boolean"); } else if (typeName.endsWith("C")) { arrayType.append("char"); } else if (typeName.endsWith("B")) { arrayType.append("byte"); } else { arrayType.append(typeName.substring(index + 2, typeName.length() - 1)); } for (int i = 0; i < (index + 1); i++) { arrayType.append("[]"); } return arrayType.toString(); } else { return typeName; } } public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof ClassInfo)) { return false; } ClassInfo classInfo = (ClassInfo) o; return m_class.getName().toString().equals(classInfo.getName().toString()); } public int hashCode() { return m_class.getName().toString().hashCode(); } public String toString() { return getName(); } public AnnotationReader getAnnotationReader() { return AnnotationReader.getReaderFor(m_class); } }