make annotationType available as early as possible for stubbed annotations; implement stubbed annotation arrays as nested annotation members

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/proxy/branches/version-2.0-work@1554962 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/core/src/main/java/org/apache/commons/proxy2/stub/AnnotationBuilder.java b/core/src/main/java/org/apache/commons/proxy2/stub/AnnotationBuilder.java
index 75b3ae3..48dafd8 100644
--- a/core/src/main/java/org/apache/commons/proxy2/stub/AnnotationBuilder.java
+++ b/core/src/main/java/org/apache/commons/proxy2/stub/AnnotationBuilder.java
@@ -235,18 +235,21 @@
     {
         super(PROXY_FACTORY, type, invoker);
         this.annotationType = type;
+        train(new AnnotationTypeTrainer<A>(type));
     }
 
     private AnnotationBuilder(Class<A> type, ObjectProvider<? extends A> provider)
     {
         super(PROXY_FACTORY, type, provider);
         this.annotationType = type;
+        train(new AnnotationTypeTrainer<A>(type));
     }
 
     private AnnotationBuilder(Class<A> type, A target)
     {
         super(PROXY_FACTORY, type, target);
         this.annotationType = type;
+        train(new AnnotationTypeTrainer<A>(type));
     }
 
     public AnnotationBuilder<A> withMembers(Map<String, ?> members)
@@ -259,18 +262,4 @@
     {
         return (AnnotationBuilder<A>) super.train(trainer);
     }
-
-    @Override
-    public A build()
-    {
-        train(new AnnotationTrainer<A>(annotationType)
-        {
-            @Override
-            protected void train(A trainee)
-            {
-                when(trainee.annotationType()).thenReturn(annotationType);
-            }
-        });
-        return super.build();
-    }
 }
diff --git a/core/src/main/java/org/apache/commons/proxy2/stub/AnnotationTypeTrainer.java b/core/src/main/java/org/apache/commons/proxy2/stub/AnnotationTypeTrainer.java
new file mode 100644
index 0000000..5754458
--- /dev/null
+++ b/core/src/main/java/org/apache/commons/proxy2/stub/AnnotationTypeTrainer.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.proxy2.stub;
+
+import java.lang.annotation.Annotation;
+
+class AnnotationTypeTrainer<R extends Annotation> extends AnnotationTrainer<R>
+{
+
+    AnnotationTypeTrainer(Class<R> annotationType)
+    {
+        super(annotationType);
+    }
+
+    @Override
+    protected void train(R trainee)
+    {
+        when(trainee.annotationType()).thenReturn(traineeType);
+    }
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/apache/commons/proxy2/stub/BaseAnnotationTrainer.java b/core/src/main/java/org/apache/commons/proxy2/stub/BaseAnnotationTrainer.java
index 39ae6b7..2a23fcf 100644
--- a/core/src/main/java/org/apache/commons/proxy2/stub/BaseAnnotationTrainer.java
+++ b/core/src/main/java/org/apache/commons/proxy2/stub/BaseAnnotationTrainer.java
@@ -21,26 +21,31 @@
 
 import org.apache.commons.proxy2.interceptor.InterceptorUtils;
 
-public abstract class BaseAnnotationTrainer<S extends BaseAnnotationTrainer<S, A>, A extends Annotation> extends BaseTrainer<S, A>
+public abstract class BaseAnnotationTrainer<S extends BaseAnnotationTrainer<S, A>, A extends Annotation> extends
+        BaseTrainer<S, A>
 {
-    protected BaseAnnotationTrainer() {
+    protected BaseAnnotationTrainer()
+    {
         super();
     }
 
-    protected BaseAnnotationTrainer(Class<A> traineeType) {
+    protected BaseAnnotationTrainer(Class<A> traineeType)
+    {
         super(traineeType);
     }
 
     protected class WhenAnnotation<R> extends WhenObject<R>
     {
-    	public S thenStub(Class<R> type) {
+        public S thenStub(Class<R> type)
+        {
             trainingContext().push(type);
             trainingContext().then(InterceptorUtils.constant(trainingContext().pop(AnnotationInvoker.INSTANCE)));
             return self();
         }
 
         @Override
-        public S thenStub(BaseTrainer<?, R> trainer) {
+        public S thenStub(BaseTrainer<?, R> trainer)
+        {
             final R trainee = trainingContext().push(trainer.traineeType);
             trainer.train(trainee);
             trainingContext().then(InterceptorUtils.constant(trainingContext().pop(AnnotationInvoker.INSTANCE)));
@@ -48,9 +53,59 @@
         }
     }
 
+    protected class WhenAnnotationArray<R> extends WhenObjectArray<R>
+    {
+        protected WhenAnnotationArray(Class<? extends R> componentType)
+        {
+            super(componentType);
+        }
+
+        @Override
+        public StubAnnotationArrayBuilder<R> thenBuildArray()
+        {
+            return new StubAnnotationArrayBuilder<R>(componentType);
+        }
+    }
+
+    protected class StubAnnotationArrayBuilder<R> extends StubArrayBuilder<R>
+    {
+        private final BaseTrainer<?, R> annotationTypeTrainer;
+
+        private <N extends Annotation> StubAnnotationArrayBuilder(final Class<? extends R> componentType)
+        {
+            super(componentType);
+            @SuppressWarnings("unchecked")
+            final Class<N> annotationType = (Class<N>) componentType;
+            @SuppressWarnings("unchecked")
+            final BaseTrainer<?, R> annotationTypeTrainer = (BaseTrainer<?, R>) new AnnotationTypeTrainer<N>(
+                    annotationType);
+            this.annotationTypeTrainer = annotationTypeTrainer;
+        }
+
+        @Override
+        public StubAnnotationArrayBuilder<R> addElement(BaseTrainer<?, R> trainer)
+        {
+            final R trainee = trainingContext().push(trainer.traineeType);
+
+            annotationTypeTrainer.train(trainee);
+            trainer.train(trainee);
+
+            elements.add(trainingContext().<R> pop());
+            return this;
+        }
+    }
+
     @Override
-	public <R> WhenAnnotation<R> when(R expression) {
+    public <R> WhenAnnotation<R> when(R expression)
+    {
         return new WhenAnnotation<R>();
     }
 
+    @Override
+    public <R> WhenAnnotationArray<R> when(R[] expression)
+    {
+        @SuppressWarnings("unchecked")
+        final Class<? extends R> componentType = (Class<? extends R>) expression.getClass().getComponentType();
+        return new WhenAnnotationArray<R>(componentType);
+    }
 }
diff --git a/core/src/main/java/org/apache/commons/proxy2/stub/BaseTrainer.java b/core/src/main/java/org/apache/commons/proxy2/stub/BaseTrainer.java
index 6c97c8b..f053875 100644
--- a/core/src/main/java/org/apache/commons/proxy2/stub/BaseTrainer.java
+++ b/core/src/main/java/org/apache/commons/proxy2/stub/BaseTrainer.java
@@ -300,9 +300,9 @@
 
     protected class WhenObjectArray<R> extends BaseWhen<R[]>
     {
-        private final Class<? extends R> componentType;
+        protected final Class<? extends R> componentType;
 
-        private WhenObjectArray(Class<? extends R> componentType)
+        protected WhenObjectArray(Class<? extends R> componentType)
         {
             this.componentType = componentType;
         }
@@ -321,10 +321,10 @@
 
     protected class StubArrayBuilder<R>
     {
-        private final Class<? extends R> componentType;
-        private final List<R> elements = new ArrayList<R>();
+        protected final List<R> elements = new ArrayList<R>();
+        protected final Class<? extends R> componentType;
 
-        private StubArrayBuilder(Class<? extends R> componentType)
+        protected StubArrayBuilder(Class<? extends R> componentType)
         {
             this.componentType = componentType;
         }
diff --git a/test/src/test/java/org/apache/commons/proxy2/stub/AnnotationBuilderTest.java b/test/src/test/java/org/apache/commons/proxy2/stub/AnnotationBuilderTest.java
index 5a010be..d6f2bf0 100644
--- a/test/src/test/java/org/apache/commons/proxy2/stub/AnnotationBuilderTest.java
+++ b/test/src/test/java/org/apache/commons/proxy2/stub/AnnotationBuilderTest.java
@@ -102,6 +102,41 @@
         assertEquals(Object.class, customAnnotation.someType());
     }
 
+    @Test
+    public void testNestedStubbedAnnotationArray()
+    {
+        final NestingAnnotation nestingAnnotation = AnnotationBuilder.of(NestingAnnotation.class)
+                .train(new AnnotationTrainer<NestingAnnotation>()
+                {
+
+                    @Override
+                    protected void train(NestingAnnotation trainee)
+                    {
+                        when(trainee.children()).thenBuildArray().addElement(new AnnotationTrainer<CustomAnnotation>()
+                        {
+                            @Override
+                            protected void train(CustomAnnotation trainee)
+                            {
+                                when(trainee.finiteValues()).thenReturn(FiniteValues.ONE, FiniteValues.THREE);
+                            }
+                        }).addElement(new AnnotationTrainer<CustomAnnotation>()
+                        {
+                            @Override
+                            protected void train(CustomAnnotation trainee)
+                            {
+                                when(trainee.finiteValues()).thenReturn(FiniteValues.TWO);
+                            }
+                        }).build();
+                    }
+                }).build();
+
+        assertNull(nestingAnnotation.child());
+        assertEquals(2, nestingAnnotation.children().length);
+        assertArrayEquals(new FiniteValues[] { FiniteValues.ONE, FiniteValues.THREE },
+                nestingAnnotation.children()[0].finiteValues());
+        assertArrayEquals(new FiniteValues[] { FiniteValues.TWO }, nestingAnnotation.children()[1].finiteValues());
+    }
+
     @Test(expected = IllegalArgumentException.class)
     public void testBadMemberMap()
     {
@@ -114,6 +149,8 @@
         CustomAnnotation child();
 
         String somethingElse();
+
+        CustomAnnotation[] children() default {};
     }
 
     public @interface CustomAnnotation