You are viewing a plain text version of this content. The canonical link for it is here.
Posted to xbean-scm@geronimo.apache.org by db...@apache.org on 2011/03/22 21:00:26 UTC

svn commit: r1084331 - in /geronimo/xbean/trunk/xbean-finder/src: main/java/org/apache/xbean/finder/ test/java/org/apache/xbean/finder/

Author: dblevins
Date: Tue Mar 22 20:00:26 2011
New Revision: 1084331

URL: http://svn.apache.org/viewvc?rev=1084331&view=rev
Log:
XBEAN-165: Meta Annotation Support
XBEAN-166 : Class Name Filtering for Optimized Scanning
XBEAN-167:  Archive Interface for cleaner integration of Classpath and OSGi based systems

Moving these over to an AnnotationFinder class with restored svn history

Removed:
    geronimo/xbean/trunk/xbean-finder/src/main/java/org/apache/xbean/finder/MetaAnnotationFinder.java
Modified:
    geronimo/xbean/trunk/xbean-finder/src/main/java/org/apache/xbean/finder/AnnotationFinder.java
    geronimo/xbean/trunk/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedClassTest.java
    geronimo/xbean/trunk/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedMethodTest.java

Modified: geronimo/xbean/trunk/xbean-finder/src/main/java/org/apache/xbean/finder/AnnotationFinder.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-finder/src/main/java/org/apache/xbean/finder/AnnotationFinder.java?rev=1084331&r1=1084330&r2=1084331&view=diff
==============================================================================
--- geronimo/xbean/trunk/xbean-finder/src/main/java/org/apache/xbean/finder/AnnotationFinder.java (original)
+++ geronimo/xbean/trunk/xbean-finder/src/main/java/org/apache/xbean/finder/AnnotationFinder.java Tue Mar 22 20:00:26 2011
@@ -1,50 +1,55 @@
-/**
- * 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
+/*
+ * 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
+ *  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.
+ * 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 org.apache.xbean.finder;
 
 import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
 import org.objectweb.asm.ClassReader;
 import org.objectweb.asm.FieldVisitor;
 import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Type;
 import org.objectweb.asm.commons.EmptyVisitor;
 import org.objectweb.asm.signature.SignatureReader;
 import org.objectweb.asm.signature.SignatureVisitor;
 
-import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.annotation.Annotation;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.lang.reflect.AnnotatedElement;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
+import java.lang.reflect.Member;
 import java.lang.reflect.Method;
-import java.lang.reflect.AnnotatedElement;
-import java.net.URL;
-import java.net.JarURLConnection;
-import java.net.URLDecoder;
+import java.lang.reflect.TypeVariable;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Enumeration;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.jar.JarEntry;
-import java.util.jar.JarInputStream;
+import java.util.Set;
 
 /**
  * ClassFinder searches the classpath of the specified classloader for
@@ -54,137 +59,168 @@ import java.util.jar.JarInputStream;
  * 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.
- *
- * @author David Blevins
  * @version $Rev$ $Date$
  */
-public class ClassFinder {
+public class AnnotationFinder {
+
+    private final Set<Class<? extends Annotation>> metaroots = new HashSet<Class<? extends Annotation>>();
+    {
+        metaroots.add(Metatype.class);
+    }
+
     private final Map<String, List<Info>> annotated = new HashMap<String, List<Info>>();
-    private final List<ClassInfo> classInfos = new ArrayList<ClassInfo>();
 
-    private final ClassLoader classLoader;
+    protected final Map<String, ClassInfo> classInfos = new HashMap<String, ClassInfo>();
     private final List<String> classesNotLoaded = new ArrayList<String>();
     private final int ASM_FLAGS = ClassReader.SKIP_CODE + ClassReader.SKIP_DEBUG + ClassReader.SKIP_FRAMES;
+    private final Archive archive;
 
-    /**
-     * Creates a ClassFinder that will search the urls in the specified classloader
-     * excluding the urls in the classloader's parent.
-     *
-     * To include the parent classloader, use:
-     *
-     *    new ClassFinder(classLoader, false);
-     *
-     * To exclude the parent's parent, use:
-     *
-     *    new ClassFinder(classLoader, classLoader.getParent().getParent());
-     *
-     * @param classLoader source of classes to scan
-     * @throws Exception if something goes wrong
-     */
-    public ClassFinder(ClassLoader classLoader) throws Exception {
-        this(classLoader, true);
+    public AnnotationFinder(Archive archive) {
+        this.archive = archive;
+
+        for (String className : this.archive) {
+            try {
+                readClassDef(archive.getBytecode(className));
+            } catch (NoClassDefFoundError e) {
+                throw new NoClassDefFoundError("Could not fully load class: " + className + "\n due to:" + e.getMessage());
+            } catch (ClassNotFoundException e) {
+                e.printStackTrace();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
     }
 
+
     /**
-     * Creates a ClassFinder that will search the urls in the specified classloader.
+     * The link() method must be called to successfully use the findSubclasses and findImplementations methods
      *
-     * @param classLoader source of classes to scan
-     * @param excludeParent Allegedly excludes classes from parent classloader, whatever that might mean
-     * @throws Exception if something goes wrong.
+     * @return
+     * @throws java.io.IOException
      */
-    public ClassFinder(ClassLoader classLoader, boolean excludeParent) throws Exception {
-        this(classLoader, getUrls(classLoader, excludeParent));
+    public AnnotationFinder link() throws IOException, ClassNotFoundException {
+        for (ClassInfo classInfo : classInfos.values().toArray(new ClassInfo[classInfos.size()])) {
+
+            linkParent(classInfo);
+        }
+
+        for (ClassInfo classInfo : classInfos.values().toArray(new ClassInfo[classInfos.size()])) {
+
+            linkInterfaces(classInfo);
+
+        }
+
+        // diff new and old lists
+        resolveAnnotations(new ArrayList<String>());
+
+        return this;
     }
 
     /**
-     * Creates a ClassFinder that will search the urls in the specified classloader excluding
-     * the urls in the 'exclude' classloader.
+     * Used to support meta annotations
+     * <p/>
+     * Once the list of classes has been read from the Archive, we
+     * iterate over all the annotations that are used by those classes
+     * and recursively resolve any annotations those annotations use.
      *
-     * @param classLoader source of classes to scan
-     * @param exclude source of classes to exclude from scanning
-     * @throws Exception if something goes wrong
+     * @param scanned
+     * @throws ClassNotFoundException
+     * @throws IOException
      */
-    public ClassFinder(ClassLoader classLoader, ClassLoader exclude) throws Exception {
-        this(classLoader, getUrls(classLoader, exclude));
-    }
+    private void resolveAnnotations(List<String> scanned) throws ClassNotFoundException, IOException {
 
-    public ClassFinder(ClassLoader classLoader, URL url) {
-        this(classLoader, Arrays.asList(url));
-    }
+        // Get a list of the annotations that exist before we start
+        List<String> annotations = new ArrayList<String>(annotated.keySet());
 
-    public ClassFinder(ClassLoader classLoader, Collection<URL> urls) {
-        this.classLoader = classLoader;
+        for (String annotation : annotations) {
+            if (scanned.contains(annotation)) continue;
+            readClassDef(annotation);
+        }
 
-        List<String> classNames = new ArrayList<String>();
-        for (URL location : urls) {
+        for (Info info : annotated.get(Metatype.class.getName())) {
             try {
-                if (location.getProtocol().equals("jar")) {
-                    classNames.addAll(jar(location));
-                } else if (location.getProtocol().equals("file")) {
-                    try {
-                        // See if it's actually a jar
-                        URL jarUrl = new URL("jar", "", location.toExternalForm() + "!/");
-                        JarURLConnection juc = (JarURLConnection) jarUrl.openConnection();
-                        juc.getJarFile();
-                        classNames.addAll(jar(jarUrl));
-                    } catch (IOException e) {
-                        classNames.addAll(file(location));
-                    }
-                }
-            } catch (Exception e) {
-                e.printStackTrace();
+                readClassDef(info.getName() + "$$");
+            } catch (ClassNotFoundException e) {
+                // we don't care, this is only a convenience
             }
         }
 
-        for (String className : classNames) {
-            readClassDef(className);
+        // If the "annotated" list has grown, then we must scan those
+        if (annotated.keySet().size() != annotations.size()) {
+            resolveAnnotations(annotations);
         }
     }
 
-    public ClassFinder(Class... classes){
-        this(Arrays.asList(classes));
+    private void linkParent(ClassInfo classInfo) throws IOException, ClassNotFoundException {
+        if (classInfo.superType == null) return;
+        if (classInfo.superType.equals("java.lang.Object")) return;
+
+        ClassInfo parentInfo = classInfo.superclassInfo;
+
+        if (parentInfo == null) {
+
+            parentInfo = classInfos.get(classInfo.superType);
+
+            if (parentInfo == null) {
+
+                if (classInfo.clazz != null) {
+                    readClassDef(((Class<?>) classInfo.clazz).getSuperclass());
+                } else {
+                    readClassDef(classInfo.superType);
+                }
+
+                parentInfo = classInfos.get(classInfo.superType);
+
+                if (parentInfo == null) return;
+
+                linkParent(parentInfo);
+            }
+
+            classInfo.superclassInfo = parentInfo;
+        }
+
+        if (!parentInfo.subclassInfos.contains(classInfo)) {
+            parentInfo.subclassInfos.add(classInfo);
+        }
     }
 
-    public ClassFinder(List<Class> classes){
-        this.classLoader = null;
-        List<Info> infos = new ArrayList<Info>();
-        List<Package> packages = new ArrayList<Package>();
-        for (Class clazz : classes) {
+    private void linkInterfaces(ClassInfo classInfo) throws IOException, ClassNotFoundException {
+        final List<ClassInfo> infos = new ArrayList<ClassInfo>();
 
-            try {
-                Package aPackage = clazz.getPackage();
-                if (aPackage != null && !packages.contains(aPackage)){
-                    infos.add(new PackageInfo(aPackage));
-                    packages.add(aPackage);
+        if (classInfo.clazz != null) {
+            final Class<?>[] interfaces = classInfo.clazz.getInterfaces();
+
+            for (Class<?> clazz : interfaces) {
+                ClassInfo interfaceInfo = classInfos.get(clazz.getName());
+
+                if (interfaceInfo == null) {
+                    readClassDef(clazz);
                 }
 
-                ClassInfo classInfo = new ClassInfo(clazz);
-                infos.add(classInfo);
-                classInfos.add(classInfo);
-                for (Method method : clazz.getDeclaredMethods()) {
-                    infos.add(new MethodInfo(classInfo, method));
+                interfaceInfo = classInfos.get(clazz.getName());
+
+                if (interfaceInfo != null) {
+                    infos.add(interfaceInfo);
                 }
+            }
+        } else {
+            for (String className : classInfo.interfaces) {
+                ClassInfo interfaceInfo = classInfos.get(className);
 
-                for (Constructor constructor : clazz.getConstructors()) {
-                    infos.add(new MethodInfo(classInfo, constructor));
+                if (interfaceInfo == null) {
+                    readClassDef(className);
                 }
 
-                for (Field field : clazz.getDeclaredFields()) {
-                    infos.add(new FieldInfo(classInfo, field));
+                interfaceInfo = classInfos.get(className);
+
+                if (interfaceInfo != null) {
+                    infos.add(interfaceInfo);
                 }
-            } catch (NoClassDefFoundError e) {
-                throw new NoClassDefFoundError("Could not fully load class: " + clazz.getName() + "\n due to:" + e.getMessage() + "\n in classLoader: \n" + clazz.getClassLoader());
             }
         }
 
-        for (Info info : infos) {
-            for (AnnotationInfo annotation : info.getAnnotations()) {
-                List<Info> annotationInfos = getAnnotationInfos(annotation.getName());
-                annotationInfos.add(info);
-            }
+        for (ClassInfo info : infos) {
+            linkInterfaces(info);
         }
     }
 
@@ -203,13 +239,14 @@ public class ClassFinder {
      * results from the last findAnnotated* method call.
      * <p/>
      * This method is not thread safe.
+     *
      * @return an unmodifiable live view of classes that could not be loaded in previous findAnnotated* call.
      */
     public List<String> getClassesNotLoaded() {
         return Collections.unmodifiableList(classesNotLoaded);
     }
 
-    public List<Package> findAnnotatedPackages(Class<? extends Annotation> annotation) {
+    public List<AnnotatedTarget<Package>> findAnnotatedPackages(Class<? extends Annotation> annotation) {
         classesNotLoaded.clear();
         List<Package> packages = new ArrayList<Package>();
         List<Info> infos = getAnnotationInfos(annotation.getName());
@@ -227,22 +264,62 @@ public class ClassFinder {
                 }
             }
         }
-        return packages;
+        return null;
+    }
+
+    public List<Class<?>> findAnnotatedClasses(Class<? extends Annotation> annotation) {
+        classesNotLoaded.clear();
+        List<Class<?>> classes = new ArrayList<Class<?>>();
+        List<Info> infos = getAnnotationInfos(annotation.getName());
+        for (Info info : infos) {
+            if (info instanceof ClassInfo) {
+                ClassInfo classInfo = (ClassInfo) info;
+                try {
+                    Class clazz = classInfo.get();
+                    // double check via proper reflection
+                    if (clazz.isAnnotationPresent(annotation)) {
+                        classes.add(clazz);
+                    }
+                } catch (ClassNotFoundException e) {
+                    classesNotLoaded.add(classInfo.getName());
+                }
+            }
+        }
+        return classes;
     }
 
-    public List<Class> findAnnotatedClasses(Class<? extends Annotation> annotation) {
+    public List<AnnotatedTarget<Class<?>>> findMetaAnnotatedClasses(Class<? extends Annotation> annotation) {
         classesNotLoaded.clear();
-        List<Class> classes = new ArrayList<Class>();
+        Set<Class<?>> classes = findMetaAnnotatedClasses(annotation, new HashSet<Class<?>>());
+
+        List<AnnotatedTarget<Class<?>>> list = new ArrayList<AnnotatedTarget<Class<?>>>();
+
+        for (Class<?> clazz : classes) {
+            list.add(new MetaAnnotatedClass(clazz));
+        }
+
+        return list;
+    }
+
+    private Set<Class<?>> findMetaAnnotatedClasses(Class<? extends Annotation> annotation, Set<Class<?>> classes) {
         List<Info> infos = getAnnotationInfos(annotation.getName());
         for (Info info : infos) {
             if (info instanceof ClassInfo) {
                 ClassInfo classInfo = (ClassInfo) info;
                 try {
                     Class clazz = classInfo.get();
+
+                    if (classes.contains(clazz)) continue;
+
                     // double check via proper reflection
                     if (clazz.isAnnotationPresent(annotation)) {
                         classes.add(clazz);
                     }
+
+                    String meta = info.getMetaAnnotationName();
+                    if (meta != null) {
+                        classes.addAll(findMetaAnnotatedClasses((Class<? extends Annotation>) clazz, classes));
+                    }
                 } catch (ClassNotFoundException e) {
                     classesNotLoaded.add(classInfo.getName());
                 }
@@ -257,21 +334,21 @@ public class ClassFinder {
      * @param annotation
      * @return list of directly or indirectly (inherited) annotated classes
      */
-    public List<Class> findInheritedAnnotatedClasses(Class<? extends Annotation> annotation) {
+    public List<AnnotatedTarget<Class<?>>> findInheritedAnnotatedClasses(Class<? extends Annotation> annotation) {
         classesNotLoaded.clear();
-        List<Class> classes = new ArrayList<Class>();
+        List<Class<?>> classes = new ArrayList<Class<?>>();
         List<Info> infos = getAnnotationInfos(annotation.getName());
         for (Info info : infos) {
             try {
-            	if(info instanceof ClassInfo){
-                   classes.add(((ClassInfo) info).get());
-            	}
+                if (info instanceof ClassInfo) {
+                    classes.add(((ClassInfo) info).get());
+                }
             } catch (ClassNotFoundException cnfe) {
                 // TODO: ignored, but a log message would be appropriate
             }
         }
         boolean annClassFound;
-        List<ClassInfo> tempClassInfos = new ArrayList<ClassInfo>(classInfos);
+        List<ClassInfo> tempClassInfos = new ArrayList<ClassInfo>(classInfos.values());
         do {
             annClassFound = false;
             for (int pos = 0; pos < tempClassInfos.size(); pos++) {
@@ -289,9 +366,9 @@ public class ClassFinder {
                     }
                     // check whether any interface is annotated
                     List<String> interfces = classInfo.getInterfaces();
-                    for (String interfce: interfces) {
+                    for (String interfce : interfces) {
                         for (Class clazz : classes) {
-                            if (interfce.replaceFirst("<.*>","").equals(clazz.getName())) {
+                            if (interfce.replaceFirst("<.*>", "").equals(clazz.getName())) {
                                 classes.add(classInfo.get());
                                 tempClassInfos.remove(pos);
                                 annClassFound = true;
@@ -306,7 +383,7 @@ public class ClassFinder {
                 }
             }
         } while (annClassFound);
-        return classes;
+        return null;
     }
 
     public List<Method> findAnnotatedMethods(Class<? extends Annotation> annotation) {
@@ -338,7 +415,66 @@ public class ClassFinder {
         return methods;
     }
 
-    public List<Constructor> findAnnotatedConstructors(Class<? extends Annotation> annotation) {
+    public List<AnnotatedMethod<Method>> findMetaAnnotatedMethods(Class<? extends Annotation> annotation) {
+        classesNotLoaded.clear();
+
+        Set<Method> methods = findMetaAnnotatedMethods(annotation, new HashSet<Method>(), new HashSet<String>());
+
+        List<AnnotatedMethod<Method>> targets = new ArrayList<AnnotatedMethod<Method>>();
+
+        for (Method method : methods) {
+            targets.add(new MetaAnnotatedMethod(method));
+        }
+
+        return targets;
+    }
+
+    private Set<Method> findMetaAnnotatedMethods(Class<? extends Annotation> annotation, Set<Method> methods, Set<String> seen) {
+        List<Info> infos = getAnnotationInfos(annotation.getName());
+
+        for (Info info : infos) {
+
+            String meta = info.getMetaAnnotationName();
+            if (meta != null) {
+                if (meta.equals(annotation.getName())) continue;
+                if (!seen.add(meta)) continue;
+
+
+                ClassInfo metaInfo = classInfos.get(meta);
+
+                Class<?> clazz;
+                try {
+                    clazz = metaInfo.get();
+                } catch (ClassNotFoundException e) {
+                    classesNotLoaded.add(metaInfo.getName());
+                    continue;
+                }
+
+                findMetaAnnotatedMethods((Class<? extends Annotation>) clazz, methods, seen);
+
+            } else if (info instanceof MethodInfo && !((MethodInfo) info).isConstructor()) {
+
+                MethodInfo methodInfo = (MethodInfo) info;
+
+                ClassInfo classInfo = methodInfo.getDeclaringClass();
+
+                try {
+                    Class clazz = classInfo.get();
+                    for (Method method : clazz.getDeclaredMethods()) {
+                        if (method.isAnnotationPresent(annotation)) {
+                            methods.add(method);
+                        }
+                    }
+                } catch (ClassNotFoundException e) {
+                    classesNotLoaded.add(classInfo.getName());
+                }
+            }
+        }
+
+        return methods;
+    }
+
+    public List<AnnotatedMethod<Constructor>> findAnnotatedConstructors(Class<? extends Annotation> annotation) {
         classesNotLoaded.clear();
         List<ClassInfo> seen = new ArrayList<ClassInfo>();
         List<Constructor> constructors = new ArrayList<Constructor>();
@@ -364,10 +500,10 @@ public class ClassFinder {
                 }
             }
         }
-        return constructors;
+        return null;
     }
 
-    public List<Field> findAnnotatedFields(Class<? extends Annotation> annotation) {
+    public List<AnnotatedMember<Field>> findAnnotatedFields(Class<? extends Annotation> annotation) {
         classesNotLoaded.clear();
         List<ClassInfo> seen = new ArrayList<ClassInfo>();
         List<Field> fields = new ArrayList<Field>();
@@ -393,17 +529,17 @@ public class ClassFinder {
                 }
             }
         }
-        return fields;
+        return null;
     }
 
-    public List<Class> findClassesInPackage(String packageName, boolean recursive) {
+    public List<Class<?>> findClassesInPackage(String packageName, boolean recursive) {
         classesNotLoaded.clear();
-        List<Class> classes = new ArrayList<Class>();
-        for (ClassInfo classInfo : classInfos) {
+        List<Class<?>> classes = new ArrayList<Class<?>>();
+        for (ClassInfo classInfo : classInfos.values()) {
             try {
-                if (recursive && classInfo.getPackageName().startsWith(packageName)){
+                if (recursive && classInfo.getPackageName().startsWith(packageName)) {
                     classes.add(classInfo.get());
-                } else if (classInfo.getPackageName().equals(packageName)){
+                } else if (classInfo.getPackageName().equals(packageName)) {
                     classes.add(classInfo.get());
                 }
             } catch (ClassNotFoundException e) {
@@ -413,82 +549,187 @@ public class ClassFinder {
         return classes;
     }
 
-    private static Collection<URL> getUrls(ClassLoader classLoader, boolean excludeParent) throws IOException {
-        return getUrls(classLoader, excludeParent? classLoader.getParent() : null);
+    public <T> List<Class<? extends T>> findSubclasses(Class<T> clazz) {
+        if (clazz == null) throw new NullPointerException("class cannot be null");
+
+        classesNotLoaded.clear();
+
+        final ClassInfo classInfo = classInfos.get(clazz.getName());
+
+        List<Class<? extends T>> found = new ArrayList<Class<? extends T>>();
+
+        if (classInfo == null) return found;
+
+        findSubclasses(classInfo, found, clazz);
+
+        return found;
     }
 
-    private static Collection<URL> getUrls(ClassLoader classLoader, ClassLoader excludeParent) throws IOException {
-        UrlSet urlSet = new UrlSet(classLoader);
-        if (excludeParent != null){
-            urlSet = urlSet.exclude(excludeParent);
+    private <T> void findSubclasses(ClassInfo classInfo, List<Class<? extends T>> found, Class<T> clazz) {
+
+        for (ClassInfo subclassInfo : classInfo.subclassInfos) {
+
+            try {
+                found.add(subclassInfo.get().asSubclass(clazz));
+            } catch (ClassNotFoundException e) {
+                classesNotLoaded.add(subclassInfo.getName());
+            }
+
+            findSubclasses(subclassInfo, found, clazz);
         }
-        return urlSet.getUrls();
     }
 
-    private List<String> file(URL location) {
-        List<String> classNames = new ArrayList<String>();
-        File dir = new File(URLDecoder.decode(location.getPath()));
-        if (dir.getName().equals("META-INF")) {
-            dir = dir.getParentFile(); // Scrape "META-INF" off
-        }
-        if (dir.isDirectory()) {
-            scanDir(dir, classNames, "");
+    private <T> List<Class<? extends T>> _findSubclasses(Class<T> clazz) {
+        if (clazz == null) throw new NullPointerException("class cannot be null");
+
+        List<Class<? extends T>> classes = new ArrayList<Class<? extends T>>();
+
+
+        for (ClassInfo classInfo : classInfos.values()) {
+
+            try {
+
+                if (clazz.getName().equals(classInfo.superType)) {
+
+                    if (clazz.isAssignableFrom(classInfo.get())) {
+
+                        classes.add(classInfo.get().asSubclass(clazz));
+
+                        classes.addAll(_findSubclasses(classInfo.get().asSubclass(clazz)));
+                    }
+                }
+
+            } catch (ClassNotFoundException e) {
+                classesNotLoaded.add(classInfo.getName());
+            }
+
         }
-        return classNames;
+
+        return classes;
     }
 
-    private void scanDir(File dir, List<String> classNames, String packageName) {
-        File[] files = dir.listFiles();
-        for (File file : files) {
-            if (file.isDirectory()) {
-                scanDir(file, classNames, packageName + file.getName() + ".");
-            } else if (file.getName().endsWith(".class")) {
-                String name = file.getName();
-                name = name.replaceFirst(".class$", "");
-                if (name.contains(".")) continue;
-                classNames.add(packageName + name);
+    public <T> List<Class<? extends T>> findImplementations(Class<T> clazz) {
+        if (clazz == null) throw new NullPointerException("class cannot be null");
+        if (!clazz.isInterface()) new IllegalArgumentException("class must be an interface");
+        classesNotLoaded.clear();
+
+        final String interfaceName = clazz.getName();
+
+        // Collect all interfaces extending the main interface (recursively)
+        // Collect all implementations of interfaces
+        // i.e. all *directly* implementing classes
+        List<ClassInfo> infos = collectImplementations(interfaceName);
+
+        // Collect all subclasses of implementations
+        List<Class<? extends T>> classes = new ArrayList<Class<? extends T>>();
+        for (ClassInfo info : infos) {
+            try {
+                final Class<? extends T> impl = (Class<? extends T>) info.get();
+
+                if (clazz.isAssignableFrom(impl)) {
+                    classes.add(impl);
+
+                    // Optimization: Don't need to call this method if parent class was already searched
+
+
+                    classes.addAll(_findSubclasses(impl));
+                }
+
+            } catch (ClassNotFoundException e) {
+                classesNotLoaded.add(info.getName());
             }
         }
+        return classes;
     }
 
-    private List<String> jar(URL location) throws IOException {
-        String jarPath = location.getFile();
-        if (jarPath.indexOf("!") > -1){
-            jarPath = jarPath.substring(0, jarPath.indexOf("!"));
+    private List<ClassInfo> collectImplementations(String interfaceName) {
+        final List<ClassInfo> infos = new ArrayList<ClassInfo>();
+
+        for (ClassInfo classInfo : classInfos.values()) {
+
+            if (classInfo.interfaces.contains(interfaceName)) {
+
+                infos.add(classInfo);
+
+                try {
+
+                    final Class clazz = classInfo.get();
+
+                    if (clazz.isInterface() && !clazz.isAnnotation()) {
+
+                        infos.addAll(collectImplementations(classInfo.name));
+
+                    }
+
+                } catch (ClassNotFoundException ignore) {
+                    // we'll deal with this later
+                }
+            }
         }
-        URL url = new URL(jarPath);
-        InputStream in = url.openStream();
-        try {
-            JarInputStream jarStream = new JarInputStream(in);
-            return jar(jarStream);
-        } finally {
-            in.close();
+        return infos;
+    }
+
+    protected List<Info> getAnnotationInfos(String name) {
+        List<Info> infos = annotated.get(name);
+        if (infos == null) {
+            infos = new SingleLinkedList<Info>();
+            annotated.put(name, infos);
         }
+        return infos;
     }
 
-    private List<String> jar(JarInputStream jarStream) throws IOException {
-        List<String> classNames = new ArrayList<String>();
+    protected void readClassDef(String className) throws ClassNotFoundException, IOException {
+        if (classInfos.containsKey(className)) return;
+        readClassDef(archive.getBytecode(className));
+    }
 
-        JarEntry entry;
-        while ((entry = jarStream.getNextJarEntry()) != null) {
-            if (entry.isDirectory() || !entry.getName().endsWith(".class")) {
-                continue;
+    protected void readClassDef(InputStream in) throws IOException {
+        ClassReader classReader = new ClassReader(in);
+        classReader.accept(new InfoBuildingVisitor(), ASM_FLAGS);
+    }
+
+    protected void readClassDef(Class clazz) {
+        List<Info> infos = new ArrayList<Info>();
+
+        Package aPackage = clazz.getPackage();
+        if (aPackage != null) {
+            final PackageInfo info = new PackageInfo(aPackage);
+            for (AnnotationInfo annotation : info.getAnnotations()) {
+                List<Info> annotationInfos = getAnnotationInfos(annotation.getName());
+                if (!annotationInfos.contains(info)) {
+                    annotationInfos.add(info);
+                }
             }
-            String className = entry.getName();
-            className = className.replaceFirst(".class$", "");
-            if (className.contains(".")) continue;
-            className = className.replace('/', '.');
-            classNames.add(className);
         }
 
-        return classNames;
+        ClassInfo classInfo = new ClassInfo(clazz);
+        infos.add(classInfo);
+        classInfos.put(clazz.getName(), classInfo);
+        for (Method method : clazz.getDeclaredMethods()) {
+            infos.add(new MethodInfo(classInfo, method));
+        }
+
+        for (Constructor constructor : clazz.getConstructors()) {
+            infos.add(new MethodInfo(classInfo, constructor));
+        }
+
+        for (Field field : clazz.getDeclaredFields()) {
+            infos.add(new FieldInfo(classInfo, field));
+        }
+
+        for (Info info : infos) {
+            for (AnnotationInfo annotation : info.getAnnotations()) {
+                List<Info> annotationInfos = getAnnotationInfos(annotation.getName());
+                annotationInfos.add(info);
+            }
+        }
     }
 
     public class Annotatable {
         private final List<AnnotationInfo> annotations = new ArrayList<AnnotationInfo>();
 
         public Annotatable(AnnotatedElement element) {
-            for (Annotation annotation : element.getAnnotations()) {
+            for (Annotation annotation : getAnnotations(element)) {
                 annotations.add(new AnnotationInfo(annotation.annotationType().getName()));
             }
         }
@@ -496,16 +737,54 @@ public class ClassFinder {
         public Annotatable() {
         }
 
+        public Annotation[] getDeclaredAnnotations() {
+            return new Annotation[0];
+        }
+
         public List<AnnotationInfo> getAnnotations() {
             return annotations;
         }
 
+        public String getMetaAnnotationName() {
+            return null;
+        }
+
+        /**
+         * Utility method to get around some errors caused by
+         * interactions between the Equinox class loaders and
+         * the OpenJPA transformation process.  There is a window
+         * where the OpenJPA transformation process can cause
+         * an annotation being processed to get defined in a
+         * classloader during the actual defineClass call for
+         * that very class (e.g., recursively).  This results in
+         * a LinkageError exception.  If we see one of these,
+         * retry the request.  Since the annotation will be
+         * defined on the second pass, this should succeed.  If
+         * we get a second exception, then it's likely some
+         * other problem.
+         *
+         * @param element The AnnotatedElement we need information for.
+         * @return An array of the Annotations defined on the element.
+         */
+        private Annotation[] getAnnotations(AnnotatedElement element) {
+            try {
+                return element.getAnnotations();
+            } catch (LinkageError e) {
+                return element.getAnnotations();
+            }
+        }
+
     }
 
     public static interface Info {
+
+        String getMetaAnnotationName();
+
         String getName();
 
         List<AnnotationInfo> getAnnotations();
+
+        Annotation[] getDeclaredAnnotations();
     }
 
     public class PackageInfo extends Annotatable implements Info {
@@ -513,7 +792,7 @@ public class ClassFinder {
         private final ClassInfo info;
         private final Package pkg;
 
-        public PackageInfo(Package pkg){
+        public PackageInfo(Package pkg) {
             super(pkg);
             this.pkg = pkg;
             this.name = pkg.getName();
@@ -531,26 +810,48 @@ public class ClassFinder {
         }
 
         public Package get() throws ClassNotFoundException {
-            return (pkg != null)?pkg:info.get().getPackage();
+            return (pkg != null) ? pkg : info.get().getPackage();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            PackageInfo that = (PackageInfo) o;
+
+            if (name != null ? !name.equals(that.name) : that.name != null) return false;
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            return name != null ? name.hashCode() : 0;
         }
     }
 
     public class ClassInfo extends Annotatable implements Info {
         private String name;
-        private final List<MethodInfo> methods = new ArrayList<MethodInfo>();
-        private final List<MethodInfo> constructors = new ArrayList<MethodInfo>();
+        private final List<MethodInfo> methods = new SingleLinkedList<MethodInfo>();
+        private final List<MethodInfo> constructors = new SingleLinkedList<MethodInfo>();
         private String superType;
-        private final List<String> interfaces = new ArrayList<String>();
-        private final List<FieldInfo> fields = new ArrayList<FieldInfo>();
+        private ClassInfo superclassInfo;
+        private final List<ClassInfo> subclassInfos = new SingleLinkedList<ClassInfo>();
+        private final List<String> interfaces = new SingleLinkedList<String>();
+        private final List<FieldInfo> fields = new SingleLinkedList<FieldInfo>();
         private Class<?> clazz;
-        private ClassNotFoundException notFound;
+
 
         public ClassInfo(Class clazz) {
             super(clazz);
             this.clazz = clazz;
             this.name = clazz.getName();
             Class superclass = clazz.getSuperclass();
-            this.superType = superclass != null ? superclass.getName(): null;
+            this.superType = superclass != null ? superclass.getName() : null;
+            for (Class intrface : clazz.getInterfaces()) {
+                this.interfaces.add(intrface.getName());
+            }
         }
 
         public ClassInfo(String name, String superType) {
@@ -558,8 +859,24 @@ public class ClassFinder {
             this.superType = superType;
         }
 
-        public String getPackageName(){
-        	  return name.substring(0,name.lastIndexOf("."));
+        @Override
+        public String getMetaAnnotationName() {
+            for (AnnotationInfo info : getAnnotations()) {
+                if (info.getName().equals(Metatype.class.getName())) return name;
+            }
+
+            if (name.endsWith("$$")) {
+                ClassInfo info = classInfos.get(name.substring(0, name.length() - 2));
+                if (info != null) {
+                    return info.getMetaAnnotationName();
+                }
+            }
+
+            return null;
+        }
+
+        public String getPackageName() {
+            return name.indexOf(".") > 0 ? name.substring(0, name.lastIndexOf(".")) : "";
         }
 
         public List<MethodInfo> getConstructors() {
@@ -586,15 +903,18 @@ public class ClassFinder {
             return superType;
         }
 
-        public Class get() throws ClassNotFoundException {
+        public boolean isAnnotation() {
+            return "java.lang.Object".equals(superType) && interfaces.size() == 1 && "java.lang.annotation.Annotation".equals(interfaces.get(0));
+        }
+
+        public Class<?> get() throws ClassNotFoundException {
             if (clazz != null) return clazz;
-            if (notFound != null) throw notFound;
             try {
-                this.clazz = classLoader.loadClass(name.replaceFirst("<.*>",""));
+                String fixedName = name.replaceFirst("<.*>", "");
+                this.clazz = archive.loadClass(fixedName);
                 return clazz;
             } catch (ClassNotFoundException notFound) {
                 classesNotLoaded.add(name);
-                this.notFound = notFound;
                 throw notFound;
             }
         }
@@ -606,28 +926,49 @@ public class ClassFinder {
 
     public class MethodInfo extends Annotatable implements Info {
         private final ClassInfo declaringClass;
-        private final String returnType;
+        private final String descriptor;
         private final String name;
         private final List<List<AnnotationInfo>> parameterAnnotations = new ArrayList<List<AnnotationInfo>>();
+        private Member method;
 
-        public MethodInfo(ClassInfo info, Constructor constructor){
+        public MethodInfo(ClassInfo info, Constructor constructor) {
             super(constructor);
             this.declaringClass = info;
             this.name = "<init>";
-            this.returnType = Void.TYPE.getName();
+            this.descriptor = null;
         }
 
-        public MethodInfo(ClassInfo info, Method method){
+        public MethodInfo(ClassInfo info, Method method) {
             super(method);
             this.declaringClass = info;
             this.name = method.getName();
-            this.returnType = method.getReturnType().getName();
+            this.descriptor = method.getReturnType().getName();
+            this.method = method;
         }
 
-        public MethodInfo(ClassInfo declarignClass, String name, String returnType) {
+        public MethodInfo(ClassInfo declarignClass, String name, String descriptor) {
             this.declaringClass = declarignClass;
             this.name = name;
-            this.returnType = returnType;
+            this.descriptor = descriptor;
+        }
+
+        @Override
+        public String getMetaAnnotationName() {
+            return declaringClass.getMetaAnnotationName();
+        }
+
+        @Override
+        public Annotation[] getDeclaredAnnotations() {
+            super.getDeclaredAnnotations();
+            try {
+                return ((AnnotatedElement) get()).getDeclaredAnnotations();
+            } catch (ClassNotFoundException e) {
+                return super.getDeclaredAnnotations();
+            }
+        }
+
+        public boolean isConstructor() {
+            return getName().equals("<init>");
         }
 
         public List<List<AnnotationInfo>> getParameterAnnotations() {
@@ -652,13 +993,50 @@ public class ClassFinder {
             return declaringClass;
         }
 
-        public String getReturnType() {
-            return returnType;
-        }
-
         public String toString() {
             return declaringClass + "@" + name;
         }
+
+        public Member get() throws ClassNotFoundException {
+            if (method == null) {
+                method = toMethod();
+            }
+
+            return method;
+        }
+
+        private Method toMethod() throws ClassNotFoundException {
+            org.objectweb.asm.commons.Method method = new org.objectweb.asm.commons.Method(name, descriptor);
+
+            Class<?> clazz = this.declaringClass.get();
+            List<Class> parameterTypes = new ArrayList<Class>();
+
+            for (Type type : method.getArgumentTypes()) {
+                String paramType = type.getClassName();
+                try {
+                    parameterTypes.add(Classes.forName(paramType, clazz.getClassLoader()));
+                } catch (ClassNotFoundException cnfe) {
+                    throw new IllegalStateException("Parameter class could not be loaded for type " + paramType, cnfe);
+                }
+            }
+
+            Class[] parameters = parameterTypes.toArray(new Class[parameterTypes.size()]);
+
+            IllegalStateException noSuchMethod = null;
+            while (clazz != null) {
+                try {
+                    return clazz.getDeclaredMethod(name, parameters);
+                } catch (NoSuchMethodException e) {
+                    if (noSuchMethod == null) {
+                        noSuchMethod = new IllegalStateException("Callback method does not exist: " + clazz.getName() + "." + name, e);
+                    }
+                    clazz = clazz.getSuperclass();
+                }
+            }
+
+            throw noSuchMethod;
+        }
+
     }
 
     public class FieldInfo extends Annotatable implements Info {
@@ -666,7 +1044,7 @@ public class ClassFinder {
         private final String type;
         private final ClassInfo declaringClass;
 
-        public FieldInfo(ClassInfo info, Field field){
+        public FieldInfo(ClassInfo info, Field field) {
             super(field);
             this.declaringClass = info;
             this.name = field.getName();
@@ -699,7 +1077,7 @@ public class ClassFinder {
     public class AnnotationInfo extends Annotatable implements Info {
         private final String name;
 
-        public AnnotationInfo(Annotation annotation){
+        public AnnotationInfo(Annotation annotation) {
             this(annotation.getClass().getName());
         }
 
@@ -708,8 +1086,7 @@ public class ClassFinder {
         }
 
         public AnnotationInfo(String name) {
-            name = name.replaceAll("^L|;$", "");
-            name = name.replace('/', '.');
+            name = Type.getType(name).getClassName();
             this.name = name.intern();
         }
 
@@ -722,38 +1099,6 @@ public class ClassFinder {
         }
     }
 
-    private List<Info> getAnnotationInfos(String name) {
-        List<Info> infos = annotated.get(name);
-        if (infos == null) {
-            infos = new ArrayList<Info>();
-            annotated.put(name, infos);
-        }
-        return infos;
-    }
-
-    private void readClassDef(String className) {
-        if (!className.endsWith(".class")) {
-            className = className.replace('.', '/') + ".class";
-        }
-        try {
-            URL resource = classLoader.getResource(className);
-            if (resource != null) {
-                InputStream in = resource.openStream();
-                try {
-                    ClassReader classReader = new ClassReader(in);
-                    classReader.accept(new InfoBuildingVisitor(), ASM_FLAGS);
-                } finally {
-                    in.close();
-                }
-            } else {
-                new Exception("Could not load " + className).printStackTrace();
-            }
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-
-    }
-
     public class InfoBuildingVisitor extends EmptyVisitor {
         private Info info;
 
@@ -764,10 +1109,12 @@ public class ClassFinder {
             this.info = info;
         }
 
+        @Override
         public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
             if (name.endsWith("package-info")) {
                 info = new PackageInfo(javaName(name));
             } else {
+
                 ClassInfo classInfo = new ClassInfo(javaName(name), javaName(superName));
 
                 if (signature == null) {
@@ -779,14 +1126,26 @@ public class ClassFinder {
                     new SignatureReader(signature).accept(new GenericAwareInfoBuildingVisitor(GenericAwareInfoBuildingVisitor.TYPE.CLASS, classInfo));
                 }
                 info = classInfo;
-                classInfos.add(classInfo);                
+
+                classInfos.put(classInfo.getName(), classInfo);
             }
         }
 
         private String javaName(String name) {
-            return (name == null)? null:name.replace('/', '.');
+            return (name == null) ? null : name.replace('/', '.');
         }
 
+        @Override
+        public void visitInnerClass(String name, String outerName, String innerName, int access) {
+            super.visitInnerClass(name, outerName, innerName, access);
+        }
+
+        @Override
+        public void visitAttribute(Attribute attribute) {
+            super.visitAttribute(attribute);
+        }
+
+        @Override
         public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
             AnnotationInfo annotationInfo = new AnnotationInfo(desc);
             info.getAnnotations().add(annotationInfo);
@@ -794,6 +1153,7 @@ public class ClassFinder {
             return new InfoBuildingVisitor(annotationInfo);
         }
 
+        @Override
         public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
             ClassInfo classInfo = ((ClassInfo) info);
             FieldInfo fieldInfo = new FieldInfo(classInfo, name, desc);
@@ -801,13 +1161,17 @@ public class ClassFinder {
             return new InfoBuildingVisitor(fieldInfo);
         }
 
+        @Override
         public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
             ClassInfo classInfo = ((ClassInfo) info);
             MethodInfo methodInfo = new MethodInfo(classInfo, name, desc);
+
             classInfo.getMethods().add(methodInfo);
             return new InfoBuildingVisitor(methodInfo);
         }
 
+
+        @Override
         public AnnotationVisitor visitParameterAnnotation(int param, String desc, boolean visible) {
             MethodInfo methodInfo = ((MethodInfo) info);
             List<AnnotationInfo> annotationInfos = methodInfo.getParameterAnnotations(param);
@@ -828,18 +1192,18 @@ public class ClassFinder {
         }
 
         private Info info;
-        private TYPE type;
-        private STATE state;
+        private GenericAwareInfoBuildingVisitor.TYPE type;
+        private GenericAwareInfoBuildingVisitor.STATE state;
 
         private static boolean debug = false;
 
         public GenericAwareInfoBuildingVisitor() {
         }
 
-        public GenericAwareInfoBuildingVisitor(TYPE type, Info info) {
+        public GenericAwareInfoBuildingVisitor(GenericAwareInfoBuildingVisitor.TYPE type, Info info) {
             this.type = type;
             this.info = info;
-            this.state = STATE.BEGIN;
+            this.state = GenericAwareInfoBuildingVisitor.STATE.BEGIN;
         }
 
         public void visitFormalTypeParameter(String s) {
@@ -848,7 +1212,7 @@ public class ClassFinder {
                 case BEGIN:
                     ((ClassInfo) info).name += "<" + s;
             }
-            state = STATE.FORMAL_TYPE_PARAM;
+            state = GenericAwareInfoBuildingVisitor.STATE.FORMAL_TYPE_PARAM;
         }
 
         public SignatureVisitor visitClassBound() {
@@ -863,14 +1227,14 @@ public class ClassFinder {
 
         public SignatureVisitor visitSuperclass() {
             if (debug) System.out.println(" visitSuperclass()");
-            state = STATE.SUPERCLASS;
+            state = GenericAwareInfoBuildingVisitor.STATE.SUPERCLASS;
             return this;
         }
 
         public SignatureVisitor visitInterface() {
             if (debug) System.out.println(" visitInterface()");
             ((ClassInfo) info).getInterfaces().add("");
-            state = STATE.INTERFACE;
+            state = GenericAwareInfoBuildingVisitor.STATE.INTERFACE;
             return this;
         }
 
@@ -968,12 +1332,286 @@ public class ClassFinder {
                         ((ClassInfo) info).name += ">";
                     }
             }
-            state = STATE.END;
+            state = GenericAwareInfoBuildingVisitor.STATE.END;
         }
 
         private String javaName(String name) {
-            return (name == null)? null:name.replace('/', '.');
+            return (name == null) ? null : name.replace('/', '.');
+        }
+
+    }
+
+    /**
+     * @version $Rev$ $Date$
+     */
+    public abstract class MetaAnnotatedTarget<T> implements AnnotatedTarget<T> {
+        protected final Map<Class<? extends Annotation>, MetaAnnotation> found = new HashMap<Class<? extends Annotation>, MetaAnnotation>();
+        protected final T target;
+
+        public MetaAnnotatedTarget(T target) {
+            this.target = target;
+        }
+
+        @Override
+        public T getTarget() {
+            return target;
+        }
+
+        @Override
+        public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
+            return found.containsKey(annotationClass);
+        }
+
+        @Override
+        public TypeVariable<?>[] getTypeParameters() {
+            return ((Class<?>) getTarget()).getTypeParameters();
+        }
+
+        @Override
+        public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
+            MetaAnnotation<T> annotation = found.get(annotationClass);
+            return (annotation == null) ? null : annotation.getAnnotation();
+        }
+
+        @Override
+        public Annotation[] getAnnotations() {
+            Annotation[] annotations = new Annotation[found.size()];
+
+            int i = 0;
+            for (MetaAnnotation annotation : found.values()) {
+                annotations[i++] = annotation.getAnnotation();
+            }
+
+            return annotations;
+        }
+
+        protected void unroll(Class<? extends Annotation> clazz, int depth) {
+            if (!isMetaAnnotation(clazz)) return;
+
+            for (Annotation annotation : getDeclaredMetaAnnotations(clazz)) {
+                Class<? extends Annotation> type = annotation.annotationType();
+
+                MetaAnnotation existing = found.get(type);
+
+                if (existing != null) {
+                    if (existing.getDepth() > depth) {
+//                        System.out.println("OVERWRITE " + type);
+                        //overwrite
+                        found.put(type, new MetaAnnotation(annotation, depth, clazz));
+                        unroll(type, depth + 1);
+                    } else if (existing.getDepth() < depth) {
+//                        System.out.println("IGNORE " + type);
+                        // ignore, what we have already is higher priority
+                    } else {
+//                        System.out.println("CONFLICT " + type);
+                        // They are the same depth and therefore conflicting
+                        existing.getConflicts().add(new MetaAnnotation(annotation, depth, clazz));
+                    }
+                } else {
+//                    System.out.println("NEW " + type);
+                    found.put(type, new MetaAnnotation(annotation, depth, clazz));
+                    unroll(type, depth + 1);
+                }
+            }
+        }
+
+        private Collection<Annotation> getDeclaredMetaAnnotations(Class<? extends Annotation> clazz) {
+            Map<Class, Annotation> map = new HashMap<Class, Annotation>();
+
+            for (Annotation annotation : clazz.getDeclaredAnnotations()) {
+                map.put(annotation.annotationType(), annotation);
+            }
+
+            for (Info info : annotated.get(clazz.getName())) {
+                if (!clazz.getName().equals(info.getMetaAnnotationName())) continue;
+                for (Annotation annotation : info.getDeclaredAnnotations()) {
+                    map.put(annotation.annotationType(), annotation);
+                }
+
+            }
+
+            Class[] invalid = {
+                    Target.class,
+                    Retention.class,
+                    Documented.class,
+                    Metatype.class,
+                    clazz
+            };
+
+            for (Class c : invalid) {
+                map.remove(c);
+            }
+
+            return map.values();
+        }
+
+        private boolean isMetaAnnotation(Class<? extends Annotation> clazz) {
+            if (!clazz.isAnnotation()) return false;
+
+            for (Annotation annotation : clazz.getDeclaredAnnotations()) {
+                if (metaroots.contains(annotation.annotationType())) return true;
+            }
+
+            return false;
+        }
+
+    }
+
+    private static class MetaAnnotation<T extends Annotation> {
+        private final Class<?> declaringClass;
+        private final T annotation;
+        private final int depth;
+
+        private final List<MetaAnnotation<T>> conflicts = new ArrayList<MetaAnnotation<T>>();
+
+        private MetaAnnotation(T annotation, int depth, Class<?> declaringClass) {
+            this.annotation = annotation;
+            this.depth = depth;
+            this.declaringClass = declaringClass;
+        }
+
+        public T getAnnotation() {
+            return annotation;
+        }
+
+        public int getDepth() {
+            return depth;
+        }
+
+        public Class<?> getDeclaringClass() {
+            return declaringClass;
+        }
+
+        public List<MetaAnnotation<T>> getConflicts() {
+            return conflicts;
+        }
+    }
+
+    private class MetaAnnotatedClass extends MetaAnnotatedTarget<Class<?>> {
+
+        private MetaAnnotatedClass(Class<?> clazz) {
+            super(clazz);
+            for (Annotation annotation : getTarget().getDeclaredAnnotations()) {
+                found.put(annotation.annotationType(), new MetaAnnotation(annotation, 0, getTarget()));
+                unroll(annotation.annotationType(), 1);
+            }
+        }
+
+        @Override
+        public Annotation[] getDeclaredAnnotations() {
+            return getTarget().getDeclaredAnnotations();
+        }
+    }
+
+
+    private class MetaAnnotatedField extends MetaAnnotatedTarget<Field> {
+
+        private MetaAnnotatedField(Field target) {
+            super(target);
+            for (Annotation annotation : target.getDeclaredAnnotations()) {
+                found.put(annotation.annotationType(), new MetaAnnotation(annotation, 0, target.getDeclaringClass()));
+                unroll(annotation.annotationType(), 1);
+            }
+        }
+
+        @Override
+        public Annotation[] getDeclaredAnnotations() {
+            return getTarget().getDeclaredAnnotations();
+        }
+    }
+
+    private class MetaAnnotatedMethod extends MetaAnnotatedTarget<Method> implements AnnotatedMethod<Method> {
+
+        private Method target;
+
+        private MetaAnnotatedMethod(Method target) {
+            super(target);
+            for (Annotation annotation : target.getDeclaredAnnotations()) {
+                found.put(annotation.annotationType(), new MetaAnnotation(annotation, 0, target.getDeclaringClass()));
+                unroll(annotation.annotationType(), 1);
+            }
+
+        }
+
+        @Override
+        public Annotation[] getDeclaredAnnotations() {
+            return getTarget().getDeclaredAnnotations();
+        }
+
+        @Override
+        public Annotation[][] getParameterAnnotations() {
+            return getTarget().getParameterAnnotations();
+        }
+
+        @Override
+        public Class<?> getDeclaringClass() {
+            return getTarget().getDeclaringClass();
+        }
+
+        @Override
+        public String getName() {
+            return getTarget().getName();
+        }
+
+        @Override
+        public int getModifiers() {
+            return getTarget().getModifiers();
+        }
+
+        @Override
+        public TypeVariable<Method>[] getTypeParameters() {
+            return getTarget().getTypeParameters();
+        }
+
+        @Override
+        public Class<?>[] getParameterTypes() {
+            return getTarget().getParameterTypes();
         }
-        
+
+        @Override
+        public java.lang.reflect.Type[] getGenericParameterTypes() {
+            return getTarget().getGenericParameterTypes();
+        }
+
+        @Override
+        public Class<?>[] getExceptionTypes() {
+            return getTarget().getExceptionTypes();
+        }
+
+        @Override
+        public java.lang.reflect.Type[] getGenericExceptionTypes() {
+            return getTarget().getGenericExceptionTypes();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            return getTarget().equals(obj);
+        }
+
+        @Override
+        public int hashCode() {
+            return getTarget().hashCode();
+        }
+
+        @Override
+        public String toString() {
+            return getTarget().toString();
+        }
+
+        @Override
+        public String toGenericString() {
+            return getTarget().toGenericString();
+        }
+
+        @Override
+        public boolean isVarArgs() {
+            return getTarget().isVarArgs();
+        }
+
+        @Override
+        public boolean isSynthetic() {
+            return getTarget().isSynthetic();
+        }
+
     }
-}
+}
\ No newline at end of file

Modified: geronimo/xbean/trunk/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedClassTest.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedClassTest.java?rev=1084331&r1=1084330&r2=1084331&view=diff
==============================================================================
--- geronimo/xbean/trunk/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedClassTest.java (original)
+++ geronimo/xbean/trunk/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedClassTest.java Tue Mar 22 20:00:26 2011
@@ -40,7 +40,7 @@ import static java.lang.annotation.Reten
 public class MetaAnnotatedClassTest extends TestCase {
 
     public void test() throws Exception {
-        MetaAnnotationFinder finder = new MetaAnnotationFinder(new ClassesArchive(Square.class, Circle.class, Triangle.class, Fake.class, Store.class, Farm.class, None.class)).link();
+        AnnotationFinder finder = new AnnotationFinder(new ClassesArchive(Square.class, Circle.class, Triangle.class, Fake.class, Store.class, Farm.class, None.class)).link();
 
         Map<Class<?>, AnnotatedTarget<Class<?>>> map = new HashMap<Class<?>, AnnotatedTarget<Class<?>>>();
 

Modified: geronimo/xbean/trunk/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedMethodTest.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedMethodTest.java?rev=1084331&r1=1084330&r2=1084331&view=diff
==============================================================================
--- geronimo/xbean/trunk/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedMethodTest.java (original)
+++ geronimo/xbean/trunk/xbean-finder/src/test/java/org/apache/xbean/finder/MetaAnnotatedMethodTest.java Tue Mar 22 20:00:26 2011
@@ -35,7 +35,7 @@ import static java.lang.annotation.Reten
 public class MetaAnnotatedMethodTest extends TestCase {
 
     public void test() throws Exception {
-        MetaAnnotationFinder finder = new MetaAnnotationFinder(new ClassesArchive(Square.class, Circle.class, Triangle.class, Oval.class, Store.class, Farm.class, None.class)).link();
+        AnnotationFinder finder = new AnnotationFinder(new ClassesArchive(Square.class, Circle.class, Triangle.class, Oval.class, Store.class, Farm.class, None.class)).link();
 
         Map<String, AnnotatedMethod<Method>> map = new HashMap<String, AnnotatedMethod<Method>>();