[jboss-cvs] javassist/src/main/javassist/bytecode/annotation ...

Adrian Brock adrian.brock at jboss.com
Wed Jan 31 11:17:42 EST 2007


  User: adrian  
  Date: 07/01/31 11:17:42

  Modified:    src/main/javassist/bytecode/annotation   Annotation.java
                        AnnotationImpl.java
  Log:
  Fix the annotation hashCode and equals implementation.
  Use the annotation.toString() for the real annotation
  since it is better.
  Also some javadoc tidyup.
  
  The default annotation value processing is still ugly.
  
  Revision  Changes    Path
  1.7       +34 -6     javassist/src/main/javassist/bytecode/annotation/Annotation.java
  
  (In the diff below, changes in quantity of whitespace are not shown.)
  
  Index: Annotation.java
  ===================================================================
  RCS file: /cvsroot/jboss/javassist/src/main/javassist/bytecode/annotation/Annotation.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -b -r1.6 -r1.7
  --- Annotation.java	13 Aug 2006 16:49:43 -0000	1.6
  +++ Annotation.java	31 Jan 2007 16:17:42 -0000	1.7
  @@ -20,6 +20,7 @@
   import javassist.ClassPool;
   import javassist.CtClass;
   import javassist.CtMethod;
  +import javassist.NotFoundException;
   
   import java.io.IOException;
   import java.util.HashMap;
  @@ -41,6 +42,7 @@
    *
    * @author <a href="mailto:bill at jboss.org">Bill Burke</a>
    * @author Shigeru Chiba
  + * @author <a href="mailto:adrian at jboss.org">Adrian Brock</a>
    */
   public class Annotation {
       static class Pair {
  @@ -91,9 +93,10 @@
        *
        * @param cp        the constant pool table.
        * @param clazz     the interface.
  +     * @throws NotFoundException when the clazz is not found 
        */
       public Annotation(ConstPool cp, CtClass clazz)
  -        throws javassist.NotFoundException
  +        throws NotFoundException
       {
           // todo Enums are not supported right now.
           this(cp.addUtf8Info(Descriptor.of(clazz.getName())), cp);
  @@ -103,13 +106,15 @@
                   "Only interfaces are allowed for Annotation creation.");
   
           CtMethod methods[] = clazz.getDeclaredMethods();
  -        if (methods.length > 0)
  +        if (methods.length > 0) {
               members = new HashMap();
  +        }
   
           for (int i = 0; i < methods.length; i++) {
               CtClass returnType = methods[i].getReturnType();
               addMemberValue(methods[i].getName(),
                              createMemberValue(cp, returnType));
  +            
           }
       }
   
  @@ -118,9 +123,11 @@
        *
        * @param cp            the constant pool table.
        * @param type          the type of the member.
  +     * @return the member value
  +     * @throws NotFoundException when the type is not found
        */
       public static MemberValue createMemberValue(ConstPool cp, CtClass type)
  -        throws javassist.NotFoundException
  +        throws NotFoundException
       {
           if (type == CtClass.booleanType)
               return new BooleanMemberValue(cp);
  @@ -201,9 +208,6 @@
           members.put(name, pair);
       }
   
  -    /**
  -     * Returns a string representation of this object.
  -     */
       public String toString() {
           StringBuffer buf = new StringBuffer("@");
           buf.append(getTypeName());
  @@ -224,6 +228,8 @@
   
       /**
        * Obtains the name of the annotation type.
  +     * 
  +     * @return the type name
        */
       public String getTypeName() {
           return Descriptor.toClassName(pool.getUtf8Info(typeIndex));
  @@ -250,6 +256,7 @@
        * <code>MemberValue</code> with the default value.
        * The default value can be obtained from the annotation type.
        *
  +     * @param name the member name
        * @return null if the member cannot be found or if the value is
        * the default value.
        *
  @@ -274,6 +281,8 @@
        * 
        * @param cl        class loader for loading an annotation type.
        * @param cp        class pool for obtaining class files.
  +     * @return the annotation
  +     * @throws ClassNotFoundException when the class cannot found
        */
       public Object toAnnotationType(ClassLoader cl, ClassPool cp)
           throws ClassNotFoundException
  @@ -287,6 +296,7 @@
        * Writes this annotation.
        *
        * @param writer            the output.
  +     * @throws IOException for an error during the write
        */
       public void write(AnnotationsWriter writer) throws IOException {
           String typeName = pool.getUtf8Info(typeIndex);
  @@ -303,4 +313,22 @@
               pair.value.write(writer);
           }
       }
  +    
  +    public boolean equals(Object obj) {
  +        if (obj == this)
  +            return true;
  +        if (obj == null || obj instanceof Annotation == false)
  +            return false;
  +        
  +        Annotation other = (Annotation) obj;
  +
  +        if (getTypeName().equals(other.getTypeName()) == false)
  +            return false;
  +
  +        if (members == null && other.members != null)
  +           return false;
  +        if (members != null && other.members == null)
  +           return false;
  +        return members.equals(other.members);
  +    }
   }
  
  
  
  1.12      +180 -16   javassist/src/main/javassist/bytecode/annotation/AnnotationImpl.java
  
  (In the diff below, changes in quantity of whitespace are not shown.)
  
  Index: AnnotationImpl.java
  ===================================================================
  RCS file: /cvsroot/jboss/javassist/src/main/javassist/bytecode/annotation/AnnotationImpl.java,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -b -r1.11 -r1.12
  --- AnnotationImpl.java	12 Aug 2006 06:01:59 -0000	1.11
  +++ AnnotationImpl.java	31 Jan 2007 16:17:42 -0000	1.12
  @@ -15,6 +15,10 @@
   
   package javassist.bytecode.annotation;
   
  +import java.lang.reflect.InvocationHandler;
  +import java.lang.reflect.Method;
  +import java.lang.reflect.Proxy;
  +
   import javassist.ClassPool;
   import javassist.CtClass;
   import javassist.NotFoundException;
  @@ -22,16 +26,34 @@
   import javassist.bytecode.ClassFile;
   import javassist.bytecode.MethodInfo;
   
  -import java.lang.reflect.*;
  -
   /**
    * Internal-use only.  This is a helper class internally used for implementing
    * <code>toAnnotationType()</code> in <code>Annotation</code>.  
  + *   
  + * @author Shigeru Chiba
  + * @author <a href="mailto:bill at jboss.org">Bill Burke</a>
  + * @author <a href="mailto:adrian at jboss.org">Adrian Brock</a>
    */
   public class AnnotationImpl implements InvocationHandler {
  +    private static final String JDK_ANNOTATION_CLASS_NAME = "java.lang.annotation.Annotation";
  +    private static Method JDK_ANNOTATION_TYPE_METHOD = null;
  +   
       private Annotation annotation;
       private ClassPool pool;
       private ClassLoader classLoader;
  +    private transient Class annotationType;
  +    private transient int cachedHashCode = Integer.MIN_VALUE;
  +
  +    static {
  +        // Try to resolve the JDK annotation type method
  +        try {
  +            Class clazz = Class.forName(JDK_ANNOTATION_CLASS_NAME);
  +            JDK_ANNOTATION_TYPE_METHOD = clazz.getMethod("annotationType", null);
  +        }
  +        catch (Exception ignored) {
  +            // Probably not JDK5+
  +        }
  +    }
   
       /**
        * Constructs an annotation object.
  @@ -41,6 +63,7 @@
        * @param cp        class pool for containing an annotation
        *                  type (or null).
        * @param anon      the annotation.
  +     * @return the annotation
        */
       public static Object make(ClassLoader cl, Class clazz, ClassPool cp,
                                 Annotation anon) {
  @@ -56,13 +79,38 @@
   
       /**
        * Obtains the name of the annotation type.
  +     * 
  +     * @return the type name
        */
       public String getTypeName() {
           return annotation.getTypeName();
       }
   
       /**
  +     * Get the annotation type
  +     * 
  +     * @return the annotation class
  +     * @throws NoClassDefFoundError when the class could not loaded
  +     */
  +    private Class getAnnotationType() {
  +        if (annotationType == null) {
  +            String typeName = annotation.getTypeName();
  +            try {
  +                annotationType = classLoader.loadClass(typeName);
  +            }
  +            catch (ClassNotFoundException e) {
  +                NoClassDefFoundError error = new NoClassDefFoundError("Error loading annotation class: " + typeName);
  +                error.setStackTrace(e.getStackTrace());
  +                throw error;
  +            }
  +        }
  +        return annotationType;
  +    }
  +    
  +    /**
        * Obtains the internal data structure representing the annotation.
  +     * 
  +     * @return the annotation
        */
       public Annotation getAnnotation() {
           return annotation;
  @@ -82,23 +130,16 @@
           if (Object.class == method.getDeclaringClass()) {
               if ("equals".equals(name)) {
                   Object obj = args[0];
  -                if (obj == null || obj instanceof Proxy == false)
  -                    return Boolean.FALSE;
  -
  -                Object other = Proxy.getInvocationHandler(obj);
  -                if (this.equals(other))
  -                    return Boolean.TRUE;
  -                else
  -                    return Boolean.FALSE;
  +                return new Boolean(checkEquals(obj));
               }
               else if ("toString".equals(name))
  -                return annotation.getTypeName() + '@' + hashCode();
  +                return annotation.toString();
               else if ("hashCode".equals(name))
                   return new Integer(hashCode());
           }
           else if ("annotationType".equals(name)
                    && method.getParameterTypes().length == 0)
  -           return classLoader.loadClass(getTypeName());
  +           return getAnnotationType();
   
           MemberValue mv = annotation.getMemberValue(name);
           if (mv == null)
  @@ -111,7 +152,7 @@
           throws ClassNotFoundException, RuntimeException
       {
           String classname = annotation.getTypeName();
  -        if (pool != null)
  +        if (pool != null) {
               try {
                   CtClass cc = pool.get(classname);
                   ClassFile cf = cc.getClassFile2();
  @@ -130,8 +171,131 @@
                   throw new RuntimeException("cannot find a class file: "
                                              + classname);
               }
  +        }
   
           throw new RuntimeException("no default value: " + classname + "."
                                      + name + "()");
       }
  +    
  +    public int hashCode() {
  +        if (cachedHashCode == Integer.MIN_VALUE) {
  +            int hashCode = 0;
  +
  +            // Load the annotation class
  +            getAnnotationType();
  +
  +            Method[] methods = annotationType.getDeclaredMethods();
  +            for (int i = 0; i < methods.length; ++ i) {
  +                String name = methods[i].getName();
  +                int valueHashCode = 0;
  +
  +                // Get the value
  +                MemberValue mv = annotation.getMemberValue(name);
  +                Object value = null;
  +                try {
  +                   if (mv != null)
  +                       value = mv.getValue(classLoader, pool, methods[i]);
  +                   if (value == null)
  +                       value = getDefault(name, methods[i]);
  +                }
  +                catch (RuntimeException e) {
  +                    throw e;
  +                }
  +                catch (Exception e) {
  +                    throw new RuntimeException("Error retrieving value " + name + " for annotation " + annotation.getTypeName(), e);
  +                }
  +
  +                // Calculate the hash code
  +                if (value != null) {
  +                    if (value.getClass().isArray())
  +                        valueHashCode = arrayHashCode(value);
  +                    else
  +                        valueHashCode = value.hashCode();
  +                } 
  +                hashCode += 127 * name.hashCode() ^ valueHashCode;
  +            }
  +          
  +            cachedHashCode = hashCode;
  +        }
  +        return cachedHashCode;
  +    }
  +    
  +    /**
  +     * Check that another annotation equals ourselves
  +     * 
  +     * @param obj the other annotation
  +     * @return the true when equals false otherwise
  +     * @throws Exception for any problem
  +     */
  +    private boolean checkEquals(Object obj) throws Exception {
  +        if (obj == null)
  +            return false;
  +
  +        // Optimization when the other is one of ourselves
  +        if (obj instanceof Proxy) {
  +            InvocationHandler ih = Proxy.getInvocationHandler(obj);
  +            if (ih instanceof AnnotationImpl) {
  +                AnnotationImpl other = (AnnotationImpl) ih;
  +                return annotation.equals(other.annotation);
  +            }
  +        }
  +
  +        Class otherAnnotationType = (Class) JDK_ANNOTATION_TYPE_METHOD.invoke(obj, null);
  +        if (getAnnotationType().equals(otherAnnotationType) == false)
  +           return false;
  +        
  +        Method[] methods = annotationType.getDeclaredMethods();
  +        for (int i = 0; i < methods.length; ++ i) {
  +            String name = methods[i].getName();
  +
  +            // Get the value
  +            MemberValue mv = annotation.getMemberValue(name);
  +            Object value = null;
  +            Object otherValue = null;
  +            try {
  +               if (mv != null)
  +                   value = mv.getValue(classLoader, pool, methods[i]);
  +               if (value == null)
  +                   value = getDefault(name, methods[i]);
  +               otherValue = methods[i].invoke(obj, null);
  +            }
  +            catch (RuntimeException e) {
  +                throw e;
  +            }
  +            catch (Exception e) {
  +                throw new RuntimeException("Error retrieving value " + name + " for annotation " + annotation.getTypeName(), e);
  +            }
  +
  +            if (value == null && otherValue != null)
  +                return false;
  +            if (value != null && value.equals(otherValue) == false)
  +                return false;
  +        }
  +        
  +        return true;
  +    }
  +
  +    /**
  +     * Calculates the hashCode of an array using the same
  +     * algorithm as java.util.Arrays.hashCode()
  +     * 
  +     * @param object the object
  +     * @return the hashCode
  +     */
  +    private static int arrayHashCode(Object object)
  +    {
  +       if (object == null)
  +          return 0;
  +
  +       int result = 1;
  +       
  +       Object[] array = (Object[]) object;
  +       for (int i = 0; i < array.length; ++i) {
  +           int elementHashCode = 0;
  +           if (array[i] != null)
  +              elementHashCode = array[i].hashCode();
  +           result = 31 * result + elementHashCode;
  +       }
  +       return result;
  +    }
   }
  
  
  



More information about the jboss-cvs-commits mailing list