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
<[email protected]>
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;
+ }
+ }
+ }
+ }
+ }
}