add hashCode and toString methods to AnnotationUtils, plus tests
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/lang/trunk@999169 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
8c19964d55
commit
4c0f8dba50
|
@ -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
|
||||
*/
|
||||
|
@ -78,6 +78,83 @@ public class AnnotationUtils {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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(", "));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue