Author: britter Date: Sun Jul 20 09:15:04 2014 New Revision: 1612063 URL: http://svn.apache.org/r1612063 Log: LANG-1021: Provide methods to retrieve all fields/methods annotated with a specific type. Thanks to Alexander Müller.
Added: commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/reflect/testbed/Annotated.java Modified: commons/proper/lang/trunk/src/changes/changes.xml commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/reflect/FieldUtils.java commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/reflect/FieldUtilsTest.java commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java Modified: commons/proper/lang/trunk/src/changes/changes.xml URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/changes/changes.xml?rev=1612063&r1=1612062&r2=1612063&view=diff ============================================================================== --- commons/proper/lang/trunk/src/changes/changes.xml [utf-8] (original) +++ commons/proper/lang/trunk/src/changes/changes.xml [utf-8] Sun Jul 20 09:15:04 2014 @@ -22,6 +22,7 @@ <body> <release version="3.4" date="tba" description="tba"> + <action issue="LANG-1021" type="add" dev="britter" due-to="Alexander Müller">Provide methods to retrieve all fields/methods annotated with a specific type</action> <action issue="LANG-1026" type="update" dev="britter" due-to="Alex Yursha">Bring static method references in StringUtils to consistent style</action> <action issue="LANG-1016" type="add" dev="britter" due-to="Juan Pablo Santos Rodríguez">NumberUtils#isParsable method(s)</action> <action issue="LANG-1017" type="update" dev="britter" due-to="Christoph Schneegans">Use non-ASCII digits in Javadoc examples for StringUtils.isNumeric</action> Modified: commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/reflect/FieldUtils.java URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/reflect/FieldUtils.java?rev=1612063&r1=1612062&r2=1612063&view=diff ============================================================================== --- commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/reflect/FieldUtils.java (original) +++ commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/reflect/FieldUtils.java Sun Jul 20 09:15:04 2014 @@ -20,6 +20,7 @@ import org.apache.commons.lang3.ClassUti import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; +import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; @@ -223,6 +224,45 @@ public class FieldUtils { } /** + * Gets all fields of the given class and its parents (if any) that are annotated with the given annotation. + * @param cls + * the {@link Class} to query + * @param annotationCls + * the {@link Annotation} that must be present on a field to be matched + * @return an array of Fields (possibly empty). + * @throws IllegalArgumentException + * if the class or annotation are {@code null} + * @since 3.4 + */ + public static Field[] getFieldsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) { + final List<Field> annotatedFieldsList = getFieldsListWithAnnotation(cls, annotationCls); + return annotatedFieldsList.toArray(new Field[annotatedFieldsList.size()]); + } + + /** + * Gets all fields of the given class and its parents (if any) that are annotated with the given annotation. + * @param cls + * the {@link Class} to query + * @param annotationCls + * the {@link Annotation} that must be present on a field to be matched + * @return a list of Fields (possibly empty). + * @throws IllegalArgumentException + * if the class or annotation are {@code null} + * @since 3.4 + */ + public static List<Field> getFieldsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) { + Validate.isTrue(annotationCls != null, "The annotation class must not be null"); + final List<Field> allFields = getAllFieldsList(cls); + final List<Field> annotatedFields = new ArrayList<Field>(); + for (final Field field : allFields) { + if (field.getAnnotation(annotationCls) != null) { + annotatedFields.add(field); + } + } + return annotatedFields; + } + + /** * Reads an accessible {@code static} {@link Field}. * * @param field Modified: commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java?rev=1612063&r1=1612062&r2=1612063&view=diff ============================================================================== --- commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java (original) +++ commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java Sun Jul 20 09:15:04 2014 @@ -16,14 +16,17 @@ */ package org.apache.commons.lang3.reflect; +import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.Arrays; +import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -546,4 +549,44 @@ public class MethodUtils { return result; } + /** + * Gets all methods of the given class that are annotated with the given annotation. + * @param cls + * the {@link Class} to query + * @param annotationCls + * the {@link java.lang.annotation.Annotation} that must be present on a method to be matched + * @return an array of Methods (possibly empty). + * @throws IllegalArgumentException + * if the class or annotation are {@code null} + * @since 3.4 + */ + public static Method[] getMethodsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) { + final List<Method> annotatedMethodsList = getMethodsListWithAnnotation(cls, annotationCls); + return annotatedMethodsList.toArray(new Method[annotatedMethodsList.size()]); + } + + /** + * Gets all methods of the given class that are annotated with the given annotation. + * @param cls + * the {@link Class} to query + * @param annotationCls + * the {@link Annotation} that must be present on a method to be matched + * @return a list of Methods (possibly empty). + * @throws IllegalArgumentException + * if the class or annotation are {@code null} + * @since 3.4 + */ + public static List<Method> getMethodsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) { + Validate.isTrue(cls != null, "The class must not be null"); + Validate.isTrue(annotationCls != null, "The annotation class must not be null"); + final Method[] allMethods = cls.getMethods(); + final List<Method> annotatedMethods = new ArrayList<Method>(); + for (final Method method : allMethods) { + if (method.getAnnotation(annotationCls) != null) { + annotatedMethods.add(method); + } + } + return annotatedMethods; + } + } Modified: commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/reflect/FieldUtilsTest.java URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/reflect/FieldUtilsTest.java?rev=1612063&r1=1612062&r2=1612063&view=diff ============================================================================== --- commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/reflect/FieldUtilsTest.java (original) +++ commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/reflect/FieldUtilsTest.java Sun Jul 20 09:15:04 2014 @@ -43,8 +43,10 @@ public class FieldUtilsTest { static final Double D0 = Double.valueOf(0.0); static final Double D1 = Double.valueOf(1.0); + @Annotated private PublicChild publicChild; private PubliclyShadowedChild publiclyShadowedChild; + @Annotated private PrivatelyShadowedChild privatelyShadowedChild; private final Class<? super PublicChild> parentClass = PublicChild.class.getSuperclass(); @@ -167,6 +169,59 @@ public class FieldUtilsTest { } @Test + public void testGetFieldsWithAnnotation() throws NoSuchFieldException { + assertArrayEquals(new Field[0], FieldUtils.getFieldsWithAnnotation(Object.class, Annotated.class)); + final Field[] annotatedFields = new Field[]{ + FieldUtilsTest.class.getDeclaredField("publicChild"), + FieldUtilsTest.class.getDeclaredField("privatelyShadowedChild") + }; + assertArrayEquals(annotatedFields, FieldUtils.getFieldsWithAnnotation(FieldUtilsTest.class, Annotated.class)); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetFieldsWithAnnotationIllegalArgumentException1() { + FieldUtils.getFieldsWithAnnotation(FieldUtilsTest.class, null); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetFieldsWithAnnotationIllegalArgumentException2() { + FieldUtils.getFieldsWithAnnotation(null, Annotated.class); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetFieldsWithAnnotationIllegalArgumentException3() { + FieldUtils.getFieldsWithAnnotation(null, null); + } + + @Test + public void testGetFieldsListWithAnnotation() throws NoSuchFieldException { + assertEquals(0, FieldUtils.getFieldsListWithAnnotation(Object.class, Annotated.class).size()); + final List<Field> annotatedFields = Arrays.asList( + FieldUtilsTest.class.getDeclaredField("publicChild"), + FieldUtilsTest.class.getDeclaredField("privatelyShadowedChild") + ); + final List<Field> fieldUtilsTestAnnotatedFields = FieldUtils.getFieldsListWithAnnotation(FieldUtilsTest.class, Annotated.class); + assertEquals(annotatedFields.size(),fieldUtilsTestAnnotatedFields.size()); + assertTrue(fieldUtilsTestAnnotatedFields.contains(annotatedFields.get(0))); + assertTrue(fieldUtilsTestAnnotatedFields.contains(annotatedFields.get(1))); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetFieldsListWithAnnotationIllegalArgumentException1() { + FieldUtils.getFieldsListWithAnnotation(FieldUtilsTest.class, null); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetFieldsListWithAnnotationIllegalArgumentException2() { + FieldUtils.getFieldsListWithAnnotation(null, Annotated.class); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetFieldsListWithAnnotationIllegalArgumentException3() { + FieldUtils.getFieldsListWithAnnotation(null, null); + } + + @Test public void testGetDeclaredField() { assertNull(FieldUtils.getDeclaredField(PublicChild.class, "VALUE")); assertNull(FieldUtils.getDeclaredField(PublicChild.class, "s")); @@ -818,28 +873,28 @@ public class FieldUtilsTest { assertEquals("new", StaticContainer.getMutablePrivate()); field = StaticContainer.class.getDeclaredField("IMMUTABLE_PUBLIC"); try { - FieldUtils.writeStaticField(field, "new", true); + FieldUtils.writeStaticField(field, "new", true); fail("Expected IllegalAccessException"); } catch (final IllegalAccessException e) { // pass } field = StaticContainer.class.getDeclaredField("IMMUTABLE_PROTECTED"); try { - FieldUtils.writeStaticField(field, "new", true); + FieldUtils.writeStaticField(field, "new", true); fail("Expected IllegalAccessException"); } catch (final IllegalAccessException e) { // pass } field = StaticContainer.class.getDeclaredField("IMMUTABLE_PACKAGE"); try { - FieldUtils.writeStaticField(field, "new", true); + FieldUtils.writeStaticField(field, "new", true); fail("Expected IllegalAccessException"); } catch (final IllegalAccessException e) { // pass } field = StaticContainer.class.getDeclaredField("IMMUTABLE_PRIVATE"); try { - FieldUtils.writeStaticField(field, "new", true); + FieldUtils.writeStaticField(field, "new", true); fail("Expected IllegalAccessException"); } catch (final IllegalAccessException e) { // pass Modified: commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java?rev=1612063&r1=1612062&r2=1612063&view=diff ============================================================================== --- commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java (original) +++ commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java Sun Jul 20 09:15:04 2014 @@ -16,6 +16,7 @@ */ package org.apache.commons.lang3.reflect; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -30,6 +31,7 @@ import java.lang.reflect.Type; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import org.apache.commons.lang3.ArrayUtils; @@ -37,6 +39,7 @@ import org.apache.commons.lang3.math.Num import org.apache.commons.lang3.mutable.Mutable; import org.apache.commons.lang3.mutable.MutableObject; import org.apache.commons.lang3.ClassUtils.Interfaces; +import org.apache.commons.lang3.reflect.testbed.Annotated; import org.apache.commons.lang3.reflect.testbed.GenericConsumer; import org.apache.commons.lang3.reflect.testbed.GenericParent; import org.apache.commons.lang3.reflect.testbed.StringParameterizedChild; @@ -424,6 +427,61 @@ public class MethodUtilsTest { } assertFalse(expected.hasNext()); } + + @Test + @Annotated + public void testGetMethodsWithAnnotation() throws NoSuchMethodException { + assertArrayEquals(new Method[0], MethodUtils.getMethodsWithAnnotation(Object.class, Annotated.class)); + final Method[] annotatedMethods = new Method[]{ + MethodUtilsTest.class.getMethod("testGetMethodsWithAnnotation"), + MethodUtilsTest.class.getMethod("testGetMethodsListWithAnnotation") + }; + assertArrayEquals(annotatedMethods, MethodUtils.getMethodsWithAnnotation(MethodUtilsTest.class, Annotated.class)); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetMethodsWithAnnotationIllegalArgumentException1() { + MethodUtils.getMethodsWithAnnotation(FieldUtilsTest.class, null); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetMethodsWithAnnotationIllegalArgumentException2() { + MethodUtils.getMethodsWithAnnotation(null, Annotated.class); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetMethodsWithAnnotationIllegalArgumentException3() { + MethodUtils.getMethodsWithAnnotation(null, null); + } + + @Test + @Annotated + public void testGetMethodsListWithAnnotation() throws NoSuchMethodException { + assertEquals(0, MethodUtils.getMethodsListWithAnnotation(Object.class, Annotated.class).size()); + final List<Method> annotatedMethods = Arrays.asList( + MethodUtilsTest.class.getMethod("testGetMethodsWithAnnotation"), + MethodUtilsTest.class.getMethod("testGetMethodsListWithAnnotation") + ); + final List<Method> methodUtilsTestAnnotatedFields = MethodUtils.getMethodsListWithAnnotation(MethodUtilsTest.class, Annotated.class); + assertEquals(annotatedMethods.size(), methodUtilsTestAnnotatedFields.size()); + assertTrue(methodUtilsTestAnnotatedFields.contains(annotatedMethods.get(0))); + assertTrue(methodUtilsTestAnnotatedFields.contains(annotatedMethods.get(1))); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetMethodsListWithAnnotationIllegalArgumentException1() { + MethodUtils.getMethodsListWithAnnotation(FieldUtilsTest.class, null); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetMethodsListWithAnnotationIllegalArgumentException2() { + MethodUtils.getMethodsListWithAnnotation(null, Annotated.class); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetMethodsListWithAnnotationIllegalArgumentException3() { + MethodUtils.getMethodsListWithAnnotation(null, null); + } private void expectMatchingAccessibleMethodParameterTypes(final Class<?> cls, final String methodName, final Class<?>[] requestTypes, final Class<?>[] actualTypes) { Added: commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/reflect/testbed/Annotated.java URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/reflect/testbed/Annotated.java?rev=1612063&view=auto ============================================================================== --- commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/reflect/testbed/Annotated.java (added) +++ commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/reflect/testbed/Annotated.java Sun Jul 20 09:15:04 2014 @@ -0,0 +1,11 @@ +package org.apache.commons.lang3.reflect.testbed; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD}) +public @interface Annotated { +}