(re-)add notion of a special Annotation-specific StubConfigurer subclass, AnnotationConfigurer. Not required for use, but if used, provides methods to simplify the creation of nested annotations a bit
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/proxy/branches/version-2.0-work@1000298 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationConfigurer.java b/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationConfigurer.java
new file mode 100644
index 0000000..2345715
--- /dev/null
+++ b/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationConfigurer.java
@@ -0,0 +1,66 @@
+/*
+ * 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;
+
+/**
+ * Special {@link StubConfigurer} subclass that makes creating nested annotations (somewhat more) convenient.
+ *
+ * @param <A>
+ */
+public abstract class AnnotationConfigurer<A extends Annotation> extends StubConfigurer<A> {
+ AnnotationFactory annotationFactory;
+
+ /**
+ * Create a child annotation of the specified type using a StubConfigurer.
+ * @param <T>
+ * @param configurer, should not be <code>this</code>
+ * @return T
+ * @throws IllegalStateException if called other than when an {@link AnnotationFactory} is executing {@link #configure(Object)}
+ * @throws IllegalArgumentException if <code>configurer == this</code>
+ */
+ protected final <T extends Annotation> T child(StubConfigurer<T> configurer) {
+ if (configurer == this) {
+ throw new IllegalArgumentException("An AnnotationConfigurer cannot configure its own child annotation");
+ }
+ return requireAnnotationFactory().create(configurer);
+ }
+
+ /**
+ * Create a child annotation of the specified type with default behavior.
+ * @param <T>
+ * @param annotationType
+ * @return T
+ * @throws IllegalStateException if called other than when an {@link AnnotationFactory} is executing {@link #configure(Object)}
+ */
+ protected final <T extends Annotation> T child(Class<T> annotationType) {
+ return requireAnnotationFactory().create(annotationType);
+ }
+
+ /**
+ * Get the registered annotationFactory.
+ * @return AnnotationFactory
+ * @throws IllegalStateException if no ongoing annotation stubbing could be detected
+ */
+ synchronized AnnotationFactory requireAnnotationFactory() throws IllegalStateException {
+ if (annotationFactory == null) {
+ throw new IllegalStateException("Could not detect ongoing annotation stubbing");
+ }
+ return annotationFactory;
+ }
+}
\ No newline at end of file
diff --git a/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationFactory.java b/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationFactory.java
index 346d113..e33fbfd 100644
--- a/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationFactory.java
+++ b/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationFactory.java
@@ -155,7 +155,7 @@
private static final ThreadLocal<Object> CONFIGURER = new ThreadLocal<Object>();
- private static final StubConfigurer<Annotation> SHARED_CONFIGURER = new StubConfigurer<Annotation>() {
+ private final StubConfigurer<Annotation> sharedConfigurer = new StubConfigurer<Annotation>() {
/**
* {@inheritDoc}
@@ -175,7 +175,21 @@
if (o instanceof StubConfigurer<?>) {
@SuppressWarnings("unchecked")
final StubConfigurer<Annotation> configurer = (StubConfigurer<Annotation>) o;
- configurer.configure(requireStubInterceptor(), stub);
+ boolean deregisterFactory = false;
+ synchronized (configurer) {
+ try {
+ if (configurer instanceof AnnotationConfigurer<?>) {
+ AnnotationConfigurer<?> annotationConfigurer = (AnnotationConfigurer<?>) configurer;
+ deregisterFactory = true;
+ annotationConfigurer.annotationFactory = AnnotationFactory.this;
+ }
+ configurer.configure(requireStubInterceptor(), stub);
+ } finally {
+ if (deregisterFactory) {
+ ((AnnotationConfigurer<?>) configurer).annotationFactory = null;
+ }
+ }
+ }
}
}
};
@@ -186,7 +200,7 @@
* Create a new AnnotationFactory instance.
*/
public AnnotationFactory() {
- this.proxyFactory = new StubProxyFactory(PROXY_FACTORY, SHARED_CONFIGURER);
+ this.proxyFactory = new StubProxyFactory(PROXY_FACTORY, sharedConfigurer);
}
/**
diff --git a/stub/src/test/java/org/apache/commons/proxy2/stub/AnnotationFactoryTest.java b/stub/src/test/java/org/apache/commons/proxy2/stub/AnnotationFactoryTest.java
index 9937d59..d4c2139 100644
--- a/stub/src/test/java/org/apache/commons/proxy2/stub/AnnotationFactoryTest.java
+++ b/stub/src/test/java/org/apache/commons/proxy2/stub/AnnotationFactoryTest.java
@@ -19,6 +19,8 @@
import static org.junit.Assert.*;
+import java.lang.annotation.Annotation;
+
import org.junit.Before;
import org.junit.Test;
@@ -36,11 +38,11 @@
@Test
public void testDefaultAnnotation() {
CustomAnnotation customAnnotation = annotationFactory.create(CustomAnnotation.class);
+ assertNotNull(customAnnotation);
assertEquals(CustomAnnotation.class, customAnnotation.annotationType());
assertEquals("", customAnnotation.annString());
assertEquals(0, customAnnotation.finiteValues().length);
assertNull(customAnnotation.someType());
-
}
@Test
@@ -54,6 +56,7 @@
}
});
+ assertNotNull(customAnnotation);
assertEquals(CustomAnnotation.class, customAnnotation.annotationType());
assertEquals("hey", customAnnotation.annString());
assertArrayEquals(new FiniteValues[] { FiniteValues.ONE, FiniteValues.THREE }, customAnnotation.finiteValues());
@@ -61,21 +64,103 @@
}
@Test
- public void testNestedStubbedAnnotation() {
+ public void testStubbedAnnotationWithDefaultChild() {
NestingAnnotation nestingAnnotation =
- annotationFactory.create(new StubConfigurer<NestingAnnotation>() {
+ annotationFactory.create(new AnnotationConfigurer<NestingAnnotation>() {
@Override
protected void configure(NestingAnnotation stub) {
- when(stub.child()).thenReturn(annotationFactory.create(CustomAnnotation.class))
+ when(stub.child()).thenReturn(child(CustomAnnotation.class))
.when(stub.somethingElse()).thenReturn("somethingElse");
}
});
+ assertNotNull(nestingAnnotation);
+ assertNotNull(nestingAnnotation.child());
assertEquals("", nestingAnnotation.child().annString());
assertEquals(0, nestingAnnotation.child().finiteValues().length);
assertEquals(null, nestingAnnotation.child().someType());
assertEquals("somethingElse", nestingAnnotation.somethingElse());
}
+ @Test
+ public void testStubbedAnnotationWithConfiguredChild() {
+ NestingAnnotation nestingAnnotation = annotationFactory.create(new AnnotationConfigurer<NestingAnnotation>() {
+ @Override
+ protected void configure(NestingAnnotation stub) {
+ when(stub.child()).thenReturn(child(new StubConfigurer<CustomAnnotation>() {
+
+ @Override
+ protected void configure(CustomAnnotation stub) {
+ when(stub.annString()).thenReturn("wow").when(stub.finiteValues())
+ .thenReturn(FiniteValues.values()).when(stub.someType()).thenReturn(Class.class);
+ }
+ })).when(stub.somethingElse()).thenReturn("somethingElse");
+ }
+ });
+ assertNotNull(nestingAnnotation);
+ assertNotNull(nestingAnnotation.child());
+ assertEquals("wow", nestingAnnotation.child().annString());
+ assertEquals(3, nestingAnnotation.child().finiteValues().length);
+ assertEquals(Class.class, nestingAnnotation.child().someType());
+ assertEquals("somethingElse", nestingAnnotation.somethingElse());
+ }
+
+ @Test
+ public void testStubbedAnnotationWithDoubleNesting() {
+ OuterContainer outerContainer = annotationFactory.create(new AnnotationConfigurer<OuterContainer>() {
+
+ @Override
+ protected void configure(OuterContainer stub) {
+ when(stub.nest()).thenReturn(child(new AnnotationConfigurer<NestingAnnotation>() {
+
+ @Override
+ protected void configure(NestingAnnotation stub) {
+ when(stub.child()).thenReturn(child(new AnnotationConfigurer<CustomAnnotation>() {
+
+ @Override
+ protected void configure(CustomAnnotation stub) {
+ when(stub.annString()).thenReturn("wow").when(stub.finiteValues())
+ .thenReturn(FiniteValues.values()).when(stub.someType()).thenReturn(Class.class);
+ }
+
+ })).when(stub.somethingElse()).thenReturn("somethingElse");
+ }
+ }));
+ }
+ });
+ assertNotNull(outerContainer);
+ NestingAnnotation nestingAnnotation = outerContainer.nest();
+ assertNotNull(nestingAnnotation);
+ assertNotNull(nestingAnnotation.child());
+ assertEquals("wow", nestingAnnotation.child().annString());
+ assertEquals(3, nestingAnnotation.child().finiteValues().length);
+ assertEquals(Class.class, nestingAnnotation.child().someType());
+ assertEquals("somethingElse", nestingAnnotation.somethingElse());
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testCannotConfigureOwnChild() {
+ annotationFactory.create(new AnnotationConfigurer<NestingAnnotation>() {
+
+ @Override
+ protected void configure(NestingAnnotation stub) {
+ child(this);
+ }
+ });
+ }
+
+ @Test(expected=IllegalStateException.class)
+ public void testChildRequiresOngoingStubbing() {
+ new AnnotationConfigurer<Annotation>() {
+ {
+ child(CustomAnnotation.class);
+ }
+
+ @Override
+ protected void configure(Annotation stub) {
+ }
+ };
+ }
+
public @interface NestingAnnotation {
CustomAnnotation child();
@@ -90,6 +175,10 @@
Class<?> someType();
}
+ public @interface OuterContainer {
+ NestingAnnotation nest();
+ }
+
public enum FiniteValues {
ONE, TWO, THREE;
}