Save This Page
Home » xwork-2.1.5 » com.opensymphony.xwork2.util.finder » [javadoc | source]
    1   /*
    2    * Copyright (c) 2002-2003 by OpenSymphony
    3    * All rights reserved.
    4    */
    5   package com.opensymphony.xwork2.util.finder;
    6   
    7   import com.opensymphony.xwork2.util.logging.Logger;
    8   import com.opensymphony.xwork2.util.logging.LoggerFactory;
    9   import com.opensymphony.xwork2.util.URLUtil;
   10   import com.opensymphony.xwork2.XWorkException;
   11   import org.objectweb.asm.AnnotationVisitor;
   12   import org.objectweb.asm.ClassReader;
   13   import org.objectweb.asm.FieldVisitor;
   14   import org.objectweb.asm.MethodVisitor;
   15   import org.objectweb.asm.commons.EmptyVisitor;
   16   import org.apache.commons.lang.StringUtils;
   17   
   18   import java.io.File;
   19   import java.io.IOException;
   20   import java.io.InputStream;
   21   import java.lang.annotation.Annotation;
   22   import java.lang.reflect.AnnotatedElement;
   23   import java.lang.reflect.Constructor;
   24   import java.lang.reflect.Field;
   25   import java.lang.reflect.Method;
   26   import java.net.JarURLConnection;
   27   import java.net.URL;
   28   import java.net.URLDecoder;
   29   import java.util;
   30   import java.util.jar.JarEntry;
   31   import java.util.jar.JarInputStream;
   32   
   33   /**
   34    * ClassFinder searches the classpath of the specified ClassLoaderInterface for
   35    * packages, classes, constructors, methods, or fields with specific annotations.
   36    *
   37    * For security reasons ASM is used to find the annotations.  Classes are not
   38    * loaded unless they match the requirements of a called findAnnotated* method.
   39    * Once loaded, these classes are cached.
   40    *
   41    * The getClassesNotLoaded() method can be used immediately after any find*
   42    * method to get a list of classes which matched the find requirements (i.e.
   43    * contained the annotation), but were unable to be loaded.
   44    *
   45    * @author David Blevins
   46    * @version $Rev$ $Date$
   47    */
   48   public class ClassFinder {
   49       private static final Logger LOG = LoggerFactory.getLogger(ClassFinder.class);
   50   
   51       private final Map<String, List<Info>> annotated = new HashMap<String, List<Info>>();
   52       private final Map<String, ClassInfo> classInfos = new LinkedHashMap<String, ClassInfo>();
   53   
   54       private final List<String> classesNotLoaded = new ArrayList<String>();
   55   
   56       private boolean extractBaseInterfaces;
   57       private ClassLoaderInterface classLoaderInterface;
   58   
   59       /**
   60        * Creates a ClassFinder that will search the urls in the specified ClassLoaderInterface
   61        * excluding the urls in the ClassLoaderInterface's parent.
   62        *
   63        * To include the parent ClassLoaderInterface, use:
   64        *
   65        *    new ClassFinder(ClassLoaderInterface, false);
   66        *
   67        * To exclude the parent's parent, use:
   68        *
   69        *    new ClassFinder(ClassLoaderInterface, ClassLoaderInterface.getParent().getParent());
   70        *
   71        * @param classLoader source of classes to scan
   72        * @throws Exception if something goes wrong
   73        */
   74       public ClassFinder(ClassLoaderInterface classLoader) throws Exception {
   75           this(classLoader, true);
   76       }
   77   
   78       /**
   79        * Creates a ClassFinder that will search the urls in the specified ClassLoaderInterface.
   80        *
   81        * @param classLoader source of classes to scan
   82        * @param excludeParent Allegedly excludes classes from parent ClassLoaderInterface, whatever that might mean
   83        * @throws Exception if something goes wrong.
   84        */
   85       public ClassFinder(ClassLoaderInterface classLoader, boolean excludeParent) throws Exception {
   86           this(classLoader, getUrls(classLoader, excludeParent));
   87       }
   88   
   89       /**
   90        * Creates a ClassFinder that will search the urls in the specified classloader excluding
   91        * the urls in the 'exclude' ClassLoaderInterface.
   92        *
   93        * @param classLoader source of classes to scan
   94        * @param exclude source of classes to exclude from scanning
   95        * @throws Exception if something goes wrong
   96        */
   97       public ClassFinder(ClassLoaderInterface classLoader, ClassLoaderInterface exclude) throws Exception {
   98           this(classLoader, getUrls(classLoader, exclude));
   99       }
  100   
  101       public ClassFinder(ClassLoaderInterface classLoader, URL url) {
  102           this(classLoader, Arrays.asList(url));
  103       }
  104   
  105       public ClassFinder(ClassLoaderInterface classLoader, String... dirNames) {
  106           this(classLoader, getURLs(classLoader, dirNames));
  107       }
  108   
  109       public ClassFinder(ClassLoaderInterface classLoaderInterface, Collection<URL> urls) {
  110           this(classLoaderInterface, urls, false);
  111       }
  112   
  113       public ClassFinder(ClassLoaderInterface classLoaderInterface, Collection<URL> urls, boolean extractBaseInterfaces) {
  114           this(classLoaderInterface, urls, extractBaseInterfaces, new HashSet(){
  115               {
  116                   add("jar");
  117               }
  118           });
  119       }
  120   
  121       public ClassFinder(ClassLoaderInterface classLoaderInterface, Collection<URL> urls, boolean extractBaseInterfaces, Set<String> protocols) {
  122           this.classLoaderInterface = classLoaderInterface;
  123           this.extractBaseInterfaces = extractBaseInterfaces;
  124   
  125           List<String> classNames = new ArrayList<String>();
  126           for (URL location : urls) {
  127               try {
  128                   if (protocols.contains(location.getProtocol())) {
  129                       classNames.addAll(jar(location));
  130                   } else if ("file".equals(location.getProtocol())) {
  131                       try {
  132                           // See if it's actually a jar
  133                           URL jarUrl = new URL("jar", "", location.toExternalForm() + "!/");
  134                           JarURLConnection juc = (JarURLConnection) jarUrl.openConnection();
  135                           juc.getJarFile();
  136                           classNames.addAll(jar(jarUrl));
  137                       } catch (IOException e) {
  138                           classNames.addAll(file(location));
  139                       }
  140                   }
  141               } catch (Exception e) {
  142                   if (LOG.isErrorEnabled())
  143                       LOG.error("Unable to read URL [#0]", e, location.toExternalForm());
  144               }
  145           }
  146   
  147           for (String className : classNames) {
  148               try {
  149                   readClassDef(className);
  150               } catch (Throwable e) {
  151                   if (LOG.isErrorEnabled())
  152                       LOG.error("Unable to read class [#0]", e, className);
  153               }
  154           }
  155       }
  156   
  157       public ClassFinder(Class... classes){
  158           this(Arrays.asList(classes));
  159       }
  160   
  161       public ClassFinder(List<Class> classes){
  162           this.classLoaderInterface = null;
  163           List<Info> infos = new ArrayList<Info>();
  164           List<Package> packages = new ArrayList<Package>();
  165           for (Class clazz : classes) {
  166   
  167               Package aPackage = clazz.getPackage();
  168               if (aPackage != null && !packages.contains(aPackage)){
  169                   infos.add(new PackageInfo(aPackage));
  170                   packages.add(aPackage);
  171               }
  172   
  173               ClassInfo classInfo = new ClassInfo(clazz);
  174               infos.add(classInfo);
  175               classInfos.put(classInfo.getName(), classInfo);
  176               for (Method method : clazz.getDeclaredMethods()) {
  177                   infos.add(new MethodInfo(classInfo, method));
  178               }
  179   
  180               for (Constructor constructor : clazz.getConstructors()) {
  181                   infos.add(new MethodInfo(classInfo, constructor));
  182               }
  183   
  184               for (Field field : clazz.getDeclaredFields()) {
  185                   infos.add(new FieldInfo(classInfo, field));
  186               }
  187           }
  188   
  189           for (Info info : infos) {
  190               for (AnnotationInfo annotation : info.getAnnotations()) {
  191                   List<Info> annotationInfos = getAnnotationInfos(annotation.getName());
  192                   annotationInfos.add(info);
  193               }
  194           }
  195       }
  196   
  197       public boolean isAnnotationPresent(Class<? extends Annotation> annotation) {
  198           List<Info> infos = annotated.get(annotation.getName());
  199           return infos != null && !infos.isEmpty();
  200       }
  201   
  202       /**
  203        * Returns a list of classes that could not be loaded in last invoked findAnnotated* method.
  204        * <p/>
  205        * The list will only contain entries of classes whose byte code matched the requirements
  206        * of last invoked find* method, but were unable to be loaded and included in the results.
  207        * <p/>
  208        * The list returned is unmodifiable.  Once obtained, the returned list will be a live view of the
  209        * results from the last findAnnotated* method call.
  210        * <p/>
  211        * This method is not thread safe.
  212        * @return an unmodifiable live view of classes that could not be loaded in previous findAnnotated* call.
  213        */
  214       public List<String> getClassesNotLoaded() {
  215           return Collections.unmodifiableList(classesNotLoaded);
  216       }
  217   
  218       public List<Package> findAnnotatedPackages(Class<? extends Annotation> annotation) {
  219           classesNotLoaded.clear();
  220           List<Package> packages = new ArrayList<Package>();
  221           List<Info> infos = getAnnotationInfos(annotation.getName());
  222           for (Info info : infos) {
  223               if (info instanceof PackageInfo) {
  224                   PackageInfo packageInfo = (PackageInfo) info;
  225                   try {
  226                       Package pkg = packageInfo.get();
  227                       // double check via proper reflection
  228                       if (pkg.isAnnotationPresent(annotation)) {
  229                           packages.add(pkg);
  230                       }
  231                   } catch (ClassNotFoundException e) {
  232                       classesNotLoaded.add(packageInfo.getName());
  233                   }
  234               }
  235           }
  236           return packages;
  237       }
  238   
  239       public List<Class> findAnnotatedClasses(Class<? extends Annotation> annotation) {
  240           classesNotLoaded.clear();
  241           List<Class> classes = new ArrayList<Class>();
  242           List<Info> infos = getAnnotationInfos(annotation.getName());
  243           for (Info info : infos) {
  244               if (info instanceof ClassInfo) {
  245                   ClassInfo classInfo = (ClassInfo) info;
  246                   try {
  247                       Class clazz = classInfo.get();
  248                       // double check via proper reflection
  249                       if (clazz.isAnnotationPresent(annotation)) {
  250                           classes.add(clazz);
  251                       }
  252                   } catch (Throwable e) {
  253                       if (LOG.isErrorEnabled())
  254                           LOG.error("Error loading class [#0]", e, classInfo.getName());
  255                       classesNotLoaded.add(classInfo.getName());
  256                   }
  257               }
  258           }
  259           return classes;
  260       }
  261   
  262       public List<Method> findAnnotatedMethods(Class<? extends Annotation> annotation) {
  263           classesNotLoaded.clear();
  264           List<ClassInfo> seen = new ArrayList<ClassInfo>();
  265           List<Method> methods = new ArrayList<Method>();
  266           List<Info> infos = getAnnotationInfos(annotation.getName());
  267           for (Info info : infos) {
  268               if (info instanceof MethodInfo && !"<init>".equals(info.getName())) {
  269                   MethodInfo methodInfo = (MethodInfo) info;
  270                   ClassInfo classInfo = methodInfo.getDeclaringClass();
  271   
  272                   if (seen.contains(classInfo)) continue;
  273   
  274                   seen.add(classInfo);
  275   
  276                   try {
  277                       Class clazz = classInfo.get();
  278                       for (Method method : clazz.getDeclaredMethods()) {
  279                           if (method.isAnnotationPresent(annotation)) {
  280                               methods.add(method);
  281                           }
  282                       }
  283                   } catch (Throwable e) {
  284                       if (LOG.isErrorEnabled())
  285                           LOG.error("Error loading class [#0]", e, classInfo.getName());
  286                       classesNotLoaded.add(classInfo.getName());
  287                   }
  288               }
  289           }
  290           return methods;
  291       }
  292   
  293       public List<Constructor> findAnnotatedConstructors(Class<? extends Annotation> annotation) {
  294           classesNotLoaded.clear();
  295           List<ClassInfo> seen = new ArrayList<ClassInfo>();
  296           List<Constructor> constructors = new ArrayList<Constructor>();
  297           List<Info> infos = getAnnotationInfos(annotation.getName());
  298           for (Info info : infos) {
  299               if (info instanceof MethodInfo && "<init>".equals(info.getName())) {
  300                   MethodInfo methodInfo = (MethodInfo) info;
  301                   ClassInfo classInfo = methodInfo.getDeclaringClass();
  302   
  303                   if (seen.contains(classInfo)) continue;
  304   
  305                   seen.add(classInfo);
  306   
  307                   try {
  308                       Class clazz = classInfo.get();
  309                       for (Constructor constructor : clazz.getConstructors()) {
  310                           if (constructor.isAnnotationPresent(annotation)) {
  311                               constructors.add(constructor);
  312                           }
  313                       }
  314                   } catch (Throwable e) {
  315                       if (LOG.isErrorEnabled())
  316                           LOG.error("Error loading class [#0]", e, classInfo.getName());
  317                       classesNotLoaded.add(classInfo.getName());
  318                   }
  319               }
  320           }
  321           return constructors;
  322       }
  323   
  324       public List<Field> findAnnotatedFields(Class<? extends Annotation> annotation) {
  325           classesNotLoaded.clear();
  326           List<ClassInfo> seen = new ArrayList<ClassInfo>();
  327           List<Field> fields = new ArrayList<Field>();
  328           List<Info> infos = getAnnotationInfos(annotation.getName());
  329           for (Info info : infos) {
  330               if (info instanceof FieldInfo) {
  331                   FieldInfo fieldInfo = (FieldInfo) info;
  332                   ClassInfo classInfo = fieldInfo.getDeclaringClass();
  333   
  334                   if (seen.contains(classInfo)) continue;
  335   
  336                   seen.add(classInfo);
  337   
  338                   try {
  339                       Class clazz = classInfo.get();
  340                       for (Field field : clazz.getDeclaredFields()) {
  341                           if (field.isAnnotationPresent(annotation)) {
  342                               fields.add(field);
  343                           }
  344                       }
  345                   } catch (Throwable e) {
  346                       if (LOG.isErrorEnabled())
  347                           LOG.error("Error loading class [#0]", e, classInfo.getName());
  348                       classesNotLoaded.add(classInfo.getName());
  349                   }
  350               }
  351           }
  352           return fields;
  353       }
  354   
  355       public List<Class> findClassesInPackage(String packageName, boolean recursive) {
  356           classesNotLoaded.clear();
  357           List<Class> classes = new ArrayList<Class>();
  358           for (ClassInfo classInfo : classInfos.values()) {
  359               try {
  360                   if (recursive && classInfo.getPackageName().startsWith(packageName)){
  361                       classes.add(classInfo.get());
  362                   } else if (classInfo.getPackageName().equals(packageName)){
  363                       classes.add(classInfo.get());
  364                   }
  365               } catch (Throwable e) {
  366                   if (LOG.isErrorEnabled())
  367                       LOG.error("Error loading class [#0]", e, classInfo.getName());
  368                   classesNotLoaded.add(classInfo.getName());
  369               }
  370           }
  371           return classes;
  372       }
  373   
  374       public List<Class> findClasses(Test<ClassInfo> test) {
  375           classesNotLoaded.clear();
  376           List<Class> classes = new ArrayList<Class>();
  377           for (ClassInfo classInfo : classInfos.values()) {
  378               try {
  379                   if (test.test(classInfo)) {
  380                       classes.add(classInfo.get());
  381                   }
  382               } catch (Throwable e) {
  383                   if (LOG.isErrorEnabled())
  384                       LOG.error("Error loading class [#0]", e, classInfo.getName());
  385                   classesNotLoaded.add(classInfo.getName());
  386               }
  387           }
  388           return classes;
  389       }
  390   
  391       public List<Class> findClasses() {
  392           classesNotLoaded.clear();
  393           List<Class> classes = new ArrayList<Class>();
  394           for (ClassInfo classInfo : classInfos.values()) {
  395               try {
  396                   classes.add(classInfo.get());
  397               } catch (Throwable e) {
  398                   if (LOG.isErrorEnabled())
  399                       LOG.error("Error loading class [#0]", e, classInfo.getName());
  400                   classesNotLoaded.add(classInfo.getName());
  401               }
  402           }
  403           return classes;
  404       }
  405   
  406       private static List<URL> getURLs(ClassLoaderInterface classLoader, String[] dirNames) {
  407           List<URL> urls = new ArrayList<URL>();
  408           for (String dirName : dirNames) {
  409               try {
  410                   Enumeration<URL> classLoaderURLs = classLoader.getResources(dirName);
  411                   while (classLoaderURLs.hasMoreElements()) {
  412                       URL url = classLoaderURLs.nextElement();
  413                       urls.add(url);
  414                   }
  415               } catch (IOException ioe) {
  416                   if (LOG.isErrorEnabled())
  417                       LOG.error("Could not read driectory [#0]", ioe, dirName);
  418               }
  419           }
  420   
  421           return urls;
  422       }
  423   
  424       private static Collection<URL> getUrls(ClassLoaderInterface classLoaderInterface, boolean excludeParent) throws IOException {
  425           return getUrls(classLoaderInterface, excludeParent? classLoaderInterface.getParent() : null);
  426       }
  427   
  428       private static Collection<URL> getUrls(ClassLoaderInterface classLoader, ClassLoaderInterface excludeParent) throws IOException {
  429           UrlSet urlSet = new UrlSet(classLoader);
  430           if (excludeParent != null){
  431               urlSet = urlSet.exclude(excludeParent);
  432           }
  433           return urlSet.getUrls();
  434       }
  435   
  436       private List<String> file(URL location) {
  437           List<String> classNames = new ArrayList<String>();
  438           File dir = new File(URLDecoder.decode(location.getPath()));
  439           if ("META-INF".equals(dir.getName())) {
  440               dir = dir.getParentFile(); // Scrape "META-INF" off
  441           }
  442           if (dir.isDirectory()) {
  443               scanDir(dir, classNames, "");
  444           }
  445           return classNames;
  446       }
  447   
  448       private void scanDir(File dir, List<String> classNames, String packageName) {
  449           File[] files = dir.listFiles();
  450           for (File file : files) {
  451               if (file.isDirectory()) {
  452                   scanDir(file, classNames, packageName + file.getName() + ".");
  453               } else if (file.getName().endsWith(".class")) {
  454                   String name = file.getName();
  455                   name = name.replaceFirst(".class$", "");
  456                   classNames.add(packageName + name);
  457               }
  458           }
  459       }
  460   
  461       private List<String> jar(URL location) throws IOException {
  462           URL url = URLUtil.normalizeToFileProtocol(location);
  463           if (url != null) {
  464               InputStream in = url.openStream();
  465               try {
  466                   JarInputStream jarStream = new JarInputStream(in);
  467                   return jar(jarStream);
  468               } finally {
  469                   in.close();
  470               }
  471           } else if (LOG.isDebugEnabled())
  472               LOG.debug("Unable to read [#0]", location.toExternalForm());
  473           
  474           return Collections.emptyList();
  475       }
  476   
  477       private List<String> jar(JarInputStream jarStream) throws IOException {
  478           List<String> classNames = new ArrayList<String>();
  479   
  480           JarEntry entry;
  481           while ((entry = jarStream.getNextJarEntry()) != null) {
  482               if (entry.isDirectory() || !entry.getName().endsWith(".class")) {
  483                   continue;
  484               }
  485               String className = entry.getName();
  486               className = className.replaceFirst(".class$", "");
  487   
  488               //war files are treated as .jar files, so takeout WEB-INF/classes
  489               className = StringUtils.removeStart(className, "WEB-INF/classes/"); 
  490   
  491               className = className.replace('/', '.');
  492               classNames.add(className);
  493           }
  494   
  495           return classNames;
  496       }
  497   
  498       public class Annotatable {
  499           private final List<AnnotationInfo> annotations = new ArrayList<AnnotationInfo>();
  500   
  501           public Annotatable(AnnotatedElement element) {
  502               for (Annotation annotation : element.getAnnotations()) {
  503                   annotations.add(new AnnotationInfo(annotation.annotationType().getName()));
  504               }
  505           }
  506   
  507           public Annotatable() {
  508           }
  509   
  510           public List<AnnotationInfo> getAnnotations() {
  511               return annotations;
  512           }
  513   
  514       }
  515   
  516       public static interface Info {
  517           String getName();
  518   
  519           List<AnnotationInfo> getAnnotations();
  520       }
  521   
  522       public class PackageInfo extends Annotatable implements Info {
  523           private final String name;
  524           private final ClassInfo info;
  525           private final Package pkg;
  526   
  527           public PackageInfo(Package pkg){
  528               super(pkg);
  529               this.pkg = pkg;
  530               this.name = pkg.getName();
  531               this.info = null;
  532           }
  533   
  534           public PackageInfo(String name) {
  535               info = new ClassInfo(name, null);
  536               this.name = name;
  537               this.pkg = null;
  538           }
  539   
  540           public String getName() {
  541               return name;
  542           }
  543   
  544           public Package get() throws ClassNotFoundException {
  545               return (pkg != null)?pkg:info.get().getPackage();
  546           }
  547       }
  548   
  549       public class ClassInfo extends Annotatable implements Info {
  550           private final String name;
  551           private final List<MethodInfo> methods = new ArrayList<MethodInfo>();
  552           private final List<MethodInfo> constructors = new ArrayList<MethodInfo>();
  553           private final String superType;
  554           private final List<String> interfaces = new ArrayList<String>();
  555           private final List<String> superInterfaces = new ArrayList<String>();
  556           private final List<FieldInfo> fields = new ArrayList<FieldInfo>();
  557           private Class<?> clazz;
  558           private ClassNotFoundException notFound;
  559   
  560           public ClassInfo(Class clazz) {
  561               super(clazz);
  562               this.clazz = clazz;
  563               this.name = clazz.getName();
  564               Class superclass = clazz.getSuperclass();
  565               this.superType = superclass != null ? superclass.getName(): null;
  566           }
  567   
  568           public ClassInfo(String name, String superType) {
  569               this.name = name;
  570               this.superType = superType;
  571           }
  572   
  573           public String getPackageName(){
  574               return name.indexOf(".") > 0 ? name.substring(0, name.lastIndexOf(".")) : "" ;
  575           }
  576   
  577           public List<MethodInfo> getConstructors() {
  578               return constructors;
  579           }
  580   
  581           public List<String> getInterfaces() {
  582               return interfaces;
  583           }
  584   
  585           public List<String> getSuperInterfaces() {
  586               return superInterfaces;
  587           }
  588   
  589           public List<FieldInfo> getFields() {
  590               return fields;
  591           }
  592   
  593           public List<MethodInfo> getMethods() {
  594               return methods;
  595           }
  596   
  597           public String getName() {
  598               return name;
  599           }
  600   
  601           public String getSuperType() {
  602               return superType;
  603           }
  604   
  605           public Class get() throws ClassNotFoundException {
  606               if (clazz != null) return clazz;
  607               if (notFound != null) throw notFound;
  608               try {
  609                   this.clazz = classLoaderInterface.loadClass(name);
  610                   return clazz;
  611               } catch (ClassNotFoundException notFound) {
  612                   classesNotLoaded.add(name);
  613                   this.notFound = notFound;
  614                   throw notFound;
  615               }
  616           }
  617   
  618           @Override
  619           public String toString() {
  620               return name;
  621           }
  622       }
  623   
  624       public class MethodInfo extends Annotatable implements Info {
  625           private final ClassInfo declaringClass;
  626           private final String returnType;
  627           private final String name;
  628           private final List<List<AnnotationInfo>> parameterAnnotations = new ArrayList<List<AnnotationInfo>>();
  629   
  630           public MethodInfo(ClassInfo info, Constructor constructor){
  631               super(constructor);
  632               this.declaringClass = info;
  633               this.name = "<init>";
  634               this.returnType = Void.TYPE.getName();
  635           }
  636   
  637           public MethodInfo(ClassInfo info, Method method){
  638               super(method);
  639               this.declaringClass = info;
  640               this.name = method.getName();
  641               this.returnType = method.getReturnType().getName();
  642           }
  643   
  644           public MethodInfo(ClassInfo declarignClass, String name, String returnType) {
  645               this.declaringClass = declarignClass;
  646               this.name = name;
  647               this.returnType = returnType;
  648           }
  649   
  650           public List<List<AnnotationInfo>> getParameterAnnotations() {
  651               return parameterAnnotations;
  652           }
  653   
  654           public List<AnnotationInfo> getParameterAnnotations(int index) {
  655               if (index >= parameterAnnotations.size()) {
  656                   for (int i = parameterAnnotations.size(); i <= index; i++) {
  657                       List<AnnotationInfo> annotationInfos = new ArrayList<AnnotationInfo>();
  658                       parameterAnnotations.add(i, annotationInfos);
  659                   }
  660               }
  661               return parameterAnnotations.get(index);
  662           }
  663   
  664           public String getName() {
  665               return name;
  666           }
  667   
  668           public ClassInfo getDeclaringClass() {
  669               return declaringClass;
  670           }
  671   
  672           public String getReturnType() {
  673               return returnType;
  674           }
  675   
  676           @Override
  677           public String toString() {
  678               return declaringClass + "@" + name;
  679           }
  680       }
  681   
  682       public class FieldInfo extends Annotatable implements Info {
  683           private final String name;
  684           private final String type;
  685           private final ClassInfo declaringClass;
  686   
  687           public FieldInfo(ClassInfo info, Field field){
  688               super(field);
  689               this.declaringClass = info;
  690               this.name = field.getName();
  691               this.type = field.getType().getName();
  692           }
  693   
  694           public FieldInfo(ClassInfo declaringClass, String name, String type) {
  695               this.declaringClass = declaringClass;
  696               this.name = name;
  697               this.type = type;
  698           }
  699   
  700           public String getName() {
  701               return name;
  702           }
  703   
  704           public ClassInfo getDeclaringClass() {
  705               return declaringClass;
  706           }
  707   
  708           public String getType() {
  709               return type;
  710           }
  711   
  712           @Override
  713           public String toString() {
  714               return declaringClass + "#" + name;
  715           }
  716       }
  717   
  718       public class AnnotationInfo extends Annotatable implements Info {
  719           private final String name;
  720   
  721           public AnnotationInfo(Annotation annotation){
  722               this(annotation.getClass().getName());
  723           }
  724   
  725           public AnnotationInfo(Class<? extends Annotation> annotation) {
  726               this.name = annotation.getName().intern();
  727           }
  728   
  729           public AnnotationInfo(String name) {
  730               name = name.replaceAll("^L|;$", "");
  731               name = name.replace('/', '.');
  732               this.name = name.intern();
  733           }
  734   
  735           public String getName() {
  736               return name;
  737           }
  738   
  739           @Override
  740           public String toString() {
  741               return name;
  742           }
  743       }
  744   
  745       private List<Info> getAnnotationInfos(String name) {
  746           List<Info> infos = annotated.get(name);
  747           if (infos == null) {
  748               infos = new ArrayList<Info>();
  749               annotated.put(name, infos);
  750           }
  751           return infos;
  752       }
  753   
  754       private void readClassDef(String className) {
  755           if (!className.endsWith(".class")) {
  756               className = className.replace('.', '/') + ".class";
  757           }
  758           try {
  759               URL resource = classLoaderInterface.getResource(className);
  760               if (resource != null) {
  761                   InputStream in = resource.openStream();
  762                   try {
  763                       ClassReader classReader = new ClassReader(in);
  764                       classReader.accept(new InfoBuildingVisitor(), ClassReader.SKIP_DEBUG);
  765                   } finally {
  766                       in.close();
  767                   }
  768               } else {
  769                   throw new XWorkException("Could not load " + className);
  770               }
  771           } catch (IOException e) {
  772               throw new XWorkException("Could not load " + className, e);
  773           }
  774   
  775       }
  776   
  777       public class InfoBuildingVisitor extends EmptyVisitor {
  778           private Info info;
  779   
  780           public InfoBuildingVisitor() {
  781           }
  782   
  783           public InfoBuildingVisitor(Info info) {
  784               this.info = info;
  785           }
  786   
  787           @Override
  788           public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
  789               if (name.endsWith("package-info")) {
  790                   info = new PackageInfo(javaName(name));
  791               } else {
  792                   ClassInfo classInfo = new ClassInfo(javaName(name), javaName(superName));
  793   
  794                   for (String interfce : interfaces) {
  795                       classInfo.getInterfaces().add(javaName(interfce));
  796                   }
  797                   info = classInfo;
  798                   classInfos.put(classInfo.getName(), classInfo);
  799   
  800                   if (extractBaseInterfaces)
  801                       extractSuperInterfaces(classInfo);
  802               }
  803           }
  804   
  805           private void extractSuperInterfaces(ClassInfo classInfo) {
  806               String superType = classInfo.getSuperType();
  807   
  808               if (superType != null) {
  809                   ClassInfo base = classInfos.get(superType);
  810   
  811                   if (base == null) {
  812                       //try to load base
  813                       String resource = superType.replace('.', '/') + ".class";
  814                       readClassDef(resource);
  815                       base = classInfos.get(superType);
  816                   }
  817   
  818                   if (base != null) {
  819                       List<String> interfaces = classInfo.getSuperInterfaces();
  820                       interfaces.addAll(base.getSuperInterfaces());
  821                       interfaces.addAll(base.getInterfaces());
  822                   }
  823               }
  824           }
  825   
  826           private String javaName(String name) {
  827               return (name == null)? null:name.replace('/', '.');
  828           }
  829   
  830           @Override
  831           public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
  832               AnnotationInfo annotationInfo = new AnnotationInfo(desc);
  833               info.getAnnotations().add(annotationInfo);
  834               getAnnotationInfos(annotationInfo.getName()).add(info);
  835               return new InfoBuildingVisitor(annotationInfo);
  836           }
  837   
  838           @Override
  839           public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
  840               ClassInfo classInfo = ((ClassInfo) info);
  841               FieldInfo fieldInfo = new FieldInfo(classInfo, name, desc);
  842               classInfo.getFields().add(fieldInfo);
  843               return new InfoBuildingVisitor(fieldInfo);
  844           }
  845   
  846           @Override
  847           public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
  848               ClassInfo classInfo = ((ClassInfo) info);
  849               MethodInfo methodInfo = new MethodInfo(classInfo, name, desc);
  850               classInfo.getMethods().add(methodInfo);
  851               return new InfoBuildingVisitor(methodInfo);
  852           }
  853   
  854           @Override
  855           public AnnotationVisitor visitParameterAnnotation(int param, String desc, boolean visible) {
  856               MethodInfo methodInfo = ((MethodInfo) info);
  857               List<AnnotationInfo> annotationInfos = methodInfo.getParameterAnnotations(param);
  858               AnnotationInfo annotationInfo = new AnnotationInfo(desc);
  859               annotationInfos.add(annotationInfo);
  860               return new InfoBuildingVisitor(annotationInfo);
  861           }
  862       }
  863   }
  864   

Save This Page
Home » xwork-2.1.5 » com.opensymphony.xwork2.util.finder » [javadoc | source]