From de67dec0774115848a9f37feda05b522a716d1fb Mon Sep 17 00:00:00 2001 From: Benedikt Ritter Date: Sun, 20 Jul 2014 09:15:04 +0000 Subject: [PATCH] =?UTF-8?q?LANG-1021:=20Provide=20methods=20to=20retrieve?= =?UTF-8?q?=20all=20fields/methods=20annotated=20with=20a=20specific=20typ?= =?UTF-8?q?e.=20Thanks=20to=20Alexander=20M=C3=BCller.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: https://svn.apache.org/repos/asf/commons/proper/lang/trunk@1612063 13f79535-47bb-0310-9956-ffa450edef68 --- src/changes/changes.xml | 1 + .../commons/lang3/reflect/FieldUtils.java | 40 ++++++++++++ .../commons/lang3/reflect/MethodUtils.java | 43 +++++++++++++ .../commons/lang3/reflect/FieldUtilsTest.java | 63 +++++++++++++++++-- .../lang3/reflect/MethodUtilsTest.java | 58 +++++++++++++++++ .../lang3/reflect/testbed/Annotated.java | 11 ++++ 6 files changed, 212 insertions(+), 4 deletions(-) create mode 100644 src/test/java/org/apache/commons/lang3/reflect/testbed/Annotated.java diff --git a/src/changes/changes.xml b/src/changes/changes.xml index e693104fb..cb830ba12 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -22,6 +22,7 @@ + Provide methods to retrieve all fields/methods annotated with a specific type Bring static method references in StringUtils to consistent style NumberUtils#isParsable method(s) Use non-ASCII digits in Javadoc examples for StringUtils.isNumeric diff --git a/src/main/java/org/apache/commons/lang3/reflect/FieldUtils.java b/src/main/java/org/apache/commons/lang3/reflect/FieldUtils.java index 8f68fab72..72114708f 100644 --- a/src/main/java/org/apache/commons/lang3/reflect/FieldUtils.java +++ b/src/main/java/org/apache/commons/lang3/reflect/FieldUtils.java @@ -20,6 +20,7 @@ import org.apache.commons.lang3.ClassUtils; 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; @@ -222,6 +223,45 @@ public class FieldUtils { return allFields; } + /** + * 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 annotationCls) { + final List 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 getFieldsListWithAnnotation(final Class cls, final Class annotationCls) { + Validate.isTrue(annotationCls != null, "The annotation class must not be null"); + final List allFields = getAllFieldsList(cls); + final List annotatedFields = new ArrayList(); + for (final Field field : allFields) { + if (field.getAnnotation(annotationCls) != null) { + annotatedFields.add(field); + } + } + return annotatedFields; + } + /** * Reads an accessible {@code static} {@link Field}. * diff --git a/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java b/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java index d66394f89..e03e63730 100644 --- a/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java +++ b/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java @@ -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 annotationCls) { + final List 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 getMethodsListWithAnnotation(final Class cls, final Class 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 annotatedMethods = new ArrayList(); + for (final Method method : allMethods) { + if (method.getAnnotation(annotationCls) != null) { + annotatedMethods.add(method); + } + } + return annotatedMethods; + } + } diff --git a/src/test/java/org/apache/commons/lang3/reflect/FieldUtilsTest.java b/src/test/java/org/apache/commons/lang3/reflect/FieldUtilsTest.java index 30e801f5c..4a7d6ad08 100644 --- a/src/test/java/org/apache/commons/lang3/reflect/FieldUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/reflect/FieldUtilsTest.java @@ -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 parentClass = PublicChild.class.getSuperclass(); @@ -166,6 +168,59 @@ public class FieldUtilsTest { assertEquals(5, FieldUtils.getAllFieldsList(PublicChild.class).size()); } + @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 annotatedFields = Arrays.asList( + FieldUtilsTest.class.getDeclaredField("publicChild"), + FieldUtilsTest.class.getDeclaredField("privatelyShadowedChild") + ); + final List 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")); @@ -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 diff --git a/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java b/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java index e7dfd8af1..fca4461a7 100644 --- a/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java @@ -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.NumberUtils; 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 annotatedMethods = Arrays.asList( + MethodUtilsTest.class.getMethod("testGetMethodsWithAnnotation"), + MethodUtilsTest.class.getMethod("testGetMethodsListWithAnnotation") + ); + final List 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) { diff --git a/src/test/java/org/apache/commons/lang3/reflect/testbed/Annotated.java b/src/test/java/org/apache/commons/lang3/reflect/testbed/Annotated.java new file mode 100644 index 000000000..c0c1054ab --- /dev/null +++ b/src/test/java/org/apache/commons/lang3/reflect/testbed/Annotated.java @@ -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 { +}