1 /*
   2  * Copyright 2003-2008 Sun Microsystems, Inc.  All Rights Reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any questions.
  24  */
  25 
  26 package sun.reflect.annotation;
  27 
  28 import java.lang.annotation.*;
  29 import java.lang.reflect.*;
  30 import java.io.Serializable;
  31 import java.util.*;
  32 import java.lang.annotation.*;
  33 import java.security.AccessController;
  34 import java.security.PrivilegedAction;
  35 
  36 /**
  37  * InvocationHandler for dynamic proxy implementation of Annotation.
  38  *
  39  * @author  Josh Bloch
  40  * @since   1.5
  41  */
  42 class AnnotationInvocationHandler implements InvocationHandler, Serializable {
  43     private final Class<? extends Annotation> type;
  44     private final Map<String, Object> memberValues;
  45 
  46     AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
  47         this.type = type;
  48         this.memberValues = memberValues;
  49     }
  50 
  51     public Object invoke(Object proxy, Method method, Object[] args) {
  52         String member = method.getName();
  53         Class<?>[] paramTypes = method.getParameterTypes();
  54 
  55         // Handle Object and Annotation methods
  56         if (member.equals("equals") && paramTypes.length == 1 &&
  57             paramTypes[0] == Object.class)
  58             return equalsImpl(args[0]);
  59         assert paramTypes.length == 0;
  60         if (member.equals("toString"))
  61             return toStringImpl();
  62         if (member.equals("hashCode"))
  63             return hashCodeImpl();
  64         if (member.equals("annotationType"))
  65             return type;
  66 
  67         // Handle annotation member accessors
  68         Object result = memberValues.get(member);
  69 
  70         if (result == null)
  71             throw new IncompleteAnnotationException(type, member);
  72 
  73         if (result instanceof ExceptionProxy)
  74             throw ((ExceptionProxy) result).generateException();
  75 
  76         if (result.getClass().isArray() && Array.getLength(result) != 0)
  77             result = cloneArray(result);
  78 
  79         return result;
  80     }
  81 
  82     /**
  83      * This method, which clones its array argument, would not be necessary
  84      * if Cloneable had a public clone method.
  85      */
  86     private Object cloneArray(Object array) {
  87         Class<?> type = array.getClass();
  88 
  89         if (type == byte[].class) {
  90             byte[] byteArray = (byte[])array;
  91             return byteArray.clone();
  92         }
  93         if (type == char[].class) {
  94             char[] charArray = (char[])array;
  95             return charArray.clone();
  96         }
  97         if (type == double[].class) {
  98             double[] doubleArray = (double[])array;
  99             return doubleArray.clone();
 100         }
 101         if (type == float[].class) {
 102             float[] floatArray = (float[])array;
 103             return floatArray.clone();
 104         }
 105         if (type == int[].class) {
 106             int[] intArray = (int[])array;
 107             return intArray.clone();
 108         }
 109         if (type == long[].class) {
 110             long[] longArray = (long[])array;
 111             return longArray.clone();
 112         }
 113         if (type == short[].class) {
 114             short[] shortArray = (short[])array;
 115             return shortArray.clone();
 116         }
 117         if (type == boolean[].class) {
 118             boolean[] booleanArray = (boolean[])array;
 119             return booleanArray.clone();
 120         }
 121 
 122         Object[] objectArray = (Object[])array;
 123         return objectArray.clone();
 124     }
 125 
 126 
 127     /**
 128      * Implementation of dynamicProxy.toString()
 129      */
 130     private String toStringImpl() {
 131         StringBuffer result = new StringBuffer(128);
 132         result.append('@');
 133         result.append(type.getName());
 134         result.append('(');
 135         boolean firstMember = true;
 136         for (Map.Entry<String, Object> e : memberValues.entrySet()) {
 137             if (firstMember)
 138                 firstMember = false;
 139             else
 140                 result.append(", ");
 141 
 142             result.append(e.getKey());
 143             result.append('=');
 144             result.append(memberValueToString(e.getValue()));
 145         }
 146         result.append(')');
 147         return result.toString();
 148     }
 149 
 150     /**
 151      * Translates a member value (in "dynamic proxy return form") into a string
 152      */
 153     private static String memberValueToString(Object value) {
 154         Class<?> type = value.getClass();
 155         if (!type.isArray())    // primitive, string, class, enum const,
 156                                 // or annotation
 157             return value.toString();
 158 
 159         if (type == byte[].class)
 160             return Arrays.toString((byte[]) value);
 161         if (type == char[].class)
 162             return Arrays.toString((char[]) value);
 163         if (type == double[].class)
 164             return Arrays.toString((double[]) value);
 165         if (type == float[].class)
 166             return Arrays.toString((float[]) value);
 167         if (type == int[].class)
 168             return Arrays.toString((int[]) value);
 169         if (type == long[].class)
 170             return Arrays.toString((long[]) value);
 171         if (type == short[].class)
 172             return Arrays.toString((short[]) value);
 173         if (type == boolean[].class)
 174             return Arrays.toString((boolean[]) value);
 175         return Arrays.toString((Object[]) value);
 176     }
 177 
 178     /**
 179      * Implementation of dynamicProxy.equals(Object o)
 180      */
 181     private Boolean equalsImpl(Object o) {
 182         if (o == this)
 183             return true;
 184 
 185         if (!type.isInstance(o))
 186             return false;
 187         for (Method memberMethod : getMemberMethods()) {
 188             String member = memberMethod.getName();
 189             Object ourValue = memberValues.get(member);
 190             Object hisValue = null;
 191             AnnotationInvocationHandler hisHandler = asOneOfUs(o);
 192             if (hisHandler != null) {
 193                 hisValue = hisHandler.memberValues.get(member);
 194             } else {
 195                 try {
 196                     hisValue = memberMethod.invoke(o);
 197                 } catch (InvocationTargetException e) {
 198                     return false;
 199                 } catch (IllegalAccessException e) {
 200                     throw new AssertionError(e);
 201                 }
 202             }
 203             if (!memberValueEquals(ourValue, hisValue))
 204                 return false;
 205         }
 206         return true;
 207     }
 208 
 209     /**
 210      * Returns an object's invocation handler if that object is a dynamic
 211      * proxy with a handler of type AnnotationInvocationHandler.
 212      * Returns null otherwise.
 213      */
 214     private AnnotationInvocationHandler asOneOfUs(Object o) {
 215         if (Proxy.isProxyClass(o.getClass())) {
 216             InvocationHandler handler = Proxy.getInvocationHandler(o);
 217             if (handler instanceof AnnotationInvocationHandler)
 218                 return (AnnotationInvocationHandler) handler;
 219         }
 220         return null;
 221     }
 222 
 223     /**
 224      * Returns true iff the two member values in "dynamic proxy return form"
 225      * are equal using the appropriate equality function depending on the
 226      * member type.  The two values will be of the same type unless one of
 227      * the containing annotations is ill-formed.  If one of the containing
 228      * annotations is ill-formed, this method will return false unless the
 229      * two members are identical object references.
 230      */
 231     private static boolean memberValueEquals(Object v1, Object v2) {
 232         Class<?> type = v1.getClass();
 233 
 234         // Check for primitive, string, class, enum const, annotation,
 235         // or ExceptionProxy
 236         if (!type.isArray())
 237             return v1.equals(v2);
 238 
 239         // Check for array of string, class, enum const, annotation,
 240         // or ExceptionProxy
 241         if (v1 instanceof Object[] && v2 instanceof Object[])
 242             return Arrays.equals((Object[]) v1, (Object[]) v2);
 243 
 244         // Check for ill formed annotation(s)
 245         if (v2.getClass() != type)
 246             return false;
 247 
 248         // Deal with array of primitives
 249         if (type == byte[].class)
 250             return Arrays.equals((byte[]) v1, (byte[]) v2);
 251         if (type == char[].class)
 252             return Arrays.equals((char[]) v1, (char[]) v2);
 253         if (type == double[].class)
 254             return Arrays.equals((double[]) v1, (double[]) v2);
 255         if (type == float[].class)
 256             return Arrays.equals((float[]) v1, (float[]) v2);
 257         if (type == int[].class)
 258             return Arrays.equals((int[]) v1, (int[]) v2);
 259         if (type == long[].class)
 260             return Arrays.equals((long[]) v1, (long[]) v2);
 261         if (type == short[].class)
 262             return Arrays.equals((short[]) v1, (short[]) v2);
 263         assert type == boolean[].class;
 264         return Arrays.equals((boolean[]) v1, (boolean[]) v2);
 265     }
 266 
 267     /**
 268      * Returns the member methods for our annotation type.  These are
 269      * obtained lazily and cached, as they're expensive to obtain
 270      * and we only need them if our equals method is invoked (which should
 271      * be rare).
 272      */
 273     private Method[] getMemberMethods() {
 274         if (memberMethods == null) {
 275             memberMethods = AccessController.doPrivileged(
 276                 new PrivilegedAction<Method[]>() {
 277                     public Method[] run() {
 278                         final Method[] mm = type.getDeclaredMethods();
 279                         AccessibleObject.setAccessible(mm, true);
 280                         return mm;
 281                     }
 282                 });
 283         }
 284         return memberMethods;
 285     }
 286     private transient volatile Method[] memberMethods = null;
 287 
 288     /**
 289      * Implementation of dynamicProxy.hashCode()
 290      */
 291     private int hashCodeImpl() {
 292         int result = 0;
 293         for (Map.Entry<String, Object> e : memberValues.entrySet()) {
 294             result += (127 * e.getKey().hashCode()) ^
 295                 memberValueHashCode(e.getValue());
 296         }
 297         return result;
 298     }
 299 
 300     /**
 301      * Computes hashCode of a member value (in "dynamic proxy return form")
 302      */
 303     private static int memberValueHashCode(Object value) {
 304         Class<?> type = value.getClass();
 305         if (!type.isArray())    // primitive, string, class, enum const,
 306                                 // or annotation
 307             return value.hashCode();
 308 
 309         if (type == byte[].class)
 310             return Arrays.hashCode((byte[]) value);
 311         if (type == char[].class)
 312             return Arrays.hashCode((char[]) value);
 313         if (type == double[].class)
 314             return Arrays.hashCode((double[]) value);
 315         if (type == float[].class)
 316             return Arrays.hashCode((float[]) value);
 317         if (type == int[].class)
 318             return Arrays.hashCode((int[]) value);
 319         if (type == long[].class)
 320             return Arrays.hashCode((long[]) value);
 321         if (type == short[].class)
 322             return Arrays.hashCode((short[]) value);
 323         if (type == boolean[].class)
 324             return Arrays.hashCode((boolean[]) value);
 325         return Arrays.hashCode((Object[]) value);
 326     }
 327 
 328     private void readObject(java.io.ObjectInputStream s)
 329         throws java.io.IOException, ClassNotFoundException {
 330         s.defaultReadObject();
 331 
 332 
 333         // Check to make sure that types have not evolved incompatibly
 334 
 335         AnnotationType annotationType = null;
 336         try {
 337             annotationType = AnnotationType.getInstance(type);
 338         } catch(IllegalArgumentException e) {
 339             // Class is no longer an annotation type; all bets are off
 340             return;
 341         }
 342 
 343         Map<String, Class<?>> memberTypes = annotationType.memberTypes();
 344 
 345         for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
 346             String name = memberValue.getKey();
 347             Class<?> memberType = memberTypes.get(name);
 348             if (memberType != null) {  // i.e. member still exists
 349                 Object value = memberValue.getValue();
 350                 if (!(memberType.isInstance(value) ||
 351                       value instanceof ExceptionProxy)) {
 352                     memberValue.setValue(
 353                         new AnnotationTypeMismatchExceptionProxy(
 354                             value.getClass() + "[" + value + "]").setMember(
 355                                 annotationType.members().get(name)));
 356                 }
 357             }
 358         }
 359     }
 360 }