org.aspectj.weaver.reflect.Java15AnnotationFinder Maven / Gradle / Ivy
/* *******************************************************************
* Copyright (c) 2005, 2017 Contributors.
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License v1.0
* which accompanies this distribution and is available at
* http://eclipse.org/legal/epl-v10.html
* ******************************************************************/
package org.aspectj.weaver.reflect;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import org.aspectj.apache.bcel.classfile.AnnotationDefault;
import org.aspectj.apache.bcel.classfile.Attribute;
import org.aspectj.apache.bcel.classfile.JavaClass;
import org.aspectj.apache.bcel.classfile.LocalVariable;
import org.aspectj.apache.bcel.classfile.LocalVariableTable;
import org.aspectj.apache.bcel.util.ClassLoaderRepository;
import org.aspectj.apache.bcel.util.NonCachingClassLoaderRepository;
import org.aspectj.apache.bcel.util.Repository;
import org.aspectj.weaver.AnnotationAJ;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.World;
import org.aspectj.weaver.bcel.BcelAnnotation;
import org.aspectj.weaver.bcel.BcelWeakClassLoaderReference;
/**
*
* @author Adrian Colyer
* @author Andy Clement
*/
public class Java15AnnotationFinder implements AnnotationFinder, ArgNameFinder {
public static final ResolvedType[][] NO_PARAMETER_ANNOTATIONS = new ResolvedType[][] {};
private Repository bcelRepository;
private BcelWeakClassLoaderReference classLoaderRef;
private World world;
private static boolean useCachingClassLoaderRepository;
static {
try {
useCachingClassLoaderRepository = System.getProperty("Xset:bcelRepositoryCaching","true").equalsIgnoreCase("true");
} catch (Throwable t) {
useCachingClassLoaderRepository = false;
}
}
// must have no-arg constructor for reflective construction
public Java15AnnotationFinder() {
}
public void setClassLoader(ClassLoader aLoader) {
this.classLoaderRef = new BcelWeakClassLoaderReference(aLoader);
if (useCachingClassLoaderRepository) {
this.bcelRepository = new ClassLoaderRepository(classLoaderRef);
} else {
this.bcelRepository = new NonCachingClassLoaderRepository(classLoaderRef);
}
}
public void setWorld(World aWorld) {
this.world = aWorld;
}
public Object getAnnotation(ResolvedType annotationType, Object onObject) {
try {
Class extends Annotation> annotationClass = (Class extends Annotation>) Class.forName(annotationType.getName(),
false, getClassLoader());
if (onObject.getClass().isAnnotationPresent(annotationClass)) {
return onObject.getClass().getAnnotation(annotationClass);
}
} catch (ClassNotFoundException ex) {
// just return null
}
return null;
}
public Object getAnnotationFromClass(ResolvedType annotationType, Class aClass) {
try {
Class extends Annotation> annotationClass = (Class extends Annotation>) Class.forName(annotationType.getName(),
false, getClassLoader());
if (aClass.isAnnotationPresent(annotationClass)) {
return aClass.getAnnotation(annotationClass);
}
} catch (ClassNotFoundException ex) {
// just return null
}
return null;
}
public Object getAnnotationFromMember(ResolvedType annotationType, Member aMember) {
if (!(aMember instanceof AccessibleObject))
return null;
AccessibleObject ao = (AccessibleObject) aMember;
try {
Class annotationClass = Class.forName(annotationType.getName(), false, getClassLoader());
if (ao.isAnnotationPresent(annotationClass)) {
return ao.getAnnotation(annotationClass);
}
} catch (ClassNotFoundException ex) {
// just return null
}
return null;
}
private ClassLoader getClassLoader() {
return classLoaderRef.getClassLoader();
}
public AnnotationAJ getAnnotationOfType(UnresolvedType ofType, Member onMember) {
if (!(onMember instanceof AccessibleObject))
return null;
// here we really want both the runtime visible AND the class visible
// annotations
// so we bail out to Bcel and then chuck away the JavaClass so that we
// don't hog
// memory.
try {
JavaClass jc = bcelRepository.loadClass(onMember.getDeclaringClass());
org.aspectj.apache.bcel.classfile.annotation.AnnotationGen[] anns = new org.aspectj.apache.bcel.classfile.annotation.AnnotationGen[0];
if (onMember instanceof Method) {
org.aspectj.apache.bcel.classfile.Method bcelMethod = jc.getMethod((Method) onMember);
if (bcelMethod == null) {
// pr220430
// System.err.println(
// "Unexpected problem in Java15AnnotationFinder: cannot retrieve annotations on method '"
// +
// onMember.getName()+"' in class '"+jc.getClassName()+"'");
} else {
anns = bcelMethod.getAnnotations();
}
} else if (onMember instanceof Constructor) {
org.aspectj.apache.bcel.classfile.Method bcelCons = jc.getMethod((Constructor) onMember);
anns = bcelCons.getAnnotations();
} else if (onMember instanceof Field) {
org.aspectj.apache.bcel.classfile.Field bcelField = jc.getField((Field) onMember);
anns = bcelField.getAnnotations();
}
// the answer is cached and we don't want to hold on to memory
bcelRepository.clear();
// OPTIMIZE make constant 0 size array for sharing
if (anns == null)
anns = new org.aspectj.apache.bcel.classfile.annotation.AnnotationGen[0];
// convert to our Annotation type
for (int i = 0; i < anns.length; i++) {
if (anns[i].getTypeSignature().equals(ofType.getSignature())) {
return new BcelAnnotation(anns[i], world);
}
}
return null;
} catch (ClassNotFoundException cnfEx) {
// just use reflection then
}
return null;
}
public String getAnnotationDefaultValue(Member onMember) {
try {
JavaClass jc = bcelRepository.loadClass(onMember.getDeclaringClass());
if (onMember instanceof Method) {
org.aspectj.apache.bcel.classfile.Method bcelMethod = jc.getMethod((Method) onMember);
if (bcelMethod == null) {
// pr220430
// System.err.println(
// "Unexpected problem in Java15AnnotationFinder: cannot retrieve annotations on method '"
// +
// onMember.getName()+"' in class '"+jc.getClassName()+"'");
} else {
Attribute[] attrs = bcelMethod.getAttributes();
for (int i = 0; i < attrs.length; i++) {
Attribute attribute = attrs[i];
if (attribute.getName().equals("AnnotationDefault")) {
AnnotationDefault def = (AnnotationDefault) attribute;
return def.getElementValue().stringifyValue();
}
}
return null;
}
}
} catch (ClassNotFoundException cnfEx) {
// just use reflection then
}
return null;
}
public ResolvedType[] getAnnotations(Member onMember, boolean areRuntimeAnnotationsSufficient) {
if (!(onMember instanceof AccessibleObject)) {
return ResolvedType.NONE;
}
// If annotations with class level retention are required then we need to open
// open the class file. If only runtime retention annotations are required
// we can just use reflection.
if (!areRuntimeAnnotationsSufficient) {
try {
JavaClass jc = bcelRepository.loadClass(onMember.getDeclaringClass());
org.aspectj.apache.bcel.classfile.annotation.AnnotationGen[] anns = null;
if (onMember instanceof Method) {
org.aspectj.apache.bcel.classfile.Method bcelMethod = jc.getMethod((Method) onMember);
if (bcelMethod != null) {
anns = bcelMethod.getAnnotations();
}
} else if (onMember instanceof Constructor) {
org.aspectj.apache.bcel.classfile.Method bcelCons = jc.getMethod((Constructor) onMember);
anns = bcelCons.getAnnotations();
} else if (onMember instanceof Field) {
org.aspectj.apache.bcel.classfile.Field bcelField = jc.getField((Field) onMember);
anns = bcelField.getAnnotations();
}
// the answer is cached and we don't want to hold on to memory
bcelRepository.clear();
if (anns == null || anns.length == 0) {
return ResolvedType.NONE;
}
ResolvedType[] annotationTypes = new ResolvedType[anns.length];
for (int i = 0; i < anns.length; i++) {
annotationTypes[i] = world.resolve(UnresolvedType.forSignature(anns[i].getTypeSignature()));
}
return annotationTypes;
} catch (ClassNotFoundException cnfEx) {
// just use reflection then
}
}
AccessibleObject ao = (AccessibleObject) onMember;
Annotation[] anns = ao.getDeclaredAnnotations();
if (anns.length == 0) {
return ResolvedType.NONE;
}
ResolvedType[] annotationTypes = new ResolvedType[anns.length];
for (int i = 0; i < anns.length; i++) {
annotationTypes[i] = UnresolvedType.forName(anns[i].annotationType().getName()).resolve(world);
}
return annotationTypes;
}
public ResolvedType[] getAnnotations(Class forClass, World inWorld) {
// here we really want both the runtime visible AND the class visible
// annotations so we bail out to Bcel and then chuck away the JavaClass so that we
// don't hog memory.
try {
JavaClass jc = bcelRepository.loadClass(forClass);
org.aspectj.apache.bcel.classfile.annotation.AnnotationGen[] anns = jc.getAnnotations();
bcelRepository.clear();
if (anns == null) {
return ResolvedType.NONE;
} else {
ResolvedType[] ret = new ResolvedType[anns.length];
for (int i = 0; i < ret.length; i++) {
ret[i] = inWorld.resolve(UnresolvedType.forSignature(anns[i].getTypeSignature()));
}
return ret;
}
} catch (ClassNotFoundException cnfEx) {
// just use reflection then
}
Annotation[] classAnnotations = forClass.getAnnotations();
ResolvedType[] ret = new ResolvedType[classAnnotations.length];
for (int i = 0; i < classAnnotations.length; i++) {
ret[i] = inWorld.resolve(classAnnotations[i].annotationType().getName());
}
return ret;
}
public String[] getParameterNames(Member forMember) {
if (!(forMember instanceof AccessibleObject))
return null;
try {
JavaClass jc = bcelRepository.loadClass(forMember.getDeclaringClass());
LocalVariableTable lvt = null;
int numVars = 0;
if (forMember instanceof Method) {
org.aspectj.apache.bcel.classfile.Method bcelMethod = jc.getMethod((Method) forMember);
lvt = bcelMethod.getLocalVariableTable();
numVars = bcelMethod.getArgumentTypes().length;
} else if (forMember instanceof Constructor) {
org.aspectj.apache.bcel.classfile.Method bcelCons = jc.getMethod((Constructor) forMember);
lvt = bcelCons.getLocalVariableTable();
numVars = bcelCons.getArgumentTypes().length;
}
return getParameterNamesFromLVT(lvt, numVars);
} catch (ClassNotFoundException cnfEx) {
; // no luck
}
return null;
}
private String[] getParameterNamesFromLVT(LocalVariableTable lvt, int numVars) {
if (lvt == null)
return null;// pr222987 - prevent NPE
LocalVariable[] vars = lvt.getLocalVariableTable();
if (vars.length < numVars) {
// basic error, we can't get the names...
return null;
}
String[] ret = new String[numVars];
for (int i = 0; i < numVars; i++) {
ret[i] = vars[i + 1].getName();
}
return ret;
}
public ResolvedType[][] getParameterAnnotationTypes(Member onMember) {
if (!(onMember instanceof AccessibleObject))
return NO_PARAMETER_ANNOTATIONS;
// here we really want both the runtime visible AND the class visible
// annotations
// so we bail out to Bcel and then chuck away the JavaClass so that we
// don't hog
// memory.
try {
JavaClass jc = bcelRepository.loadClass(onMember.getDeclaringClass());
org.aspectj.apache.bcel.classfile.annotation.AnnotationGen[][] anns = null;
if (onMember instanceof Method) {
org.aspectj.apache.bcel.classfile.Method bcelMethod = jc.getMethod((Method) onMember);
if (bcelMethod == null) {
// pr220430
// System.err.println(
// "Unexpected problem in Java15AnnotationFinder: cannot retrieve annotations on method '"
// +
// onMember.getName()+"' in class '"+jc.getClassName()+"'");
} else {
anns = bcelMethod.getParameterAnnotations();
}
} else if (onMember instanceof Constructor) {
org.aspectj.apache.bcel.classfile.Method bcelCons = jc.getMethod((Constructor) onMember);
anns = bcelCons.getParameterAnnotations();
} else if (onMember instanceof Field) {
// anns = null;
}
// the answer is cached and we don't want to hold on to memory
bcelRepository.clear();
if (anns == null)
return NO_PARAMETER_ANNOTATIONS;
ResolvedType[][] result = new ResolvedType[anns.length][];
// CACHING??
for (int i = 0; i < anns.length; i++) {
if (anns[i] != null) {
result[i] = new ResolvedType[anns[i].length];
for (int j = 0; j < anns[i].length; j++) {
result[i][j] = world.resolve(UnresolvedType.forSignature(anns[i][j].getTypeSignature()));
}
}
}
return result;
} catch (ClassNotFoundException cnfEx) {
// just use reflection then
}
// reflection...
AccessibleObject ao = (AccessibleObject) onMember;
Annotation[][] anns = null;
if (onMember instanceof Method) {
anns = ((Method) ao).getParameterAnnotations();
} else if (onMember instanceof Constructor) {
anns = ((Constructor) ao).getParameterAnnotations();
} else if (onMember instanceof Field) {
// anns = null;
}
if (anns == null)
return NO_PARAMETER_ANNOTATIONS;
ResolvedType[][] result = new ResolvedType[anns.length][];
// CACHING??
for (int i = 0; i < anns.length; i++) {
if (anns[i] != null) {
result[i] = new ResolvedType[anns[i].length];
for (int j = 0; j < anns[i].length; j++) {
result[i][j] = UnresolvedType.forName(anns[i][j].annotationType().getName()).resolve(world);
}
}
}
return result;
}
}