From f6cce60bed5b4d49831058f24c9f46de7a2b0265 Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Sun, 13 Apr 2008 15:59:50 +0000 Subject: Fix for unstable adapter annotation hash code. --- .../com/amazon/carbonado/layout/LayoutFactory.java | 92 ++++++++++++++++++++-- 1 file changed, 84 insertions(+), 8 deletions(-) (limited to 'src/main/java/com/amazon/carbonado') 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); + } + } } -- cgit v1.2.3