Author: mbenson Date: Thu Jun 7 15:26:56 2012 New Revision: 1347680 URL: http://svn.apache.org/viewvc?rev=1347680&view=rev Log: delegating annotation stubs
Modified: commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationFactory.java commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/AnnotationFactoryTest.java Modified: commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationFactory.java URL: http://svn.apache.org/viewvc/commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationFactory.java?rev=1347680&r1=1347679&r2=1347680&view=diff ============================================================================== --- commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationFactory.java (original) +++ commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationFactory.java Thu Jun 7 15:26:56 2012 @@ -26,6 +26,7 @@ import java.lang.reflect.Proxy; 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.ObjectP 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 @@ public class AnnotationFactory { @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 @@ public class AnnotationFactory { * @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 class AnnotationFactory { */ 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 @@ public class AnnotationFactory { 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) { Modified: commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/AnnotationFactoryTest.java URL: http://svn.apache.org/viewvc/commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/AnnotationFactoryTest.java?rev=1347680&r1=1347679&r2=1347680&view=diff ============================================================================== --- commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/AnnotationFactoryTest.java (original) +++ commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/AnnotationFactoryTest.java Thu Jun 7 15:26:56 2012 @@ -22,9 +22,13 @@ import static org.junit.Assert.assertEqu 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 @@ import org.junit.Test; * 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 @@ public class AnnotationFactoryTest { 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 "";