commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mben...@apache.org
Subject svn commit: r999169 - in /commons/proper/lang/trunk/src: main/java/org/apache/commons/lang3/AnnotationUtils.java test/java/org/apache/commons/lang3/AnnotationUtilsTest.java
Date Tue, 21 Sep 2010 00:27:40 GMT
Author: mbenson
Date: Tue Sep 21 00:27:40 2010
New Revision: 999169

URL: http://svn.apache.org/viewvc?rev=999169&view=rev
Log:
add hashCode and toString methods to AnnotationUtils, plus tests

Modified:
    commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/AnnotationUtils.java
    commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/AnnotationUtilsTest.java

Modified: 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=999169&r1=999168&r2=999169&view=diff
==============================================================================
--- commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/AnnotationUtils.java
(original)
+++ commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/AnnotationUtils.java
Tue Sep 21 00:27:40 2010
@@ -17,8 +17,10 @@
 package org.apache.commons.lang3;
 
 import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.Arrays;
+import java.util.Iterator;
 
 /**
  * Helper methods for working with {@link Annotation}s.
@@ -38,12 +40,10 @@ public class 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.
+     * Learn whether two annotations are equivalent; dynamically created
+     * {@link Annotation} instances are always proxy objects which cannot be
+     * depended upon to know how to implement {@link Annotation#equals(Object)}
+     * per spec.
      * @param a1 the first Annotation to compare
      * @param a2 the second Annotation to compare
      */
@@ -79,6 +79,83 @@ public class AnnotationUtils {
     }
 
     /**
+     * Generate a hashcode for the given annotation; dynamically created
+     * {@link Annotation} instances are always proxy objects which cannot be
+     * depended upon to know how to implement {@link Annotation#hashCode()} per
+     * spec.
+     * 
+     * @param a the Annotation for a hashcode calculation is desired
+     * @return the calculated hashcode
+     * @throws InvocationTargetException
+     * @throws IllegalAccessException
+     * @throws IllegalArgumentException
+     */
+    public static int hashCode(Annotation a) throws IllegalArgumentException,
+            IllegalAccessException, InvocationTargetException {
+        int result = 0;
+        Class<? extends Annotation> type = a.annotationType();
+        for (Method m : type.getDeclaredMethods()) {
+            result += hashMember(m.getName(), m.invoke(a));
+        }
+        return result;
+    }
+
+    /**
+     * Generate a string representation of an Annotation, as suggested by
+     * {@link Annotation#toString()}.
+     * @param a the annotation of which a string representation is desired
+     * @return String
+     */
+    public static String toString(final Annotation a) {
+        return new StringBuilder(a.annotationType().getName()).insert(0, '@').append('(')
+                .append(StringUtils.join(new Iterable<String>() {
+
+                    public Iterator<String> iterator() {
+                        final Method[] methods = a.annotationType().getDeclaredMethods();
+                        return new Iterator<String>() {
+                            int pos = 0;
+
+                            public boolean hasNext() {
+                                return pos < methods.length;
+                            }
+
+                            public String next() {
+                                Method m = methods[pos++];
+                                try {
+                                    return new StringBuilder(m.getName()).append('=')
+                                            .append(m.invoke(a)).toString();
+                                } catch (Exception e) {
+                                    throw new RuntimeException(e);
+                                }
+                            }
+
+                            public void remove() {
+                                throw new UnsupportedOperationException();
+                            }
+
+                        };
+                    }
+
+                }, ", ")).append(')').toString();
+    }
+
+    //besides modularity, this has the advantage of autoboxing primitives:
+    private static int hashMember(String name, Object value) throws IllegalArgumentException,
+            IllegalAccessException, InvocationTargetException {
+        int part1 = name.hashCode() * 127;
+        if (value == null) {
+            return part1;
+        }
+        if (value.getClass().isArray()) {
+            return part1 ^ arrayMemberHash(value.getClass().getComponentType(), value);
+        }
+        if (value instanceof Annotation) {
+            return part1 ^ hashCode((Annotation) value);
+        }
+        return part1 ^ value.hashCode();
+    }
+
+    /**
      * 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.
@@ -154,4 +231,33 @@ public class AnnotationUtils {
         }
         return true;
     }
+
+    private static int arrayMemberHash(Class<?> componentType, Object o) {
+        if (componentType.equals(Byte.TYPE)) {
+            return Arrays.hashCode((byte[]) o);
+        }
+        if (componentType.equals(Short.TYPE)) {
+            return Arrays.hashCode((short[]) o);
+        }
+        if (componentType.equals(Integer.TYPE)) {
+            return Arrays.hashCode((int[]) o);
+        }
+        if (componentType.equals(Character.TYPE)) {
+            return Arrays.hashCode((char[]) o);
+        }
+        if (componentType.equals(Long.TYPE)) {
+            return Arrays.hashCode((long[]) o);
+        }
+        if (componentType.equals(Float.TYPE)) {
+            return Arrays.hashCode((float[]) o);
+        }
+        if (componentType.equals(Double.TYPE)) {
+            return Arrays.hashCode((double[]) o);
+        }
+        if (componentType.equals(Boolean.TYPE)) {
+            return Arrays.hashCode((boolean[]) o);
+        }
+        return Arrays.hashCode((Object[]) o);
+    }
+
 }

Modified: 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=999169&r1=999168&r2=999169&view=diff
==============================================================================
--- commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/AnnotationUtilsTest.java
(original)
+++ commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/AnnotationUtilsTest.java
Tue Sep 21 00:27:40 2010
@@ -470,4 +470,23 @@ public class AnnotationUtilsTest {
         assertTrue(AnnotationUtils.equals(generated, generated2));
         assertTrue(AnnotationUtils.equals(generated2, generated));
     }
+
+    @Test(timeout = 666)
+    public void testHashCode() throws Exception {
+        final Test testAnno = getClass().getDeclaredMethod("testHashCode")
+                .getAnnotation(Test.class);
+        assertEquals(testAnno.hashCode(), AnnotationUtils.hashCode(testAnno));
+    }
+
+    @Test(timeout = 666)
+    public void testToString() throws Exception {
+        final Test testAnno = getClass().getDeclaredMethod("testToString")
+                .getAnnotation(Test.class);
+        String toString = AnnotationUtils.toString(testAnno);
+        assertTrue(toString.startsWith("@org.junit.Test("));
+        assertTrue(toString.endsWith(")"));
+        assertTrue(toString.contains("expected=class org.junit.Test$None"));
+        assertTrue(toString.contains("timeout=666"));
+        assertTrue(toString.contains(", "));
+    }
 }



Mime
View raw message