2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 import java.lang.reflect.AccessibleObject;
20 import java.lang.reflect.Field;
21 import java.lang.reflect.InvocationTargetException;
22 import java.lang.reflect.Method;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collections;
26 import java.util.Comparator;
27 import java.util.EnumSet;
28 import java.util.HashSet;
29 import java.util.List;
30 import org.apache.harmony.kernel.vm.LangAccess;
31 import org.apache.harmony.kernel.vm.ReflectionAccess;
34 * Cache of per-class data, meant to help the performance of reflection
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>
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>
46 /*package*/ class ClassCache<T> {
47 // TODO: Add caching for constructors and fields.
49 /** non-null; comparator used for enumerated values */
50 private static final EnumComparator ENUM_COMPARATOR =
53 /** non-null; reflection access bridge */
54 /*package*/ static final ReflectionAccess REFLECT = getReflectionAccess();
56 /** non-null; class that this instance represents */
57 private final Class<T> clazz;
59 /** null-ok; list of all public methods, both direct and inherited */
60 private volatile Method[] methods;
62 /** null-ok; list of all declared methods */
63 private volatile Method[] declaredMethods;
65 /** null-ok; list of all public declared methods */
66 private volatile Method[] declaredPublicMethods;
68 /** null-ok; list of all declared fields */
69 private volatile Field[] declaredFields;
71 /** null-ok; list of all public declared fields */
72 private volatile Field[] declaredPublicFields;
74 /** null-ok; list of all fields, both direct and inherited */
75 private volatile Field[] allFields;
77 /** null-ok; list of all public fields, both direct and inherited */
78 private volatile Field[] allPublicFields;
81 * null-ok; array of enumerated values in their original order, if this
82 * instance's class is an enumeration
84 private volatile T[] enumValuesInOrder;
87 * null-ok; array of enumerated values sorted by name, if this
88 * instance's class is an enumeration
90 private volatile T[] enumValuesByName;
94 * Provide access to this package from java.util as part of
95 * bootstrap. TODO: See if this can be removed in favor of the
96 * simpler mechanism below. (That is, see if EnumSet will be
97 * happy calling LangAccess.getInstance().)
102 field = EnumSet.class.getDeclaredField("LANG_BOOTSTRAP");
103 REFLECT.setAccessibleNoCheck(field, true);
104 } catch (NoSuchFieldException ex) {
105 // This shouldn't happen because the field is in fact defined.
106 throw new AssertionError(ex);
110 field.set(null, LangAccessImpl.THE_ONE);
111 } catch (IllegalAccessException ex) {
112 // This shouldn't happen because we made the field accessible.
113 throw new AssertionError(ex);
116 // Also set up the bootstrap-classpath-wide access mechanism.
117 LangAccess.setInstance(LangAccessImpl.THE_ONE);
121 * Constructs an instance.
123 * @param clazz non-null; class that this instance represents
125 /*package*/ ClassCache(Class<T> clazz) {
127 throw new NullPointerException("clazz == null");
134 * Gets the list of all declared methods.
136 * @return non-null; the list of all declared methods
138 public Method[] getDeclaredMethods() {
139 if (declaredMethods == null) {
140 declaredMethods = Class.getDeclaredMethods(clazz, false);
143 return declaredMethods;
147 * Gets the list of all declared public methods.
149 * @return non-null; the list of all declared public methods
151 private Method[] getDeclaredPublicMethods() {
152 if (declaredPublicMethods == null) {
153 declaredPublicMethods = Class.getDeclaredMethods(clazz, true);
156 return declaredPublicMethods;
160 * Returns public methods defined by {@code clazz}, its superclasses and all
161 * implemented interfaces, not including overridden methods. This method
162 * performs no security checks.
164 public Method[] getMethods() {
165 Method[] cachedResult = methods;
166 if (cachedResult == null) {
167 methods = findMethods();
173 private Method[] findMethods() {
174 List<Method> allMethods = new ArrayList<Method>();
175 getMethodsRecursive(clazz, allMethods);
178 * Remove methods defined by multiple types, preferring to keep methods
179 * declared by derived types.
181 * Classes may define multiple methods with the same name and parameter
182 * types due to covariant return types. In this case both are returned,
183 * with the non-synthetic method first because it is preferred by
184 * getMethod(String,Class[]).
186 Collections.sort(allMethods, Method.ORDER_BY_SIGNATURE);
187 List<Method> natural = new ArrayList<Method>(allMethods.size());
188 List<Method> synthetic = new ArrayList<Method>(allMethods.size());
189 Method previous = null;
190 for (Method method : allMethods) {
192 && Method.ORDER_BY_SIGNATURE.compare(method, previous) == 0
193 && method.getDeclaringClass() != previous.getDeclaringClass()) {
196 if (method.isSynthetic()) {
197 synthetic.add(method);
203 List<Method> result = new ArrayList<Method>(allMethods.size());
204 result.addAll(natural);
205 result.addAll(synthetic);
206 return result.toArray(new Method[result.size()]);
210 * Populates {@code sink} with public methods defined by {@code clazz}, its
211 * superclasses, and all implemented interfaces, including overridden methods.
212 * This method performs no security checks.
214 private static void getMethodsRecursive(Class<?> clazz, List<Method> result) {
215 for (Class<?> c = clazz; c != null; c = c.getSuperclass()) {
216 for (Method method : c.getClassCache().getDeclaredPublicMethods()) {
221 for (Class<?> ifc : clazz.getInterfaces()) {
222 getMethodsRecursive(ifc, result);
227 * Finds and returns a method with a given name and signature. Use
228 * this with one of the method lists returned by instances of this class.
230 * @param list non-null; the list of methods to search through
231 * @param parameterTypes non-null; the formal parameter list
232 * @return non-null; the matching method
233 * @throws NoSuchMethodException thrown if the method does not exist
235 public static Method findMethodByName(Method[] list, String name,
236 Class<?>[] parameterTypes) throws NoSuchMethodException {
238 throw new NullPointerException("Method name must not be null.");
240 for (Method method : list) {
241 if (method.getName().equals(name)
242 && compareClassLists(method.getParameterTypes(), parameterTypes)) {
247 throw new NoSuchMethodException(name);
251 * Compares two class lists for equality. Empty and
252 * <code>null</code> lists are considered equal. This is useful
253 * for matching methods and constructors.
255 * <p>TODO: Take into account assignment compatibility?</p>
257 * @param a null-ok; the first list of types
258 * @param b null-ok; the second list of types
259 * @return true if and only if the lists are equal
261 public static boolean compareClassLists(Class<?>[] a, Class<?>[] b) {
263 return (b == null) || (b.length == 0);
266 int length = a.length;
269 return (length == 0);
272 if (length != b.length) {
276 for (int i = length - 1; i >= 0; i--) {
286 * Makes a deep copy of the given array of methods. This is useful
287 * when handing out arrays from the public API.
289 * <p><b>Note:</b> In such cases, it is insufficient to just make
290 * a shallow copy of the array, since method objects aren't
291 * immutable due to the existence of {@link
292 * AccessibleObject#setAccessible}.</p>
294 * @param orig non-null; array to copy
295 * @return non-null; a deep copy of the given array
297 public static Method[] deepCopy(Method[] orig) {
298 int length = orig.length;
299 Method[] result = new Method[length];
301 for (int i = length - 1; i >= 0; i--) {
302 result[i] = REFLECT.clone(orig[i]);
309 * Gets the list of all declared fields.
311 * @return non-null; the list of all declared fields
313 public Field[] getDeclaredFields() {
314 if (declaredFields == null) {
315 declaredFields = Class.getDeclaredFields(clazz, false);
318 return declaredFields;
322 * Gets the list of all declared public fields.
324 * @return non-null; the list of all declared public fields
326 public Field[] getDeclaredPublicFields() {
327 if (declaredPublicFields == null) {
328 declaredPublicFields = Class.getDeclaredFields(clazz, true);
331 return declaredPublicFields;
335 * Gets either the list of declared fields or the list of declared
338 * @param publicOnly whether to only return public fields
340 public Field[] getDeclaredFields(boolean publicOnly) {
341 return publicOnly ? getDeclaredPublicFields() : getDeclaredFields();
345 * Gets the list of all fields, both directly
346 * declared and inherited.
348 * @return non-null; the list of all fields
350 public Field[] getAllFields() {
351 if (allFields == null) {
352 allFields = getFullListOfFields(false);
359 * Gets the list of all public fields, both directly
360 * declared and inherited.
362 * @return non-null; the list of all public fields
364 public Field[] getAllPublicFields() {
365 if (allPublicFields == null) {
366 allPublicFields = getFullListOfFields(true);
369 return allPublicFields;
373 * Returns the list of fields without performing any security checks
374 * first. This includes the fields inherited from superclasses. If no
375 * fields exist at all, an empty array is returned.
377 * @param publicOnly reflects whether we want only public fields
379 * @return the list of fields
381 private Field[] getFullListOfFields(boolean publicOnly) {
382 ArrayList<Field> fields = new ArrayList<Field>();
383 HashSet<String> seen = new HashSet<String>();
385 findAllfields(clazz, fields, seen, publicOnly);
387 return fields.toArray(new Field[fields.size()]);
391 * Collects the list of fields without performing any security checks
392 * first. This includes the fields inherited from superclasses and from
393 * all implemented interfaces. The latter may also implement multiple
394 * interfaces, so we (potentially) recursively walk through a whole tree of
395 * classes. If no fields exist at all, an empty array is returned.
397 * @param clazz non-null; class to inspect
398 * @param fields non-null; the target list to add the results to
399 * @param seen non-null; a set of signatures we've already seen
400 * @param publicOnly reflects whether we want only public fields
403 private static void findAllfields(Class<?> clazz,
404 ArrayList<Field> fields, HashSet<String> seen,
405 boolean publicOnly) {
407 // Traverse class and superclasses, get rid of dupes by signature
408 while (clazz != null) {
409 for (Field field : clazz.getClassCache().getDeclaredFields(publicOnly)) {
410 String signature = field.toString();
411 if (!seen.contains(signature)) {
417 // Traverse all interfaces, and do the same recursively.
418 Class<?>[] interfaces = clazz.getInterfaces();
419 for (Class<?> intf : interfaces) {
420 findAllfields(intf, fields, seen, publicOnly);
423 clazz = clazz.getSuperclass();
428 * Finds and returns a field with a given name and signature. Use
429 * this with one of the field lists returned by instances of this class.
431 * @param list non-null; the list of fields to search through
432 * @return non-null; the matching field
433 * @throws NoSuchFieldException thrown if the field does not exist
435 public static Field findFieldByName(Field[] list, String name)
436 throws NoSuchFieldException {
438 throw new NullPointerException("Field name must not be null.");
440 for (Field field : list) {
441 if (field.getName().equals(name)) {
446 throw new NoSuchFieldException(name);
450 * Makes a deep copy of the given array of fields. This is useful
451 * when handing out arrays from the public API.
453 * <p><b>Note:</b> In such cases, it is insufficient to just make
454 * a shallow copy of the array, since field objects aren't
455 * immutable due to the existence of {@link
456 * AccessibleObject#setAccessible}.</p>
458 * @param orig non-null; array to copy
459 * @return non-null; a deep copy of the given array
461 public static Field[] deepCopy(Field[] orig) {
462 int length = orig.length;
463 Field[] result = new Field[length];
465 for (int i = length - 1; i >= 0; i--) {
466 result[i] = REFLECT.clone(orig[i]);
473 * Gets the enumerated value with a given name.
475 * @param name non-null; name of the value
476 * @return null-ok; the named enumerated value or <code>null</code>
477 * if this instance's class doesn't have such a value (including
478 * if this instance isn't in fact an enumeration)
480 @SuppressWarnings("unchecked")
481 public T getEnumValue(String name) {
482 Enum[] values = (Enum[]) getEnumValuesByName();
484 if (values == null) {
491 int max = values.length - 1;
495 * The guessIdx calculation is equivalent to ((min + max)
496 * / 2) but won't go wonky when min and max are close to
499 int guessIdx = min + ((max - min) >> 1);
500 Enum guess = values[guessIdx];
501 int cmp = name.compareTo(guess.name());
505 } else if (cmp > 0) {
516 * Gets the array of enumerated values, sorted by name.
518 * @return null-ok; the value array, or <code>null</code> if this
519 * instance's class isn't in fact an enumeration
521 public T[] getEnumValuesByName() {
522 if (enumValuesByName == null) {
523 T[] values = getEnumValuesInOrder();
525 if (values != null) {
526 values = (T[]) values.clone();
527 Arrays.sort((Enum<?>[]) values, ENUM_COMPARATOR);
530 * Note: It's only safe (concurrency-wise) to set the
531 * instance variable after the array is properly sorted.
533 enumValuesByName = values;
537 return enumValuesByName;
541 * Gets the array of enumerated values, in their original declared
544 * @return null-ok; the value array, or <code>null</code> if this
545 * instance's class isn't in fact an enumeration
547 public T[] getEnumValuesInOrder() {
548 if ((enumValuesInOrder == null) && clazz.isEnum()) {
549 enumValuesInOrder = callEnumValues();
552 return enumValuesInOrder;
556 * Calls the static method <code>values()</code> on this
557 * instance's class, which is presumed to be a properly-formed
558 * enumeration class, using proper privilege hygiene.
560 * @return non-null; the array of values as reported by
561 * <code>value()</code>
563 @SuppressWarnings("unchecked")
564 private T[] callEnumValues() {
568 Method[] methods = getDeclaredPublicMethods();
569 method = findMethodByName(methods, "values", (Class[]) null);
570 method = REFLECT.accessibleClone(method);
571 } catch (NoSuchMethodException ex) {
572 // This shouldn't happen if the class is a well-formed enum.
573 throw new UnsupportedOperationException(ex);
577 return (T[]) method.invoke((Object[]) null);
578 } catch (IllegalAccessException ex) {
579 // This shouldn't happen because the method is "accessible."
581 } catch (InvocationTargetException ex) {
582 Throwable te = ex.getTargetException();
583 if (te instanceof RuntimeException) {
584 throw (RuntimeException) te;
585 } else if (te instanceof Error) {
594 * Gets the reflection access object. This uses reflection to do
595 * so. My head is spinning.
597 * @return non-null; the reflection access object
599 private static ReflectionAccess getReflectionAccess() {
601 * Note: We can't do AccessibleObject.class.getCache() to
602 * get the cache, since that would cause a circularity in
603 * initialization. So instead, we do a direct call into the
607 Class.getDeclaredMethods(AccessibleObject.class, false);
610 Method method = findMethodByName(methods, "getReflectionAccess",
612 Class.setAccessibleNoCheck(method, true);
613 return (ReflectionAccess) method.invoke((Object[]) null);
614 } catch (NoSuchMethodException ex) {
616 * This shouldn't happen because the method
617 * AccessibleObject.getReflectionAccess() really is defined
621 } catch (IllegalAccessException ex) {
622 // This shouldn't happen because the method is "accessible."
624 } catch (InvocationTargetException ex) {
630 * Comparator class for enumerated values. It compares strictly
633 private static class EnumComparator implements Comparator<Enum<?>> {
634 public int compare(Enum<?> e1, Enum<?> e2) {
635 return e1.name().compareTo(e2.name());