1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package java.lang; 18 19import org.apache.harmony.kernel.vm.LangAccess; 20import org.apache.harmony.kernel.vm.ReflectionAccess; 21 22import java.lang.reflect.AccessibleObject; 23import java.lang.reflect.Field; 24import java.lang.reflect.InvocationTargetException; 25import java.lang.reflect.Method; 26import java.lang.reflect.Modifier; 27import java.util.ArrayList; 28import java.util.Arrays; 29import java.util.Comparator; 30import java.util.EnumSet; 31import java.util.HashSet; 32 33/** 34 * Cache of per-class data, meant to help the performance of reflection 35 * methods. 36 * 37 * <p><b>Note:</b> None of the methods perform access checks. It is up 38 * to the (package internal) clients of this code to perform such 39 * checks as necessary.</p> 40 * 41 * <p><b>Also Note:</b> None of the returned array values are 42 * protected in any way. It is up to the (again, package internal) 43 * clients of this code to protect the arrays if they should ever 44 * escape the package.</p> 45 */ 46/*package*/ class ClassCache<T> { 47 // TODO: Add caching for constructors and fields. 48 49 /** non-null; comparator used for enumerated values */ 50 private static final EnumComparator ENUM_COMPARATOR = 51 new EnumComparator(); 52 53 /** non-null; reflection access bridge */ 54 /*package*/ static final ReflectionAccess REFLECT = getReflectionAccess(); 55 56 /** non-null; class that this instance represents */ 57 private final Class<T> clazz; 58 59 /** null-ok; list of all declared methods */ 60 private volatile Method[] declaredMethods; 61 62 /** null-ok; list of all public declared methods */ 63 private volatile Method[] declaredPublicMethods; 64 65 /** null-ok; list of all methods, both direct and inherited */ 66 private volatile Method[] allMethods; 67 68 /** null-ok; list of all public methods, both direct and inherited */ 69 private volatile Method[] allPublicMethods; 70 71 /** null-ok; list of all declared fields */ 72 private volatile Field[] declaredFields; 73 74 /** null-ok; list of all public declared fields */ 75 private volatile Field[] declaredPublicFields; 76 77 /** null-ok; list of all fields, both direct and inherited */ 78 private volatile Field[] allFields; 79 80 /** null-ok; list of all public fields, both direct and inherited */ 81 private volatile Field[] allPublicFields; 82 83 /** 84 * null-ok; array of enumerated values in their original order, if this 85 * instance's class is an enumeration 86 */ 87 private volatile T[] enumValuesInOrder; 88 89 /** 90 * null-ok; array of enumerated values sorted by name, if this 91 * instance's class is an enumeration 92 */ 93 private volatile T[] enumValuesByName; 94 95 static { 96 /* 97 * Provide access to this package from java.util as part of 98 * bootstrap. TODO: See if this can be removed in favor of the 99 * simpler mechanism below. (That is, see if EnumSet will be 100 * happy calling LangAccess.getInstance().) 101 */ 102 Field field; 103 104 try { 105 field = EnumSet.class.getDeclaredField("LANG_BOOTSTRAP"); 106 REFLECT.setAccessibleNoCheck(field, true); 107 } catch (NoSuchFieldException ex) { 108 // This shouldn't happen because the field is in fact defined. 109 throw new AssertionError(ex); 110 } 111 112 try { 113 field.set(null, LangAccessImpl.THE_ONE); 114 } catch (IllegalAccessException ex) { 115 // This shouldn't happen because we made the field accessible. 116 throw new AssertionError(ex); 117 } 118 119 // Also set up the bootstrap-classpath-wide access mechanism. 120 LangAccess.setInstance(LangAccessImpl.THE_ONE); 121 } 122 123 /** 124 * Constructs an instance. 125 * 126 * @param clazz non-null; class that this instance represents 127 */ 128 /*package*/ ClassCache(Class<T> clazz) { 129 if (clazz == null) { 130 throw new NullPointerException("clazz == null"); 131 } 132 133 this.clazz = clazz; 134 this.declaredMethods = null; 135 this.declaredPublicMethods = null; 136 this.allMethods = null; 137 this.allPublicMethods = null; 138 this.enumValuesInOrder = null; 139 this.enumValuesByName = null; 140 this.declaredFields = null; 141 this.declaredPublicFields = null; 142 this.allFields = null; 143 this.allPublicFields = null; 144 } 145 146 /** 147 * Gets the list of all declared methods. 148 * 149 * @return non-null; the list of all declared methods 150 */ 151 public Method[] getDeclaredMethods() { 152 if (declaredMethods == null) { 153 declaredMethods = Class.getDeclaredMethods(clazz, false); 154 } 155 156 return declaredMethods; 157 } 158 159 /** 160 * Gets the list of all declared public methods. 161 * 162 * @return non-null; the list of all declared public methods 163 */ 164 public Method[] getDeclaredPublicMethods() { 165 if (declaredPublicMethods == null) { 166 declaredPublicMethods = Class.getDeclaredMethods(clazz, true); 167 } 168 169 return declaredPublicMethods; 170 } 171 172 /** 173 * Gets either the list of declared methods or the list of declared 174 * public methods. 175 * 176 * @param publicOnly whether to only return public methods 177 */ 178 public Method[] getDeclaredMethods(boolean publicOnly) { 179 return publicOnly ? getDeclaredPublicMethods() : getDeclaredMethods(); 180 } 181 182 /** 183 * Gets the list of all methods, both directly 184 * declared and inherited. 185 * 186 * @return non-null; the list of all methods 187 */ 188 public Method[] getAllMethods() { 189 if (allMethods == null) { 190 allMethods = getFullListOfMethods(false); 191 } 192 193 return allMethods; 194 } 195 196 /** 197 * Gets the list of all public methods, both directly 198 * declared and inherited. 199 * 200 * @return non-null; the list of all public methods 201 */ 202 public Method[] getAllPublicMethods() { 203 if (allPublicMethods == null) { 204 allPublicMethods = getFullListOfMethods(true); 205 } 206 207 return allPublicMethods; 208 } 209 210 /* 211 * Returns the list of methods without performing any security checks 212 * first. This includes the methods inherited from superclasses. If no 213 * methods exist at all, an empty array is returned. 214 * 215 * @param publicOnly reflects whether we want only public methods 216 * or all of them 217 * @return the list of methods 218 */ 219 private Method[] getFullListOfMethods(boolean publicOnly) { 220 ArrayList<Method> methods = new ArrayList<Method>(); 221 HashSet<String> seen = new HashSet<String>(); 222 223 findAllMethods(clazz, methods, seen, publicOnly); 224 225 return methods.toArray(new Method[methods.size()]); 226 } 227 228 /** 229 * Collects the list of methods without performing any security checks 230 * first. This includes the methods inherited from superclasses and from 231 * all implemented interfaces. The latter may also implement multiple 232 * interfaces, so we (potentially) recursively walk through a whole tree of 233 * classes. If no methods exist at all, an empty array is returned. 234 * 235 * @param clazz non-null; class to inspect 236 * @param methods non-null; the target list to add the results to 237 * @param seen non-null; a set of signatures we've already seen 238 * @param publicOnly reflects whether we want only public methods 239 * or all of them 240 */ 241 private static void findAllMethods(Class<?> clazz, 242 ArrayList<Method> methods, HashSet<String> seen, 243 boolean publicOnly) { 244 StringBuilder builder = new StringBuilder(); 245 Class<?> origClass = clazz; 246 247 // Traverse class and superclasses, get rid of dupes by signature 248 while (clazz != null) { 249 Method[] declaredMethods = 250 clazz.getClassCache().getDeclaredMethods(publicOnly); 251 int length = declaredMethods.length; 252 if (length != 0) { 253 for (Method method : declaredMethods) { 254 builder.setLength(0); 255 builder.append(method.getName()); 256 builder.append('('); 257 Class<?>[] types = method.getParameterTypes(); 258 if (types.length != 0) { 259 builder.append(types[0].getName()); 260 for (int j = 1; j < types.length; j++) { 261 builder.append(','); 262 builder.append(types[j].getName()); 263 } 264 } 265 builder.append(')'); 266 267 String signature = builder.toString(); 268 if (!seen.contains(signature)) { 269 methods.add(method); 270 seen.add(signature); 271 } 272 } 273 } 274 275 clazz = clazz.getSuperclass(); 276 } 277 278 // Traverse all interfaces, and do the same recursively. 279 Class<?>[] interfaces = origClass.getInterfaces(); 280 for (Class<?> intf : interfaces) { 281 findAllMethods(intf, methods, seen, publicOnly); 282 } 283 } 284 285 /** 286 * Finds and returns a method with a given name and signature. Use 287 * this with one of the method lists returned by instances of this class. 288 * 289 * @param list non-null; the list of methods to search through 290 * @param parameterTypes non-null; the formal parameter list 291 * @return non-null; the matching method 292 * @throws NoSuchMethodException thrown if the method does not exist 293 */ 294 public static Method findMethodByName(Method[] list, String name, 295 Class<?>[] parameterTypes) throws NoSuchMethodException { 296 if (name == null) { 297 throw new NullPointerException("Method name must not be null."); 298 } 299 for (int i = list.length - 1; i >= 0; i--) { 300 Method method = list[i]; 301 if (method.getName().equals(name) 302 && compareClassLists( 303 method.getParameterTypes(), parameterTypes)) { 304 return method; 305 } 306 } 307 308 throw new NoSuchMethodException(name); 309 } 310 311 /** 312 * Compares two class lists for equality. Empty and 313 * <code>null</code> lists are considered equal. This is useful 314 * for matching methods and constructors. 315 * 316 * <p>TODO: Take into account assignment compatibility?</p> 317 * 318 * @param a null-ok; the first list of types 319 * @param b null-ok; the second list of types 320 * @return true if and only if the lists are equal 321 */ 322 public static boolean compareClassLists(Class<?>[] a, Class<?>[] b) { 323 if (a == null) { 324 return (b == null) || (b.length == 0); 325 } 326 327 int length = a.length; 328 329 if (b == null) { 330 return (length == 0); 331 } 332 333 if (length != b.length) { 334 return false; 335 } 336 337 for (int i = length - 1; i >= 0; i--) { 338 if (a[i] != b[i]) { 339 return false; 340 } 341 } 342 343 return true; 344 } 345 346 /** 347 * Makes a deep copy of the given array of methods. This is useful 348 * when handing out arrays from the public API. 349 * 350 * <p><b>Note:</b> In such cases, it is insufficient to just make 351 * a shallow copy of the array, since method objects aren't 352 * immutable due to the existence of {@link 353 * AccessibleObject#setAccessible}.</p> 354 * 355 * @param orig non-null; array to copy 356 * @return non-null; a deep copy of the given array 357 */ 358 public static Method[] deepCopy(Method[] orig) { 359 int length = orig.length; 360 Method[] result = new Method[length]; 361 362 for (int i = length - 1; i >= 0; i--) { 363 result[i] = REFLECT.clone(orig[i]); 364 } 365 366 return result; 367 } 368 369 /** 370 * Gets the list of all declared fields. 371 * 372 * @return non-null; the list of all declared fields 373 */ 374 public Field[] getDeclaredFields() { 375 if (declaredFields == null) { 376 declaredFields = Class.getDeclaredFields(clazz, false); 377 } 378 379 return declaredFields; 380 } 381 382 /** 383 * Gets the list of all declared public fields. 384 * 385 * @return non-null; the list of all declared public fields 386 */ 387 public Field[] getDeclaredPublicFields() { 388 if (declaredPublicFields == null) { 389 declaredPublicFields = Class.getDeclaredFields(clazz, true); 390 } 391 392 return declaredPublicFields; 393 } 394 395 /** 396 * Gets either the list of declared fields or the list of declared 397 * public fields. 398 * 399 * @param publicOnly whether to only return public fields 400 */ 401 public Field[] getDeclaredFields(boolean publicOnly) { 402 return publicOnly ? getDeclaredPublicFields() : getDeclaredFields(); 403 } 404 405 /** 406 * Gets the list of all fields, both directly 407 * declared and inherited. 408 * 409 * @return non-null; the list of all fields 410 */ 411 public Field[] getAllFields() { 412 if (allFields == null) { 413 allFields = getFullListOfFields(false); 414 } 415 416 return allFields; 417 } 418 419 /** 420 * Gets the list of all public fields, both directly 421 * declared and inherited. 422 * 423 * @return non-null; the list of all public fields 424 */ 425 public Field[] getAllPublicFields() { 426 if (allPublicFields == null) { 427 allPublicFields = getFullListOfFields(true); 428 } 429 430 return allPublicFields; 431 } 432 433 /* 434 * Returns the list of fields without performing any security checks 435 * first. This includes the fields inherited from superclasses. If no 436 * fields exist at all, an empty array is returned. 437 * 438 * @param publicOnly reflects whether we want only public fields 439 * or all of them 440 * @return the list of fields 441 */ 442 private Field[] getFullListOfFields(boolean publicOnly) { 443 ArrayList<Field> fields = new ArrayList<Field>(); 444 HashSet<String> seen = new HashSet<String>(); 445 446 findAllfields(clazz, fields, seen, publicOnly); 447 448 return fields.toArray(new Field[fields.size()]); 449 } 450 451 /** 452 * Collects the list of fields without performing any security checks 453 * first. This includes the fields inherited from superclasses and from 454 * all implemented interfaces. The latter may also implement multiple 455 * interfaces, so we (potentially) recursively walk through a whole tree of 456 * classes. If no fields exist at all, an empty array is returned. 457 * 458 * @param clazz non-null; class to inspect 459 * @param fields non-null; the target list to add the results to 460 * @param seen non-null; a set of signatures we've already seen 461 * @param publicOnly reflects whether we want only public fields 462 * or all of them 463 */ 464 private static void findAllfields(Class<?> clazz, 465 ArrayList<Field> fields, HashSet<String> seen, 466 boolean publicOnly) { 467 468 // Traverse class and superclasses, get rid of dupes by signature 469 while (clazz != null) { 470 Field[] declaredFields = 471 clazz.getClassCache().getDeclaredFields(publicOnly); 472 for (Field field : declaredFields) { 473 String signature = field.toString(); 474 if (!seen.contains(signature)) { 475 fields.add(field); 476 seen.add(signature); 477 } 478 } 479 480 // Traverse all interfaces, and do the same recursively. 481 Class<?>[] interfaces = clazz.getInterfaces(); 482 for (Class<?> intf : interfaces) { 483 findAllfields(intf, fields, seen, publicOnly); 484 } 485 486 clazz = clazz.getSuperclass(); 487 } 488 } 489 490 /** 491 * Finds and returns a field with a given name and signature. Use 492 * this with one of the field lists returned by instances of this class. 493 * 494 * @param list non-null; the list of fields to search through 495 * @return non-null; the matching field 496 * @throws NoSuchFieldException thrown if the field does not exist 497 */ 498 public static Field findFieldByName(Field[] list, String name) 499 throws NoSuchFieldException { 500 if (name == null) { 501 throw new NullPointerException("Field name must not be null."); 502 } 503 for (int i = 0; i < list.length; i++) { 504 Field field = list[i]; 505 if (field.getName().equals(name)) { 506 return field; 507 } 508 } 509 510 throw new NoSuchFieldException(name); 511 } 512 513 /** 514 * Makes a deep copy of the given array of fields. This is useful 515 * when handing out arrays from the public API. 516 * 517 * <p><b>Note:</b> In such cases, it is insufficient to just make 518 * a shallow copy of the array, since field objects aren't 519 * immutable due to the existence of {@link 520 * AccessibleObject#setAccessible}.</p> 521 * 522 * @param orig non-null; array to copy 523 * @return non-null; a deep copy of the given array 524 */ 525 public static Field[] deepCopy(Field[] orig) { 526 int length = orig.length; 527 Field[] result = new Field[length]; 528 529 for (int i = length - 1; i >= 0; i--) { 530 result[i] = REFLECT.clone(orig[i]); 531 } 532 533 return result; 534 } 535 536 /** 537 * Gets the enumerated value with a given name. 538 * 539 * @param name non-null; name of the value 540 * @return null-ok; the named enumerated value or <code>null</code> 541 * if this instance's class doesn't have such a value (including 542 * if this instance isn't in fact an enumeration) 543 */ 544 @SuppressWarnings("unchecked") 545 public T getEnumValue(String name) { 546 Enum[] values = (Enum[]) getEnumValuesByName(); 547 548 if (values == null) { 549 return null; 550 } 551 552 // Binary search. 553 554 int min = 0; 555 int max = values.length - 1; 556 557 while (min <= max) { 558 /* 559 * The guessIdx calculation is equivalent to ((min + max) 560 * / 2) but won't go wonky when min and max are close to 561 * Integer.MAX_VALUE. 562 */ 563 int guessIdx = min + ((max - min) >> 1); 564 Enum guess = values[guessIdx]; 565 int cmp = name.compareTo(guess.name()); 566 567 if (cmp < 0) { 568 max = guessIdx - 1; 569 } else if (cmp > 0) { 570 min = guessIdx + 1; 571 } else { 572 return (T) guess; 573 } 574 } 575 576 return null; 577 } 578 579 /** 580 * Gets the array of enumerated values, sorted by name. 581 * 582 * @return null-ok; the value array, or <code>null</code> if this 583 * instance's class isn't in fact an enumeration 584 */ 585 public T[] getEnumValuesByName() { 586 if (enumValuesByName == null) { 587 T[] values = getEnumValuesInOrder(); 588 589 if (values != null) { 590 values = (T[]) values.clone(); 591 Arrays.sort((Enum<?>[]) values, ENUM_COMPARATOR); 592 593 /* 594 * Note: It's only safe (concurrency-wise) to set the 595 * instance variable after the array is properly sorted. 596 */ 597 enumValuesByName = values; 598 } 599 } 600 601 return enumValuesByName; 602 } 603 604 /** 605 * Gets the array of enumerated values, in their original declared 606 * order. 607 * 608 * @return null-ok; the value array, or <code>null</code> if this 609 * instance's class isn't in fact an enumeration 610 */ 611 public T[] getEnumValuesInOrder() { 612 if ((enumValuesInOrder == null) && clazz.isEnum()) { 613 enumValuesInOrder = callEnumValues(); 614 } 615 616 return enumValuesInOrder; 617 } 618 619 /** 620 * Calls the static method <code>values()</code> on this 621 * instance's class, which is presumed to be a properly-formed 622 * enumeration class, using proper privilege hygiene. 623 * 624 * @return non-null; the array of values as reported by 625 * <code>value()</code> 626 */ 627 @SuppressWarnings("unchecked") 628 private T[] callEnumValues() { 629 Method method; 630 631 try { 632 Method[] methods = getDeclaredPublicMethods(); 633 method = findMethodByName(methods, "values", (Class[]) null); 634 method = REFLECT.accessibleClone(method); 635 } catch (NoSuchMethodException ex) { 636 // This shouldn't happen if the class is a well-formed enum. 637 throw new UnsupportedOperationException(ex); 638 } 639 640 try { 641 return (T[]) method.invoke((Object[]) null); 642 } catch (IllegalAccessException ex) { 643 // This shouldn't happen because the method is "accessible." 644 throw new Error(ex); 645 } catch (InvocationTargetException ex) { 646 Throwable te = ex.getTargetException(); 647 if (te instanceof RuntimeException) { 648 throw (RuntimeException) te; 649 } else if (te instanceof Error) { 650 throw (Error) te; 651 } else { 652 throw new Error(te); 653 } 654 } 655 } 656 657 /** 658 * Gets the reflection access object. This uses reflection to do 659 * so. My head is spinning. 660 * 661 * @return non-null; the reflection access object 662 */ 663 private static ReflectionAccess getReflectionAccess() { 664 /* 665 * Note: We can't do AccessibleObject.class.getCache() to 666 * get the cache, since that would cause a circularity in 667 * initialization. So instead, we do a direct call into the 668 * native side. 669 */ 670 Method[] methods = 671 Class.getDeclaredMethods(AccessibleObject.class, false); 672 673 try { 674 Method method = findMethodByName(methods, "getReflectionAccess", 675 (Class[]) null); 676 Class.setAccessibleNoCheck(method, true); 677 return (ReflectionAccess) method.invoke((Object[]) null); 678 } catch (NoSuchMethodException ex) { 679 /* 680 * This shouldn't happen because the method 681 * AccessibleObject.getReflectionAccess() really is defined 682 * in this module. 683 */ 684 throw new Error(ex); 685 } catch (IllegalAccessException ex) { 686 // This shouldn't happen because the method is "accessible." 687 throw new Error(ex); 688 } catch (InvocationTargetException ex) { 689 throw new Error(ex); 690 } 691 } 692 693 /** 694 * Comparator class for enumerated values. It compares strictly 695 * by name. 696 */ 697 private static class EnumComparator implements Comparator<Enum<?>> { 698 public int compare(Enum<?> e1, Enum<?> e2) { 699 return e1.name().compareTo(e2.name()); 700 } 701 } 702} 703