package umontreal.iro.lecuyer.util;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
/**
* Provides utility methods for introspection using
* Java Reflection API.
*
*/
public class Introspection {
private Introspection() {}
/**
* Returns all the methods declared and inherited
* by a class. This is similar to {@link java.lang.Class#getMethods(()) getMethods}
* except that it enumerates non-public methods as well as public ones.
* This method uses {@link java.lang.Class#getDeclaredMethods(()) getDeclaredMethods}
* to get the declared methods of c.
* It also gets the declared methods of superclasses.
* If a method is defined in a superclass and overriden
* in a subclass, only the overriden method will be
* in the returned array.
*
*
* Note that since this method uses
* {@link java.lang.Class#getDeclaredMethods(()) getDeclaredMethods},
* it can throw a {@link java.lang.SecurityException SecurityException} if
* a security manager is present.
*
* @param c the class being processed.
*
* @return the array of methods.
*
*/
public static Method[] getMethods (Class> c) {
// Creates the set of methods for the class.
List lst = internalGetMethods (c);
// Copy the methods to the array that will be returned.
return lst.toArray (new Method[lst.size()]);
}
private static List internalGetMethods (Class> c) {
// Inspired from java.lang.Class
List methods = new ArrayList();
Method[] mt = c.getDeclaredMethods();
for (int i = 0; i < mt.length; i++)
methods.add (mt[i]);
List inheritedMethods = new ArrayList();
Class[] iface = c.getInterfaces();
for (int i = 0; i < iface.length; i++)
inheritedMethods.addAll (internalGetMethods (iface[i]));
if (!c.isInterface()) {
Class> s = c.getSuperclass();
if (s != null) {
List supers = internalGetMethods (s);
for (Method m : supers) {
// Filter out concrete implementations of any interface
// methods.
if (m != null && !Modifier.isAbstract (m.getModifiers()))
removeByNameAndSignature (inheritedMethods, m);
}
supers.addAll (inheritedMethods);
inheritedMethods = supers;
}
}
// Filter out all local methods from inherited ones
for (Method m : methods)
removeByNameAndSignature (inheritedMethods, m);
for (Method m : inheritedMethods) {
if (m == null)
continue;
if (!methods.contains (m))
methods.add (m);
}
return methods;
}
private static void removeByNameAndSignature (List methods, Method m) {
for (ListIterator it = methods.listIterator(); it.hasNext(); ) {
Method tst = it.next();
if (tst == null)
continue;
if (tst.getName().equals (m.getName()) &&
tst.getReturnType() == m.getReturnType() &&
sameSignature (tst, m))
it.set (null);
}
}
/**
* Determines if two methods m1 and m2
* share the same signature. For the signature to be identical,
* methods must have the same
* number of parameters and the same parameter types.
*
* @param m1 the first method.
*
* @param m2 the second method.
*
* @return true if the signatures are the same, false
* otherwise.
*
*/
public static boolean sameSignature (Method m1, Method m2) {
Class[] pt1 = m1.getParameterTypes();
Class[] pt2 = m2.getParameterTypes();
if (pt1.length != pt2.length)
return false;
for (int i = 0; i < pt1.length; i++)
if (pt1[i] != pt2[i])
return false;
return true;
}
/**
* Returns all the fields declared and inherited
* by a class. This is similar to {@link java.lang.Class#getFields(()) getFields}
* except that it enumerates non-public fields as well as public ones.
* This method uses {@link java.lang.Class#getDeclaredFields(()) getDeclaredFields}
* to get the declared fields of c.
* It also gets the declared fields of superclasses and implemented
* interfaces.
*
*
* Note that since this method uses
* {@link java.lang.Class#getDeclaredFields(()) getDeclaredFields},
* it can throw a {@link java.lang.SecurityException SecurityException} if
* a security manager is present.
*
* @param c the class being processed.
*
* @return the array of fields.
*
*/
public static Field[] getFields (Class> c) {
// Creates the set of fields for the class.
List lst = new ArrayList();
processFields (c, lst);
Set> traversedInterfaces = new HashSet>();
processInterfaceFields (c, lst, traversedInterfaces);
if (!c.isInterface()) {
Class> s = c.getSuperclass();
while (s != null) {
processFields (s, lst);
processInterfaceFields (s, lst, traversedInterfaces);
s = s.getSuperclass();
}
}
// Copy the fields to the array that will be returned.
return lst.toArray (new Field[lst.size()]);
}
private static void processFields (final Class> c, final List lst) {
Field[] f = c.getDeclaredFields();
for (int i = 0; i < f.length; i++)
lst.add (f[i]);
}
private static void processInterfaceFields (final Class> c, final List lst, final Set> traversedInterfaces) {
Class[] iface = c.getInterfaces();
for (int i = 0; i < iface.length; i++) {
if (traversedInterfaces.contains (iface[i]))
continue;
traversedInterfaces.add (iface[i]);
processFields (iface[i], lst);
processInterfaceFields (iface[i], lst, traversedInterfaces);
}
}
/**
* This is like {@link java.lang.Class#getMethod((String, Class...)) getMethod}, except that it can return non-public methods.
*
* @param c the class being processed.
*
* @param name the name of the method.
*
* @param pt the parameter types.
*
* @exception NoSuchMethodException if the method cannot be found.
*
*
*/
public static Method getMethod (Class> c, String name, Class[] pt)
throws NoSuchMethodException {
try {
return c.getDeclaredMethod (name, pt);
}
catch (NoSuchMethodException nme) {}
if (!c.isInterface())
try {
Class> s = c.getSuperclass();
if (s != null)
return getMethod (s, name, pt);
}
catch (NoSuchMethodException nme) {}
Class[] iface = c.getInterfaces();
for (int i = 0; i < iface.length; i++)
try {
return getMethod (iface[i], name, pt);
}
catch (NoSuchMethodException nme) {}
throw new NoSuchMethodException
("Cannot find method " + name + " in class " + c.getName());
}
/**
* This is like {@link java.lang.Class#getField((String)) getField},
* except that it can return non-public fields.
*
*
* Note that since this method uses
* {@link java.lang.Class#getDeclaredField((String)) getDeclaredField},
* it can throw a {@link java.lang.SecurityException SecurityException} if
* a security manager is present.
*
* @param c the class being processed.
*
* @param name the name of the method.
*
* @exception NoSuchFieldException if the field cannot be found.
*
*
*/
public static Field getField (Class> c, String name)
throws NoSuchFieldException {
try {
return c.getDeclaredField (name);
}
catch (NoSuchFieldException nfe) {}
Class[] iface = c.getInterfaces();
for (int i = 0; i < iface.length; i++)
try {
return getField (iface[i], name);
}
catch (NoSuchFieldException nme) {}
if (!c.isInterface())
try {
Class s = c.getSuperclass();
if (s != null)
return getField (s, name);
}
catch (NoSuchFieldException nfe) {}
throw new NoSuchFieldException
("Cannot find field " + name + " in " + c.getName());
}
/**
* Returns the field name corresponding to the value of
* an enumerated type val.
* This method gets the class of val and
* scans its fields to find a public static and final field
* containing val. If such a field is found,
* its name is returned. Otherwise, null is returned.
*
* @param val the value of the enumerated type.
*
* @return the field name or null.
*
*/
public static String getFieldName (Object val) {
Class> enumType = val.getClass();
Field[] f = enumType.getFields();
for (int i = 0; i < f.length; i++) {
if (Modifier.isPublic (f[i].getModifiers()) &&
Modifier.isStatic (f[i].getModifiers()) &&
Modifier.isFinal (f[i].getModifiers())) {
try {
if (f[i].get (null) == val)
return f[i].getName();
}
catch (IllegalAccessException iae) {}
}
}
return null;
}
/**
* Returns the field of class cls corresponding to
* the name name. This method looks for
* a public, static, and final field with name name and
* returns its value. If no appropriate field can be
* found, an {@link java.lang.IllegalArgumentException IllegalArgumentException} is thrown.
*
* @param cls the class to look for a field in.
*
* @param name the name of field.
*
* @return the object in the field.
* @exception IllegalArgumentException if name does
* not correspond to a valid field name.
*
*
*/
public static T valueOf (Class cls, String name) {
try {
Field field = cls.getField (name);
if (Modifier.isStatic (field.getModifiers()) &&
Modifier.isFinal (field.getModifiers()) &&
cls.isAssignableFrom (field.getType()))
return (T)field.get (null);
}
catch (NoSuchFieldException nfe) {}
catch (IllegalAccessException iae) {}
throw new IllegalArgumentException ("Invalid field name: " + name);
}
/**
* Similar to {@link #valueOf((Class, String)) valueOf} (cls, name),
* with case insensitive field name look-up.
* If cls defines several fields with the same case insensitive
* name name, an {@link java.lang.IllegalArgumentException IllegalArgumentException} is thrown.
*
* @param cls the class to look for a field in.
*
* @param name the name of field.
*
* @return the object in the field.
* @exception IllegalArgumentException if name does
* not correspond to a valid field name, or if
* the class defines several fields with the same name.
*
*/
public static T valueOfIgnoreCase (Class cls, String name) {
Field[] fields = cls.getFields();
T res = null;
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
if (field.getName().equalsIgnoreCase (name) &&
Modifier.isStatic (field.getModifiers()) &&
Modifier.isFinal (field.getModifiers()) &&
cls.isAssignableFrom (field.getType()))
try {
T res2 = (T)field.get (null);
if (res != null && res2 != null)
throw new IllegalArgumentException
("Found more than one field with the same name in class " +
cls.getName() +
" if case is ignored");
res = res2;
}
catch (IllegalAccessException iae) {}
}
if (res == null)
throw new IllegalArgumentException ("Invalid field name: " + name);
return res;
}
}
Ads help maintain this website.