[WEAVER-12] Provide a mechanism for working with all classfiles found in the weave environment

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/weaver/trunk@1743444 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/processor/src/main/java/org/apache/commons/weaver/Finder.java b/processor/src/main/java/org/apache/commons/weaver/Finder.java
index 475a008..b31a8a2 100644
--- a/processor/src/main/java/org/apache/commons/weaver/Finder.java
+++ b/processor/src/main/java/org/apache/commons/weaver/Finder.java
@@ -28,11 +28,14 @@
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.IdentityHashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Predicate;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.Validate;
@@ -390,23 +393,40 @@
             return result;
         }
 
+        /**
+         * Get the list of objects representing all scanned classes.
+         * @since 1.3
+         * @return {@link List} of {@link Annotated}{@code <Class<?>>}
+         */
+        public List<Annotated<Class<?>>> getAllClasses() {
+            return annotate(originalInfos.values());
+        }
+
         public List<Annotated<Class<?>>> findAnnotatedClasses(final Class<? extends Annotation> annotation) {
             Finder.this.findAnnotatedClasses(annotation);
+
+            final List<Annotated<Class<?>>> annotatedClasses = annotate(getAnnotationInfos(annotation.getName()));
+            CollectionUtils.filter(annotatedClasses, new Predicate<Annotated<Class<?>>>() {
+
+                @Override
+                public boolean evaluate(Annotated<Class<?>> annotatedClass) {
+                    return annotatedClass.isAnnotationPresent(annotation);
+                }
+            });
+            return annotatedClasses;
+        }
+
+        private List<Annotated<Class<?>>> annotate(Collection<? extends Info> infos) {
             final List<Annotated<Class<?>>> result = new ArrayList<Annotated<Class<?>>>();
-            for (final Info info : getAnnotationInfos(annotation.getName())) {
+            for (final Info info : infos) {
                 if (info instanceof ClassInfo) {
                     final ClassInfo classInfo = (ClassInfo) info;
 
-                    IncludesClassfile<Class<?>> annotated;
                     try {
-                        annotated =
-                            new IncludesClassfile<Class<?>>(classInfo.get(), classfileAnnotationsFor(classInfo));
+                        result.add(new IncludesClassfile<Class<?>>(classInfo.get(), classfileAnnotationsFor(classInfo)));
                     } catch (final ClassNotFoundException e) {
                         continue;
                     }
-                    if (annotated.isAnnotationPresent(annotation)) {
-                        result.add(annotated);
-                    }
                 }
             }
             return result;
@@ -660,57 +680,63 @@
     public ScanResult scan(final ScanRequest request) {
         final ScanResult result = new ScanResult();
 
-        for (final WeaveInterest interest : request.getInterests()) {
-            switch (interest.target) {
-            case PACKAGE:
-                for (final Annotated<Package> pkg : this.withAnnotations().findAnnotatedPackages(
-                    interest.annotationType)) {
-                    result.getWeavable(pkg.get()).addAnnotations(pkg.getAnnotations());
+        if (!request.isConstrained() || request.getSupertypes().contains(Object.class)) {
+            for (final Annotated<Class<?>> type : this.withAnnotations().getAllClasses()) {
+                result.getWeavable(type.get()).addAnnotations(type.getAnnotations());
+            }
+        } else {
+            for (final WeaveInterest interest : request.getInterests()) {
+                switch (interest.target) {
+                case PACKAGE:
+                    for (final Annotated<Package> pkg : this.withAnnotations().findAnnotatedPackages(
+                        interest.annotationType)) {
+                        result.getWeavable(pkg.get()).addAnnotations(pkg.getAnnotations());
+                    }
+                    break;
+                case TYPE:
+                    for (final Annotated<Class<?>> type : this.withAnnotations().findAnnotatedClasses(
+                        interest.annotationType)) {
+                        result.getWeavable(type.get()).addAnnotations(type.getAnnotations());
+                    }
+                    break;
+                case METHOD:
+                    for (final Annotated<Method> method : this.withAnnotations().findAnnotatedMethods(
+                        interest.annotationType)) {
+                        result.getWeavable(method.get()).addAnnotations(method.getAnnotations());
+                    }
+                    break;
+                case CONSTRUCTOR:
+                    for (final Annotated<Constructor<?>> ctor : this.withAnnotations().findAnnotatedConstructors(
+                        interest.annotationType)) {
+                        result.getWeavable(ctor.get()).addAnnotations(ctor.getAnnotations());
+                    }
+                    break;
+                case FIELD:
+                    for (final Annotated<Field> fld : this.withAnnotations().findAnnotatedFields(interest.annotationType)) {
+                        result.getWeavable(fld.get()).addAnnotations(fld.getAnnotations());
+                    }
+                    break;
+                case PARAMETER:
+                    for (final Annotated<Parameter<Method>> parameter : this.withAnnotations()
+                        .findAnnotatedMethodParameters(interest.annotationType)) {
+                        result.getWeavable(parameter.get().getDeclaringExecutable())
+                            .getWeavableParameter(parameter.get().getIndex()).addAnnotations(parameter.getAnnotations());
+                    }
+                    for (final Annotated<Parameter<Constructor<?>>> parameter : this.withAnnotations()
+                        .findAnnotatedConstructorParameters(interest.annotationType)) {
+                        result.getWeavable(parameter.get().getDeclaringExecutable())
+                            .getWeavableParameter(parameter.get().getIndex()).addAnnotations(parameter.getAnnotations());
+                    }
+                    break;
+                default:
+                    // should we log something?
+                    break;
                 }
-                break;
-            case TYPE:
-                for (final Annotated<Class<?>> type : this.withAnnotations().findAnnotatedClasses(
-                    interest.annotationType)) {
+            }
+            for (final Class<?> supertype : request.getSupertypes()) {
+                for (final Annotated<Class<?>> type : this.withAnnotations().findAssignableTypes(supertype)) {
                     result.getWeavable(type.get()).addAnnotations(type.getAnnotations());
                 }
-                break;
-            case METHOD:
-                for (final Annotated<Method> method : this.withAnnotations().findAnnotatedMethods(
-                    interest.annotationType)) {
-                    result.getWeavable(method.get()).addAnnotations(method.getAnnotations());
-                }
-                break;
-            case CONSTRUCTOR:
-                for (final Annotated<Constructor<?>> ctor : this.withAnnotations().findAnnotatedConstructors(
-                    interest.annotationType)) {
-                    result.getWeavable(ctor.get()).addAnnotations(ctor.getAnnotations());
-                }
-                break;
-            case FIELD:
-                for (final Annotated<Field> fld : this.withAnnotations().findAnnotatedFields(interest.annotationType)) {
-                    result.getWeavable(fld.get()).addAnnotations(fld.getAnnotations());
-                }
-                break;
-            case PARAMETER:
-                for (final Annotated<Parameter<Method>> parameter : this.withAnnotations()
-                    .findAnnotatedMethodParameters(interest.annotationType)) {
-                    result.getWeavable(parameter.get().getDeclaringExecutable())
-                        .getWeavableParameter(parameter.get().getIndex()).addAnnotations(parameter.getAnnotations());
-                }
-                for (final Annotated<Parameter<Constructor<?>>> parameter : this.withAnnotations()
-                    .findAnnotatedConstructorParameters(interest.annotationType)) {
-                    result.getWeavable(parameter.get().getDeclaringExecutable())
-                        .getWeavableParameter(parameter.get().getIndex()).addAnnotations(parameter.getAnnotations());
-                }
-                break;
-            default:
-                // should we log something?
-                break;
-            }
-        }
-        for (final Class<?> supertype : request.getSupertypes()) {
-            for (final Annotated<Class<?>> type : this.withAnnotations().findAssignableTypes(supertype)) {
-                result.getWeavable(type.get()).addAnnotations(type.getAnnotations());
             }
         }
         return inflater.inflate(result);
diff --git a/processor/src/main/java/org/apache/commons/weaver/model/ScanRequest.java b/processor/src/main/java/org/apache/commons/weaver/model/ScanRequest.java
index 08de040..06638b4 100644
--- a/processor/src/main/java/org/apache/commons/weaver/model/ScanRequest.java
+++ b/processor/src/main/java/org/apache/commons/weaver/model/ScanRequest.java
@@ -77,4 +77,13 @@
         return Collections.unmodifiableSet(supertypes);
     }
 
+    /**
+     * Learn whether this {@link ScanRequest} has been constrained. An unconstrained {@link ScanRequest} will return all
+     * known types.
+     * @return {@code boolean}
+     * @since 1.3
+     */
+    public boolean isConstrained() {
+        return !interests.isEmpty() || !supertypes.isEmpty();
+    }
 }
diff --git a/processor/src/test/java/org/apache/commons/weaver/FinderTest.java b/processor/src/test/java/org/apache/commons/weaver/FinderTest.java
index bdceb42..d3832ad 100644
--- a/processor/src/test/java/org/apache/commons/weaver/FinderTest.java
+++ b/processor/src/test/java/org/apache/commons/weaver/FinderTest.java
@@ -26,17 +26,22 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
 import java.lang.reflect.Field;
 import java.net.URLClassLoader;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.commons.lang3.Validate;
 import org.apache.commons.weaver.test.WeaverTestBase;
 import org.apache.commons.weaver.test.beans.AbstractTestBean;
 import org.apache.commons.weaver.test.beans.ComplexAnnotations;
@@ -49,6 +54,7 @@
 import org.apache.commons.weaver.utils.URLArray;
 import org.apache.xbean.finder.Annotated;
 import org.apache.xbean.finder.archive.FileArchive;
+import org.hamcrest.Matchers;
 import org.junit.Test;
 
 public class FinderTest extends WeaverTestBase {
@@ -282,4 +288,28 @@
         assertTrue(subclasses.contains(TestBeanWithClassAnnotation.class));
         assertTrue(subclasses.contains(TestBeanWithMethodAnnotation.class));
     }
+
+    @Test
+    public void testFindAllTypes() throws IOException {
+        addClassForScanning(TestBeanInterface.class);
+        addClassForScanning(AbstractTestBean.class);
+        addClassForScanning(TestBeanWithClassAnnotation.class);
+        addClassForScanning(TestBeanWithMethodAnnotation.class);
+
+        List<Annotated<Class<?>>> allClasses = finder().withAnnotations().getAllClasses();
+        assertThat(extract(allClasses), Matchers.<Class<?>> containsInAnyOrder(TestBeanInterface.class,
+            AbstractTestBean.class, TestBeanWithClassAnnotation.class, TestBeanWithMethodAnnotation.class));
+    }
+
+    private List<Class<?>> extract(List<Annotated<Class<?>>> input) {
+        Validate.noNullElements(input);
+        if (input.isEmpty()) {
+            return Collections.emptyList();
+        }
+        final List<Class<?>> result = new ArrayList<Class<?>>(input.size());
+        for (Annotated<Class<?>> c : input) {
+            result.add(c.get());
+        }
+        return result;
+    }
 }