FileDocCategorySizeDatePackage
ClassCache.javaAPI DocAndroid 1.5 API23527Wed May 06 22:41:04 BST 2009java.lang

ClassCache

public class ClassCache extends Object
Cache of per-class data, meant to help the performance of reflection methods.

Note: None of the methods perform access checks. It is up to the (package internal) clients of this code to perform such checks as necessary.

Also Note: None of the returned array values are protected in any way. It is up to the (again, package internal) clients of this code to protect the arrays if they should ever escape the package.

Fields Summary
private static final EnumComparator
ENUM_COMPARATOR
non-null; comparator used for enumerated values
static final org.apache.harmony.kernel.vm.ReflectionAccess
REFLECT
non-null; reflection access bridge
private final Class
clazz
non-null; class that this instance represents
private volatile Method[]
declaredMethods
null-ok; list of all declared methods
private volatile Method[]
declaredPublicMethods
null-ok; list of all public declared methods
private volatile Method[]
allMethods
null-ok; list of all methods, both direct and inherited
private volatile Method[]
allPublicMethods
null-ok; list of all public methods, both direct and inherited
private volatile Field[]
declaredFields
null-ok; list of all declared fields
private volatile Field[]
declaredPublicFields
null-ok; list of all public declared fields
private volatile Field[]
allFields
null-ok; list of all fields, both direct and inherited
private volatile Field[]
allPublicFields
null-ok; list of all public fields, both direct and inherited
private volatile T[]
enumValuesInOrder
null-ok; array of enumerated values in their original order, if this instance's class is an enumeration
private volatile T[]
enumValuesByName
null-ok; array of enumerated values sorted by name, if this instance's class is an enumeration
Constructors Summary
ClassCache(Class clazz)
Constructs an instance.

param
clazz non-null; class that this instance represents


     
        /*
         * Provide access to this package from java.util as part of
         * bootstrap. TODO: See if this can be removed in favor of the
         * simpler mechanism below. (That is, see if EnumSet will be
         * happy calling LangAccess.getInstance().)
         */
        Field field;

        try {
            field = EnumSet.class.getDeclaredField("LANG_BOOTSTRAP");
            REFLECT.setAccessibleNoCheck(field, true);
        } catch (NoSuchFieldException ex) {
            // This shouldn't happen because the field is in fact defined.
            throw new AssertionError(ex);
        }

        try {
            field.set(null, LangAccessImpl.THE_ONE);
        } catch (IllegalAccessException ex) {
            // This shouldn't happen because we made the field accessible.
            throw new AssertionError(ex);
        }

        // Also set up the bootstrap-classpath-wide access mechanism.
        LangAccess.setInstance(LangAccessImpl.THE_ONE);
    
        if (clazz == null) {
            throw new NullPointerException("clazz == null");
        }

        this.clazz = clazz;
        this.declaredMethods = null;
        this.declaredPublicMethods = null;
        this.allMethods = null;
        this.allPublicMethods = null;
        this.enumValuesInOrder = null;
        this.enumValuesByName = null;
        this.declaredFields = null;
        this.declaredPublicFields = null;
        this.allFields = null;
        this.allPublicFields = null;
    
Methods Summary
private T[]callEnumValues()
Calls the static method values() on this instance's class, which is presumed to be a properly-formed enumeration class, using proper privilege hygiene.

return
non-null; the array of values as reported by value()

        Method method;

        try {
            Method[] methods = getDeclaredPublicMethods();
            method = findMethodByName(methods, "values", (Class[]) null);
            method = REFLECT.accessibleClone(method);
        } catch (NoSuchMethodException ex) {
            // This shouldn't happen if the class is a well-formed enum.
            throw new UnsupportedOperationException(ex);
        }
        
        try {
            return (T[]) method.invoke((Object[]) null);
        } catch (IllegalAccessException ex) {
            // This shouldn't happen because the method is "accessible."
            throw new Error(ex);
        } catch (InvocationTargetException ex) {
            Throwable te = ex.getTargetException();
            if (te instanceof RuntimeException) {
                throw (RuntimeException) te;
            } else if (te instanceof Error) {
                throw (Error) te;
            } else {
                throw new Error(te);
            }
        }
    
public static booleancompareClassLists(java.lang.Class[] a, java.lang.Class[] b)
Compares two class lists for equality. Empty and null lists are considered equal. This is useful for matching methods and constructors.

TODO: Take into account assignment compatibility?

param
a null-ok; the first list of types
param
b null-ok; the second list of types
return
true if and only if the lists are equal

        if (a == null) {
            return (b == null) || (b.length == 0);
        }

        int length = a.length;
        
        if (b == null) {
            return (length == 0);
        }

        if (length != b.length) {
            return false;
        }

        for (int i = length - 1; i >= 0; i--) {
            if (a[i] != b[i]) {
                return false;
            }
        }
        
        return true;
    
public static java.lang.reflect.Method[]deepCopy(java.lang.reflect.Method[] orig)
Makes a deep copy of the given array of methods. This is useful when handing out arrays from the public API.

Note: In such cases, it is insufficient to just make a shallow copy of the array, since method objects aren't immutable due to the existence of {@link AccessibleObject#setAccessible}.

param
orig non-null; array to copy
return
non-null; a deep copy of the given array

        int length = orig.length;
        Method[] result = new Method[length];

        for (int i = length - 1; i >= 0; i--) {
            result[i] = REFLECT.clone(orig[i]);
        }

        return result;
    
public static java.lang.reflect.Field[]deepCopy(java.lang.reflect.Field[] orig)
Makes a deep copy of the given array of fields. This is useful when handing out arrays from the public API.

Note: In such cases, it is insufficient to just make a shallow copy of the array, since field objects aren't immutable due to the existence of {@link AccessibleObject#setAccessible}.

param
orig non-null; array to copy
return
non-null; a deep copy of the given array

        int length = orig.length;
        Field[] result = new Field[length];

        for (int i = length - 1; i >= 0; i--) {
            result[i] = REFLECT.clone(orig[i]);
        }

        return result;
    
private static voidfindAllMethods(java.lang.Class clazz, java.util.ArrayList methods, java.util.HashSet seen, boolean publicOnly)
Collects the list of methods without performing any security checks first. This includes the methods inherited from superclasses and from all implemented interfaces. The latter may also implement multiple interfaces, so we (potentially) recursively walk through a whole tree of classes. If no methods exist at all, an empty array is returned.

param
clazz non-null; class to inspect
param
methods non-null; the target list to add the results to
param
seen non-null; a set of signatures we've already seen
param
publicOnly reflects whether we want only public methods or all of them

        StringBuilder builder = new StringBuilder();
        Class<?> origClass = clazz;
        
        // Traverse class and superclasses, get rid of dupes by signature
        while (clazz != null) {
            Method[] declaredMethods =
                clazz.getClassCache().getDeclaredMethods(publicOnly);
            int length = declaredMethods.length;
            if (length != 0) {
                for (Method method : declaredMethods) {
                    builder.setLength(0);
                    builder.append(method.getName());
                    builder.append('(");
                    Class<?>[] types = method.getParameterTypes();
                    if (types.length != 0) {
                        builder.append(types[0].getName());
                        for (int j = 1; j < types.length; j++) {
                            builder.append(',");
                            builder.append(types[j].getName());
                        }
                    }
                    builder.append(')");
                    
                    String signature = builder.toString();
                    if (!seen.contains(signature)) {
                        methods.add(method);
                        seen.add(signature);
                    }
                }
            }
            
            clazz = clazz.getSuperclass();
        }
        
        // Traverse all interfaces, and do the same recursively.
        Class<?>[] interfaces = origClass.getInterfaces();
        for (Class<?> intf : interfaces) {
            findAllMethods(intf, methods, seen, publicOnly);
        }
    
private static voidfindAllfields(java.lang.Class clazz, java.util.ArrayList fields, java.util.HashSet seen, boolean publicOnly)
Collects the list of fields without performing any security checks first. This includes the fields inherited from superclasses and from all implemented interfaces. The latter may also implement multiple interfaces, so we (potentially) recursively walk through a whole tree of classes. If no fields exist at all, an empty array is returned.

param
clazz non-null; class to inspect
param
fields non-null; the target list to add the results to
param
seen non-null; a set of signatures we've already seen
param
publicOnly reflects whether we want only public fields or all of them

        
        // Traverse class and superclasses, get rid of dupes by signature
        while (clazz != null) {
            Field[] declaredFields =
                    clazz.getClassCache().getDeclaredFields(publicOnly);
            for (Field field : declaredFields) {                    
                String signature = field.toString();
                if (!seen.contains(signature)) {
                    fields.add(field);
                    seen.add(signature);
                }
            }
            
            // Traverse all interfaces, and do the same recursively.
            Class<?>[] interfaces = clazz.getInterfaces();
            for (Class<?> intf : interfaces) {
                findAllfields(intf, fields, seen, publicOnly);
            }
            
            clazz = clazz.getSuperclass();
        }
    
public static java.lang.reflect.FieldfindFieldByName(java.lang.reflect.Field[] list, java.lang.String name)
Finds and returns a field with a given name and signature. Use this with one of the field lists returned by instances of this class.

param
list non-null; the list of fields to search through
return
non-null; the matching field
throws
NoSuchFieldException thrown if the field does not exist

        if (name == null) {
            throw new NullPointerException("Field name must not be null.");
        }
        for (int i = 0; i < list.length; i++) {
            Field field = list[i];
            if (field.getName().equals(name)) {
                return field;
            }
        }
        
        throw new NoSuchFieldException(name);
    
public static java.lang.reflect.MethodfindMethodByName(java.lang.reflect.Method[] list, java.lang.String name, java.lang.Class[] parameterTypes)
Finds and returns a method with a given name and signature. Use this with one of the method lists returned by instances of this class.

param
list non-null; the list of methods to search through
param
parameterTypes non-null; the formal parameter list
return
non-null; the matching method
throws
NoSuchMethodException thrown if the method does not exist

        if (name == null) {
            throw new NullPointerException("Method name must not be null.");
        }
        for (int i = list.length - 1; i >= 0; i--) {
            Method method = list[i];
            if (method.getName().equals(name) 
                    && compareClassLists(
                            method.getParameterTypes(), parameterTypes)) {
                return method;
            }
        }
        
        throw new NoSuchMethodException(name);
    
public java.lang.reflect.Field[]getAllFields()
Gets the list of all fields, both directly declared and inherited.

return
non-null; the list of all fields

        if (allFields == null) {
            allFields = getFullListOfFields(false);
        }

        return allFields;
    
public java.lang.reflect.Method[]getAllMethods()
Gets the list of all methods, both directly declared and inherited.

return
non-null; the list of all methods

        if (allMethods == null) {
            allMethods = getFullListOfMethods(false);
        }

        return allMethods;
    
public java.lang.reflect.Field[]getAllPublicFields()
Gets the list of all public fields, both directly declared and inherited.

return
non-null; the list of all public fields

        if (allPublicFields == null) {
            allPublicFields = getFullListOfFields(true);
        }

        return allPublicFields;
    
public java.lang.reflect.Method[]getAllPublicMethods()
Gets the list of all public methods, both directly declared and inherited.

return
non-null; the list of all public methods

        if (allPublicMethods == null) {
            allPublicMethods = getFullListOfMethods(true);
        }

        return allPublicMethods;
    
public java.lang.reflect.Field[]getDeclaredFields()
Gets the list of all declared fields.

return
non-null; the list of all declared fields

        if (declaredFields == null) {
            declaredFields = Class.getDeclaredFields(clazz, false);
        }

        return declaredFields;
    
public java.lang.reflect.Field[]getDeclaredFields(boolean publicOnly)
Gets either the list of declared fields or the list of declared public fields.

param
publicOnly whether to only return public fields

        return publicOnly ? getDeclaredPublicFields() : getDeclaredFields();
    
public java.lang.reflect.Method[]getDeclaredMethods()
Gets the list of all declared methods.

return
non-null; the list of all declared methods

        if (declaredMethods == null) {
            declaredMethods = Class.getDeclaredMethods(clazz, false);
        }

        return declaredMethods;
    
public java.lang.reflect.Method[]getDeclaredMethods(boolean publicOnly)
Gets either the list of declared methods or the list of declared public methods.

param
publicOnly whether to only return public methods

        return publicOnly ? getDeclaredPublicMethods() : getDeclaredMethods();
    
public java.lang.reflect.Field[]getDeclaredPublicFields()
Gets the list of all declared public fields.

return
non-null; the list of all declared public fields

        if (declaredPublicFields == null) {
            declaredPublicFields = Class.getDeclaredFields(clazz, true);
        }

        return declaredPublicFields;
    
public java.lang.reflect.Method[]getDeclaredPublicMethods()
Gets the list of all declared public methods.

return
non-null; the list of all declared public methods

        if (declaredPublicMethods == null) {
            declaredPublicMethods = Class.getDeclaredMethods(clazz, true);
        }

        return declaredPublicMethods;
    
public TgetEnumValue(java.lang.String name)
Gets the enumerated value with a given name.

param
name non-null; name of the value
return
null-ok; the named enumerated value or null if this instance's class doesn't have such a value (including if this instance isn't in fact an enumeration)

        Enum[] values = (Enum[]) getEnumValuesByName();

        if (values == null) {
            return null;
        }

        // Binary search.

        int min = 0;
        int max = values.length - 1;

        while (min <= max) {
            /*
             * The guessIdx calculation is equivalent to ((min + max)
             * / 2) but won't go wonky when min and max are close to
             * Integer.MAX_VALUE.
             */
            int guessIdx = min + ((max - min) >> 1);
            Enum guess = values[guessIdx];
            int cmp = name.compareTo(guess.name());
            
            if (cmp < 0) {
                max = guessIdx - 1;
            } else if (cmp > 0) {
                min = guessIdx + 1;
            } else {
                return (T) guess;
            }
        }

        return null;
    
public T[]getEnumValuesByName()
Gets the array of enumerated values, sorted by name.

return
null-ok; the value array, or null if this instance's class isn't in fact an enumeration

        if (enumValuesByName == null) {
            T[] values = getEnumValuesInOrder();

            if (values != null) {
                values = (T[]) values.clone();
                Arrays.sort((Enum<?>[]) values, ENUM_COMPARATOR);

                /*
                 * Note: It's only safe (concurrency-wise) to set the
                 * instance variable after the array is properly sorted.
                 */
                enumValuesByName = values;
            }
        }

        return enumValuesByName;
    
public T[]getEnumValuesInOrder()
Gets the array of enumerated values, in their original declared order.

return
null-ok; the value array, or null if this instance's class isn't in fact an enumeration

        if ((enumValuesInOrder == null) && clazz.isEnum()) {
            enumValuesInOrder = callEnumValues();
        }

        return enumValuesInOrder;
    
private java.lang.reflect.Field[]getFullListOfFields(boolean publicOnly)

        ArrayList<Field> fields = new ArrayList<Field>();
        HashSet<String> seen = new HashSet<String>();

        findAllfields(clazz, fields, seen, publicOnly);
        
        return fields.toArray(new Field[fields.size()]);
    
private java.lang.reflect.Method[]getFullListOfMethods(boolean publicOnly)

        ArrayList<Method> methods = new ArrayList<Method>();
        HashSet<String> seen = new HashSet<String>();

        findAllMethods(clazz, methods, seen, publicOnly);
        
        return methods.toArray(new Method[methods.size()]);
    
private static org.apache.harmony.kernel.vm.ReflectionAccessgetReflectionAccess()
Gets the reflection access object. This uses reflection to do so. My head is spinning.

return
non-null; the reflection access object

        /*
         * Note: We can't do AccessibleObject.class.getCache() to
         * get the cache, since that would cause a circularity in
         * initialization. So instead, we do a direct call into the
         * native side.
         */
        Method[] methods =
            Class.getDeclaredMethods(AccessibleObject.class, false);

        try {
            Method method = findMethodByName(methods, "getReflectionAccess",
                    (Class[]) null);
            Class.setAccessibleNoCheck(method, true);
            return (ReflectionAccess) method.invoke((Object[]) null);
        } catch (NoSuchMethodException ex) {
            /*
             * This shouldn't happen because the method
             * AccessibleObject.getReflectionAccess() really is defined
             * in this module.
             */
            throw new Error(ex);
        } catch (IllegalAccessException ex) {
            // This shouldn't happen because the method is "accessible."
            throw new Error(ex);
        } catch (InvocationTargetException ex) {
            throw new Error(ex);
        }