/* *******************************************************************
 * 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, Andy Clement
/** * * @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; } }