/***************************************************************************
* Copyright (C) 2012 by H-Store Project *
* Brown University *
* Massachusetts Institute of Technology *
* Yale University *
* *
* http://hstore.cs.brown.edu/ *
* *
* Permission is hereby granted, free of charge, to any person obtaining *
* a copy of this software and associated documentation files (the *
* "Software"), to deal in the Software without restriction, including *
* without limitation the rights to use, copy, modify, merge, publish, *
* distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to *
* the following conditions: *
* *
* The above copyright notice and this permission notice shall be *
* included in all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, *
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR *
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
* OTHER DEALINGS IN THE SOFTWARE. *
***************************************************************************/
package edu.brown.utils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections15.CollectionUtils;
import org.apache.commons.lang.ClassUtils;
import org.apache.log4j.Logger;
import edu.brown.logging.LoggerUtil;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
/**
* @author pavlo
*/
public abstract class ClassUtil {
private static final Logger LOG = Logger.getLogger(ClassUtil.class);
private static final LoggerBoolean debug = new LoggerBoolean();
private static final LoggerBoolean trace = new LoggerBoolean();
static {
LoggerUtil.attachObserver(LOG, debug, trace);
}
private static final Map<Class<?>, List<Class<?>>> CACHE_getSuperClasses = new HashMap<Class<?>, List<Class<?>>>();
private static final Map<Class<?>, Set<Class<?>>> CACHE_getInterfaceClasses = new HashMap<Class<?>, Set<Class<?>>>();
/**
* @param clazz
* @return
*/
public static <T> Field[] getFieldsByType(Class<?> clazz, Class<? extends T> fieldType) {
List<Field> fields = new ArrayList<Field>();
for (Field f : clazz.getDeclaredFields()) {
int modifiers = f.getModifiers();
if (Modifier.isTransient(modifiers) == false &&
Modifier.isPublic(modifiers) == true &&
Modifier.isStatic(modifiers) == false &&
ClassUtil.getSuperClasses(f.getType()).contains(fieldType)) {
fields.add(f);
}
} // FOR
return (fields.toArray(new Field[fields.size()]));
}
/**
* Returns true if asserts are enabled. This assumes that
* we're always using the default system ClassLoader
*/
public static boolean isAssertsEnabled() {
boolean ret = false;
try {
assert(false);
} catch (AssertionError ex) {
ret = true;
}
return (ret);
}
/**
* Convenience method to get the name of the method that invoked this method
* This is slow and should not be used for anything other than debugging
* @return
*/
public static String getCurrentMethodName() {
StackTraceElement stack[] = Thread.currentThread().getStackTrace();
assert(stack[2] != null);
return String.format("%s.%s", stack[2].getClassName(), stack[2].getMethodName());
}
/**
* Return the stack trace for the location that calls this method.
* @return
*/
public static String[] getStackTrace() {
String ret[] = null;
try {
throw new Exception();
} catch (Exception ex) {
StackTraceElement stack[] = ex.getStackTrace();
ret = new String[stack.length-1];
for (int i = 1; i < stack.length; i++) {
ret[i-1] = stack[i].toString();
} // FOR
}
return (ret);
}
/**
* Check if the given object is an array (primitve or native).
* http://www.java2s.com/Code/Java/Reflection/Checkifthegivenobjectisanarrayprimitveornative.htm
*
* @param obj
* Object to test.
* @return True of the object is an array.
*/
public static boolean isArray(final Object obj) {
return (obj != null ? obj.getClass().isArray() : false);
}
public static boolean[] isArray(final Object objs[]) {
boolean is_array[] = new boolean[objs.length];
for (int i = 0; i < objs.length; i++) {
is_array[i] = ClassUtil.isArray(objs[i]);
} // FOR
return (is_array);
}
/**
* Convert a Enum array to a Field array This assumes that the name of each
* Enum element corresponds to a data member in the clas
*
* @param <E>
* @param clazz
* @param members
* @return
* @throws NoSuchFieldException
*/
public static <E extends Enum<?>> Field[] getFieldsFromMembersEnum(Class<?> clazz, E members[]) throws NoSuchFieldException {
Field fields[] = new Field[members.length];
for (int i = 0; i < members.length; i++) {
fields[i] = clazz.getDeclaredField(members[i].name().toLowerCase());
} // FOR
return (fields);
}
/**
* Create a mapping from Field handles to their corresponding Annotation
* @param <A>
* @param fields
* @param annotationClass
* @return
*/
public static <A extends Annotation> Map<Field, A> getFieldAnnotations(Field fields[], Class<A> annotationClass) {
Map<Field, A> ret = new LinkedHashMap<Field, A>();
for (Field f : fields) {
A a = f.getAnnotation(annotationClass);
if (a != null)
ret.put(f, a);
}
return (ret);
}
/**
* Get the generic types for the given field
*
* @param field
* @return
*/
public static List<Class<?>> getGenericTypes(Field field) {
ArrayList<Class<?>> generic_classes = new ArrayList<Class<?>>();
Type gtype = field.getGenericType();
if (gtype instanceof ParameterizedType) {
ParameterizedType ptype = (ParameterizedType) gtype;
getGenericTypesImpl(ptype, generic_classes);
}
return (generic_classes);
}
private static void getGenericTypesImpl(ParameterizedType ptype, List<Class<?>> classes) {
// list the actual type arguments
for (Type t : ptype.getActualTypeArguments()) {
if (t instanceof Class<?>) {
// System.err.println("C: " + t);
classes.add((Class<?>) t);
} else if (t instanceof ParameterizedType) {
ParameterizedType next = (ParameterizedType) t;
// System.err.println("PT: " + next);
classes.add((Class<?>) next.getRawType());
getGenericTypesImpl(next, classes);
}
} // FOR
return;
}
/**
* Return an ordered list of all the sub-classes for a given class Useful
* when dealing with generics
*
* @param element_class
* @return
*/
public static List<Class<?>> getSuperClasses(Class<?> element_class) {
List<Class<?>> ret = ClassUtil.CACHE_getSuperClasses.get(element_class);
if (ret == null) {
ret = new ArrayList<Class<?>>();
while (element_class != null) {
ret.add(element_class);
element_class = element_class.getSuperclass();
} // WHILE
ret = Collections.unmodifiableList(ret);
ClassUtil.CACHE_getSuperClasses.put(element_class, ret);
}
return (ret);
}
/**
* Get a set of all of the interfaces that the element_class implements
*
* @param element_class
* @return
*/
@SuppressWarnings("unchecked")
public static Collection<Class<?>> getInterfaces(Class<?> element_class) {
Set<Class<?>> ret = ClassUtil.CACHE_getInterfaceClasses.get(element_class);
if (ret == null) {
// ret = new HashSet<Class<?>>();
// Queue<Class<?>> queue = new LinkedList<Class<?>>();
// queue.add(element_class);
// while (!queue.isEmpty()) {
// Class<?> current = queue.poll();
// for (Class<?> i : current.getInterfaces()) {
// ret.add(i);
// queue.add(i);
// } // FOR
// } // WHILE
ret = new HashSet<Class<?>>(ClassUtils.getAllInterfaces(element_class));
if (element_class.isInterface())
ret.add(element_class);
ret = Collections.unmodifiableSet(ret);
ClassUtil.CACHE_getInterfaceClasses.put(element_class, ret);
}
return (ret);
}
@SuppressWarnings("unchecked")
public static <T> T newInstance(String class_name, Object params[], Class<?> classes[]) {
return ((T) ClassUtil.newInstance(ClassUtil.getClass(class_name), params, classes));
}
public static <T> T newInstance(Class<T> target_class, Object params[], Class<?> classes[]) {
// Class<?> const_params[] = new Class<?>[params.length];
// for (int i = 0; i < params.length; i++) {
// const_params[i] = params[i].getClass();
// System.err.println("[" + i + "] " + params[i] + " " +
// params[i].getClass());
// } // FOR
Constructor<T> constructor = ClassUtil.getConstructor(target_class, classes);
T ret = null;
try {
ret = constructor.newInstance(params);
} catch (Exception ex) {
throw new RuntimeException("Failed to create new instance of " + target_class.getSimpleName(), ex);
}
return (ret);
}
/**
* Grab the constructor for the given target class with the provided input parameters.
* This method will first try to find an exact match for the parameters, and if that
* fails then it will be smart and try to find one with the input parameters super classes.
* @param <T>
* @param target_class
* @param params
* @return
*/
@SuppressWarnings("unchecked")
public static <T> Constructor<T> getConstructor(Class<T> target_class, Class<?>... params) {
NoSuchMethodException error = null;
try {
return (target_class.getConstructor(params));
} catch (NoSuchMethodException ex) {
// The first time we get this it can be ignored
// We'll try to be nice and find a match for them
error = ex;
}
assert (error != null);
if (debug.val) {
LOG.debug("TARGET CLASS: " + target_class);
LOG.debug("TARGET PARAMS: " + Arrays.toString(params));
}
final int num_params = (params != null ? params.length : 0);
List<Class<?>> paramSuper[] = (List<Class<?>>[]) new List[num_params];
for (int i = 0; i < num_params; i++) {
paramSuper[i] = ClassUtil.getSuperClasses(params[i]);
if (debug.val)
LOG.debug(" SUPER[" + params[i].getSimpleName() + "] => " + paramSuper[i]);
} // FOR
for (Constructor<?> c : target_class.getConstructors()) {
Class<?> cTypes[] = c.getParameterTypes();
if (debug.val) {
LOG.debug("CANDIDATE: " + c);
LOG.debug("CANDIDATE PARAMS: " + Arrays.toString(cTypes));
}
if (params.length != cTypes.length)
continue;
for (int i = 0; i < num_params; i++) {
List<Class<?>> cSuper = ClassUtil.getSuperClasses(cTypes[i]);
if (debug.val)
LOG.debug(" SUPER[" + cTypes[i].getSimpleName() + "] => " + cSuper);
if (CollectionUtils.intersection(paramSuper[i], cSuper).isEmpty() == false) {
return ((Constructor<T>) c);
}
} // FOR (param)
} // FOR (constructors)
throw new RuntimeException("Failed to retrieve constructor for " + target_class.getSimpleName(), error);
}
/**
* @param class_name
* @return
*/
public static Class<?> getClass(String class_name) {
Class<?> target_class = null;
try {
ClassLoader loader = ClassLoader.getSystemClassLoader();
target_class = (Class<?>) loader.loadClass(class_name);
} catch (Exception ex) {
throw new RuntimeException("Failed to retrieve class for " + class_name, ex);
}
return (target_class);
}
}