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;
|
package org.apache.commons.lang3;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper methods for working with {@link Annotation}s.
|
* Helper methods for working with {@link Annotation}s.
|
||||||
|
@ -38,12 +40,10 @@ public class AnnotationUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Learn whether two annotations are equivalent as defined by
|
* Learn whether two annotations are equivalent; dynamically created
|
||||||
* {@link Annotation#equals(Object)}. This method is useful because
|
* {@link Annotation} instances are always proxy objects which cannot be
|
||||||
* dynamically created {@link Annotation} instances are always proxy
|
* depended upon to know how to implement {@link Annotation#equals(Object)}
|
||||||
* objects, which, though dependent upon implementation, very often cannot
|
* per spec.
|
||||||
* be depended upon to behave "normally" in terms of {@link #equals(Object)}
|
|
||||||
* implementation.
|
|
||||||
* @param a1 the first Annotation to compare
|
* @param a1 the first Annotation to compare
|
||||||
* @param a2 the second Annotation to compare
|
* @param a2 the second Annotation to compare
|
||||||
*/
|
*/
|
||||||
|
@ -78,6 +78,83 @@ public class AnnotationUtils {
|
||||||
return true;
|
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.
|
* Learn whether the specified type is permitted as an annotation member.
|
||||||
* These include {@link String}, {@link Class}, primitive types,
|
* These include {@link String}, {@link Class}, primitive types,
|
||||||
|
@ -154,4 +231,33 @@ public class AnnotationUtils {
|
||||||
}
|
}
|
||||||
return true;
|
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(generated, generated2));
|
||||||
assertTrue(AnnotationUtils.equals(generated2, generated));
|
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