summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/amazon/carbonado/layout/LayoutFactory.java92
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);
+ }
+ }
}