com.opensymphony.xwork2.util.finder.ClassFinder Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.opensymphony.xwork2.util.finder;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* ClassFinder searches the classpath of the specified ClassLoaderInterface for
* packages, classes, constructors, methods, or fields with specific annotations.
*
* For security reasons ASM is used to find the annotations. Classes are not
* loaded unless they match the requirements of a called findAnnotated* method.
* Once loaded, these classes are cached.
*
* The getClassesNotLoaded() method can be used immediately after any find*
* method to get a list of classes which matched the find requirements (i.e.
* contained the annotation), but were unable to be loaded.
*/
public interface ClassFinder {
boolean isAnnotationPresent(Class annotation);
/**
*
* Returns a list of classes that could not be loaded in last invoked findAnnotated* method.
*
*
*
* The list will only contain entries of classes whose byte code matched the requirements
* of last invoked find* method, but were unable to be loaded and included in the results.
*
*
*
* The list returned is unmodifiable. Once obtained, the returned list will be a live view of the
* results from the last findAnnotated* method call.
*
*
*
* This method is not thread safe.
*
*
* @return an unmodifiable live view of classes that could not be loaded in previous findAnnotated* call.
*/
List getClassesNotLoaded();
List findAnnotatedPackages(Class annotation);
List findAnnotatedClasses(Class annotation);
List findAnnotatedMethods(Class annotation);
List findAnnotatedConstructors(Class annotation);
List findAnnotatedFields(Class annotation);
List findClassesInPackage(String packageName, boolean recursive);
List findClasses(Test test);
List findClasses();
ClassLoaderInterface getClassLoaderInterface();
public static interface Info {
String getName();
List getAnnotations();
}
public class AnnotationInfo extends Annotatable implements Info {
private final String name;
public AnnotationInfo(Annotation annotation){
this(annotation.getClass().getName());
}
public AnnotationInfo(Class annotation) {
this.name = annotation.getName().intern();
}
public AnnotationInfo(String name) {
name = name.replaceAll("^L|;$", "");
name = name.replace('/', '.');
this.name = name.intern();
}
public String getName() {
return name;
}
@Override
public String toString() {
return name;
}
}
public class Annotatable {
private final List annotations = new ArrayList<>();
public Annotatable(AnnotatedElement element) {
for (Annotation annotation : element.getAnnotations()) {
annotations.add(new AnnotationInfo(annotation.annotationType().getName()));
}
}
public Annotatable() {
}
public List getAnnotations() {
return annotations;
}
}
public class PackageInfo extends Annotatable implements Info {
private final String name;
private final ClassInfo info;
private final Package pkg;
public PackageInfo(Package pkg){
super(pkg);
this.pkg = pkg;
this.name = pkg.getName();
this.info = null;
}
public PackageInfo(String name, ClassFinder classFinder) {
info = new ClassInfo(name, null, classFinder);
this.name = name;
this.pkg = null;
}
public String getName() {
return name;
}
public Package get() throws ClassNotFoundException {
return (pkg != null)?pkg:info.get().getPackage();
}
}
public class ClassInfo extends Annotatable implements Info {
private final String name;
private final List methods = new ArrayList<>();
private final List constructors = new ArrayList<>();
private final String superType;
private final List interfaces = new ArrayList<>();
private final List superInterfaces = new ArrayList<>();
private final List fields = new ArrayList<>();
private Class clazz;
private ClassFinder classFinder;
private ClassNotFoundException notFound;
public ClassInfo(Class clazz, ClassFinder classFinder) {
super(clazz);
this.clazz = clazz;
this.classFinder = classFinder;
this.name = clazz.getName();
Class superclass = clazz.getSuperclass();
this.superType = superclass != null ? superclass.getName(): null;
}
public ClassInfo(String name, String superType, ClassFinder classFinder) {
this.name = name;
this.superType = superType;
this.classFinder = classFinder;
}
public String getPackageName(){
return name.indexOf('.') > 0 ? name.substring(0, name.lastIndexOf('.')) : "" ;
}
public List getConstructors() {
return constructors;
}
public List getInterfaces() {
return interfaces;
}
public List getSuperInterfaces() {
return superInterfaces;
}
public List getFields() {
return fields;
}
public List getMethods() {
return methods;
}
public String getName() {
return name;
}
public String getSuperType() {
return superType;
}
public Class get() throws ClassNotFoundException {
if (clazz != null) return clazz;
if (notFound != null) throw notFound;
try {
this.clazz = classFinder.getClassLoaderInterface().loadClass(name);
return clazz;
} catch (ClassNotFoundException notFound) {
classFinder.getClassesNotLoaded().add(name);
this.notFound = notFound;
throw notFound;
}
}
@Override
public String toString() {
return name;
}
}
public class MethodInfo extends Annotatable implements Info {
private final ClassInfo declaringClass;
private final String returnType;
private final String name;
private final List> parameterAnnotations = new ArrayList<>();
public MethodInfo(ClassInfo info, Constructor constructor){
super(constructor);
this.declaringClass = info;
this.name = "";
this.returnType = Void.TYPE.getName();
}
public MethodInfo(ClassInfo info, Method method){
super(method);
this.declaringClass = info;
this.name = method.getName();
this.returnType = method.getReturnType().getName();
}
public MethodInfo(ClassInfo declarignClass, String name, String returnType) {
this.declaringClass = declarignClass;
this.name = name;
this.returnType = returnType;
}
public List> getParameterAnnotations() {
return parameterAnnotations;
}
public List getParameterAnnotations(int index) {
if (index >= parameterAnnotations.size()) {
for (int i = parameterAnnotations.size(); i <= index; i++) {
List annotationInfos = new ArrayList<>();
parameterAnnotations.add(i, annotationInfos);
}
}
return parameterAnnotations.get(index);
}
public String getName() {
return name;
}
public ClassInfo getDeclaringClass() {
return declaringClass;
}
public String getReturnType() {
return returnType;
}
@Override
public String toString() {
return declaringClass + "@" + name;
}
}
public class FieldInfo extends Annotatable implements Info {
private final String name;
private final String type;
private final ClassInfo declaringClass;
public FieldInfo(ClassInfo info, Field field){
super(field);
this.declaringClass = info;
this.name = field.getName();
this.type = field.getType().getName();
}
public FieldInfo(ClassInfo declaringClass, String name, String type) {
this.declaringClass = declaringClass;
this.name = name;
this.type = type;
}
public String getName() {
return name;
}
public ClassInfo getDeclaringClass() {
return declaringClass;
}
public String getType() {
return type;
}
@Override
public String toString() {
return declaringClass + "#" + name;
}
}
}