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