package net.sourceforge.javautil.common;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.sourceforge.javautil.common.coersion.CoersionContext;
import net.sourceforge.javautil.common.coersion.CoersionException;
import net.sourceforge.javautil.common.context.Context;
import net.sourceforge.javautil.common.exception.ThrowableManagerRegistry;
import net.sourceforge.javautil.common.io.IVirtualDirectory;
import net.sourceforge.javautil.common.io.IVirtualFile;
import net.sourceforge.javautil.common.io.impl.ISystemArtifact;
import net.sourceforge.javautil.common.io.impl.SystemDirectory;
import net.sourceforge.javautil.common.io.impl.SystemFile;
import net.sourceforge.javautil.common.reflection.ReflectionContext;
import net.sourceforge.javautil.common.reflection.cache.ClassCache;
import net.sourceforge.javautil.common.reflection.cache.ClassDescriptor;
import net.sourceforge.javautil.common.reflection.cache.ClassField;
/**
* Utilities/methods for dealing with reflection.
*
* @author ponder
* @author $Author: ponderator $
* @version $Id: ReflectionUtil.java 2722 2011-01-16 05:38:59Z ponderator $
*/
public class ReflectionUtil {
/**
* @param type The type in question
* @param index The index of the possible parameterized type
* @return The concrete class (minimum/base) for the generic type, or null if it could not be determined or was not declared
*/
public static Class<?> getConcreteGeneric (Type type, int index) {
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
if (pt.getActualTypeArguments().length <= index) return null;
return pt.getActualTypeArguments()[index] instanceof ParameterizedType ?
getConcreteGeneric(pt.getRawType(), 0) : getConcreteGeneric(pt.getActualTypeArguments()[index], 0);
} else if (type instanceof WildcardType) {
WildcardType wt = (WildcardType) type;
return wt.getUpperBounds().length <= index ? null : getConcreteGeneric(wt.getUpperBounds()[index], 0);
} else if (type instanceof TypeVariable) {
TypeVariable tt = (TypeVariable) type;
return tt.getBounds().length <= index ? null : getConcreteGeneric(tt.getBounds()[index], 0);
} else if (type instanceof GenericArrayType) {
GenericArrayType gat = (GenericArrayType) type;
return getConcreteGeneric(gat.getGenericComponentType(), index);
} else if (type instanceof Class) {
return (Class) type;
}
return null;
}
/**
* @param parameters The parameters for a method or constructor invocation
* @return The classes for each parameter, if a parameter is null, the type will be Object
*/
public static Class[] getClassesFor (Object... parameters) {
Class[] types = new Class[parameters.length];
for (int p=0; p<parameters.length; p++) {
types[p] = parameters[p] == null ? Object.class : parameters[p].getClass();
}
return types;
}
/**
* @param type The type from which to extract a super class set
* @return A unique set of super-classes (including the passed type) for this type in order of bottom-top hierarchy
*/
public static Set<Class<?>> extractSuperclasses (Class<?> type) {
return new RecursiveOperation<Class<?>, Set<Class<?>>>(type) {
@Override protected Class<?>[] evaluate(Class<?> instance, Set<Class<?>> result) {
result.add(instance);
return instance.getSuperclass() == null ? new Class[0] : new Class[] { instance.getSuperclass() };
}
}.execute(new LinkedHashSet<Class<?>>());
}
/**
* @param type The type from which to extract all unique interfaces
* @return A unique set of interfaces (including the passed type of an interface) declared in order of bottom-top hierarchy
*/
public static Set<Class<?>> extractInterfaces (Class<?> type) {
return new RecursiveOperation<Class<?>, Set<Class<?>>>(type) {
@Override protected Class<?>[] evaluate(Class<?> instance, Set<Class<?>> result) {
if (instance.isInterface()) result.add(instance);
return instance.getInterfaces();
}
}.execute(new LinkedHashSet<Class<?>>());
}
/**
* @param name The name of the class
* @return The class
*/
public static Class getClass (String name) {
return getClass(name, Thread.currentThread().getContextClassLoader());
}
/**
* @param name The name of the class
* @param loader The {@link ClassLoader} to use to load the class
* @return The class
*/
public static Class getClass (String name, ClassLoader loader) {
try {
return ReflectionContext.getReflectionManager().getClass(name, false, loader);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
/**
* @param <T> The type of object that will be created
* @param clazz The class to make a new instance of
* @param types The parameter type list of the constructor to use
* @param parameters The arguments to pass to the constructor
* @return The new instance
*/
public static <T> T newInstance (Class<T> clazz, Class[] types, Object... parameters) {
try {
return ReflectionContext.getReflectionManager().instantiate(clazz, types, parameters);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
/**
* @param object The object to invoke the method on
* @param name The name of the method
* @return What is returned by the method
*/
public static Object invokeMethod (Object object, String name) {
return invokeMethod(object.getClass(), name, object);
}
/**
* @param clazz The class to invoke this static method on
* @param name The name of the static method
* @return What is returned by the method
*/
public static Object invokeMethod (Class clazz, String name) {
return invokeMethod(clazz, name, (Object) null);
}
/**
* @param clazz The class for method lookup
* @param name The name of the method to invoke
* @param target The object to invoke the method on
* @return What is returned by the method
*/
public static Object invokeMethod (Class clazz, String name, Object target) {
return invokeMethod(clazz, name, target, Object.class);
}
/**
* @param <T> The type expected to be returned
* @param object The object to invoke the method on
* @param name The name of the method
* @param returnType The class of the type
* @return What is returned by the method
*/
public static <T> T invokeMethod (Object object, String name, Class<T> returnType) {
return invokeMethod(object.getClass(), name, object, returnType);
}
/**
* @param <T> The type expected to be returned
* @param clazz The class for method lookup
* @param name The name of the method
* @param target The object to invoke the method on
* @param returnType The class of the type
* @return What is returned by the method
*/
public static <T> T invokeMethod (Class clazz, String name, Object target, Class<T> returnType) {
return invokeMethod(clazz, name, target, returnType, new Class[0]);
}
/**
* @param <T> The type expected to be returned
* @param object The object to invoke the method on
* @param name The name of the method
* @param returnType The class of the type
* @param types The list of argument types
* @param parameters The arguments to be passed to the method invocation
* @return What is returned by the method
*/
public static <T> T invokeMethod (Object object, String name, Class<T> returnType, Class[] types, Object... parameters) {
return invokeMethod(object.getClass(), name, object, returnType, types, parameters);
}
/**
* @param <T> The type expected to be returned
* @param clazz The class for method lookup
* @param name The name of the method
* @param target The object to invoke the method on
* @param returnType The class of the type
* @param types The list of argument types
* @param parameters The arguments to be passed to the method invocation
* @return What is returned by the method
*/
public static <T> T invokeMethod (Class clazz, String name, Object target, Class<T> returnType, Class[] types, Object... parameters) {
try {
return target == null ?
(T) ReflectionContext.getReflectionManager().invoke(clazz, name, types, parameters) :
(T) ReflectionContext.getReflectionManager().invoke(target, name, types, parameters);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
/**
* Facility, will wrap {@link ClassNotFoundException}'s
* into {@link RuntimeException}'s.
*
* @param fqn The fully qualified class name
* @return The class loaded
*/
public static Class<?> forName (String fqn) {
try {
return ReflectionContext.getReflectionManager().getClass(fqn, false, Thread.currentThread().getContextClassLoader());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
/**
* Assumes the use of the current thread-level class loader.
*
* @see #classExists(ClassLoader, String)
*/
public static boolean classExists (String fqn) { return classExists(Thread.currentThread().getContextClassLoader(), fqn); }
/**
* @param loader The class loader to use to attempt loading the class
* @param fqn The fully qualified class name
* @return True if the class can be loaded by the current thread level CL, otherwise false
*/
public static boolean classExists (ClassLoader loader, String fqn) {
try {
ReflectionContext.getReflectionManager().getClass(fqn, false, loader);
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
/**
* This will use {@link #coerce(Class, String)} to attempt to
* convert a particular string to the type specified.
*
* @param types The types of classes expected
* @param args The string array of arguments to coerce
* @return An array of objects representing the coerced arguments
*/
public static Object[] coerce (Class[] types, String[] args) {
if (types.length == 1 && types[0] == String[].class) return args;
Object[] coerced = new Object[types.length];
for (int i=0; i<types.length; i++) {
if (i >= args.length) coerced[i] = null;
else coerced[i] = coerceString(types[i], args[i]);
}
return coerced;
}
/**
* @param type The type to coerce to
* @param arg The argument to coerce
* @return The coerced object
*
* @throws IllegalArgumentException
*/
public static <T> T coerce (Class<T> type, Object arg) {
if (arg == null) {
if (type.isPrimitive()) { return (T) getPrimitiveDefault(type); }
return null;
}
return CoersionContext.get().coerce(arg, type);
}
/**
* @param type The primitive type
* @return The default value for the primitive type
*/
public static Object getPrimitiveDefault (Class type) {
if (type == boolean.class || type == Boolean.class) return Boolean.FALSE;
else if (type == int.class || type == Integer.class) return new Integer(0);
else if (type == float.class || type == Float.class) return new Float(0);
else if (type == double.class || type == Double.class) return new Double(0);
else if (type == byte.class || type == Byte.class) return new Byte((byte)0);
else if (type == char.class || type == Character.class) return new Character((char)0);
else if (type == short.class || type == Short.class) return new Short((short)0);
else if (type == long.class || type == Long.class) return new Long(0);
else if (type == void.class || type == Void.class) return null;
throw new IllegalArgumentException("Not a primitive type: " + type);
}
/**
* @param primitiveType The primitive class
* @param type The type to validate
* @return True if the type is the boxed type of the primitive passed
*/
public static boolean isPrimitiveBoxedType (Class primitiveType, Class type) {
if (primitiveType == int.class && type == Integer.class) return true;
if (primitiveType == long.class && type == Long.class) return true;
if (primitiveType == short.class && type == Short.class) return true;
if (primitiveType == byte.class && type == Byte.class) return true;
if (primitiveType == boolean.class && type == Boolean.class) return true;
if (primitiveType == char.class && type == Character.class) return true;
if (primitiveType == float.class && type == Float.class) return true;
if (primitiveType == double.class && type == Double.class) return true;
if (primitiveType == void.class && type == Void.class) return true;
return false;
}
/**
* @param type The type in question (must be a primitive)
* @return The equivalent boxed type
*/
public static Class getBoxedType (Class type) {
if (type == int.class) return Integer.class;
if (type == long.class) return Long.class;
if (type == short.class) return Short.class;
if (type == byte.class) return Byte.class;
if (type == boolean.class) return Boolean.class;
if (type == char.class) return Character.class;
if (type == float.class) return Float.class;
if (type == double.class) return Double.class;
if (type == void.class) return Void.class;
throw new IllegalArgumentException("Not a primitive type: " + type);
}
/**
* @param type The type in question (must be a boxed primitive)
* @return The equivalent primitive type
*/
public static Class getPrimitiveType (Class type) {
if (type == Integer.class) return int.class;
if (type == Long.class) return long.class;
if (type == Short.class) return short.class;
if (type == Byte.class) return byte.class;
if (type == Boolean.class) return boolean.class;
if (type == Character.class) return char.class;
if (type == Float.class) return float.class;
if (type == Double.class) return double.class;
throw new IllegalArgumentException("Not a primitive type: " + type);
}
/**
* @param type The type in question
* @return True if the type is a primitive boxed type
*/
public static boolean isBoxedType (Class type) {
return type == Integer.class ||
type == Long.class ||
type == Short.class ||
type == Byte.class ||
type == Boolean.class ||
type == Character.class ||
type == Float.class ||
type == Double.class;
}
/**
* This will use the {@link CoersionContext} to coerce the string into the specified type.
*
* @throws IllegalArgumentException
*/
public static Object coerceString (Class type, String arg) {
return Context.get(CoersionContext.class).getProvider().coerce(arg, type);
}
/**
* Facility, wraps {@link InstantiationException} and {@link IllegalAccessException}
* in a {@link RuntimeException}. This will only use the no-arg constructor.
*
* @param type The class/type to create a new instance
* @return A new instance of the class
*/
public static Object instantiate (Class type) {
try {
return ReflectionContext.getReflectionManager().instantiate(type);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
/**
* This will assume no instance is needed. The method passed must be a static method.
*
* @see #callWithOptionalParameters(Method, Object, Object...)
*/
public static Object callStaticWithOptionalParameters (Method method, Object... parameters) {
return callWithOptionalParameters(method, null, parameters);
}
/**
* This will assume no instance is needed. The method passed must be a static method.
*
* @see #call(Method, Object, Object...)
*/
public static Object callStatic (Method method, Object... parameters) {
return call(method, null, parameters);
}
/**
* This will shorten the argument array if the amount of parameters accepted by
* the method is less that the arguments provided.
*
* @see #call(Method, Object, Object...)
*/
public static Object callWithOptionalParameters (Method method, Object instance, Object... parameters) {
Object[] pars = new Object[method.getParameterTypes().length];
System.arraycopy(parameters, 0, pars, 0, pars.length);
return call(method, instance, pars);
}
/**
* @param method The method to invoke
* @param instance The instance to invoke the method on, null if the method is static
* @param parameters The parameters to pass to the method
* @return The value returned by the method, null if null returned or void
*/
public static Object call (Method method, Object instance, Object... parameters) {
try {
return ReflectionContext.getReflectionManager().invoke(instance, method, parameters);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}