package com.limegroup.gnutella.util;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* a.k.a. The "ObjectMolester"
* <p>
* This class is used to access a method or field of an object no
* matter what the access modifier of the method or field. The syntax
* for accessing fields and methods is out of the ordinary because this
* class uses reflection to peel away protection.
* <p>
* Here is an example of using this to access a private member.
* <code>resolveName</code> is a private method of <code>Class</code>.
*
* <pre>
* Class c = Class.class;
* System.out.println(
* PrivilegedAccessor.invokeMethod( c,
* "resolveName",
* "/net/iss/common/PrivilegeAccessor" ) );
* </pre>
*
* @author Charlie Hubbard (chubbard@iss.net)
* @author Prashant Dhokte (pdhokte@iss.net)
* @author Christopher Rohrs (added setValue)
* @author Sam Berlin (added support for static fields/methods,
* native parameters in methods, invokeConstructor,
* and getClass)
*/
public class PrivilegedAccessor {
/**
* Gets the value of the named field and returns it as an object.
*
* @param instance the object instance
* @param fieldName the name of the field
* @return an object representing the value of the field
*/
public static Object getValue(Object instance, String fieldName )
throws IllegalAccessException, NoSuchFieldException {
Field field;
if ( instance instanceof Class )
field = getFieldImpl((Class)instance, fieldName);
else
field = getFieldImpl(instance.getClass(), fieldName);
field.setAccessible(true);
return field.get(instance);
}
/**
* Sets the value of the named field.
*
* @param instance the object instance
* @param fieldName the name of the field
* @param value an object representing the value of the field
*/
public static void setValue(Object instance,
String fieldName,
Object value)
throws IllegalAccessException, NoSuchFieldException {
Field field;
if ( instance instanceof Class )
field = getFieldImpl((Class)instance, fieldName);
else
field = getFieldImpl(instance.getClass(), fieldName);
field.setAccessible(true);
field.set(instance, value);
}
/**
* Calls a method on the given object instance with the given argument.
*
* @param instance the object instance
* @param methodName the name of the method to invoke
* @param arg the argument to pass to the method
* @see PrivilegedAccessor#invokeMethod(Object,String,Object[])
*/
public static Object invokeMethod(Object instance,
String methodName,
Object arg)
throws NoSuchMethodException,
IllegalAccessException,
InvocationTargetException {
Object[] args = new Object[1];
args[0] = arg;
return invokeMethod(instance, methodName, args);
}
/**
* Calls a method on the given object instance with the given arguments.
*
* @param instance the object instance
* @param methodName the name of the method to invoke
* @param args an array of objects to pass as arguments
* @see PrivilegedAccessor#invokeMethod(Object,String,Object)
*/
public static Object invokeMethod(Object instance,
String methodName,
Object[] args )
throws NoSuchMethodException,
IllegalAccessException,
InvocationTargetException {
Class[] classTypes = null;
if( args != null) {
classTypes = new Class[args.length];
for( int i = 0; i < args.length; i++ ) {
if( args[i] != null )
classTypes[i] = args[i].getClass();
}
}
return invokeMethod(instance, methodName, args, classTypes);
}
/**
* Calls a method on the given object instance with the given arguments
* and types.
* Necessary for using native-type parameters and when the arguments
* are subclassed objects.
*
* @param instance the object instance
* @param methodName the name of the method to invoke
* @param args an array of objects to pass as arguments
* @param classTypes an array of the types of the arguments.
* @see PrivilegedAccessor#invokeMethod(Object,String,Object)
*/
public static Object invokeMethod(Object instance,
String methodName,
Object[] args,
Class[] classTypes )
throws NoSuchMethodException,
IllegalAccessException,
InvocationTargetException {
return getMethod(instance,methodName,classTypes).invoke(instance,args);
}
/**
* Calls a static method on the given class with the given argument.
* The static method is called on all superclasses of the class to.
* The call chain starts with the supermost class, eventually leading
* back to the class supplied as the argument.
*
* @param clazz the entry point class
* @param methodName the name of the method to invoke
* @param arg the argument to pass to the method
* @see PrivilegedAccessor#invokeMethod(Object,String,Object[])
*/
public static List invokeAllStaticMethods(Class clazz,
String methodName,
Object arg )
throws NoSuchMethodException,
IllegalAccessException,
InvocationTargetException {
Object[] args = new Object[1];
args[0] = arg;
return invokeAllStaticMethods(clazz, methodName, args);
}
/**
* Calls a static method on the given class with the given arguments.
* The static method is called on all superclasses of the class to.
* The call chain starts with the supermost class, eventually leading
* back to the class supplied as the argument.
*
* Returns an array of PairTuple of (Class, Object) where each entry
* represents one return value, Class being the class that returned Object.
*
* @param clazz the class
* @param methodName the name of the method to invoke
* @param args an array of objects to pass as arguments
* @see PrivilegedAccessor#invokeAllStaticMethods(Class,String,Object)
*/
public static List invokeAllStaticMethods(Class clazz,
String methodName,
Object[] args )
throws NoSuchMethodException,
IllegalAccessException,
InvocationTargetException {
Class[] classTypes = null;
if( args != null) {
classTypes = new Class[args.length];
for( int i = 0; i < args.length; i++ ) {
if( args[i] != null )
classTypes[i] = args[i].getClass();
}
}
return invokeAllStaticMethods(clazz, methodName, args, classTypes);
}
/**
* Calls a static method on the given class with the given arguments.
* The static method is called on all superclasses of the class to.
* The call chain starts with the supermost class, eventually leading
* back to the class supplied as the argument.
*
* Returns an array of PairTuple of (Class, Object) where each entry
* represents one return value, Class being the class that returned Object.
*
* Necessary for using native-type parameters and when the arguments
* are subclassed objects.
*
* @param clazz the class
* @param methodName the name of the method to invoke
* @param args an array of objects to pass as arguments
* @param classTypes an array of the types of the arguments.
* @see PrivilegedAccessor#invokeAllStaticMethods(Class,String,Object)
*/
public static List invokeAllStaticMethods(Class clazz,
String methodName,
Object[] args,
Class[] classTypes )
throws NoSuchMethodException,
IllegalAccessException,
InvocationTargetException {
List methods = getAllStaticMethods(clazz ,methodName,classTypes);
List ret = new LinkedList();
for(Iterator i = methods.iterator(); i.hasNext(); ) {
Method m = (Method)i.next();
Object val = m.invoke(null, args);
ret.add(new PairTuple(m.getDeclaringClass(), val));
}
return ret;
}
/**
*
* @param instance the object instance
* @param methodName the
*/
public static Method getMethod(Object instance,
String methodName,
Class[] classTypes )
throws NoSuchMethodException {
Method accessMethod;
if ( instance instanceof Class )
accessMethod = getMethodImpl((Class)instance, methodName, classTypes);
else
accessMethod = getMethodImpl(instance.getClass(), methodName, classTypes);
accessMethod.setAccessible(true);
return accessMethod;
}
/**
* @param class the class to find the statics methods in
* @param methodName the name of the static method
* @param the parameter classes
*/
public static List getAllStaticMethods(Class entryClass,
String methodName,
Class[] classTypes )
throws NoSuchMethodException {
List methods = new LinkedList();
Class clazz = entryClass;
while(clazz != null) {
try {
Method add = clazz.getDeclaredMethod(methodName, classTypes);
add.setAccessible(true);
methods.add(0, add);
} catch(NoSuchMethodException ignored) {}
clazz = clazz.getSuperclass();
}
if(methods.isEmpty())
throw new NoSuchMethodException("Invalid method: " + methodName);
return methods;
}
/**
* Returns the class 'name' that was declared by class 'parent'.
*
* @param parent the class who you want to look in for this class
* @param name the class you're looking for.
*/
public static Class getClass(Class parent, String name)
throws ClassNotFoundException {
Class clazz = getClassImpl(parent, name);
return clazz;
}
/**
* Constructs an object with the given parameters.
*/
public static Object invokeConstructor(Class clazz,
Object[] args )
throws NoSuchMethodException,
IllegalAccessException,
InvocationTargetException,
InstantiationException {
Class[] classTypes = null;
if( args != null) {
classTypes = new Class[args.length];
for( int i = 0; i < args.length; i++ ) {
if( args[i] != null )
classTypes[i] = args[i].getClass();
}
}
return invokeConstructor(clazz, args, classTypes);
}
/**
* Constructs an object with the given parameters and classtypes
*/
public static Object invokeConstructor(Class clazz,
Object[] args, Class[] classTypes)
throws NoSuchMethodException,
IllegalAccessException,
InvocationTargetException,
InstantiationException {
Constructor cs = getConstructorImpl(clazz, classTypes);
cs.setAccessible(true);
return cs.newInstance(args);
}
/**
* Return the named field from the given class.
*/
private static Field getFieldImpl(Class thisClass,
String fieldName)
throws NoSuchFieldException {
if (thisClass == null)
throw new NoSuchFieldException("Invalid field : " + fieldName);
try {
return thisClass.getDeclaredField( fieldName );
}
catch(NoSuchFieldException e) {
return getFieldImpl(thisClass.getSuperclass(), fieldName);
}
}
/**
* Return the named method with a method signature matching classTypes
* from the given class.
*/
private static Method getMethodImpl(Class thisClass,
String methodName,
Class[] classTypes)
throws NoSuchMethodException {
if (thisClass == null)
throw new NoSuchMethodException("Invalid method : " + methodName);
try {
return thisClass.getDeclaredMethod( methodName, classTypes );
}
catch(NoSuchMethodException e) {
return getMethodImpl(thisClass.getSuperclass(), methodName, classTypes);
}
}
/**
* Return the constructor with the given parameters.
*/
private static Constructor getConstructorImpl(Class clazz, Class[] classTypes)
throws NoSuchMethodException {
Constructor[] cs = clazz.getDeclaredConstructors();
for (int i = 0; i < cs.length; i++) {
// check for constructors which have arguments
if ( Arrays.equals(classTypes, cs[i].getParameterTypes()) )
return cs[i];
// check for no argument constructor
if ( cs[i].getParameterTypes().length == 0 && classTypes == null )
return cs[i];
}
throw new NoSuchMethodException("invalid constructor for class: " + clazz);
}
/**
* Return the class with the given name.
*/
private static Class getClassImpl(Class parent, String name)
throws ClassNotFoundException {
Class[] clazzes = parent.getDeclaredClasses();
for(int i = 0; i < clazzes.length; i++) {
if ( clazzes[i].getName().equals(parent.getName() + "$" + name) )
return clazzes[i];
}
throw new ClassNotFoundException("Invalid class : " + parent.getName() + "$" + name);
}
}