find an accessible method passed in : Modifier « Reflection « Java






find an accessible method passed in

    

import java.util.Map;
import java.util.HashMap;
import java.util.Collection;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Queue;
import java.util.LinkedList;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import static java.lang.reflect.Modifier.isPublic;
import java.beans.PropertyDescriptor;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.IntrospectionException;

/**
 * Common utilty methods that are useful when working with reflection.
 *
 * @author Tim Fennell
 */
public class ReflectUtil {
    /** A cache of property descriptors by class and property name */
    private static Map<Class<?>, Map<String, PropertyDescriptor>> propertyDescriptors =
            new ConcurrentHashMap<Class<?>, Map<String, PropertyDescriptor>>();

    /** Static helper class, shouldn't be constructed. */
    private ReflectUtil() {}

    /**
     * Holds a map of commonly used interface types (mostly collections) to a class that
     * implements the interface and will, by default, be instantiated when an instance
     * of the interface is needed.
     */
    protected static final Map<Class<?>,Class<?>> interfaceImplementations = new HashMap<Class<?>,Class<?>>();

    /**
     * Holds a map of primitive type to the default value for that primitive type.  Isn't it
     * odd that there's no way to get this programmatically from the Class objects?
     */
    protected static final Map<Class<?>,Object> primitiveDefaults = new HashMap<Class<?>,Object>();

    static {
        interfaceImplementations.put(Collection.class, ArrayList.class);
        interfaceImplementations.put(List.class,       ArrayList.class);
        interfaceImplementations.put(Set.class,        HashSet.class);
        interfaceImplementations.put(SortedSet.class,  TreeSet.class);
        interfaceImplementations.put(Queue.class,      LinkedList.class);
        interfaceImplementations.put(Map.class,        HashMap.class);
        interfaceImplementations.put(SortedMap.class,  TreeMap.class);

        primitiveDefaults.put(Boolean.TYPE,    false);
        primitiveDefaults.put(Character.TYPE, '\0');
        primitiveDefaults.put(Byte.TYPE,       new Byte("0"));
        primitiveDefaults.put(Short.TYPE,      new Short("0"));
        primitiveDefaults.put(Integer.TYPE,    new Integer(0));
        primitiveDefaults.put(Long.TYPE,       new Long(0l));
        primitiveDefaults.put(Float.TYPE,      new Float(0f));
        primitiveDefaults.put(Double.TYPE,     new Double(0.0));
    }

    /**
     * The set of method that annotation classes inherit, and should be avoided when
     * toString()ing an annotation class.
     */
    private static final Set<String> INHERITED_ANNOTATION_METHODS =
            new HashSet(Arrays.asList("toString", "equals", "hashCode", "annotationType"));

    /**
     * Looks up the default implementing type for the supplied interface. This is done
     * based on a static map of known common interface types and implementing classes.
     *
     * @param iface an interface for which an implementing class is needed
     * @return a Class object representing the implementing type, or null if one is
     *         not found
     */
    public static Class<?> getImplementingClass(Class<?> iface) {
        return interfaceImplementations.get(iface);
    }

    /**
     * Attempts to determine an implementing class for the interface provided and instantiate
     * it using a default constructor.
     *
     * @param interfaceType an interface (or abstract class) to make an instance of
     * @return an instance of the interface type supplied
     * @throws InstantiationException if no implementation type has been configured
     * @throws IllegalAccessException if thrown by the JVM during class instantiation
     */
    @SuppressWarnings("unchecked")
  public static <T> T getInterfaceInstance(Class<T> interfaceType)
            throws InstantiationException, IllegalAccessException {
        Class impl = getImplementingClass(interfaceType);
        if (impl == null) {
            throw new InstantiationException(
                    "Stripes needed to instantiate a property who's declared type as an " +
                    "interface (which obviously cannot be instantiated. The interface is not " +
                    "one that Stripes is aware of, so no implementing class was known. The " +
                    "interface type was: '" + interfaceType.getName() + "'. To fix this " +
                    "you'll need to do one of three things. 1) Change the getter/setter methods " +
                    "to use a concrete type so that Stripes can instantiate it. 2) in the bean's " +
                    "setContext() method pre-instantiate the property so Stripes doesn't have to. " +
                    "3) Bug the Stripes author ;)  If the interface is a JDK type it can easily be " +
                    "fixed. If not, if enough people ask, a generic way to handle the problem " +
                    "might get implemented.");
        }
        else {
            return (T) impl.newInstance();
        }
    }

    /**
     * Utility method used to load a class.  Any time that Stripes needs to load of find a
     * class by name it uses this method.  As a result any time the classloading strategy
     * needs to change it can be done in one place!  Currently uses
     * {@code Thread.currentThread().getContextClassLoader().loadClass(String)}.
     *
     * @param name the fully qualified (binary) name of the class to find or load
     * @return the Class object representing the class
     * @throws ClassNotFoundException if the class cannot be loaded
     */
    @SuppressWarnings("unchecked") // this allows us to assign without casting
  public static Class findClass(String name) throws ClassNotFoundException {
        return Thread.currentThread().getContextClassLoader().loadClass(name);
    }

    /**
     * <p>A better (more concise) toString method for annotation types that yields a String
     * that should look more like the actual usage of the annotation in a class. The String produced
     * is similar to that produced by calling toString() on the annotation directly, with the
     * following differences:</p>
     *
     * <ul>
     *   <li>Uses the classes simple name instead of it's fully qualified name.</li>
     *   <li>Only outputs attributes that are set to non-default values.</li>
     *
     * <p>If, for some unforseen reason, an exception is thrown within this method it will be
     * caught and the return value will be {@code ann.toString()}.
     *
     * @param ann the annotation to convert to a human readable String
     * @return a human readable String form of the annotation and it's attributes
     */
    public static String toString(Annotation ann) {
        try {
            Class<? extends Annotation> type = ann.annotationType();
            StringBuilder builder = new StringBuilder(128);
            builder.append("@");
            builder.append(type.getSimpleName());

            boolean appendedAnyParameters = false;
            Method[] methods = type.getMethods();
            for (Method method : methods) {
                if (!INHERITED_ANNOTATION_METHODS.contains(method.getName())) {
                    Object defaultValue = method.getDefaultValue();
                    Object actualValue  = method.invoke(ann);

                    // If we have arrays, they have to be treated a little differently
                    Object[] defaultArray = null, actualArray = null;
                    if ( Object[].class.isAssignableFrom(method.getReturnType()) ) {
                        defaultArray = (Object[]) defaultValue;
                        actualArray  = (Object[]) actualValue;
                    }

                    // Only print an attribute if it isn't set to the default value
                    if ( (defaultArray != null && !Arrays.equals(defaultArray, actualArray)) ||
                            (defaultArray == null && !actualValue.equals(defaultValue)) ) {

                        if (appendedAnyParameters) {
                            builder.append(", ");
                        }
                        else {
                            builder.append("(");
                        }

                        builder.append(method.getName());
                        builder.append("=");

                        if (actualArray != null) {
                            builder.append( Arrays.toString(actualArray) );
                        }
                        else {
                            builder.append(actualValue);
                        }

                        appendedAnyParameters = true;
                    }
                }
            }

            if (appendedAnyParameters) {
                builder.append(")");
            }

            return builder.toString();
        }
        catch (Exception e) {
            return ann.toString();
        }
    }

    /**
     * Fetches all methods of all access types from the supplied class and super
     * classes. Methods that have been overridden in the inheritance hierarchy are
     * only returned once, using the instance lowest down the hierarchy.
     *
     * @param clazz the class to inspect
     * @return a collection of methods
     */
    public static Collection<Method> getMethods(Class<?> clazz) {
        Collection<Method> found = new ArrayList<Method>();
        while (clazz != null) {
            for (Method m1 : clazz.getDeclaredMethods()) {
                boolean overridden = false;

                for (Method m2 : found) {
                    if ( m2.getName().equals(m1.getName()) &&
                            Arrays.deepEquals(m1.getParameterTypes(), m2.getParameterTypes())) {
                        overridden = true;
                        break;
                    }
                }

                if (!overridden) found.add(m1);
            }

            clazz = clazz.getSuperclass();
        }

        return found;
    }

    /**
     * Fetches all fields of all access types from the supplied class and super
     * classes. Fieldss that have been overridden in the inheritance hierarchy are
     * only returned once, using the instance lowest down the hierarchy.
     *
     * @param clazz the class to inspect
     * @return a collection of fields
     */
    public static Collection<Field> getFields(Class<?> clazz) {
        Map<String,Field> fields = new HashMap<String, Field>();
        while (clazz != null) {
            for (Field field : clazz.getDeclaredFields()) {
                if ( !fields.containsKey(field.getName()) ) {
                    fields.put(field.getName(), field);
                }
            }

            clazz = clazz.getSuperclass();
        }

        return fields.values();
    }

    /**
     * Fetches the property descriptor for the named property of the supplied class. To
     * speed things up a cache is maintained of propertyName to PropertyDescriptor for
     * each class used with this method.  If there is no property with the specified name,
     * returns null.
     *
     * @param clazz the class who's properties to examine
     * @param property the String name of the property to look for
     * @return the PropertyDescriptor or null if none is found with a matching name
     */
    public static PropertyDescriptor getPropertyDescriptor(Class<?> clazz, String property) {
        Map<String,PropertyDescriptor> pds = propertyDescriptors.get(clazz);
        if (pds == null) {
            try {
                BeanInfo info = Introspector.getBeanInfo(clazz);
                PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
                pds = new HashMap<String, PropertyDescriptor>();

                for (PropertyDescriptor descriptor : descriptors) {
                    pds.put(descriptor.getName(), descriptor);
                }

                propertyDescriptors.put(clazz, pds);
            }
            catch (IntrospectionException ie) {
                throw new RuntimeException("Could not examine class '" + clazz.getName() +
                        "' using Introspector.getBeanInfo() to determine property information.", ie);
            }
        }

        return pds.get(property);
    }

    /**
     * <p>Attempts to find an accessible version of the method passed in, where accessible
     * is defined as the method itself being public and the declaring class being public.
     * Mostly useful as a workaround to the situation when
     * {@link PropertyDescriptor#getReadMethod()} and/or
     * {@link java.beans.PropertyDescriptor#getWriteMethod()} returns methods that are not
     * accessible (usually due to public implementations of interface methods in private
     * classes).</p>
     *
     * <p>Checks the method passed in and if it already meets these criteria it is returned
     * immediately. In general this leads to very little performance overhead</p>
     *
     * <p>If the method does not meet the criteria then the class' interfaces are scanned
     * for a matching method. If one is not found, then the class' superclass hierarchy
     * is searched. Finally, if no matching method can be found the original method is
     * returned.</p>
     *
     * @param m a method that may or may not be accessible
     * @return either an accessible version of the same method, or the method passed in if
     *         an accessible version cannot be found
     */
    public static Method findAccessibleMethod(final Method m) {
        // If the passed in method is accessible, then just give it back.
        if (isPublic(m.getModifiers()) && isPublic(m.getDeclaringClass().getModifiers())) return m;
        if (m.isAccessible()) return m;

        final Class<?> clazz    = m.getDeclaringClass();
        final String name    = m.getName();
        final Class<?>[] ptypes = m.getParameterTypes();

        // Else, loop through the interfaces for the declaring class, looking for a
        // public version of the method that we can call
        for (Class<?> iface : clazz.getInterfaces()) {
            try {
                Method m2 = iface.getMethod(name, ptypes);
                if (m2.isAccessible()) return m2;
                if (isPublic(iface.getModifiers()) && isPublic(m2.getModifiers())) return m2;
            }
            catch (NoSuchMethodException nsme) { /* Not Unexpected. */ }
        }

        // Else loop through the superclasses looking for a public method
        Class<?> c = clazz.getSuperclass();
        while (c != null) {
            try {
                Method m2 = c.getMethod(name, ptypes);
                if (m2.isAccessible()) return m2;
                if (isPublic(c.getModifiers()) && isPublic(m2.getModifiers())) return m2;
            }
            catch (NoSuchMethodException nsme) { /* Not Unexpected. */ }

            c = c.getSuperclass();
        }

        // If we haven't found anything at this point, just give up!
        return m;
    }



    /**
     * Looks for an instance (i.e. non-static) public field with the matching name and
     * returns it if one exists.  If no such field exists, returns null.
     *
     * @param clazz the clazz who's fields to examine
     * @param property the name of the property/field to look for
     * @return the Field object or null if no matching field exists
     */
    public static Field getField(Class<?> clazz, String property) {
        try {
            Field field = clazz.getField(property);
            return !Modifier.isStatic(field.getModifiers()) ? field : null;
        }
        catch (NoSuchFieldException nsfe) {
            return null;
        }
    }

    /**
     * Returns an appropriate default value for the class supplied. Mirrors the defaults used
     * when the JVM initializes instance variables.
     *
     * @param clazz the class for which to find the default value
     * @return null for non-primitive types and an appropriate wrapper instance for primitives
     */
    public static Object getDefaultValue(Class<?> clazz) {
        if (clazz.isPrimitive()) {
            return primitiveDefaults.get(clazz);
        }
        else {
            return null;
        }
    }
    
    /**
     * Returns a set of all interfaces implemented by class supplied. This includes all
     * interfaces directly implemented by this class as well as those implemented by
     * superclasses or interface superclasses.
     * 
     * @param clazz
     * @return all interfaces implemented by this class
     */
    public static Set<Class<?>> getImplementedInterfaces(Class<?> clazz)
    {
        Set<Class<?>> interfaces = new HashSet<Class<?>>();
        
        if (clazz.isInterface())
            interfaces.add(clazz);

        while (clazz != null) {
            for (Class<?> iface : clazz.getInterfaces())
                interfaces.addAll(getImplementedInterfaces(iface));
            clazz = clazz.getSuperclass();
        } 

        return interfaces;
    }

    /**
     * Returns an array of Type objects representing the actual type arguments
     * to targetType used by clazz.
     * 
     * @param clazz the implementing class (or subclass)
     * @param targetType the implemented generic class or interface
     * @return an array of Type objects or null
     */
    public static Type[] getActualTypeArguments(Class<?> clazz, Class<?> targetType) {
        Set<Class<?>> classes = new HashSet<Class<?>>();
        classes.add(clazz);

        if (targetType.isInterface())
            classes.addAll(getImplementedInterfaces(clazz));

        Class<?> superClass = clazz.getSuperclass();
        while (superClass != null) {
            classes.add(superClass);
            superClass = superClass.getSuperclass();
        }

        for (Class<?> search : classes) {
            for (Type type : (targetType.isInterface() ? search.getGenericInterfaces()
                    : new Type[] { search.getGenericSuperclass() })) {
                if (type instanceof ParameterizedType) {
                    ParameterizedType parameterizedType = (ParameterizedType) type;
                    if (targetType.equals(parameterizedType.getRawType()))
                        return parameterizedType.getActualTypeArguments();
                }
            }
        }

        return null;
    }
}

   
    
    
    
  








Related examples in the same category

1.Return true if the integer argument includes the public modifier, false otherwise
2.Return true if the integer argument includes the private modifier, false otherwise.
3.Return true if the integer argument includes the protected modifier,false otherwise.
4.Return true if the integer argument includes the static modifier, false otherwise.
5.Return true if the integer argument includes the final modifier, false otherwise.
6.Return true if the integer argument includes the abstract modifier, false otherwise.
7.Overriding Default Access
8.Listing the Modifiers of a Class Object
9.Listing the Modifiers of a Member Object: Field, Constructor, and Method are all subclasses of Member.
10.Is Accessible
11.Set Accessibility
12.Make field accessible, explicitly setting it accessible
13.Gets a method and forces it to be accessible, even if it is not.
14.Modifier check utility.