package org.cumulus4j.testutil;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
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.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Daniel Mazurek
* @author Marc Klinger - marc at nightlabs dot de (API documentation fixes)
* @author Alexander Bieber
*/
public final class ReflectUtil
{
private static final Logger logger = LoggerFactory.getLogger(ReflectUtil.class);
private ReflectUtil() {}
public static T newInstanceNoExceptions(Class clazz)
{
try
{
return newInstance(clazz);
}
catch (Exception e)
{
logger.debug(
"Couldn't create new instance via default constructor of type '{}'! Supressing exception.",
clazz.getName(), e
);
return null;
}
}
public static T newInstanceRuntimeException(Class clazz)
{
try
{
return newInstance(clazz);
}
catch (Exception e)
{
logger.trace(
"Couldn't create new instance via default constructor of type '{}'! Throwing runtime exception.",
clazz.getName(), e
);
throw new IllegalStateException("Couldn't create new instance via default constructor of type '" +
clazz.getName()+"'!", e);
}
}
public static T newInstance(Class clazz)
throws SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException,
IllegalAccessException, InvocationTargetException
{
Constructor extends T> defaultConstructor = null;
@SuppressWarnings("unchecked")
Constructor extends T>[] constructors = (Constructor extends T>[]) clazz.getDeclaredConstructors();
for (Constructor extends T> constructor : constructors)
{
if (constructor.getParameterTypes().length == 0)
{
defaultConstructor = constructor;
break;
}
}
if (defaultConstructor == null)
throw new IllegalArgumentException("The given Class doesn not define a default constructor! class=" +
clazz.getName());
if (! defaultConstructor.isAccessible())
defaultConstructor.setAccessible(true);
return defaultConstructor.newInstance();
}
/**
* Does the same as {@link org.nightlabs.util.reflect.ReflectUtil#clone(Object original, Class stopClass)}
* but has Object.class as stopClass
*
* @param original The instance to clone
* @return a cloned instance of the Param original
* @see #clone(Object original, Class stopClass)
*/
public static Object clone(final Object original) {
return clone(original, Object.class);
}
public static Object clone(final Object original, final String[] ignoredMembers) {
return clone(original, Object.class, ignoredMembers);
}
public static Object clone(final Object original, final Class extends CloneDelegate>[] cloneDelegates) {
return clone(original, Object.class, null, cloneDelegates);
}
public static Object clone(final Object original, final String[] ignoredMembers, final Class extends CloneDelegate>[] cloneDelegtes) {
return clone(original, Object.class, ignoredMembers, cloneDelegtes);
}
public static Map, Class extends CloneDelegate>> initCloneDelegates(final Class extends CloneDelegate>[] cloneDelegates)
throws CloneException
{
final Map, Class extends CloneDelegate>> class2CloneDelegate = new HashMap, Class extends CloneDelegate>>(cloneDelegates.length);
for (final Class extends CloneDelegate> cdClass : cloneDelegates) {
if (CloneDelegate.class.isAssignableFrom(cdClass)) {
try {
final Method getCloneClass = cdClass.getMethod("getCloneClass", (Class>) null);
final Class> cloneClass = (Class>) getCloneClass.invoke((Object) null, (Object) null);
class2CloneDelegate.put(cloneClass, cdClass);
}
catch (final SecurityException e) {
throw new CloneException(e);
}
catch (final NoSuchMethodException e) {
throw new CloneException(e);
}
catch (final IllegalArgumentException e) {
throw new CloneException(e);
}
catch (final IllegalAccessException e) {
throw new CloneException(e);
}
catch (final InvocationTargetException e) {
throw new CloneException(e);
}
}
}
return class2CloneDelegate;
}
public static Object clone(final Object original,
final Class extends Object> stopClass,
final String[] ignoredMembers,
final Class extends CloneDelegate>[] cloneDelegates)
{
try
{
final Class extends Object> orgClass = original.getClass();
final List fields = collectAllFields(orgClass, stopClass, true, ignoredMembers);
boolean cloneDelegate = false;
Map, Class extends CloneDelegate>> class2CloneDelegate = new HashMap, Class extends CloneDelegate>>(0);
if (cloneDelegates != null) {
class2CloneDelegate = new HashMap, Class extends CloneDelegate>>(initCloneDelegates(cloneDelegates));
cloneDelegate = true;
}
try
{
// Iterate through all collected fields (except static and final)
// and clone the values for the created instance
final Object dcInstance = orgClass.newInstance();
for (final Iterator it = fields.iterator(); it.hasNext(); )
{
final Field field = it.next();
final Class> fieldType = field.getType();
// if a CloneDelegate is registered
if (cloneDelegate && class2CloneDelegate.containsKey(fieldType))
{
final Class extends CloneDelegate> cdClass = class2CloneDelegate.get(fieldType);
final Method clone = cdClass.getMethod("clone", new Class[] {fieldType});
final Object clonedField = clone.invoke(cdClass.newInstance(), new Object[] {field.get(dcInstance)});
field.set(dcInstance, clonedField);
}
// check if primitive
else if (fieldType.isPrimitive())
{
field.set(dcInstance, field.get(original));
}
// check if Array
else if (fieldType.isArray())
{
final Object array = field.get(original);
if (array == null)
continue;
final int length = Array.getLength(array);
final Class> arrayType = array.getClass();
final Object clonedArray = Array.newInstance(arrayType.getComponentType(), length);
for (int i=0; i) null).invoke(org, (Object) null)
);
}
else
throw new IllegalStateException("Not all members are primitive or Cloneable! Class=\""+original.getClass().getName()+"\" Member=\""+field.getType().getName()+" "+field.getName()+"\"");
}
return dcInstance;
}
catch (final NoSuchMethodException e) {
throw new CloneException(e);
}
catch (final IllegalAccessException e) {
throw new CloneException(e);
}
catch (final InstantiationException e) {
throw new CloneException(e);
}
catch (final InvocationTargetException e) {
throw new CloneException(e);
}
}
catch (final CloneException x) {
throw new RuntimeException(x);
}
}
private static String CLONE = "clone";
public static Object clone(final Object original,
final Class extends Object> stopClass,
final String[] ignoredMembers)
{
return clone(original, stopClass, ignoredMembers, null);
// try
// {
// Class orgClass = original.getClass();
// List fields = collectAllFields(orgClass, stopClass, true, ignoredMembers);
//
// // Iterate through all collected fields (except static and final)
// // and clone the values for the created instance
// Object dcInstance = orgClass.newInstance();
// for (Iterator it = fields.iterator(); it.hasNext(); )
// {
// Field field = (Field) it.next();
// // check if primitive
// if (field.getType().isPrimitive())
// {
// field.set(dcInstance, field.get(original));
// }
// // check if Array
// else if (field.getType().isArray())
// {
// Object array = field.get(original);
// int length = Array.getLength(array);
// Class arrayType = array.getClass();
// Object clonedArray = Array.newInstance(arrayType.getComponentType(), length);
// for (int i=0; i
* The basic Mechanism works as following:
* This methods iterates through all superClasses and collects,
* all declared Fields (private, protected, public)
* until it reaches the stopClass. Then it creates a new Instance
* of the original class (a standard Constructor is needed) and
* clones the values of all fields (except FINAL and STATIC) to
* the new instance. If one field is not a primitive Type (int, boolean, ...)
* it has to implement the Cloneable Interface, otherwise a
* IllegalStateException is thrown.
*
* @throws IllegalStateException if a field is not primitive and
* does not implement Cloneable
* @throws RuntimeException if something else went wrong
* (e.g. InstantationException, when no Standard Constructor exists, ...)
* @param original The Object to be cloned
* @param stopClass The Class where the cloning should stop
* @return An cloned Instance of the original class (original.getClass())
* which fields have the same values as the original instance *
* @see java.lang.Class
* @see java.lang.Cloneable
*/
public static Object clone(final Object original, final Class extends Object> stopClass)
{
return clone(original, stopClass, null);
// try {
// Class orgClass = original.getClass();
// List fields = collectAllFields(orgClass, stopClass, true, null);
//
// // Iterate through all collected fields (except static and final)
// // and clone the values for the created instance
// Object dcInstance = orgClass.newInstance();
// for (Iterator it = fields.iterator(); it.hasNext(); ) {
// Field field = (Field) it.next();
// if (field.getType().isPrimitive()) {
// field.set(dcInstance, field.get(original));
// }
// else if (field.getType().isArray())
// {
// Object array = field.get(original);
// int length = Array.getLength(array);
// Class arrayType = array.getClass();
// Object clonedArray = Array.newInstance(arrayType.getComponentType(), length);
// for (int i=0; i collectAllFields(final Class> originalClass,
final Class> stopClass,
final boolean ignoreStaticAndFinalFields,
final String[] ignoredMembers)
{
final List fields = new ArrayList();
Class> superClass = originalClass;
// iterate through all superclasses and
// collect all declared fields (public, private, protected)
// until the stopClass is reached
while (superClass != stopClass)
{
final Field[] f = superClass.getDeclaredFields();
if (ignoreStaticAndFinalFields) {
for (final Field element : f) {
if (ignoreField(element))
continue;
else if (ignoreMembers(element, ignoredMembers))
continue;
else {
final Field field = element;
// makes also private Fields accessible
field.setAccessible(true);
fields.add(field);
}
}
}
else {
for (final Field field : f) {
if (ignoreMembers(field, ignoredMembers))
continue;
// makes also private Fields accessible
field.setAccessible(true);
fields.add(field);
}
}
superClass = superClass.getSuperclass();
}
return fields;
}
/**
* Does the same as {@link #collectAllFields(Class, Class, boolean, String[])}
* but has java.lang.Object.class as stopClass and doesn't ignore any fields
*
* @param originalClass The Class for which all Fields should be collected
* @param ignoreStaticAndFinalFields determines if static and final fields should be ignored
* @return a List of all declaredFields for the originalClass
* @see #collectAllFields(Class, Class, boolean, String[])
*/
public static List collectAllFields(final Class> originalClass, final boolean ignoreStaticAndFinalFields) {
return collectAllFields(originalClass, Object.class, ignoreStaticAndFinalFields, null);
}
/**
* ignores all members/fields that are static or final.
* This method is used to check that.
*
* @param field Ignore this field?
* @return true if the field should be ignored, false if not.
*/
private static boolean ignoreField(final Field field)
{
final int modifiers = field.getModifiers();
if ((modifiers & Modifier.STATIC) != 0)
return true;
if ((modifiers & Modifier.FINAL) != 0)
return true;
return false;
}
private static boolean ignoreMembers(final Field field, final String[] names)
{
if (names == null)
return false;
for (final String name : names) {
if (field.getName().equals(name))
return true;
}
return false;
}
/**
* a generic equals-Method for all Classes based on java.lang.reflect
*
* @param original The original Instance which should be compared
* @param target The target Instance which will be compared with the original
* @return true if the values for all Fields of both instances are equivalent,
* otherwise returns false
* @see java.lang.reflect.Field
* @see #collectAllFields(Class originalClass, boolean ignoreFields)
*/
public static boolean equals(final Object original, final Object target)
{
if (original == target)
return true;
final Class> originalClass = original.getClass();
final Class> targetClass = target.getClass();
if (!originalClass.isAssignableFrom(targetClass))
return false;
final List originalFields = collectAllFields(originalClass, true);
final List targetFields = collectAllFields(targetClass, true);
if (originalFields.size() != targetFields.size())
return false;
for (int i=0; i oClass = o.getClass();
final StringBuffer sb = new StringBuffer();
// add Class Name
sb.append(oClass.getName());
sb.append("\n");
if (withSuperClasses)
{
final List fields = collectAllFields(oClass, false);
for (final Field field : fields) {
try {
sb.append(field.getName());
sb.append(" = ");
sb.append(String.valueOf(field.get(o)));
sb.append("\n");
}
catch (final IllegalArgumentException e) {
e.printStackTrace();
}
catch (final IllegalAccessException e) {
e.printStackTrace();
}
}
}
else {
final Field[] fields = oClass.getDeclaredFields();
for (final Field field : fields) {
field.setAccessible(true);
try {
sb.append(field.getName());
sb.append(" = ");
sb.append(String.valueOf(field.get(o)));
sb.append("\n");
}
catch (final IllegalArgumentException e) {
e.printStackTrace();
}
catch (final IllegalAccessException e) {
e.printStackTrace();
}
}
}
return sb.toString();
}
public static interface IObjectFoundHandler {
public void objectFound(String path, Object object);
}
private static class ObjectFoundResult {
private boolean found = false;
}
/**
* Returns whether an object of the given class could be found in the graph of the given object.
* @param checkObject The object to scan
* @param clazz The class to filter
* @param filter Filter for the scanning (Whether the found classes should or should not be assignable to the given clazz parameter)
* @return Whether an object of the given class could be found in the graph of the given object.
*/
public static boolean findContainedObjectsByClass(final Object checkObject, final Class> clazz, final boolean filter) {
final ObjectFoundResult result = new ObjectFoundResult();
findContainedObjectsByClass(checkObject, clazz, filter, true, new IObjectFoundHandler() {
@Override
public void objectFound(final String path, final Object object) {
result.found = true;
}
});
return result.found;
}
/**
* Finds objects of the given class in the object graph of the given object.
*
* @param checkObject The object to scan
* @param clazz The class to filter
* @param filter Filter for the scanning (Whether the found classes should or should not be assignable to the given clazz parameter)
* @param returnOnFirstInBranch Whether to return after the first match in a branch
* @param objectFoundHandler The handler to invoke when an object matches
*/
public static void findContainedObjectsByClass(
final Object checkObject,
final Class> clazz,
final boolean filter, final boolean returnOnFirstInBranch,
final IObjectFoundHandler objectFoundHandler
)
{
findContainedObjectsByClass(new HashSet
*
* @param clazz The class to find the superclass-parameterization for.
* @return
* The list of types used to parameterize the first parameterized super-class of the given class,
* or null if no parameterized superclass can be found for the given class.
*/
public static List> getSuperClassTypeArguments(final Class> clazz) {
Class> checkClazz = clazz;
while (checkClazz != Object.class && checkClazz != null) {
final Type genSuperclass = checkClazz.getGenericSuperclass();
if (genSuperclass instanceof ParameterizedType) {
final ParameterizedType parameterizedType = (ParameterizedType) genSuperclass;
final Type[] typeArguments = parameterizedType.getActualTypeArguments();
final List> result = new ArrayList>(typeArguments.length);
for (int j = 0; j < typeArguments.length; j++) {
if (typeArguments[j] instanceof Class>) {
result.add((Class>) typeArguments[j]);
}
}
return result;
}
checkClazz = checkClazz.getSuperclass();
}
return null;
}
/**
*
* Collects the class-hierarchy (not including interfaces) for the given start-class until the
* given base-class is reached.
*
* @param startClass The class to start collecting the hierarchy for.
* @param baseClass The class to stop collecting.
* @param includeBaseClass Whether to include the base-class in the result.
* @return A list with the class-hierarchy of the given start-class.
*/
public static List> collectClassHierarchy(final Class> startClass, final Class> baseClass, final boolean includeBaseClass) {
Class> stopClass = baseClass;
if (stopClass == null)
stopClass = Object.class;
final List> result = new ArrayList>();
Class> checkClass = startClass;
while (checkClass != null && checkClass != stopClass) {
result.add(checkClass);
checkClass = checkClass.getSuperclass();
}
if (stopClass != Object.class && checkClass == Object.class)
throw new IllegalStateException("The given start class " + startClass + " was not a subclass of " + baseClass.getName()); //$NON-NLS-1$ //$NON-NLS-2$
if (includeBaseClass) {
result.add(stopClass);
}
Collections.reverse(result);
return result;
}
/**
* Collects the type-hierarchy (including interfaces) for the given class. This method ensures
* that the type-hierarchy including interfaces is always in the same order.
*
* @param clazz The class to get the type hierarchy for.
* @return A list containing the type hierarchy of the given class (containing the given class
* and all its direct and indirect super-classes/interfaces)
*/
public static List> collectTypeHierarchy(final Class> clazz) {
final List> classes = new ArrayList>();
computeClassOrder(clazz, classes);
return classes;
}
/**
* Internally used by {@link #collectClassHierarchy(Class, Class, boolean)}.
*/
private static void computeClassOrder(final Class> clazz, final List> classes) {
Class> _clazz = clazz;
final Set> seen = new HashSet>();
while (_clazz != null) {
classes.add(_clazz);
computeInterfaceOrder(_clazz.getInterfaces(), classes, seen);
_clazz = _clazz.isInterface() ? Object.class : _clazz.getSuperclass();
}
}
/**
* Internally used by {@link #collectClassHierarchy(Class, Class, boolean)}.
*/
private static void computeInterfaceOrder(final Class>[] interfaces, final Collection> classes, final Set> seen) {
final List> newInterfaces = new ArrayList>(interfaces.length);
for (int i = 0; i < interfaces.length; i++) {
final Class> iface = interfaces[i];
if (seen.add(iface)) {
// note we cannot recurse here without changing the resulting interface order
classes.add(iface);
newInterfaces.add(iface);
}
}
for (final Class> newInterface : newInterfaces) {
computeInterfaceOrder((newInterface).getInterfaces(), classes, seen);
}
}
}