< prev index next >

src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java

Print this page




   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.io.ObjectInputStream;
  29 import java.lang.annotation.*;
  30 import java.lang.reflect.*;
  31 import java.io.Serializable;
  32 import java.util.*;
  33 import java.util.stream.*;
  34 import java.security.AccessController;
  35 import java.security.PrivilegedAction;
















  36 
  37 /**
  38  * InvocationHandler for dynamic proxy implementation of Annotation.
  39  *
  40  * @author  Josh Bloch
  41  * @since   1.5
  42  */
  43 class AnnotationInvocationHandler implements InvocationHandler, Serializable {
  44     private static final long serialVersionUID = 6182022883658399397L;
  45     private final Class<? extends Annotation> type;
  46     private final Map<String, Object> memberValues;
  47 
  48     AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
  49         Class<?>[] superInterfaces = type.getInterfaces();
  50         if (!type.isAnnotation() ||
  51             superInterfaces.length != 1 ||
  52             superInterfaces[0] != java.lang.annotation.Annotation.class)
  53             throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");




















  54         this.type = type;
  55         this.memberValues = memberValues;






  56     }
  57 
  58     public Object invoke(Object proxy, Method method, Object[] args) {
  59         String member = method.getName();
  60         Class<?>[] paramTypes = method.getParameterTypes();
  61 
  62         // Handle Object and Annotation methods
  63         if (member.equals("equals") && paramTypes.length == 1 &&
  64             paramTypes[0] == Object.class)
  65             return equalsImpl(proxy, args[0]);
  66         if (paramTypes.length != 0)
  67             throw new AssertionError("Too many parameters for an annotation method");
  68 
  69         switch(member) {
  70         case "toString":
  71             return toStringImpl();
  72         case "hashCode":
  73             return hashCodeImpl();
  74         case "annotationType":
  75             return type;



  76         }
  77 
  78         // Handle annotation member accessors
  79         Object result = memberValues.get(member);










  80 
  81         if (result == null)
  82             throw new IncompleteAnnotationException(type, member);
  83 
  84         if (result instanceof ExceptionProxy)
  85             throw ((ExceptionProxy) result).generateException();
  86 
  87         if (result.getClass().isArray() && Array.getLength(result) != 0)
  88             result = cloneArray(result);
  89 
  90         return result;
  91     }
  92 
  93     /**
  94      * This method, which clones its array argument, would not be necessary
  95      * if Cloneable had a public clone method.
  96      */
  97     private Object cloneArray(Object array) {
  98         Class<?> type = array.getClass();
  99 
 100         if (type == byte[].class) {
 101             byte[] byteArray = (byte[])array;
 102             return byteArray.clone();


 127         }
 128         if (type == boolean[].class) {
 129             boolean[] booleanArray = (boolean[])array;
 130             return booleanArray.clone();
 131         }
 132 
 133         Object[] objectArray = (Object[])array;
 134         return objectArray.clone();
 135     }
 136 
 137 
 138     /**
 139      * Implementation of dynamicProxy.toString()
 140      */
 141     private String toStringImpl() {
 142         StringBuilder result = new StringBuilder(128);
 143         result.append('@');
 144         result.append(type.getName());
 145         result.append('(');
 146         boolean firstMember = true;
 147         for (Map.Entry<String, Object> e : memberValues.entrySet()) {
 148             if (firstMember)
 149                 firstMember = false;
 150             else
 151                 result.append(", ");
 152 
 153             result.append(e.getKey());
 154             result.append('=');
 155             result.append(memberValueToString(e.getValue()));
 156         }
 157         result.append(')');
 158         return result.toString();
 159     }
 160 
 161     /**
 162      * Translates a member value (in "dynamic proxy return form") into a string.
 163      */
 164     private static String memberValueToString(Object value) {
 165         Class<?> type = value.getClass();
 166         if (!type.isArray()) {
 167             // primitive value, string, class, enum const, or annotation
 168             if (type == Class.class)
 169                 return toSourceString((Class<?>) value);
 170             else if (type == String.class)
 171                 return  toSourceString((String) value);
 172             if (type == Character.class)
 173                 return toSourceString((char) value);
 174             else if (type == Double.class)
 175                 return  toSourceString((double) value);


 315     private static Stream<String> convert(boolean[] values) {
 316         List<String> list = new ArrayList<>(values.length);
 317         for (boolean b : values)
 318             list.add(Boolean.toString(b));
 319         return list.stream();
 320     }
 321 
 322     private static String stringStreamToString(Stream<String> stream) {
 323         return stream.collect(Collectors.joining(", ", "{", "}"));
 324     }
 325 
 326     /**
 327      * Implementation of dynamicProxy.equals(Object o)
 328      */
 329     private Boolean equalsImpl(Object proxy, Object o) {
 330         if (o == proxy)
 331             return true;
 332 
 333         if (!type.isInstance(o))
 334             return false;
 335         for (Method memberMethod : getMemberMethods()) {
 336             String member = memberMethod.getName();
 337             Object ourValue = memberValues.get(member);
 338             Object hisValue = null;
 339             AnnotationInvocationHandler hisHandler = asOneOfUs(o);
 340             if (hisHandler != null) {
 341                 hisValue = hisHandler.memberValues.get(member);













 342             } else {





 343                 try {
 344                     hisValue = memberMethod.invoke(o);
 345                 } catch (InvocationTargetException e) {



 346                     return false;
 347                 } catch (IllegalAccessException e) {
 348                     throw new AssertionError(e);
 349                 }


 350             }
 351             if (!memberValueEquals(ourValue, hisValue))
 352                 return false;
 353         }


 354         return true;
 355     }
 356 
 357     /**
 358      * Returns an object's invocation handler if that object is a dynamic
 359      * proxy with a handler of type AnnotationInvocationHandler.
 360      * Returns null otherwise.
 361      */
 362     private AnnotationInvocationHandler asOneOfUs(Object o) {
 363         if (Proxy.isProxyClass(o.getClass())) {
 364             InvocationHandler handler = Proxy.getInvocationHandler(o);
 365             if (handler instanceof AnnotationInvocationHandler)
 366                 return (AnnotationInvocationHandler) handler;
 367         }
 368         return null;
 369     }
 370 
 371     /**
 372      * Returns true iff the two member values in "dynamic proxy return form"
 373      * are equal using the appropriate equality function depending on the
 374      * member type.  The two values will be of the same type unless one of
 375      * the containing annotations is ill-formed.  If one of the containing
 376      * annotations is ill-formed, this method will return false unless the
 377      * two members are identical object references.
 378      */
 379     private static boolean memberValueEquals(Object v1, Object v2) {







 380         Class<?> type = v1.getClass();
 381 
 382         // Check for primitive, string, class, enum const, annotation,
 383         // or ExceptionProxy
 384         if (!type.isArray())
 385             return v1.equals(v2);
 386 
 387         // Check for array of string, class, enum const, annotation,
 388         // or ExceptionProxy
 389         if (v1 instanceof Object[] && v2 instanceof Object[])
 390             return Arrays.equals((Object[]) v1, (Object[]) v2);
 391 
 392         // Check for ill formed annotation(s)
 393         if (v2.getClass() != type)
 394             return false;
 395 
 396         // Deal with array of primitives
 397         if (type == byte[].class)
 398             return Arrays.equals((byte[]) v1, (byte[]) v2);
 399         if (type == char[].class)
 400             return Arrays.equals((char[]) v1, (char[]) v2);
 401         if (type == double[].class)
 402             return Arrays.equals((double[]) v1, (double[]) v2);
 403         if (type == float[].class)
 404             return Arrays.equals((float[]) v1, (float[]) v2);
 405         if (type == int[].class)
 406             return Arrays.equals((int[]) v1, (int[]) v2);
 407         if (type == long[].class)
 408             return Arrays.equals((long[]) v1, (long[]) v2);
 409         if (type == short[].class)
 410             return Arrays.equals((short[]) v1, (short[]) v2);
 411         assert type == boolean[].class;
 412         return Arrays.equals((boolean[]) v1, (boolean[]) v2);
 413     }
 414 
 415     /**
 416      * Returns the member methods for our annotation type.  These are
 417      * obtained lazily and cached, as they're expensive to obtain
 418      * and we only need them if our equals method is invoked (which should
 419      * be rare).
 420      */
 421     private Method[] getMemberMethods() {
 422         Method[] value = memberMethods;
 423         if (value == null) {
 424             value = computeMemberMethods();
 425             memberMethods = value;
 426         }
 427         return value;
 428     }
 429 
 430     private Method[] computeMemberMethods() {
 431         return AccessController.doPrivileged(
 432             new PrivilegedAction<Method[]>() {
 433                 public Method[] run() {
 434                     final Method[] methods = type.getDeclaredMethods();
 435                     validateAnnotationMethods(methods);
 436                     AccessibleObject.setAccessible(methods, true);
 437                     return methods;
 438                 }});
 439     }
 440 
 441     private transient volatile Method[] memberMethods;
 442 
 443     /**
 444      * Validates that a method is structurally appropriate for an
 445      * annotation type. As of Java SE 8, annotation types cannot
 446      * contain static methods and the declared methods of an
 447      * annotation type must take zero arguments and there are
 448      * restrictions on the return type.
 449      */
 450     private void validateAnnotationMethods(Method[] memberMethods) {
 451         /*
 452          * Specification citations below are from JLS
 453          * 9.6.1. Annotation Type Elements
 454          */
 455         boolean valid = true;
 456         for(Method method : memberMethods) {
 457             /*
 458              * "By virtue of the AnnotationTypeElementDeclaration
 459              * production, a method declaration in an annotation type
 460              * declaration cannot have formal parameters, type
 461              * parameters, or a throws clause.
 462              *
 463              * "By virtue of the AnnotationTypeElementModifier
 464              * production, a method declaration in an annotation type
 465              * declaration cannot be default or static."
 466              */
 467             if (method.getModifiers() != (Modifier.PUBLIC | Modifier.ABSTRACT) ||
 468                 method.isDefault() ||
 469                 method.getParameterCount() != 0 ||
 470                 method.getExceptionTypes().length != 0) {
 471                 valid = false;
 472                 break;
 473             }
 474 
 475             /*
 476              * "It is a compile-time error if the return type of a
 477              * method declared in an annotation type is not one of the
 478              * following: a primitive type, String, Class, any
 479              * parameterized invocation of Class, an enum type
 480              * (section 8.9), an annotation type, or an array type
 481              * (chapter 10) whose element type is one of the preceding
 482              * types."
 483              */
 484             Class<?> returnType = method.getReturnType();
 485             if (returnType.isArray()) {
 486                 returnType = returnType.getComponentType();
 487                 if (returnType.isArray()) { // Only single dimensional arrays
 488                     valid = false;
 489                     break;
 490                 }
 491             }
 492 
 493             if (!((returnType.isPrimitive() && returnType != void.class) ||
 494                   returnType == java.lang.String.class ||
 495                   returnType == java.lang.Class.class ||
 496                   returnType.isEnum() ||
 497                   returnType.isAnnotation())) {
 498                 valid = false;
 499                 break;
 500             }
 501 
 502             /*
 503              * "It is a compile-time error if any method declared in an
 504              * annotation type has a signature that is
 505              * override-equivalent to that of any public or protected
 506              * method declared in class Object or in the interface
 507              * java.lang.annotation.Annotation."
 508              *
 509              * The methods in Object or Annotation meeting the other
 510              * criteria (no arguments, contrained return type, etc.)
 511              * above are:
 512              *
 513              * String toString()
 514              * int hashCode()
 515              * Class<? extends Annotation> annotationType()
 516              */
 517             String methodName = method.getName();
 518             if ((methodName.equals("toString") && returnType == java.lang.String.class) ||
 519                 (methodName.equals("hashCode") && returnType == int.class) ||
 520                 (methodName.equals("annotationType") && returnType == java.lang.Class.class)) {
 521                 valid = false;
 522                 break;
 523             }
 524         }
 525         if (valid)
 526             return;
 527         else
 528             throw new AnnotationFormatError("Malformed method on an annotation type");
 529     }
 530 
 531     /**
 532      * Implementation of dynamicProxy.hashCode()
 533      */
 534     private int hashCodeImpl() {
 535         int result = 0;
 536         for (Map.Entry<String, Object> e : memberValues.entrySet()) {
 537             result += (127 * e.getKey().hashCode()) ^
 538                 memberValueHashCode(e.getValue());



 539         }
 540         return result;
 541     }
 542 
 543     /**
 544      * Computes hashCode of a member value (in "dynamic proxy return form")
 545      */
 546     private static int memberValueHashCode(Object value) {
 547         Class<?> type = value.getClass();
 548         if (!type.isArray())    // primitive, string, class, enum const,
 549                                 // or annotation
 550             return value.hashCode();
 551 
 552         if (type == byte[].class)
 553             return Arrays.hashCode((byte[]) value);
 554         if (type == char[].class)
 555             return Arrays.hashCode((char[]) value);
 556         if (type == double[].class)
 557             return Arrays.hashCode((double[]) value);
 558         if (type == float[].class)
 559             return Arrays.hashCode((float[]) value);
 560         if (type == int[].class)
 561             return Arrays.hashCode((int[]) value);
 562         if (type == long[].class)
 563             return Arrays.hashCode((long[]) value);
 564         if (type == short[].class)
 565             return Arrays.hashCode((short[]) value);
 566         if (type == boolean[].class)
 567             return Arrays.hashCode((boolean[]) value);
 568         return Arrays.hashCode((Object[]) value);
 569     }
 570 








































































































 571     private void readObject(java.io.ObjectInputStream s)
 572         throws java.io.IOException, ClassNotFoundException {
 573         ObjectInputStream.GetField fields = s.readFields();
 574 
 575         @SuppressWarnings("unchecked")
 576         Class<? extends Annotation> t = (Class<? extends Annotation>)fields.get("type", null);
 577         @SuppressWarnings("unchecked")
 578         Map<String, Object> streamVals = (Map<String, Object>)fields.get("memberValues", null);
 579 
 580         // Check to make sure that types have not evolved incompatibly
 581 
 582         AnnotationType annotationType = null;
 583         try {
 584             annotationType = AnnotationType.getInstance(t);
 585         } catch(IllegalArgumentException e) {
 586             // Class is no longer an annotation type; time to punch out
 587             throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
 588         }
 589 
 590         Map<String, Class<?>> memberTypes = annotationType.memberTypes();
 591         // consistent with runtime Map type
 592         Map<String, Object> mv = new LinkedHashMap<>();





 593 
 594         // If there are annotation members without values, that
 595         // situation is handled by the invoke method.
 596         for (Map.Entry<String, Object> memberValue : streamVals.entrySet()) {
 597             String name = memberValue.getKey();
 598             Object value = null;
 599             Class<?> memberType = memberTypes.get(name);
 600             if (memberType != null) {  // i.e. member still exists
 601                 value = memberValue.getValue();
 602                 if (!(memberType.isInstance(value) ||
 603                       value instanceof ExceptionProxy)) {
 604                     value = new AnnotationTypeMismatchExceptionProxy(
 605                             value.getClass() + "[" + value + "]").setMember(
 606                                 annotationType.members().get(name));
 607                 }
 608             }
 609             mv.put(name, value);
 610         }
 611 
 612         UnsafeAccessor.setType(this, t);
 613         UnsafeAccessor.setMemberValues(this, mv);
 614     }
 615 
 616     private static class UnsafeAccessor {
 617         private static final jdk.internal.misc.Unsafe unsafe;
 618         private static final long typeOffset;
 619         private static final long memberValuesOffset;
 620         static {
 621             try {
 622                 unsafe = jdk.internal.misc.Unsafe.getUnsafe();
 623                 typeOffset = unsafe.objectFieldOffset
 624                         (AnnotationInvocationHandler.class.getDeclaredField("type"));
 625                 memberValuesOffset = unsafe.objectFieldOffset
 626                         (AnnotationInvocationHandler.class.getDeclaredField("memberValues"));
 627             } catch (Exception ex) {
 628                 throw new ExceptionInInitializerError(ex);
 629             }
 630         }
 631         static void setType(AnnotationInvocationHandler o,
 632                             Class<? extends Annotation> type) {
 633             unsafe.putObject(o, typeOffset, type);
 634         }
 635 
 636         static void setMemberValues(AnnotationInvocationHandler o,
 637                                     Map<String, Object> memberValues) {
 638             unsafe.putObject(o, memberValuesOffset, memberValues);
 639         }
 640     }
 641 }


   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 jdk.internal.vm.annotation.Stable;
  29 
  30 import java.io.ObjectInputStream;
  31 import java.io.ObjectOutputStream;
  32 import java.io.ObjectStreamField;
  33 import java.io.Serializable;
  34 import java.lang.annotation.Annotation;
  35 import java.lang.annotation.AnnotationFormatError;
  36 import java.lang.annotation.IncompleteAnnotationException;
  37 import java.lang.invoke.VarHandle;
  38 import java.lang.reflect.Array;
  39 import java.lang.reflect.InvocationHandler;
  40 import java.lang.reflect.InvocationTargetException;
  41 import java.lang.reflect.Method;
  42 import java.lang.reflect.Proxy;
  43 import java.util.ArrayList;
  44 import java.util.Arrays;
  45 import java.util.LinkedHashMap;
  46 import java.util.List;
  47 import java.util.Map;
  48 import java.util.Objects;
  49 import java.util.stream.Collectors;
  50 import java.util.stream.DoubleStream;
  51 import java.util.stream.IntStream;
  52 import java.util.stream.LongStream;
  53 import java.util.stream.Stream;
  54 
  55 /**
  56  * InvocationHandler for dynamic proxy implementation of Annotation.
  57  *
  58  * @author  Josh Bloch
  59  * @since   1.5
  60  */
  61 class AnnotationInvocationHandler implements InvocationHandler, Serializable {
  62     private static final long serialVersionUID = 6182022883658399397L;


  63 
  64     /**
  65      * Serialized pseudo-fields, provided for serialization compatibility.
  66      * @serialField type The annotation type.
  67      * @serialField memberValues The member values as LinkedHashMap.
  68      */
  69     private static final ObjectStreamField[] serialPersistentFields = {
  70         new ObjectStreamField("type", Class.class),
  71         new ObjectStreamField("memberValues", Map.class)
  72     };
  73 
  74     @Stable
  75     private transient Class<? extends Annotation> type;
  76     @Stable
  77     private transient Object[] valuesTable;
  78     @Stable
  79     private transient int[] keysIndex;
  80 
  81     AnnotationInvocationHandler(Class<? extends Annotation> type,
  82                                 Map<String, Object> memberValues) {
  83 
  84         // there will never be an extraneous value in the given map since
  85         // parser already skips values that are not members of the annotation type
  86         // and so does deserialization (see readObject)
  87         assert AnnotationType.getInstance(type).members().keySet()
  88                              .containsAll(memberValues.keySet());
  89 
  90         this.type = type;
  91         this.valuesTable = toLinearProbeHashTable(
  92             memberValues,
  93             this.keysIndex = new int[memberValues.size()]
  94         );
  95 
  96         // analogue to "freeze" action for final fields
  97         VarHandle.releaseFence();
  98     }
  99 
 100     public Object invoke(Object proxy, Method method, Object[] args) {


 101 
 102         String memberName = method.getName(); // guaranteed interned String
 103         Class<?> dtype = method.getDeclaringClass();
 104 
 105         if (dtype == Object.class ||  // equals/hashCode/toString
 106             dtype == Annotation.class // annotationType
 107             ) {
 108             if (memberName == "equals") return equalsImpl(proxy, args[0]);
 109             if (memberName == "hashCode") return hashCodeImpl();
 110             if (memberName == "annotationType") return type;
 111             if (memberName == "toString") return toStringImpl();
 112             throw new AssertionError("Invalid method: " + method);
 113         }
 114 
 115         assert dtype == type;
 116         assert method.getParameterCount() == 0;
 117 
 118         return getValue(memberName);
 119     }
 120 
 121     /**
 122      * Returns a value for given interned member name.
 123      *
 124      * @param internedName an interned String containing the name of the member
 125      *                     to retrieve.
 126      * @throws IncompleteAnnotationException if invoked for annotation not
 127      *                                       containing given name.
 128      */
 129     Object getValue(String internedName) {
 130         assert internedName.intern() == internedName;
 131 
 132         Object result = getValue(valuesTable, internedName);
 133 
 134         if (result == null)
 135             throw new IncompleteAnnotationException(type, internedName);
 136 
 137         if (result instanceof ExceptionProxy)
 138             throw ((ExceptionProxy) result).generateException();
 139 
 140         if (result.getClass().isArray() && Array.getLength(result) != 0)
 141             result = cloneArray(result);
 142 
 143         return result;
 144     }
 145 
 146     /**
 147      * This method, which clones its array argument, would not be necessary
 148      * if Cloneable had a public clone method.
 149      */
 150     private Object cloneArray(Object array) {
 151         Class<?> type = array.getClass();
 152 
 153         if (type == byte[].class) {
 154             byte[] byteArray = (byte[])array;
 155             return byteArray.clone();


 180         }
 181         if (type == boolean[].class) {
 182             boolean[] booleanArray = (boolean[])array;
 183             return booleanArray.clone();
 184         }
 185 
 186         Object[] objectArray = (Object[])array;
 187         return objectArray.clone();
 188     }
 189 
 190 
 191     /**
 192      * Implementation of dynamicProxy.toString()
 193      */
 194     private String toStringImpl() {
 195         StringBuilder result = new StringBuilder(128);
 196         result.append('@');
 197         result.append(type.getName());
 198         result.append('(');
 199         boolean firstMember = true;
 200         for (int i : keysIndex) {
 201             if (firstMember)
 202                 firstMember = false;
 203             else
 204                 result.append(", ");
 205 
 206             result.append(valuesTable[i]);
 207             result.append('=');
 208             result.append(memberValueToString(valuesTable[i + 1]));
 209         }
 210         result.append(')');
 211         return result.toString();
 212     }
 213 
 214     /**
 215      * Translates a member value (in "dynamic proxy return form") into a string.
 216      */
 217     private static String memberValueToString(Object value) {
 218         Class<?> type = value.getClass();
 219         if (!type.isArray()) {
 220             // primitive value, string, class, enum const, or annotation
 221             if (type == Class.class)
 222                 return toSourceString((Class<?>) value);
 223             else if (type == String.class)
 224                 return  toSourceString((String) value);
 225             if (type == Character.class)
 226                 return toSourceString((char) value);
 227             else if (type == Double.class)
 228                 return  toSourceString((double) value);


 368     private static Stream<String> convert(boolean[] values) {
 369         List<String> list = new ArrayList<>(values.length);
 370         for (boolean b : values)
 371             list.add(Boolean.toString(b));
 372         return list.stream();
 373     }
 374 
 375     private static String stringStreamToString(Stream<String> stream) {
 376         return stream.collect(Collectors.joining(", ", "{", "}"));
 377     }
 378 
 379     /**
 380      * Implementation of dynamicProxy.equals(Object o)
 381      */
 382     private Boolean equalsImpl(Object proxy, Object o) {
 383         if (o == proxy)
 384             return true;
 385 
 386         if (!type.isInstance(o))
 387             return false;
 388 



 389         AnnotationInvocationHandler hisHandler = asOneOfUs(o);
 390 
 391         if (hisHandler != null) { // One Of Us
 392             if (valuesTable.length != hisHandler.valuesTable.length) {
 393                 return false;
 394             }
 395             for (int i = 0; i < valuesTable.length; i+=2) {
 396                 String k = (String) valuesTable[i];
 397                 if (k != null) {
 398                     Object value = valuesTable[i+1];
 399                     Object hisValue = getValue(hisHandler.valuesTable, k);
 400                     if (!memberValueEquals(value, hisValue)) {
 401                         return false;
 402                     }
 403                 }
 404             }
 405         } else {
 406             for (Method accMember : AnnotationType.getInstance(type)
 407                                                   .accessibleMembers().values()) {
 408                 String k = accMember.getName(); // guaranteed interned string
 409                 Object value = getValue(valuesTable, k);
 410                 Object hisValue;
 411                 try {
 412                     hisValue = accMember.invoke(o);
 413                 } catch (InvocationTargetException ex) {
 414                     if (ex.getTargetException() instanceof IncompleteAnnotationException) {
 415                         hisValue = null;
 416                     } else {
 417                         return false;


 418                     }
 419                 } catch (IllegalAccessException ex) {
 420                     throw new AssertionError(ex);
 421                 }
 422                 if (!memberValueEquals(value, hisValue)) {
 423                     return false;
 424                 }
 425             }
 426         }
 427         return true;
 428     }
 429 
 430     /**
 431      * Returns an object's invocation handler if that object is a dynamic
 432      * proxy with a handler of type AnnotationInvocationHandler.
 433      * Returns null otherwise.
 434      */
 435     static AnnotationInvocationHandler asOneOfUs(Object o) {
 436         if (Proxy.isProxyClass(o.getClass())) {
 437             InvocationHandler handler = Proxy.getInvocationHandler(o);
 438             if (handler instanceof AnnotationInvocationHandler)
 439                 return (AnnotationInvocationHandler) handler;
 440         }
 441         return null;
 442     }
 443 
 444     /**
 445      * Returns true iff the two member values in "dynamic proxy return form"
 446      * are equal using the appropriate equality function depending on the
 447      * member type.  The two values will be of the same type unless one of
 448      * the containing annotations is ill-formed.  If one of the containing
 449      * annotations is ill-formed, this method will return false unless the
 450      * two members are identical object references.
 451      */
 452     private static boolean memberValueEquals(Object v1, Object v2) {
 453         if (v1 == v2) {
 454             return true;
 455         }
 456         if (v1 == null || v2 == null) {
 457             return false;
 458         }
 459 
 460         Class<?> type = v1.getClass();
 461 
 462         // Check for primitive, string, class, enum const, annotation,
 463         // or ExceptionProxy
 464         if (!type.isArray())
 465             return v1.equals(v2);
 466 
 467         // Check for array of string, class, enum const, annotation,
 468         // or ExceptionProxy
 469         if (v1 instanceof Object[] && v2 instanceof Object[])
 470             return Arrays.equals((Object[]) v1, (Object[]) v2);
 471 
 472         // Check for ill formed annotation(s)
 473         if (v2.getClass() != type)
 474             return false;
 475 
 476         // Deal with array of primitives
 477         if (type == byte[].class)
 478             return Arrays.equals((byte[]) v1, (byte[]) v2);
 479         if (type == char[].class)
 480             return Arrays.equals((char[]) v1, (char[]) v2);
 481         if (type == double[].class)
 482             return Arrays.equals((double[]) v1, (double[]) v2);
 483         if (type == float[].class)
 484             return Arrays.equals((float[]) v1, (float[]) v2);
 485         if (type == int[].class)
 486             return Arrays.equals((int[]) v1, (int[]) v2);
 487         if (type == long[].class)
 488             return Arrays.equals((long[]) v1, (long[]) v2);
 489         if (type == short[].class)
 490             return Arrays.equals((short[]) v1, (short[]) v2);
 491         assert type == boolean[].class;
 492         return Arrays.equals((boolean[]) v1, (boolean[]) v2);
 493     }
 494 
 495     /**




















































































































 496      * Implementation of dynamicProxy.hashCode()
 497      */
 498     private int hashCodeImpl() {
 499         int result = 0;
 500         for (int i = 0; i < valuesTable.length; i+=2) {
 501             Object k = valuesTable[i];
 502             if (k != null) {
 503                 result += (127 * k.hashCode()) ^
 504                           memberValueHashCode(valuesTable[i+1]);
 505             }
 506         }
 507         return result;
 508     }
 509 
 510     /**
 511      * Computes hashCode of a member value (in "dynamic proxy return form")
 512      */
 513     private static int memberValueHashCode(Object value) {
 514         Class<?> type = value.getClass();
 515         if (!type.isArray())    // primitive, string, class, enum const,
 516                                 // or annotation
 517             return value.hashCode();
 518 
 519         if (type == byte[].class)
 520             return Arrays.hashCode((byte[]) value);
 521         if (type == char[].class)
 522             return Arrays.hashCode((char[]) value);
 523         if (type == double[].class)
 524             return Arrays.hashCode((double[]) value);
 525         if (type == float[].class)
 526             return Arrays.hashCode((float[]) value);
 527         if (type == int[].class)
 528             return Arrays.hashCode((int[]) value);
 529         if (type == long[].class)
 530             return Arrays.hashCode((long[]) value);
 531         if (type == short[].class)
 532             return Arrays.hashCode((short[]) value);
 533         if (type == boolean[].class)
 534             return Arrays.hashCode((boolean[]) value);
 535         return Arrays.hashCode((Object[]) value);
 536     }
 537 
 538     /**
 539      * Creates an open-addressing liner-probe hash table to hold given size
 540      * entries.
 541      */
 542     private static Object[] newLinearProbeHashTable(int size) {
 543         // capacity = smallest power of 2 greater than or equal to size * 3 / 2
 544         int cap = Integer.highestOneBit(3 * Math.max(1, size));
 545         // table length = 2 * capacity (2 slots per (key, value) entry)
 546         return new Object[2 * cap];
 547     }
 548 
 549      /**
 550      * Creates an open-addressing liner-probe hash table from given valuesMap
 551      * and fills the given keysIndex array with a sequence of indices into the
 552      * created table that point to keys in valuesMap iteration order.
 553      */
 554     private static Object[] toLinearProbeHashTable(Map<String, Object> valuesMap,
 555                                                    int[] keysIndex) {
 556         assert valuesMap.size() == keysIndex.length;
 557         Object[] table = newLinearProbeHashTable(valuesMap.size());
 558         int size = 0;
 559         for (Map.Entry<String, Object> e : valuesMap.entrySet()) {
 560             size = putValue(table, keysIndex, size, e.getKey(), e.getValue());
 561         }
 562         return table;
 563     }
 564 
 565     /**
 566      * Inserts new (key, val) entry into given open-addressing liner-probe hash
 567      * table, and adds new key index into given keysIndex at given size position.
 568      * The assumption is that the table does not already contain given key.
 569      * @return incremented size.
 570      */
 571     private static int putValue(Object[] table, int[] keysIndex, int size,
 572                                 String key, Object val) {
 573         // keys are interned strings to speed-up lookup since we already
 574         // have an interned string to match in lookup (from Method.getName())
 575         key = key.intern();
 576         int i = firstKeyIndex(key, table.length);
 577         while (table[i] != null) {
 578             assert table[i] != key;
 579             i = nextKeyIndex(i, table.length);
 580         }
 581         keysIndex[size] = i;
 582         table[i] = key;
 583         table[i + 1] = val;
 584         return size + 1;
 585     }
 586 
 587     /**
 588      * Creates a LinkedHashMap from given open-addressing liner-probe hash table
 589      * and given keysIndex array holding a sequence of indices into table
 590      * in order of insertion into returned LinkedHashMap.
 591      */
 592     private static LinkedHashMap<String, Object> toLinkedHashMap(Object[] table,
 593                                                                  int[] keysIndex) {
 594         LinkedHashMap<String, Object> map = new LinkedHashMap<>(keysIndex.length + 1, 1.0f);
 595         for (int i : keysIndex) {
 596             map.put((String) table[i], table[i + 1]);
 597         }
 598         return map;
 599     }
 600 
 601     /**
 602      * Looks-up a value in an open-addressing liner-probe hash table by given
 603      * interned String key.
 604      */
 605     private static Object getValue(Object[] table, String internedKey) {
 606         int i = firstKeyIndex(internedKey, table.length);
 607         Object k;
 608         while ((k = table[i]) != null) {
 609             if (k == internedKey) {
 610                 return table[i+1];
 611             } else {
 612                 i = nextKeyIndex(i, table.length);
 613             }
 614         }
 615         return null;
 616     }
 617 
 618     /**
 619      * Returns index for Object x.
 620      */
 621     private static int firstKeyIndex(Object x, int length) {
 622         int h = System.identityHashCode(x);
 623         // Multiply by -127, and left-shift to use least bit as part of hash
 624         return ((h << 1) - (h << 8)) & (length - 1);
 625     }
 626 
 627     /**
 628      * Circularly traverses table of power of 2 size length.
 629      */
 630     private static int nextKeyIndex(int i, int length) {
 631         return (i + 2) & (length - 1);
 632     }
 633 
 634     private void writeObject(java.io.ObjectOutputStream s)
 635         throws java.io.IOException {
 636         ObjectOutputStream.PutField fields = s.putFields();
 637         fields.put("type", type);
 638         fields.put("memberValues", toLinkedHashMap(valuesTable, keysIndex));
 639         s.writeFields();
 640     }
 641 
 642     private void readObject(java.io.ObjectInputStream s)
 643         throws java.io.IOException, ClassNotFoundException {
 644         ObjectInputStream.GetField fields = s.readFields();
 645 
 646         @SuppressWarnings("unchecked")
 647         Class<? extends Annotation> t = (Class<? extends Annotation>)fields.get("type", null);
 648         @SuppressWarnings("unchecked")
 649         Map<String, Object> memberValues = (Map<String, Object>)fields.get("memberValues", null);
 650 
 651         // Check to make sure that types have not evolved incompatibly
 652         AnnotationType annotationType;

 653         try {
 654             annotationType = AnnotationType.getInstance(t);
 655         } catch(AnnotationFormatError e) {
 656             // Class is no longer an annotation type; time to punch out
 657             throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
 658         }
 659 
 660         Map<String, Class<?>> memberTypes = annotationType.memberTypes();
 661 
 662         // just keep values that have a corresponding member, remove the rest...
 663         memberValues.keySet().retainAll(memberTypes.keySet());
 664 
 665         Object[] table = newLinearProbeHashTable(memberValues.size());
 666         int[] keysIndex = new int[memberValues.size()];
 667         int size = 0;
 668 
 669         // If there are annotation members without values, that
 670         // situation is handled by the invoke method.
 671         for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
 672             String name = memberValue.getKey();
 673             Object value = null;
 674             Class<?> memberType = memberTypes.get(name);
 675             if (memberType != null) {  // i.e. member still exists
 676                 value = memberValue.getValue();
 677                 if (!(memberType.isInstance(value) ||
 678                       value instanceof ExceptionProxy)) {
 679                     value = new AnnotationTypeMismatchExceptionProxy(
 680                             value.getClass() + "[" + value + "]").setMember(
 681                                 annotationType.members().get(name));
 682                 }
 683             }
 684             size = putValue(table, keysIndex, size, name, value);




 685         }
 686 
 687         this.type = t;
 688         this.valuesTable = table;
 689         this.keysIndex = keysIndex;





















 690     }
 691 }
< prev index next >