You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by mb...@apache.org on 2010/09/13 23:38:02 UTC

svn commit: r996695 - in /commons/proper/lang/trunk/src: main/java/org/apache/commons/lang3/AnnotationUtils.java test/java/org/apache/commons/lang3/AnnotationUtilsTest.java

Author: mbenson
Date: Mon Sep 13 21:38:02 2010
New Revision: 996695

URL: http://svn.apache.org/viewvc?rev=996695&view=rev
Log:
AnnotationUtils

Added:
    commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/AnnotationUtils.java   (with props)
    commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/AnnotationUtilsTest.java   (with props)

Added: commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/AnnotationUtils.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/AnnotationUtils.java?rev=996695&view=auto
==============================================================================
--- commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/AnnotationUtils.java (added)
+++ commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/AnnotationUtils.java Mon Sep 13 21:38:02 2010
@@ -0,0 +1,157 @@
+/*
+ * 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.lang3;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+/**
+ * Helper methods for working with {@link Annotation}s.
+ * @since 3.0
+ * @version $Id$
+ */
+public class AnnotationUtils {
+
+    /**
+     * <p><code>AnnotationUtils</code> instances should NOT be constructed in
+     * standard programming. Instead, the class should be used statically.</p>
+     * 
+     * <p>This constructor is public to permit tools that require a JavaBean
+     * instance to operate.</p>
+     */
+    public AnnotationUtils() {
+    }
+
+    /**
+     * Learn whether two annotations are equivalent as defined by
+     * {@link Annotation#equals(Object)}. This method is useful because
+     * dynamically created {@link Annotation} instances are always proxy
+     * objects, which, though dependent upon implementation, very often cannot
+     * be depended upon to behave "normally" in terms of {@link #equals(Object)}
+     * implementation.
+     * @param a1 the first Annotation to compare
+     * @param a2 the second Annotation to compare
+     */
+    public static boolean equals(Annotation a1, Annotation a2) {
+        if (a1 == a2) {
+            return true;
+        }
+        if (a1 == null || a2 == null) {
+            return false;
+        }
+        Class<? extends Annotation> type = a1.annotationType();
+        Class<? extends Annotation> type2 = a2.annotationType();
+        Validate.notNull(type, "Annotation %s with null annotationType()", a1);
+        Validate.notNull(type2, "Annotation %s with null annotationType()", a2);
+        if (!type.equals(type2)) {
+            return false;
+        }
+        try {
+            for (Method m : type.getDeclaredMethods()) {
+                if (m.getParameterTypes().length == 0
+                        && isValidAnnotationMemberType(m.getReturnType())) {
+                    Object v1 = m.invoke(a1);
+                    Object v2 = m.invoke(a2);
+                    if (!memberEquals(m.getReturnType(), v1, v2)) {
+                        return false;
+                    }
+                }
+            }
+        } catch (Exception e) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Learn whether the specified type is permitted as an annotation member.
+     * These include {@link String}, {@link Class}, primitive types,
+     * {@link Annotation}s, {@link Enum}s, and arrays of same.
+     * @param type to check
+     * @return boolean
+     */
+    public static boolean isValidAnnotationMemberType(Class<?> type) {
+        if (type == null) {
+            return false;
+        }
+        if (type.isArray()) {
+            type = type.getComponentType();
+        }
+        return type.isPrimitive() || type.isEnum() || type.isAnnotation()
+                || String.class.equals(type) || Class.class.equals(type);
+    }
+
+    private static boolean memberEquals(Class<?> type, Object o1, Object o2) {
+        if (o1 == o2) {
+            return true;
+        }
+        if (o1 == null || o2 == null) {
+            return false;
+        }
+        if (type.isArray()) {
+            return arrayMemberEquals(type.getComponentType(), o1, o2);
+        }
+        if (type.isAnnotation()) {
+            return equals((Annotation) o1, (Annotation) o2);
+        }
+        return o1.equals(o2);
+    }
+
+    private static boolean arrayMemberEquals(Class<?> componentType, Object o1, Object o2) {
+        if (componentType.isAnnotation()) {
+            return annotationArrayMemberEquals((Annotation[]) o1, (Annotation[]) o2);
+        }
+        if (componentType.equals(Byte.TYPE)) {
+            return Arrays.equals((byte[]) o1, (byte[]) o2);
+        }
+        if (componentType.equals(Short.TYPE)) {
+            return Arrays.equals((short[]) o1, (short[]) o2);
+        }
+        if (componentType.equals(Integer.TYPE)) {
+            return Arrays.equals((int[]) o1, (int[]) o2);
+        }
+        if (componentType.equals(Character.TYPE)) {
+            return Arrays.equals((char[]) o1, (char[]) o2);
+        }
+        if (componentType.equals(Long.TYPE)) {
+            return Arrays.equals((long[]) o1, (long[]) o2);
+        }
+        if (componentType.equals(Float.TYPE)) {
+            return Arrays.equals((float[]) o1, (float[]) o2);
+        }
+        if (componentType.equals(Double.TYPE)) {
+            return Arrays.equals((double[]) o1, (double[]) o2);
+        }
+        if (componentType.equals(Boolean.TYPE)) {
+            return Arrays.equals((boolean[]) o1, (boolean[]) o2);
+        }
+        return Arrays.equals((Object[]) o1, (Object[]) o2);
+    }
+
+    private static boolean annotationArrayMemberEquals(Annotation[] a1, Annotation[] a2) {
+        if (a1.length != a2.length) {
+            return false;
+        }
+        for (int i = 0; i < a1.length; i++) {
+            if (!equals(a1[i], a2[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+}

Propchange: commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/AnnotationUtils.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/AnnotationUtils.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/AnnotationUtilsTest.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/AnnotationUtilsTest.java?rev=996695&view=auto
==============================================================================
--- commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/AnnotationUtilsTest.java (added)
+++ commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/AnnotationUtilsTest.java Mon Sep 13 21:38:02 2010
@@ -0,0 +1,430 @@
+/*
+ * 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.lang3;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import static org.apache.commons.lang3.AnnotationUtilsTest.Stooge.*;
+import static org.junit.Assert.*;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @version $Id$
+ */
+public class AnnotationUtilsTest {
+    @TestAnnotation(
+            booleanValue = false,
+            booleanValues = { false },
+            byteValue = 0,
+            byteValues = { 0 },
+            charValue = 0,
+            charValues = { 0 },
+            doubleValue = 0,
+            doubleValues = { 0 },
+            floatValue = 0,
+            floatValues = { 0 },
+            intValue = 0,
+            intValues = { 0 },
+            longValue = 0,
+            longValues = { 0 },
+            nest = @NestAnnotation(
+                    booleanValue = false,
+                    booleanValues = { false },
+                    byteValue = 0,
+                    byteValues = { 0 },
+                    charValue = 0,
+                    charValues = { 0 },
+                    doubleValue = 0,
+                    doubleValues = { 0 },
+                    floatValue = 0,
+                    floatValues = { 0 },
+                    intValue = 0,
+                    intValues = { 0 },
+                    longValue = 0,
+                    longValues = { 0 },
+                    shortValue = 0,
+                    shortValues = { 0 },
+                    stooge = CURLY,
+                    stooges = { MOE, LARRY, SHEMP },
+                    string = "",
+                    strings = { "" },
+                    type = Object.class,
+                    types = { Object.class }
+            ),
+            nests = {
+                @NestAnnotation(
+                        booleanValue = false,
+                        booleanValues = { false },
+                        byteValue = 0,
+                        byteValues = { 0 },
+                        charValue = 0,
+                        charValues = { 0 },
+                        doubleValue = 0,
+                        doubleValues = { 0 },
+                        floatValue = 0,
+                        floatValues = { 0 },
+                        intValue = 0,
+                        intValues = { 0 },
+                        longValue = 0,
+                        longValues = { 0 },
+                        shortValue = 0,
+                        shortValues = { 0 },
+                        stooge = CURLY,
+                        stooges = { MOE, LARRY, SHEMP },
+                        string = "",
+                        strings = { "" },
+                        type = Object[].class,
+                        types = { Object[].class }
+                )
+            },
+            shortValue = 0,
+            shortValues = { 0 },
+            stooge = SHEMP,
+            stooges = { MOE, LARRY, CURLY },
+            string = "",
+            strings = { "" },
+            type = Object.class,
+            types = { Object.class }
+    )
+    public Object dummy1;
+
+    @TestAnnotation(
+            booleanValue = false,
+            booleanValues = { false },
+            byteValue = 0,
+            byteValues = { 0 },
+            charValue = 0,
+            charValues = { 0 },
+            doubleValue = 0,
+            doubleValues = { 0 },
+            floatValue = 0,
+            floatValues = { 0 },
+            intValue = 0,
+            intValues = { 0 },
+            longValue = 0,
+            longValues = { 0 },
+            nest = @NestAnnotation(
+                    booleanValue = false,
+                    booleanValues = { false },
+                    byteValue = 0,
+                    byteValues = { 0 },
+                    charValue = 0,
+                    charValues = { 0 },
+                    doubleValue = 0,
+                    doubleValues = { 0 },
+                    floatValue = 0,
+                    floatValues = { 0 },
+                    intValue = 0,
+                    intValues = { 0 },
+                    longValue = 0,
+                    longValues = { 0 },
+                    shortValue = 0,
+                    shortValues = { 0 },
+                    stooge = CURLY,
+                    stooges = { MOE, LARRY, SHEMP },
+                    string = "",
+                    strings = { "" },
+                    type = Object.class,
+                    types = { Object.class }
+            ),
+            nests = {
+                @NestAnnotation(
+                        booleanValue = false,
+                        booleanValues = { false },
+                        byteValue = 0,
+                        byteValues = { 0 },
+                        charValue = 0,
+                        charValues = { 0 },
+                        doubleValue = 0,
+                        doubleValues = { 0 },
+                        floatValue = 0,
+                        floatValues = { 0 },
+                        intValue = 0,
+                        intValues = { 0 },
+                        longValue = 0,
+                        longValues = { 0 },
+                        shortValue = 0,
+                        shortValues = { 0 },
+                        stooge = CURLY,
+                        stooges = { MOE, LARRY, SHEMP },
+                        string = "",
+                        strings = { "" },
+                        type = Object[].class,
+                        types = { Object[].class }
+                )
+            },
+            shortValue = 0,
+            shortValues = { 0 },
+            stooge = SHEMP,
+            stooges = { MOE, LARRY, CURLY },
+            string = "",
+            strings = { "" },
+            type = Object.class,
+            types = { Object.class }
+    )
+    public Object dummy2;
+
+    @TestAnnotation(
+            booleanValue = false,
+            booleanValues = { false },
+            byteValue = 0,
+            byteValues = { 0 },
+            charValue = 0,
+            charValues = { 0 },
+            doubleValue = 0,
+            doubleValues = { 0 },
+            floatValue = 0,
+            floatValues = { 0 },
+            intValue = 0,
+            intValues = { 0 },
+            longValue = 0,
+            longValues = { 0 },
+            nest = @NestAnnotation(
+                    booleanValue = false,
+                    booleanValues = { false },
+                    byteValue = 0,
+                    byteValues = { 0 },
+                    charValue = 0,
+                    charValues = { 0 },
+                    doubleValue = 0,
+                    doubleValues = { 0 },
+                    floatValue = 0,
+                    floatValues = { 0 },
+                    intValue = 0,
+                    intValues = { 0 },
+                    longValue = 0,
+                    longValues = { 0 },
+                    shortValue = 0,
+                    shortValues = { 0 },
+                    stooge = CURLY,
+                    stooges = { MOE, LARRY, SHEMP },
+                    string = "",
+                    strings = { "" },
+                    type = Object.class,
+                    types = { Object.class }
+            ),
+            nests = {
+                @NestAnnotation(
+                        booleanValue = false,
+                        booleanValues = { false },
+                        byteValue = 0,
+                        byteValues = { 0 },
+                        charValue = 0,
+                        charValues = { 0 },
+                        doubleValue = 0,
+                        doubleValues = { 0 },
+                        floatValue = 0,
+                        floatValues = { 0 },
+                        intValue = 0,
+                        intValues = { 0 },
+                        longValue = 0,
+                        longValues = { 0 },
+                        shortValue = 0,
+                        shortValues = { 0 },
+                        stooge = CURLY,
+                        stooges = { MOE, LARRY, SHEMP },
+                        string = "",
+                        strings = { "" },
+                        type = Object[].class,
+                        types = { Object[].class }
+                ),
+                //add a second NestAnnotation to break equality:
+                @NestAnnotation(
+                        booleanValue = false,
+                        booleanValues = { false },
+                        byteValue = 0,
+                        byteValues = { 0 },
+                        charValue = 0,
+                        charValues = { 0 },
+                        doubleValue = 0,
+                        doubleValues = { 0 },
+                        floatValue = 0,
+                        floatValues = { 0 },
+                        intValue = 0,
+                        intValues = { 0 },
+                        longValue = 0,
+                        longValues = { 0 },
+                        shortValue = 0,
+                        shortValues = { 0 },
+                        stooge = CURLY,
+                        stooges = { MOE, LARRY, SHEMP },
+                        string = "",
+                        strings = { "" },
+                        type = Object[].class,
+                        types = { Object[].class }
+                )
+            },
+            shortValue = 0,
+            shortValues = { 0 },
+            stooge = SHEMP,
+            stooges = { MOE, LARRY, CURLY },
+            string = "",
+            strings = { "" },
+            type = Object.class,
+            types = { Object.class }
+    )
+    public Object dummy3;
+
+    @NestAnnotation(
+            booleanValue = false,
+            booleanValues = { false },
+            byteValue = 0,
+            byteValues = { 0 },
+            charValue = 0,
+            charValues = { 0 },
+            doubleValue = 0,
+            doubleValues = { 0 },
+            floatValue = 0,
+            floatValues = { 0 },
+            intValue = 0,
+            intValues = { 0 },
+            longValue = 0,
+            longValues = { 0 },
+            shortValue = 0,
+            shortValues = { 0 },
+            stooge = CURLY,
+            stooges = { MOE, LARRY, SHEMP },
+            string = "",
+            strings = { "" },
+            type = Object[].class,
+            types = { Object[].class }
+    )
+
+    public Object dummy4;
+
+    @Target(FIELD)
+    @Retention(RUNTIME)
+    public @interface TestAnnotation {
+        String string();
+        String[] strings();
+        Class<?> type();
+        Class<?>[] types();
+        byte byteValue();
+        byte[] byteValues();
+        short shortValue();
+        short[] shortValues();
+        int intValue();
+        int[] intValues();
+        char charValue();
+        char[] charValues();
+        long longValue();
+        long[] longValues();
+        float floatValue();
+        float[] floatValues();
+        double doubleValue();
+        double[] doubleValues();
+        boolean booleanValue();
+        boolean[] booleanValues();
+        Stooge stooge();
+        Stooge[] stooges();
+        NestAnnotation nest();
+        NestAnnotation[] nests();
+    }
+
+    public @interface NestAnnotation {
+        String string();
+        String[] strings();
+        Class<?> type();
+        Class<?>[] types();
+        byte byteValue();
+        byte[] byteValues();
+        short shortValue();
+        short[] shortValues();
+        int intValue();
+        int[] intValues();
+        char charValue();
+        char[] charValues();
+        long longValue();
+        long[] longValues();
+        float floatValue();
+        float[] floatValues();
+        double doubleValue();
+        double[] doubleValues();
+        boolean booleanValue();
+        boolean[] booleanValues();
+        Stooge stooge();
+        Stooge[] stooges();
+    }
+
+    public static enum Stooge {
+        MOE, LARRY, CURLY, JOE, SHEMP;
+    }
+
+    private Field field1;
+    private Field field2;
+    private Field field3;
+    private Field field4;
+
+    @Before
+    public void setup() throws Exception {
+        field1 = getClass().getDeclaredField("dummy1");
+        field2 = getClass().getDeclaredField("dummy2");
+        field3 = getClass().getDeclaredField("dummy3");
+        field4 = getClass().getDeclaredField("dummy4");
+    }
+
+    @Test
+    public void testEquivalence() {
+        assertTrue(AnnotationUtils.equals(field1.getAnnotation(TestAnnotation.class), field2.getAnnotation(TestAnnotation.class)));
+    }
+
+    @Test
+    public void testSameInstance() {
+        assertTrue(AnnotationUtils.equals(field1.getAnnotation(TestAnnotation.class), field1.getAnnotation(TestAnnotation.class)));
+    }
+
+    @Test
+    public void testNonEquivalentAnnotationsOfSameType() {
+        assertFalse(AnnotationUtils.equals(field1.getAnnotation(TestAnnotation.class), field3.getAnnotation(TestAnnotation.class)));
+    }
+
+    @Test
+    public void testAnnotationsOfDifferingTypes() {
+        assertFalse(AnnotationUtils.equals(field1.getAnnotation(TestAnnotation.class), field4.getAnnotation(NestAnnotation.class)));
+    }
+
+    @Test
+    public void testOneArgNull() {
+        assertFalse(AnnotationUtils.equals(field1.getAnnotation(TestAnnotation.class), null));
+    }
+
+    @Test
+    public void testBothArgsNull() {
+        assertFalse(AnnotationUtils.equals(field1.getAnnotation(TestAnnotation.class), null));
+    }
+
+    @Test
+    public void testIsValidAnnotationMemberType() {
+        for (Class<?> type : new Class[] { byte.class, short.class, int.class, char.class,
+                long.class, float.class, double.class, boolean.class, String.class, Class.class,
+                NestAnnotation.class, TestAnnotation.class, Stooge.class, ElementType.class }) {
+            assertTrue(AnnotationUtils.isValidAnnotationMemberType(type));
+            assertTrue(AnnotationUtils.isValidAnnotationMemberType(Array.newInstance(type, 0)
+                    .getClass()));
+        }
+    }
+}

Propchange: commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/AnnotationUtilsTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/AnnotationUtilsTest.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL