diff options
author | Brian S. O'Neill <bronee@gmail.com> | 2008-04-13 15:59:50 +0000 |
---|---|---|
committer | Brian S. O'Neill <bronee@gmail.com> | 2008-04-13 15:59:50 +0000 |
commit | f6cce60bed5b4d49831058f24c9f46de7a2b0265 (patch) | |
tree | 7028dccf0c5bacf108625f22d1d8dee82bafacd5 /src/main/java/com/amazon/carbonado | |
parent | f416ca6e7d71fc67ad71b086331e1b567a5f326a (diff) |
Fix for unstable adapter annotation hash code.
Diffstat (limited to 'src/main/java/com/amazon/carbonado')
-rw-r--r-- | src/main/java/com/amazon/carbonado/layout/LayoutFactory.java | 92 |
1 files changed, 84 insertions, 8 deletions
diff --git a/src/main/java/com/amazon/carbonado/layout/LayoutFactory.java b/src/main/java/com/amazon/carbonado/layout/LayoutFactory.java index dc77b75..d7bf3da 100644 --- a/src/main/java/com/amazon/carbonado/layout/LayoutFactory.java +++ b/src/main/java/com/amazon/carbonado/layout/LayoutFactory.java @@ -19,6 +19,11 @@ package com.amazon.carbonado.layout;
import java.lang.annotation.Annotation;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import java.util.Arrays;
import java.util.Map;
import org.cojen.util.SoftValuedHashMap;
@@ -262,14 +267,7 @@ public class LayoutFactory implements LayoutCapability { // value is stored. So mix that in too.
Annotation ann = annotation.getAnnotation();
if (ann != null) {
- // Okay to mix in annotation hash code since Annotation
- // documentation defines the implementation. It should remain
- // stable between releases, but it is not critical that the
- // hash code always comes out the same. The result would be a
- // duplicate stored layout, but with a different generation.
- // Stored entries will be converted from the "different"
- // generation, causing a very slight performance degradation.
- hash = hash * multiplier + ann.hashCode();
+ hash = hash * multiplier + annHashCode(ann);
}
}
@@ -282,4 +280,82 @@ public class LayoutFactory implements LayoutCapability { }
return hash;
}
+
+ /**
+ * Returns an annotation hash code using a algorithm similar to the
+ * default. The difference is in the handling of class and enum values. The
+ * name is chosen for the hash code component instead of the instance
+ * because it is stable between invocations of the JVM.
+ */
+ private static int annHashCode(Annotation ann) {
+ int hash = 0;
+
+ Method[] methods = ann.getClass().getDeclaredMethods();
+ for (Method m : methods) {
+ if (m.getReturnType() == null || m.getReturnType() == void.class) {
+ continue;
+ }
+ if (m.getParameterTypes().length != 0) {
+ continue;
+ }
+
+ String name = m.getName();
+ if (name.equals("hashCode") ||
+ name.equals("toString") ||
+ name.equals("annotationType"))
+ {
+ continue;
+ }
+
+ Object value;
+ try {
+ value = m.invoke(ann);
+ } catch (InvocationTargetException e) {
+ continue;
+ } catch (IllegalAccessException e) {
+ continue;
+ }
+
+ hash += (127 * name.hashCode()) ^ annValueHashCode(value);
+ }
+
+ return hash;
+ }
+
+ private static int annValueHashCode(Object value) {
+ Class type = value.getClass();
+ if (!type.isArray()) {
+ if (value instanceof String || type.isPrimitive()) {
+ return value.hashCode();
+ } else if (value instanceof Class) {
+ // Use name for stable hash code.
+ return ((Class) value).getName().hashCode();
+ } else if (value instanceof Enum) {
+ // Use name for stable hash code.
+ return ((Enum) value).name().hashCode();
+ } else if (value instanceof Annotation) {
+ return annHashCode((Annotation) value);
+ } else {
+ return value.hashCode();
+ }
+ } else if (type == byte[].class) {
+ return Arrays.hashCode((byte[]) value);
+ } else if (type == char[].class) {
+ return Arrays.hashCode((char[]) value);
+ } else if (type == double[].class) {
+ return Arrays.hashCode((double[]) value);
+ } else if (type == float[].class) {
+ return Arrays.hashCode((float[]) value);
+ } else if (type == int[].class) {
+ return Arrays.hashCode((int[]) value);
+ } else if (type == long[].class) {
+ return Arrays.hashCode((long[]) value);
+ } else if (type == short[].class) {
+ return Arrays.hashCode((short[]) value);
+ } else if (type == boolean[].class) {
+ return Arrays.hashCode((boolean[]) value);
+ } else {
+ return Arrays.hashCode((Object[]) value);
+ }
+ }
}
|