openwebbeans-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bergm...@apache.org
Subject svn commit: r1385082 - /openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/container/BeanCacheKey.java
Date Sat, 15 Sep 2012 15:25:03 GMT
Author: bergmark
Date: Sat Sep 15 15:25:03 2012
New Revision: 1385082

URL: http://svn.apache.org/viewvc?rev=1385082&view=rev
Log:
OWB-703 Qualifier ordering and unit tests.  Submitted By: Udo Schnurpfeil <lofwyr@apache.org>

Modified:
    openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/container/BeanCacheKey.java

Modified: openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/container/BeanCacheKey.java
URL: http://svn.apache.org/viewvc/openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/container/BeanCacheKey.java?rev=1385082&r1=1385081&r2=1385082&view=diff
==============================================================================
--- openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/container/BeanCacheKey.java
(original)
+++ openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/container/BeanCacheKey.java
Sat Sep 15 15:25:03 2012
@@ -22,43 +22,47 @@ import org.apache.webbeans.util.Annotati
 
 import javax.enterprise.util.Nonbinding;
 import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
 import java.lang.reflect.Method;
 import java.lang.reflect.Type;
 import java.util.Arrays;
+import java.util.Comparator;
 
 final class BeanCacheKey
 {
-    private Type type;
-    private String path;
-    private Annotation qualifier;
-    private Annotation qualifiers[];
-    private int hashCode = -1;
-
+    private final Type type;
+    private final String path;
+    private final Annotation qualifier;
+    private final Annotation qualifiers[];
+    private final int hashCode;
+    private static final Comparator<Annotation> ANNOTATION_COMPARATOR = new AnnotationComparator();
 
     public BeanCacheKey( Type type, String path, Annotation... qualifiers )
     {
         this.type = type;
         this.path = path;
-        final int length = qualifiers.length;
+        final int length = qualifiers != null ? qualifiers.length : 0;
         if (length == 0)
         {
-            // do nothing
+            this.qualifier = null;
+            this.qualifiers = null;
         }
         else if (length == 1)
         {
-            qualifier = qualifiers[0];
+            this.qualifier = qualifiers[0];
+            this.qualifiers = null;
         }
         else
         {
+            this.qualifier = null;
             // to save array creations, we only create an array, if we have more than one
annotation
             this.qualifiers = new Annotation[length];
-            // TBD: is the order of the qualifiers always the same?
             System.arraycopy(qualifiers, 0, this.qualifiers, 0, length);
+            Arrays.sort(this.qualifiers, ANNOTATION_COMPARATOR);
         }
 
         // this class is directly used in ConcurrentHashMap.get() so simply init the hasCode
here
         hashCode = computeHashCode();
-
     }
 
     @Override
@@ -79,11 +83,11 @@ final class BeanCacheKey
         {
             return false;
         }
-        if (qualifier != null ? !qualifier.equals(cacheKey.qualifier) : cacheKey.qualifier
!= null)
+        if (qualifier != null ? !qualifierEquals(qualifier, cacheKey.qualifier) : cacheKey.qualifier
!= null)
         {
             return false;
         }
-        if (!Arrays.equals(qualifiers, cacheKey.qualifiers))
+        if (!qualifierArrayEquals(qualifiers, cacheKey.qualifiers))
         {
             return false;
         }
@@ -95,6 +99,33 @@ final class BeanCacheKey
         return true;
     }
 
+    private boolean qualifierArrayEquals(Annotation[] qualifiers1, Annotation[] qualifiers2)
+    {
+        if (qualifiers1 == qualifiers2)
+        {
+            return true;
+        }
+        else if (qualifiers1 == null || qualifiers2 == null)
+        {
+            return false;
+        }
+        if (qualifiers1.length != qualifiers2.length)
+        {
+            return false;
+        }
+        for (int i = 0; i < qualifiers1.length; i++)
+        {
+            Annotation a1 = qualifiers1[i];
+            Annotation a2 = qualifiers2[i];
+            if (a1 == null ? a2 != null : !qualifierEquals(a1, a2))
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
     @Override
     public int hashCode()
     {
@@ -112,7 +143,7 @@ final class BeanCacheKey
         {
             return ((Class)type).getName().hashCode();
             // the type.toString() is always the same: "java.lang.Class@<hexid>"
-            //  return type.toString().hashCode();
+            // was: return type.toString().hashCode();
         }
 
         return typeHash;
@@ -124,18 +155,22 @@ final class BeanCacheKey
     private int computeHashCode()
     {
         int computedHashCode = 31 * getTypeHashCode(type) + (path != null ? path.hashCode()
: 0);
+        if (qualifier != null)
+        {
+            computedHashCode = 31 * computedHashCode + getQualifierHashCode(qualifier);
+        }
         if (qualifiers != null)
         {
-            for (Annotation q : qualifiers)
+            for (int i = 0; i < qualifiers.length; i++)
             {
-                computedHashCode = 31 * computedHashCode + getQualifierHashCode(q);
+                computedHashCode = 31 * computedHashCode + getQualifierHashCode(qualifiers[i]);
             }
         }
         return computedHashCode;
     }
 
     /**
-     * Calculate the hashCode of a Qualifier
+     * Calculate the hashCode() of a qualifier, which ignores {@link Nonbinding} members.
      */
     private int getQualifierHashCode(Annotation a)
     {
@@ -155,18 +190,19 @@ final class BeanCacheKey
         // the following algorithm is defined by the Annotation class definition
         // see the JavaDoc for Annotation!
         // we only change it so far that we skip evaluating @Nonbinding members
-        Method[] methods = annotationClass.getDeclaredMethods();
+        final Method[] members = annotationClass.getDeclaredMethods();
 
-        for (Method method : methods)
+        for (Method member : members)
         {
-            if (method.isAnnotationPresent(Nonbinding.class))
+            if (member.isAnnotationPresent(Nonbinding.class))
             {
+                // ignore the non binding
                 continue;
             }
 
             // Member value
-            Object object = callMethod(a, method);
-            int value = 0;
+            final Object object = callMethod(a, member);
+            final int value;
             if(object.getClass().isArray())
             {
                 Class<?> type = object.getClass().getComponentType();
@@ -174,35 +210,39 @@ final class BeanCacheKey
                 {
                     if(Long.TYPE == type)
                     {
-                        value = Arrays.hashCode((Long[]) object);
+                        value = Arrays.hashCode((long[]) object);
                     }
                     else if(Integer.TYPE == type)
                     {
-                        value = Arrays.hashCode((Integer[])object);
+                        value = Arrays.hashCode((int[])object);
                     }
                     else if(Short.TYPE == type)
                     {
-                        value = Arrays.hashCode((Short[])object);
+                        value = Arrays.hashCode((short[])object);
                     }
                     else if(Double.TYPE == type)
                     {
-                        value = Arrays.hashCode((Double[])object);
+                        value = Arrays.hashCode((double[])object);
                     }
                     else if(Float.TYPE == type)
                     {
-                        value = Arrays.hashCode((Float[])object);
+                        value = Arrays.hashCode((float[])object);
                     }
                     else if(Boolean.TYPE == type)
                     {
-                        value = Arrays.hashCode((Long[])object);
+                        value = Arrays.hashCode((boolean[])object);
                     }
                     else if(Byte.TYPE == type)
                     {
-                        value = Arrays.hashCode((Byte[])object);
+                        value = Arrays.hashCode((byte[])object);
                     }
                     else if(Character.TYPE == type)
                     {
-                        value = Arrays.hashCode((Character[])object);
+                        value = Arrays.hashCode((char[])object);
+                    }
+                    else
+                    {
+                        value = 0;
                     }
                 }
                 else
@@ -216,14 +256,21 @@ final class BeanCacheKey
             }
 
             hashCode = 29 * hashCode + value;
-            hashCode = 29 * hashCode + method.getName().hashCode();
-            hashCode = 29 * hashCode + a.hashCode();
+            hashCode = 29 * hashCode + member.getName().hashCode();
         }
 
         return hashCode;
     }
 
-    private Class getAnnotationClass(Class a)
+    /**
+     * Implements the equals() method for qualifiers, which ignores {@link Nonbinding} members.
+     */
+    private boolean qualifierEquals(Annotation qualifier1, Annotation qualifier2)
+    {
+        return ANNOTATION_COMPARATOR.compare(qualifier1, qualifier2) == 0;
+    }
+
+    private static Class getAnnotationClass(Class a)
     {
         for (Class i : a.getInterfaces())
         {
@@ -238,7 +285,7 @@ final class BeanCacheKey
     /**
      * Helper method for calculating the hashCode of an annotation.
      */
-    private Object callMethod(Object instance, Method method)
+    private static Object callMethod(Object instance, Method method)
     {
         try
         {
@@ -255,4 +302,127 @@ final class BeanCacheKey
         }
 
     }
+
+    /**
+     * for debugging ...
+     */
+    @Override
+    public String toString()
+    {
+        return "BeanCacheKey{" + "type=" + type + ", path='" + path + '\''
+                + ", qualifiers="
+                + (qualifiers == null ? qualifier : Arrays.asList(qualifiers)) + ", hashCode="
+ hashCode + '}';
+    }
+
+    /**
+     * to keep the annotations ordered.
+     */
+    private static class AnnotationComparator implements Comparator<Annotation>
+    {
+
+        // Notice: Sorting is a bit costly, but the use of this code is very rar.
+        public int compare(Annotation annotation1, Annotation annotation2)
+        {
+            final Class<? extends Annotation> type1 = annotation1.annotationType();
+            final Class<? extends Annotation> type2 = annotation2.annotationType();
+            final int temp = type1.getName().compareTo(type2.getName());
+            if (temp != 0)
+            {
+                return temp;
+            }
+            final Method[] member1 = type1.getDeclaredMethods();
+            final Method[] member2 = type2.getDeclaredMethods();
+
+            // TBD: the order of the list of members seems to be deterministic
+
+            int i = 0;
+            int j = 0;
+            final int length1 = member1.length;
+            final int length2 = member2.length;
+
+            // find next nonbinding
+            for (;; i++, j++)
+            {
+                while (i < length1 && member1[i].isAnnotationPresent(Nonbinding.class))
+                {
+                    i++;
+                }
+                while (j < length2 && member2[j].isAnnotationPresent(Nonbinding.class))
+                {
+                    j++;
+                }
+                if (i >= length1 && j >= length2)
+                { // both ended
+                    return 0;
+                }
+                else if (i >= length1)
+                { // #1 ended
+                    return 1;
+                }
+                else if (j >= length2)
+                { // #2 ended
+                    return -1;
+                }
+                else
+                { // not ended
+                    int c = member1[i].getName().compareTo(member2[j].getName());
+                    if (c != 0)
+                    {
+                        return c;
+                    }
+                    final Object value1 = callMethod(annotation1, member1[i]);
+                    final Object value2 = callMethod(annotation2, member2[j]);
+                    assert value1.getClass().equals(value2.getClass());
+
+                    if (value1 instanceof Comparable)
+                    {
+                        c = ((Comparable)value1).compareTo(value2);
+                        if (c != 0)
+                        {
+                            return c;
+                        }
+                    }
+                    else if (value1.getClass().isArray())
+                    {
+                        c = value1.getClass().getComponentType().getName()
+                                .compareTo(value2.getClass().getComponentType().getName());
+                        if (c != 0)
+                        {
+                            return c;
+                        }
+
+                        final int length = Array.getLength(value1);
+                        c = length - Array.getLength(value2);
+                        if (c != 0)
+                        {
+                            return c;
+                        }
+                        for (int k = 0; k < length; k++)
+                        {
+                            c = ((Comparable)Array.get(value1, k)).compareTo(Array.get(value2,
k));
+                            if (c != 0)
+                            {
+                                return c;
+                            }
+                        }
+
+                    }
+                    else if (value1 instanceof Class)
+                    {
+
+                        c = ((Class)value1).getName().compareTo(((Class) value2).getName());
+                        if (c != 0)
+                        {
+                            return c;
+                        }
+                    }
+                    else
+                    {
+                        // valid types for members are only Comparable, Arrays, or Class
+                        assert false;
+                    }
+                }
+            }
+        }
+    }
 }



Mime
View raw message