1 /*
   2  * Copyright (c) 2003, 2013, Oracle and/or its affiliates. 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * 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 static final long serialVersionUID = 6182022883658399397L;
  44     private final Class<? extends Annotation> type;
  45     private final Map<String, Object> memberValues;
  46 
  47     AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
  48         this.type = type;
  49         this.memberValues = memberValues;
  50     }
  51 
  52     public Object invoke(Object proxy, Method method, Object[] args) {
  53         String member = method.getName();
  54         // method names are guaranteed to be interned Strings (see Method.equals())
  55         // so reference compare with string literals (which are interned too) is possible...
  56 
  57         if (member == "equals" && method.getParameterCount() == 1) {
  58             assert method.getParameterTypes()[0] == Object.class;
  59             return equalsImpl(args[0]);
  60         }
  61 
  62         assert method.getParameterCount() == 0;
  63 
  64         if (member == "annotationType") {
  65             return type;
  66         }
  67 
  68         if (member == "toString") {
  69             return toStringImpl();
  70         }
  71 
  72         if (member == "hashCode") {
  73             return hashCodeImpl();
  74         }
  75 
  76         // Handle annotation member accessors
  77         Object result = memberValues.get(member);
  78         if (result == null) {
  79             throw new IncompleteAnnotationException(type, member);
  80         }
  81         if (result instanceof ExceptionProxy) {
  82             throw ((ExceptionProxy) result).generateException();
  83         }
  84         if (result.getClass().isArray() && Array.getLength(result) != 0) {
  85             result = cloneArray(result);
  86         }
  87         return result;
  88     }
  89 
  90     /**
  91      * This method, which clones its array argument, would not be necessary
  92      * if Cloneable had a public clone method.
  93      */
  94     private Object cloneArray(Object array) {
  95         Class<?> type = array.getClass();
  96 
  97         if (type == byte[].class) {
  98             byte[] byteArray = (byte[])array;
  99             return byteArray.clone();
 100         }
 101         if (type == char[].class) {
 102             char[] charArray = (char[])array;
 103             return charArray.clone();
 104         }
 105         if (type == double[].class) {
 106             double[] doubleArray = (double[])array;
 107             return doubleArray.clone();
 108         }
 109         if (type == float[].class) {
 110             float[] floatArray = (float[])array;
 111             return floatArray.clone();
 112         }
 113         if (type == int[].class) {
 114             int[] intArray = (int[])array;
 115             return intArray.clone();
 116         }
 117         if (type == long[].class) {
 118             long[] longArray = (long[])array;
 119             return longArray.clone();
 120         }
 121         if (type == short[].class) {
 122             short[] shortArray = (short[])array;
 123             return shortArray.clone();
 124         }
 125         if (type == boolean[].class) {
 126             boolean[] booleanArray = (boolean[])array;
 127             return booleanArray.clone();
 128         }
 129 
 130         Object[] objectArray = (Object[])array;
 131         return objectArray.clone();
 132     }
 133 
 134 
 135     /**
 136      * Implementation of dynamicProxy.toString()
 137      */
 138     private String toStringImpl() {
 139         StringBuilder result = new StringBuilder(128);
 140         result.append('@');
 141         result.append(type.getName());
 142         result.append('(');
 143         boolean firstMember = true;
 144         for (Map.Entry<String, Object> e : memberValues.entrySet()) {
 145             if (firstMember)
 146                 firstMember = false;
 147             else
 148                 result.append(", ");
 149 
 150             result.append(e.getKey());
 151             result.append('=');
 152             result.append(memberValueToString(e.getValue()));
 153         }
 154         result.append(')');
 155         return result.toString();
 156     }
 157 
 158     /**
 159      * Translates a member value (in "dynamic proxy return form") into a string
 160      */
 161     private static String memberValueToString(Object value) {
 162         Class<?> type = value.getClass();
 163         if (!type.isArray())    // primitive, string, class, enum const,
 164                                 // or annotation
 165             return value.toString();
 166 
 167         if (type == byte[].class)
 168             return Arrays.toString((byte[]) value);
 169         if (type == char[].class)
 170             return Arrays.toString((char[]) value);
 171         if (type == double[].class)
 172             return Arrays.toString((double[]) value);
 173         if (type == float[].class)
 174             return Arrays.toString((float[]) value);
 175         if (type == int[].class)
 176             return Arrays.toString((int[]) value);
 177         if (type == long[].class)
 178             return Arrays.toString((long[]) value);
 179         if (type == short[].class)
 180             return Arrays.toString((short[]) value);
 181         if (type == boolean[].class)
 182             return Arrays.toString((boolean[]) value);
 183         return Arrays.toString((Object[]) value);
 184     }
 185 
 186     /**
 187      * Implementation of dynamicProxy.equals(Object o)
 188      */
 189     private Boolean equalsImpl(Object o) {
 190         if (o == this)
 191             return true;
 192 
 193         if (!type.isInstance(o))
 194             return false;
 195 
 196         // one of us?
 197         AnnotationInvocationHandler hisHandler = asOneOfUs(o);
 198         if (hisHandler != null) { // yes
 199             for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
 200                 Object ourValue = memberValue.getValue();
 201                 Object hisValue = hisHandler.memberValues.get(memberValue.getKey());
 202                 if (!memberValueEquals(ourValue, hisValue)) {
 203                     return false;
 204                 }
 205             }
 206         } else { // not one of us
 207             for (Map.Entry<String, Method> memberMethod : AnnotationType.getInstance(type).members().entrySet()) {
 208                 Object ourValue = memberValues.get(memberMethod.getKey());
 209                 Object hisValue;
 210                 try {
 211                     hisValue = memberMethod.getValue().invoke(o);
 212                 } catch (InvocationTargetException e) {
 213                     return false;
 214                 } catch (IllegalAccessException e) {
 215                     throw new AssertionError(e);
 216                 }
 217                 if (!memberValueEquals(ourValue, hisValue)) {
 218                     return false;
 219                 }
 220             }
 221         }
 222         return true;
 223     }
 224 
 225     /**
 226      * Returns an object's invocation handler if that object is a dynamic
 227      * proxy with a handler of type AnnotationInvocationHandler.
 228      * Returns null otherwise.
 229      */
 230     private static AnnotationInvocationHandler asOneOfUs(Object o) {
 231         if (Proxy.isProxyClass(o.getClass())) {
 232             InvocationHandler handler = Proxy.getInvocationHandler(o);
 233             if (handler instanceof AnnotationInvocationHandler)
 234                 return (AnnotationInvocationHandler) handler;
 235         }
 236         return null;
 237     }
 238 
 239     /**
 240      * Returns true iff the two member values in "dynamic proxy return form"
 241      * are equal using the appropriate equality function depending on the
 242      * member type.  The two values will be of the same type unless one of
 243      * the containing annotations is ill-formed.  If one of the containing
 244      * annotations is ill-formed, this method will return false unless the
 245      * two members are identical object references.
 246      */
 247     private static boolean memberValueEquals(Object v1, Object v2) {
 248         Class<?> type = v1.getClass();
 249 
 250         // Check for primitive, string, class, enum const, annotation,
 251         // or ExceptionProxy
 252         if (!type.isArray())
 253             return v1.equals(v2);
 254 
 255         // Check for array of string, class, enum const, annotation,
 256         // or ExceptionProxy
 257         if (v1 instanceof Object[] && v2 instanceof Object[])
 258             return Arrays.equals((Object[]) v1, (Object[]) v2);
 259 
 260         // Check for ill formed annotation(s)
 261         if (v2.getClass() != type)
 262             return false;
 263 
 264         // Deal with array of primitives
 265         if (type == byte[].class)
 266             return Arrays.equals((byte[]) v1, (byte[]) v2);
 267         if (type == char[].class)
 268             return Arrays.equals((char[]) v1, (char[]) v2);
 269         if (type == double[].class)
 270             return Arrays.equals((double[]) v1, (double[]) v2);
 271         if (type == float[].class)
 272             return Arrays.equals((float[]) v1, (float[]) v2);
 273         if (type == int[].class)
 274             return Arrays.equals((int[]) v1, (int[]) v2);
 275         if (type == long[].class)
 276             return Arrays.equals((long[]) v1, (long[]) v2);
 277         if (type == short[].class)
 278             return Arrays.equals((short[]) v1, (short[]) v2);
 279         assert type == boolean[].class;
 280         return Arrays.equals((boolean[]) v1, (boolean[]) v2);
 281     }
 282 
 283     /**
 284      * Implementation of dynamicProxy.hashCode()
 285      */
 286     private int hashCodeImpl() {
 287         int result = 0;
 288         for (Map.Entry<String, Object> e : memberValues.entrySet()) {
 289             result += (127 * e.getKey().hashCode()) ^
 290                 memberValueHashCode(e.getValue());
 291         }
 292         return result;
 293     }
 294 
 295     /**
 296      * Computes hashCode of a member value (in "dynamic proxy return form")
 297      */
 298     private static int memberValueHashCode(Object value) {
 299         Class<?> type = value.getClass();
 300         if (!type.isArray())    // primitive, string, class, enum const,
 301                                 // or annotation
 302             return value.hashCode();
 303 
 304         if (type == byte[].class)
 305             return Arrays.hashCode((byte[]) value);
 306         if (type == char[].class)
 307             return Arrays.hashCode((char[]) value);
 308         if (type == double[].class)
 309             return Arrays.hashCode((double[]) value);
 310         if (type == float[].class)
 311             return Arrays.hashCode((float[]) value);
 312         if (type == int[].class)
 313             return Arrays.hashCode((int[]) value);
 314         if (type == long[].class)
 315             return Arrays.hashCode((long[]) value);
 316         if (type == short[].class)
 317             return Arrays.hashCode((short[]) value);
 318         if (type == boolean[].class)
 319             return Arrays.hashCode((boolean[]) value);
 320         return Arrays.hashCode((Object[]) value);
 321     }
 322 
 323     private void readObject(java.io.ObjectInputStream s)
 324         throws java.io.IOException, ClassNotFoundException {
 325         s.defaultReadObject();
 326 
 327 
 328         // Check to make sure that types have not evolved incompatibly
 329 
 330         AnnotationType annotationType = null;
 331         try {
 332             annotationType = AnnotationType.getInstance(type);
 333         } catch(IllegalArgumentException e) {
 334             // Class is no longer an annotation type; time to punch out
 335             throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
 336         }
 337 
 338         Map<String, Class<?>> memberTypes = annotationType.memberTypes();
 339 
 340 
 341         // If there are annotation members without values, that
 342         // situation is handled by the invoke method.
 343         for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
 344             String name = memberValue.getKey();
 345             Class<?> memberType = memberTypes.get(name);
 346             if (memberType != null) {  // i.e. member still exists
 347                 Object value = memberValue.getValue();
 348                 if (!(memberType.isInstance(value) ||
 349                       value instanceof ExceptionProxy)) {
 350                     memberValue.setValue(
 351                         new AnnotationTypeMismatchExceptionProxy(
 352                             value.getClass() + "[" + value + "]").setMember(
 353                                 annotationType.members().get(name)));
 354                 }
 355             }
 356         }
 357     }
 358 
 359 
 360     /**
 361      * Quick access to member values. If the returned value is an array it might not be a cloned
 362      * copy but shared reference, so it must be cloned before exposed to users.
 363      *
 364      * @param annotation an annotation instance
 365      * @param member a member name
 366      * @return the value of the member of given annotation or null if no such member exists or
 367      *         if invoking member method throws exception
 368      */
 369     static Object getMemberValue(Annotation annotation, String member) {
 370         // one of us?
 371         AnnotationInvocationHandler handler = asOneOfUs(annotation);
 372         if (handler != null) { // yes
 373             return handler.memberValues.get(member);
 374         } else { // not one of us
 375             Method method = AnnotationType.getInstance(annotation.annotationType()).members().get(member);
 376             if (method == null) {
 377                 return null;
 378             } else {
 379                 try {
 380                     return method.invoke(annotation);
 381                 } catch (InvocationTargetException e) {
 382                     return null;
 383                 } catch (IllegalAccessException e) {
 384                     throw new AssertionError(e);
 385                 }
 386             }
 387         }
 388     }
 389 }