delegating annotation stubs
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/proxy/branches/version-2.0-work@1347680 13f79535-47bb-0310-9956-ffa450edef68
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 90a4a73..578997e 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
@@ -26,6 +26,7 @@
import java.util.Map;
import org.apache.commons.lang3.AnnotationUtils;
+import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.proxy2.Interceptor;
import org.apache.commons.proxy2.Invocation;
@@ -34,6 +35,7 @@
import org.apache.commons.proxy2.ProxyFactory;
import org.apache.commons.proxy2.ProxyUtils;
import org.apache.commons.proxy2.impl.AbstractProxyFactory;
+import org.apache.commons.proxy2.provider.ConstantProvider;
/**
* {@link AnnotationFactory} provides a simplified API over {@link StubProxyFactory}
@@ -164,6 +166,9 @@
@Override
protected void configure(A stub) {
+ if (attributes == null) {
+ return;
+ }
When<Object> bud;
StubConfiguration dy = this;
for (Map.Entry<String, Object> attr : attributes.entrySet()) {
@@ -251,61 +256,100 @@
* @return stubbed annotation proxy
*/
public <A extends Annotation> A create(StubConfigurer<A> configurer) {
- @SuppressWarnings("unchecked")
- final A result = (A) createInternal(Thread.currentThread().getContextClassLoader(), configurer);
- return result;
+ return create(Thread.currentThread().getContextClassLoader(), Validate.notNull(configurer, "null configurer"));
+ }
+
+ /**
+ * Create a delegating annotation of the type supported by <code>configurer</code>.
+ * @param <A>
+ * @param target not {@code null}
+ * @param configurer not {@code null}
+ * @return stubbed annotation proxy
+ */
+ public <A extends Annotation> A createDelegator(A target, StubConfigurer<A> configurer) {
+ return createInternal(Thread.currentThread().getContextClassLoader(),
+ Validate.notNull(target, "null target"), Validate.notNull(configurer, "null configurer"));
}
/**
* Create an annotation of the type supported by <code>configurer</code> in the specified classpath.
* @param <A>
- * @param classLoader
- * @param configurer
+ * @param classLoader not {@code null}
+ * @param configurer not {@code null}
* @return stubbed annotation proxy
*/
public <A extends Annotation> A create(ClassLoader classLoader, StubConfigurer<A> configurer) {
- @SuppressWarnings("unchecked")
- final A result = (A) createInternal(classLoader, configurer);
- return result;
+ return createInternal(Validate.notNull(classLoader, "null classLoader"),
+ null, Validate.notNull(configurer, "null configurer"));
+ }
+
+ /**
+ * Create a delegating annotation of the type supported by <code>configurer</code> in the specified classpath.
+ * @param <A>
+ * @param classLoader not {@code null}
+ * @param target not {@code null}
+ * @param configurer not {@code null}
+ * @return stubbed annotation proxy
+ */
+ public <A extends Annotation> A createDelegator(ClassLoader classLoader, A target, StubConfigurer<A> configurer) {
+ return createInternal(Validate.notNull(classLoader, "null classLoader"),
+ Validate.notNull(target, "null target"), Validate.notNull(configurer, "null configurer"));
}
/**
* Create an annotation of <code>annotationType</code> with fully default behavior.
* @param <A>
- * @param classLoader
- * @param annotationType
+ * @param annotationType not {@code null}
* @return stubbed annotation proxy
*/
public <A extends Annotation> A create(Class<A> annotationType) {
@SuppressWarnings("unchecked")
- final A result = (A) createInternal(Thread.currentThread().getContextClassLoader(), annotationType);
+ final A result =
+ (A) createInternal(Thread.currentThread().getContextClassLoader(),
+ Validate.notNull(annotationType, "null annotationType"));
return result;
}
/**
* Create an annotation of <code>annotationType</code> with fully default behavior.
* @param <A>
- * @param classLoader
- * @param annotationType
+ * @param classLoader not {@code null}
+ * @param annotationType not {@code null}
* @return stubbed annotation proxy
*/
public <A extends Annotation> A create(ClassLoader classLoader, Class<A> annotationType) {
@SuppressWarnings("unchecked")
- final A result = (A) createInternal(classLoader, annotationType);
+ final A result =
+ (A) createInternal(Validate.notNull(classLoader, "null classLoader"),
+ Validate.notNull(annotationType, "null annotationType"));
return result;
}
/**
* Create an annotation of <code>annotationType</code> with behavior specified by a {@link String}-keyed {@link Map}.
* @param <A>
- * @param classLoader
- * @param annotationType
+ * @param annotationType not {@code null}
* @param attributes
* @return stubbed annotation proxy
*/
public <A extends Annotation> A create(Class<A> annotationType, Map<String, Object> attributes) {
- return attributes == null || attributes.isEmpty() ? create(annotationType)
- : create(new MapBasedAnnotationConfigurer<A>(annotationType, attributes));
+ if (attributes == null || attributes.isEmpty()) {
+ return create(annotationType);
+ }
+ return create(new MapBasedAnnotationConfigurer<A>(annotationType, attributes));
+ }
+
+ /**
+ * Create a delegating annotation of <code>annotationType</code> with behavior specified by a {@link String}-keyed {@link Map}.
+ * @param <A>
+ * @param target not {@code null}
+ * @param attributes
+ * @return stubbed annotation proxy
+ */
+ public <A extends Annotation> A createDelegator(A target, Map<String, Object> attributes) {
+ @SuppressWarnings("unchecked")
+ final Class<A> annotationType = (Class<A>) Validate.notNull(target, "null target").annotationType();
+ return createDelegator(target, new MapBasedAnnotationConfigurer<A>(annotationType, attributes));
}
/**
@@ -318,11 +362,28 @@
*/
public <A extends Annotation> A create(ClassLoader classLoader, Class<A> annotationType,
Map<String, Object> attributes) {
- return attributes == null || attributes.isEmpty() ? create(classLoader, annotationType) : create(classLoader,
- new MapBasedAnnotationConfigurer<A>(annotationType, attributes));
+ return create(classLoader, new MapBasedAnnotationConfigurer<A>(annotationType, attributes));
+ }
+
+ /**
+ * Create a delegating annotation of <code>annotationType</code> with behavior specified by a {@link String}-keyed {@link Map}.
+ * @param <A>
+ * @param classLoader
+ * @param target
+ * @param attributes
+ * @return stubbed annotation proxy
+ */
+ public <A extends Annotation> A createDelegator(ClassLoader classLoader, A target, Map<String, Object> attributes) {
+ @SuppressWarnings("unchecked")
+ final Class<A> annotationType = (Class<A>) Validate.notNull(target, "null target").annotationType();
+ return createDelegator(classLoader, target, new MapBasedAnnotationConfigurer<A>(annotationType, attributes));
}
private <A extends Annotation> A createInternal(ClassLoader classLoader, Object configurer) {
+ return createInternal(classLoader, null, configurer);
+ }
+
+ private <A extends Annotation> A createInternal(ClassLoader classLoader, A target, Object configurer) {
final Object existingConfigurer = CONFIGURER.get();
final boolean outerContext = CONTEXT.get() == null;
try {
@@ -330,8 +391,17 @@
if (outerContext) {
CONTEXT.set(ImmutablePair.of(this, classLoader));
}
- @SuppressWarnings("unchecked")
- final A result = (A) proxyFactory.createInvokerProxy(classLoader, ANNOTATION_INVOKER, getStubType());
+ final A result;
+ if (target == null) {
+ @SuppressWarnings("unchecked")
+ A invoker = (A) proxyFactory.createInvokerProxy(classLoader, ANNOTATION_INVOKER, getStubType());
+ result = invoker;
+ } else {
+ @SuppressWarnings("unchecked")
+ A delegator =
+ (A) proxyFactory.createDelegatorProxy(classLoader, new ConstantProvider<A>(target), getStubType());
+ result = delegator;
+ }
return validate(result);
} finally {
if (existingConfigurer == null) {
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 aa37ab7..7a3b04b 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
@@ -22,9 +22,13 @@
import static org.junit.Assert.assertNotNull;
import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import org.apache.commons.lang3.reflect.FieldUtils;
import org.junit.Before;
import org.junit.Test;
@@ -32,6 +36,7 @@
* Test {@link AnnotationFactory}.
*/
public class AnnotationFactoryTest {
+ @CustomAnnotation(annString = "FOO", finiteValues = { FiniteValues.ONE, FiniteValues.TWO, FiniteValues.THREE }, someType = Object.class)
private AnnotationFactory annotationFactory;
@Before
@@ -167,12 +172,65 @@
annotationFactory.create(CustomAnnotation.class, attributes);
}
+ @Test
+ public void testDelegator() {
+ final boolean forceAccess = true;
+ final CustomAnnotation sourceAnnotation =
+ FieldUtils.getDeclaredField(AnnotationFactoryTest.class, "annotationFactory", forceAccess).getAnnotation(
+ CustomAnnotation.class);
+ assertNotNull(sourceAnnotation);
+ CustomAnnotation stub =
+ annotationFactory.createDelegator(sourceAnnotation, new AnnotationConfigurer<CustomAnnotation>() {
+
+ @Override
+ protected void configure(CustomAnnotation stub) {
+ when(stub.finiteValues()).thenReturn(FiniteValues.ONE);
+ }
+ });
+ assertEquals(CustomAnnotation.class, stub.annotationType());
+ assertEquals(Object.class, stub.someType());
+ assertEquals("FOO", stub.annString());
+ assertArrayEquals(new FiniteValues[] { FiniteValues.ONE }, stub.finiteValues());
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testDelegatorMissingTarget() {
+ annotationFactory.createDelegator(null, new StubConfigurer<CustomAnnotation>() {
+
+ @Override
+ protected void configure(CustomAnnotation stub) {
+ }
+ });
+ }
+
+ @Test
+ public void testDelegatorWithAttributes() {
+ final boolean forceAccess = true;
+ final CustomAnnotation sourceAnnotation =
+ FieldUtils.getDeclaredField(AnnotationFactoryTest.class, "annotationFactory", forceAccess).getAnnotation(
+ CustomAnnotation.class);
+ assertNotNull(sourceAnnotation);
+ Map<String, Object> attributes =
+ Collections.<String, Object> singletonMap("finiteValues", new FiniteValues[] { FiniteValues.ONE });
+ CustomAnnotation stub = annotationFactory.createDelegator(sourceAnnotation, attributes);
+ assertEquals(CustomAnnotation.class, stub.annotationType());
+ assertEquals(Object.class, stub.someType());
+ assertEquals("FOO", stub.annString());
+ assertArrayEquals(new FiniteValues[] { FiniteValues.ONE }, stub.finiteValues());
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testDelegatorWithAttributesMissingTarget() {
+ annotationFactory.createDelegator(null, Collections.<String, Object> emptyMap());
+ }
+
public @interface NestingAnnotation {
CustomAnnotation child();
String somethingElse();
}
+ @Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnnotation {
String annString() default "";