001 /* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013 package org.jikesrvm.classloader; 014 015 import java.io.DataInputStream; 016 import java.io.IOException; 017 import java.lang.annotation.Annotation; 018 import java.lang.reflect.Array; 019 import java.lang.reflect.Method; 020 import java.lang.reflect.Proxy; 021 import java.lang.reflect.InvocationHandler; 022 import java.util.Arrays; 023 import org.jikesrvm.VM; 024 import org.jikesrvm.runtime.Statics; 025 import org.jikesrvm.util.ImmutableEntryHashMapRVM; 026 import org.vmmagic.pragma.Uninterruptible; 027 import org.vmmagic.pragma.Pure; 028 import org.vmmagic.unboxed.Offset; 029 030 /** 031 * Internal representation of an annotation. We use a proxy class to implement 032 * actual annotations {@link RVMClass}. 033 */ 034 public final class RVMAnnotation { 035 /** 036 * The type of the annotation. This is an interface name that the 037 * annotation value will implement 038 */ 039 private final TypeReference type; 040 /** 041 * Members of this annotation 042 */ 043 private final AnnotationMember[] elementValuePairs; 044 /** 045 * Remembered unique annotations 046 */ 047 private static final ImmutableEntryHashMapRVM<RVMAnnotation, RVMAnnotation> 048 uniqueMap = new ImmutableEntryHashMapRVM<RVMAnnotation, RVMAnnotation>(); 049 050 /** 051 * The concrete annotation represented by this RVMAnnotation 052 */ 053 private Annotation value; 054 055 /** Encoding of when a result cannot be returned */ 056 private static final Object NO_VALUE = new Object(); 057 058 /** 059 * Construct a read annotation 060 * @param type the name of the type this annotation's value will 061 * implement 062 * @param elementValuePairs values for the fields in the annotation 063 * that override the defaults 064 */ 065 private RVMAnnotation(TypeReference type, AnnotationMember[] elementValuePairs) { 066 this.type = type; 067 this.elementValuePairs = elementValuePairs; 068 } 069 070 /** 071 * Read an annotation attribute from the class file 072 * 073 * @param constantPool from constant pool being loaded 074 * @param input the data being read 075 */ 076 static RVMAnnotation readAnnotation(int[] constantPool, DataInputStream input, ClassLoader classLoader) 077 throws IOException, ClassNotFoundException { 078 TypeReference type; 079 // Read type 080 int typeIndex = input.readUnsignedShort(); 081 type = TypeReference.findOrCreate(classLoader, ClassFileReader.getUtf(constantPool, typeIndex)); 082 // Read values 083 int numAnnotationMembers = input.readUnsignedShort(); 084 AnnotationMember[] elementValuePairs = new AnnotationMember[numAnnotationMembers]; 085 for (int i = 0; i < numAnnotationMembers; i++) { 086 elementValuePairs[i] = AnnotationMember.readAnnotationMember(type, constantPool, input, classLoader); 087 } 088 // Arrays.sort(elementValuePairs); 089 RVMAnnotation result = new RVMAnnotation(type, elementValuePairs); 090 RVMAnnotation unique = uniqueMap.get(result); 091 if (unique != null) { 092 return unique; 093 } else { 094 uniqueMap.put(result, result); 095 return result; 096 } 097 } 098 099 /** 100 * Return the annotation represented by this RVMAnnotation. If this 101 * is the first time this annotation has been accessed the subclass 102 * of annotation this class represents needs creating. 103 * @return the annotation represented 104 */ 105 Annotation getValue() { 106 if (value == null) { 107 value = createValue(); 108 } 109 return value; 110 } 111 112 /** 113 * Create an instance of this type of annotation with the values 114 * given in the members 115 * 116 * @return the created annotation 117 */ 118 private Annotation createValue() { 119 // Find the annotation then find its implementing class 120 final RVMClass annotationInterface = type.resolve().asClass(); 121 annotationInterface.resolve(); 122 Class<?> interfaceClass = annotationInterface.getClassForType(); 123 ClassLoader classLoader = interfaceClass.getClassLoader(); 124 if (classLoader == null) { 125 classLoader = BootstrapClassLoader.getBootstrapClassLoader(); 126 } 127 return (Annotation) Proxy.newProxyInstance(classLoader, new Class[] { interfaceClass }, 128 new AnnotationFactory()); 129 } 130 131 /** 132 * Read the element_value field of an annotation 133 * 134 * @param type the type of the value to read or null 135 * @param constantPool the constant pool for the class being read 136 * @param input stream to read from 137 * @return object representing the value read 138 */ 139 static <T> Object readValue(TypeReference type, int[] constantPool, DataInputStream input, ClassLoader classLoader) 140 throws IOException, ClassNotFoundException { 141 // Read element value's tag 142 byte elementValue_tag = input.readByte(); 143 return readValue(type, constantPool, input, classLoader, elementValue_tag); 144 } 145 private static <T> Object readValue(TypeReference type, int[] constantPool, DataInputStream input, ClassLoader classLoader, byte elementValue_tag) 146 throws IOException, ClassNotFoundException { 147 // decode 148 Object value; 149 switch (elementValue_tag) { 150 case'B': { 151 if(VM.VerifyAssertions) VM._assert(type == null || type == TypeReference.Byte); 152 Offset offset = ClassFileReader.getLiteralOffset(constantPool, input.readUnsignedShort()); 153 value = (byte) Statics.getSlotContentsAsInt(offset); 154 break; 155 } 156 case'C': { 157 if(VM.VerifyAssertions) VM._assert(type == null || type == TypeReference.Char); 158 Offset offset = ClassFileReader.getLiteralOffset(constantPool, input.readUnsignedShort()); 159 value = (char) Statics.getSlotContentsAsInt(offset); 160 break; 161 } 162 case'D': { 163 if(VM.VerifyAssertions) VM._assert(type == null || type == TypeReference.Double); 164 Offset offset = ClassFileReader.getLiteralOffset(constantPool, input.readUnsignedShort()); 165 long longValue = Statics.getSlotContentsAsLong(offset); 166 value = Double.longBitsToDouble(longValue); 167 break; 168 } 169 case'F': { 170 if(VM.VerifyAssertions) VM._assert(type == null || type == TypeReference.Float); 171 Offset offset = ClassFileReader.getLiteralOffset(constantPool, input.readUnsignedShort()); 172 int intValue = Statics.getSlotContentsAsInt(offset); 173 value = Float.intBitsToFloat(intValue); 174 break; 175 } 176 case'I': { 177 if(VM.VerifyAssertions) VM._assert(type == null || type == TypeReference.Int); 178 Offset offset = ClassFileReader.getLiteralOffset(constantPool, input.readUnsignedShort()); 179 value = Statics.getSlotContentsAsInt(offset); 180 break; 181 } 182 case'J': { 183 if(VM.VerifyAssertions) VM._assert(type == null || type == TypeReference.Long); 184 Offset offset = ClassFileReader.getLiteralOffset(constantPool, input.readUnsignedShort()); 185 value = Statics.getSlotContentsAsLong(offset); 186 break; 187 } 188 case'S': { 189 if(VM.VerifyAssertions) VM._assert(type == null || type == TypeReference.Short); 190 Offset offset = ClassFileReader.getLiteralOffset(constantPool, input.readUnsignedShort()); 191 value = (short) Statics.getSlotContentsAsInt(offset); 192 break; 193 } 194 case'Z': { 195 if(VM.VerifyAssertions) VM._assert(type == null || type == TypeReference.Boolean); 196 Offset offset = ClassFileReader.getLiteralOffset(constantPool, input.readUnsignedShort()); 197 value = Statics.getSlotContentsAsInt(offset) == 1; 198 break; 199 } 200 case's': { 201 if(VM.VerifyAssertions) VM._assert(type == null || type == TypeReference.JavaLangString); 202 value = ClassFileReader.getUtf(constantPool, input.readUnsignedShort()).toString(); 203 break; 204 } 205 case'e': { 206 int typeNameIndex = input.readUnsignedShort(); 207 @SuppressWarnings("unchecked") Class enumType = 208 TypeReference.findOrCreate(classLoader, 209 ClassFileReader.getUtf(constantPool, typeNameIndex)).resolve().getClassForType(); 210 int constNameIndex = input.readUnsignedShort(); 211 212 //noinspection unchecked 213 value = Enum.valueOf(enumType, ClassFileReader.getUtf(constantPool, constNameIndex).toString()); 214 break; 215 } 216 case'c': { 217 if(VM.VerifyAssertions) VM._assert(type == null || type == TypeReference.JavaLangClass); 218 int classInfoIndex = input.readUnsignedShort(); 219 // Value should be a class but resolving the class at this point could cause infinite recursion in class loading 220 TypeReference unresolvedValue = TypeReference.findOrCreate(classLoader, ClassFileReader.getUtf(constantPool, classInfoIndex)); 221 if (unresolvedValue.peekType() != null) { 222 value = unresolvedValue.peekType().getClassForType(); 223 } else { 224 value = unresolvedValue; 225 } 226 break; 227 } 228 case'@': 229 value = RVMAnnotation.readAnnotation(constantPool, input, classLoader); 230 break; 231 case'[': { 232 int numValues = input.readUnsignedShort(); 233 if (numValues == 0) { 234 if (type != null) { 235 value = Array.newInstance(type.getArrayElementType().resolve().getClassForType(), 0); 236 } else { 237 value = new Object[0]; 238 } 239 } else { 240 byte innerElementValue_tag = input.readByte(); 241 TypeReference innerType = type == null ? null : type.getArrayElementType(); 242 switch(innerElementValue_tag) { 243 case 'B': { 244 byte[] array = new byte[numValues]; 245 array[0] = (Byte)readValue(innerType, constantPool, input, classLoader, innerElementValue_tag); 246 for (int i = 1; i < numValues; i++) { 247 array[i] = (Byte)readValue(innerType, constantPool, input, classLoader); 248 } 249 value = array; 250 break; 251 } 252 case 'C': { 253 char[] array = new char[numValues]; 254 array[0] = (Character)readValue(innerType, constantPool, input, classLoader, innerElementValue_tag); 255 for (int i = 1; i < numValues; i++) { 256 array[i] = (Character)readValue(innerType, constantPool, input, classLoader); 257 } 258 value = array; 259 break; 260 } 261 case 'D': { 262 double[] array = new double[numValues]; 263 array[0] = (Double)readValue(innerType, constantPool, input, classLoader, innerElementValue_tag); 264 for (int i = 1; i < numValues; i++) { 265 array[i] = (Double)readValue(innerType, constantPool, input, classLoader); 266 } 267 value = array; 268 break; 269 } 270 case 'F': { 271 float[] array = new float[numValues]; 272 array[0] = (Float)readValue(innerType, constantPool, input, classLoader, innerElementValue_tag); 273 for (int i = 1; i < numValues; i++) { 274 array[i] = (Float)readValue(innerType, constantPool, input, classLoader); 275 } 276 value = array; 277 break; 278 } 279 case 'I': { 280 int[] array = new int[numValues]; 281 array[0] = (Integer)readValue(innerType, constantPool, input, classLoader, innerElementValue_tag); 282 for (int i = 1; i < numValues; i++) { 283 array[i] = (Integer)readValue(innerType, constantPool, input, classLoader); 284 } 285 value = array; 286 break; 287 } 288 case 'J': { 289 long[] array = new long[numValues]; 290 array[0] = (Long)readValue(innerType, constantPool, input, classLoader, innerElementValue_tag); 291 for (int i = 1; i < numValues; i++) { 292 array[i] = (Long)readValue(innerType, constantPool, input, classLoader); 293 } 294 value = array; 295 break; 296 } 297 case 'S': { 298 short[] array = new short[numValues]; 299 array[0] = (Short)readValue(innerType, constantPool, input, classLoader, innerElementValue_tag); 300 for (int i = 1; i < numValues; i++) { 301 array[i] = (Short)readValue(innerType, constantPool, input, classLoader, innerElementValue_tag); 302 } 303 value = array; 304 break; 305 } 306 case 'Z': { 307 boolean[] array = new boolean[numValues]; 308 array[0] = (Boolean)readValue(innerType, constantPool, input, classLoader, innerElementValue_tag); 309 for (int i = 1; i < numValues; i++) { 310 array[i] = (Boolean)readValue(innerType, constantPool, input, classLoader); 311 } 312 value = array; 313 break; 314 } 315 case 's': 316 case '@': 317 case 'e': 318 case '[': { 319 Object value1 = readValue(innerType, constantPool, input, classLoader, innerElementValue_tag); 320 value = Array.newInstance(value1.getClass(), numValues); 321 Array.set(value, 0, value1); 322 for (int i = 1; i < numValues; i++) { 323 Array.set(value, i, readValue(innerType, constantPool, input, classLoader)); 324 } 325 break; 326 } 327 case 'c': { 328 Object value1 = readValue(innerType, constantPool, input, classLoader, innerElementValue_tag); 329 Object[] values = new Object[numValues]; 330 values[0] = value1; 331 boolean allClasses = value1 instanceof Class; 332 for (int i = 1; i < numValues; i++) { 333 values[i] = readValue(innerType, constantPool, input, classLoader); 334 if (allClasses && !(values[i] instanceof Class)) { 335 allClasses = false; 336 } 337 } 338 if (allClasses == true) { 339 Class<?>[] newValues = new Class[numValues]; 340 for (int i = 0; i < numValues; i++) { 341 newValues[i] = (Class<?>)values[i]; 342 } 343 value = newValues; 344 } else { 345 value = values; 346 } 347 break; 348 } 349 default: 350 throw new ClassFormatError("Unknown element_value tag '" + (char) innerElementValue_tag + "'"); 351 } 352 } 353 break; 354 } 355 default: 356 throw new ClassFormatError("Unknown element_value tag '" + (char) elementValue_tag + "'"); 357 } 358 return value; 359 } 360 361 /** Handle late resolution of class value annotations */ 362 static Object firstUse(Object value) { 363 if (value instanceof TypeReference) { 364 return ((TypeReference)value).resolve().getClassForType(); 365 } else if (value instanceof Object[]) { 366 Object[] values = (Object[])value; 367 boolean typeChanged = false; 368 for (int i=0; i < values.length; i++) { 369 Object newVal = firstUse(values[i]); 370 if (newVal.getClass() != values[i].getClass()) { 371 typeChanged = true; 372 } 373 values[i] = newVal; 374 } 375 if (typeChanged) { 376 Object[] newValues = (Object[])Array.newInstance(values[0].getClass(), values.length); 377 for (int i=0; i < values.length; i++) { 378 newValues[i] = values[i]; 379 } 380 return newValues; 381 } else { 382 return values; 383 } 384 } 385 return value; 386 } 387 388 /** 389 * Return the TypeReference of the declared annotation, ie an 390 * interface and not the class object of this instance 391 * 392 * @return TypeReferernce of interface annotation object implements 393 */ 394 @Uninterruptible 395 TypeReference annotationType() { return type; } 396 397 /* 398 * Hash map support 399 */ 400 @Override 401 public int hashCode() { 402 return type.hashCode(); 403 } 404 405 @Override 406 public boolean equals(Object o) { 407 if (o instanceof RVMAnnotation) { 408 RVMAnnotation that = (RVMAnnotation)o; 409 if (type == that.type) { 410 if (elementValuePairs.length != that.elementValuePairs.length) { 411 return false; 412 } 413 for (int i=0; i<elementValuePairs.length; i++) { 414 if (!elementValuePairs[i].equals(that.elementValuePairs[i])) { 415 return false; 416 } 417 } 418 return true; 419 } else { 420 return false; 421 } 422 } else { 423 return false; 424 } 425 } 426 427 /** 428 * Return a string representation of the annotation of the form 429 * "@type(name1=val1, ...nameN=valN)" 430 */ 431 @Override 432 public String toString() { 433 RVMClass annotationInterface = type.resolve().asClass(); 434 RVMMethod[] annotationMethods = annotationInterface.getDeclaredMethods(); 435 String result = "@" + type.resolve().getClassForType().getName() + "("; 436 try { 437 for (int i=0; i < annotationMethods.length; i++) { 438 String name=annotationMethods[i].getName().toUnicodeString(); 439 Object value=getElementValue(name, annotationMethods[i].getReturnType().resolve().getClassForType()); 440 result += elementString(name, value); 441 if (i < (annotationMethods.length - 1)) { 442 result += ", "; 443 } 444 } 445 } catch (java.io.UTFDataFormatException e) { 446 throw new Error(e); 447 } 448 result += ")"; 449 return result; 450 } 451 452 /** 453 * String representation of the value pair of the form 454 * "name=value" 455 */ 456 private String elementString(String name, Object value) { 457 return name + "=" + toStringHelper(value); 458 } 459 private static String toStringHelper(Object value) { 460 if (value instanceof Object[]) { 461 StringBuilder result = new StringBuilder("["); 462 Object[] a = (Object[]) value; 463 for (int i = 0; i < a.length; i++) { 464 result.append(toStringHelper(a[i])); 465 if (i < (a.length - 1)) { 466 result.append(", "); 467 } 468 } 469 result.append("]"); 470 return result.toString(); 471 } else { 472 return value.toString(); 473 } 474 } 475 476 /** Find the value for an annotation */ 477 private Object getElementValue(String name, Class<?> valueType) { 478 for (AnnotationMember evp : elementValuePairs) { 479 String evpFieldName = evp.getName().toString(); 480 if (name.equals(evpFieldName)) { 481 return evp.getValue(); 482 } 483 } 484 MethodReference methRef = MemberReference.findOrCreate( 485 type, 486 Atom.findOrCreateAsciiAtom(name), 487 Atom.findOrCreateAsciiAtom("()" + TypeReference.findOrCreate(valueType).getName()) 488 ).asMethodReference(); 489 try { 490 return methRef.resolve().getAnnotationDefault(); 491 } catch (Throwable t) { 492 return NO_VALUE; 493 } 494 } 495 496 /** Hash code for annotation value */ 497 private int annotationHashCode() { 498 RVMClass annotationInterface = type.resolve().asClass(); 499 RVMMethod[] annotationMethods = annotationInterface.getDeclaredMethods(); 500 String typeString = type.toString(); 501 int result = typeString.substring(1, typeString.length() - 1).hashCode(); 502 try { 503 for (RVMMethod method : annotationMethods) { 504 String name = method.getName().toUnicodeString(); 505 Object value = getElementValue(name, method.getReturnType().resolve().getClassForType()); 506 int part_result = name.hashCode() * 127; 507 if (value.getClass().isArray()) { 508 if (value instanceof Object[]) { 509 part_result ^= Arrays.hashCode((Object[]) value); 510 } else if (value instanceof boolean[]) { 511 part_result ^= Arrays.hashCode((boolean[]) value); 512 } else if (value instanceof byte[]) { 513 part_result ^= Arrays.hashCode((byte[]) value); 514 } else if (value instanceof char[]) { 515 part_result ^= Arrays.hashCode((char[]) value); 516 } else if (value instanceof short[]) { 517 part_result ^= Arrays.hashCode((short[]) value); 518 } else if (value instanceof int[]) { 519 part_result ^= Arrays.hashCode((int[]) value); 520 } else if (value instanceof long[]) { 521 part_result ^= Arrays.hashCode((long[]) value); 522 } else if (value instanceof float[]) { 523 part_result ^= Arrays.hashCode((float[]) value); 524 } else if (value instanceof double[]) { 525 part_result ^= Arrays.hashCode((double[]) value); 526 } 527 } else { 528 part_result ^= value.hashCode(); 529 } 530 result += part_result; 531 } 532 } catch (java.io.UTFDataFormatException e) { 533 throw new Error(e); 534 } 535 return result; 536 } 537 538 /** Are two annotations equal? */ 539 private boolean annotationEquals(Annotation a, Annotation b) { 540 if (a == b) { 541 return true; 542 } else if (a.getClass() != b.getClass()) { 543 return false; 544 } else { 545 RVMClass annotationInterface = type.resolve().asClass(); 546 RVMMethod[] annotationMethods = annotationInterface.getDeclaredMethods(); 547 AnnotationFactory afB = (AnnotationFactory)Proxy.getInvocationHandler(b); 548 try { 549 for (RVMMethod method : annotationMethods) { 550 String name = method.getName().toUnicodeString(); 551 Object objA = getElementValue(name, method.getReturnType().resolve().getClassForType()); 552 Object objB = afB.getValue(name, method.getReturnType().resolve().getClassForType()); 553 if (!objA.getClass().isArray()) { 554 if (!objA.equals(objB)) { 555 return false; 556 } 557 } else { 558 if(!Arrays.equals((Object[]) objA, (Object[]) objB)) { 559 return false; 560 } 561 } 562 } 563 } catch (java.io.UTFDataFormatException e) { 564 throw new Error(e); 565 } 566 return true; 567 } 568 } 569 570 /** 571 * Class used to implement annotations as proxies 572 */ 573 private final class AnnotationFactory implements InvocationHandler { 574 /** Cache of hash code */ 575 private int cachedHashCode; 576 577 AnnotationFactory() { 578 } 579 580 /** Entry point to factory */ 581 @Override 582 public Object invoke(Object proxy, Method method, Object[] args) { 583 if (method.getName().equals("annotationType")) { 584 return type.resolve().getClassForType(); 585 } 586 if (method.getName().equals("hashCode")) { 587 if (cachedHashCode == 0) { 588 cachedHashCode = annotationHashCode(); 589 } 590 return cachedHashCode; 591 } 592 if (method.getName().equals("equals")) { 593 return annotationEquals((Annotation)proxy, (Annotation)args[0]); 594 } 595 if (method.getName().equals("toString")) { 596 return RVMAnnotation.this.toString(); 597 } 598 Object value = getValue(method.getName(), method.getReturnType()); 599 if (value != NO_VALUE) { 600 return value; 601 } 602 throw new IllegalArgumentException("Invalid method for annotation type: " + method); 603 } 604 605 private Object getValue(String name, Class<?> valueType) { 606 return RVMAnnotation.this.getElementValue(name, valueType); 607 } 608 609 } 610 /** 611 * A class to decode and hold the name and its associated value for 612 * an annotation member 613 */ 614 private static final class AnnotationMember implements Comparable<AnnotationMember> { 615 /** 616 * Name of element 617 */ 618 private final MethodReference meth; 619 /** 620 * Elements value, decoded from its tag 621 */ 622 private Object value; 623 /** 624 * Is this not the first use of the member? 625 */ 626 private boolean notFirstUse = false; 627 /** 628 * Construct a read value pair 629 */ 630 private AnnotationMember(MethodReference meth, Object value) { 631 this.meth = meth; 632 this.value = value; 633 } 634 635 /** 636 * Read the pair from the input stream and create object 637 * @param constantPool the constant pool for the class being read 638 * @param input stream to read from 639 * @param classLoader the class loader being used to load this annotation 640 * @return a newly created annotation member 641 */ 642 static AnnotationMember readAnnotationMember(TypeReference type, int[] constantPool, DataInputStream input, ClassLoader classLoader) 643 throws IOException, ClassNotFoundException { 644 // Read name of pair 645 int elemNameIndex = input.readUnsignedShort(); 646 Atom name = ClassFileReader.getUtf(constantPool, elemNameIndex); 647 MethodReference meth; 648 Object value; 649 if (type.isResolved()) { 650 meth = type.resolve().asClass().findDeclaredMethod(name).getMemberRef().asMethodReference(); 651 value = RVMAnnotation.readValue(meth.getReturnType(), constantPool, input, classLoader); 652 } else { 653 value = RVMAnnotation.readValue(null, constantPool, input, classLoader); 654 if (value instanceof Object[] && ((Object[])value).length == 0) { 655 // We blindly guessed Object[] in readValue. 656 // No choice but to force type to be resolved so we actually 657 // create an empty array of the appropriate type. 658 meth = type.resolve().asClass().findDeclaredMethod(name).getMemberRef().asMethodReference(); 659 value = Array.newInstance(meth.getReturnType().getArrayElementType().resolve().getClassForType(), 0); 660 } else { 661 // Reading the value lets us make a MemberReference that is likely to be correct. 662 meth = MemberReference.findOrCreate(type, name, 663 Atom.findOrCreateAsciiAtom("()"+TypeReference.findOrCreate(value.getClass()).getName()) 664 ).asMethodReference(); 665 } 666 } 667 return new AnnotationMember(meth, value); 668 } 669 670 /** @return the name of the of the given pair */ 671 Atom getName() { 672 return meth.getName(); 673 } 674 /** @return the value of the of the given pair */ 675 @Pure 676 Object getValue() { 677 if (!notFirstUse) { 678 synchronized(this) { 679 value = firstUse(value); 680 notFirstUse = true; 681 } 682 } 683 return value; 684 } 685 686 /** 687 * Are two members equivalent? 688 */ 689 @Override 690 public boolean equals(Object o) { 691 if (o instanceof AnnotationMember) { 692 AnnotationMember that = (AnnotationMember)o; 693 return that.meth == meth && that.value.equals(value); 694 } else { 695 return false; 696 } 697 } 698 699 /** 700 * Compute hashCode from meth 701 */ 702 @Override 703 public int hashCode() { 704 return meth.hashCode(); 705 } 706 707 /** 708 * Ordering for sorted annotation members 709 */ 710 @Override 711 public int compareTo(AnnotationMember am) { 712 if (am.meth != this.meth) { 713 return am.getName().toString().compareTo(this.getName().toString()); 714 } else { 715 if (value.getClass().isArray()) { 716 return Arrays.hashCode((Object[]) value) - Arrays.hashCode((Object[]) am.value); 717 } else { 718 @SuppressWarnings("unchecked") // True generic programming, we can't type check it in Java 719 Comparable<Object> cValue = (Comparable) value; 720 return cValue.compareTo(am.value); 721 } 722 } 723 } 724 } 725 }