/*******************************************************************************
* Breakout Cave Survey Visualizer
*
* Copyright (C) 2014 James Edwards
*
* jedwards8 at fastmail dot fm
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*******************************************************************************/
package org.andork.reflect;
import static org.andork.util.ArrayUtils.copyOf;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.GenericDeclaration;
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.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.andork.util.ArrayUtils;
public class ReflectionUtils {
public static final class GenericArrayTypeImpl implements GenericArrayType {
private final Type genericComponentType;
protected GenericArrayTypeImpl(Type genericComponentType) {
super();
this.genericComponentType = genericComponentType;
}
@Override
public boolean equals(Object o) {
if (o instanceof GenericArrayType) {
return genericComponentType == ((GenericArrayType) o).getGenericComponentType();
}
return false;
}
@Override
public Type getGenericComponentType() {
return genericComponentType;
}
@Override
public int hashCode() {
return genericComponentType.hashCode();
}
@Override
public String toString() {
return format(this);
}
}
public static final class ParameterizedTypeImpl implements ParameterizedType {
private final Type rawType;
private final Type ownerType;
private final Type[] actualTypeArguments;
public ParameterizedTypeImpl(Type rawType, Type ownerType, Type... actualTypeArguments) {
super();
this.rawType = rawType;
this.ownerType = ownerType;
this.actualTypeArguments = copyOf(actualTypeArguments);
}
@Override
public Type[] getActualTypeArguments() {
return copyOf(actualTypeArguments);
}
@Override
public Type getOwnerType() {
return ownerType;
}
@Override
public Type getRawType() {
return rawType;
}
@Override
public String toString() {
return format(this);
}
}
public static final class TypeVariableImpl<D extends GenericDeclaration> implements TypeVariable<D> {
private final D genericDeclaration;
private final Type[] bounds;
private final String name;
protected TypeVariableImpl(D genericDeclaration, Type[] bounds, String name) {
this.genericDeclaration = genericDeclaration;
this.bounds = copyOf(bounds);
this.name = name;
}
@Override
public AnnotatedType[] getAnnotatedBounds() {
throw new UnsupportedOperationException();
}
@Override
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
throw new UnsupportedOperationException();
}
@Override
public Annotation[] getAnnotations() {
throw new UnsupportedOperationException();
}
@Override
public Type[] getBounds() {
return copyOf(bounds);
}
@Override
public Annotation[] getDeclaredAnnotations() {
throw new UnsupportedOperationException();
}
@Override
public D getGenericDeclaration() {
return genericDeclaration;
}
@Override
public String getName() {
return name;
}
@Override
public String toString() {
return format(this);
}
}
public static final class WildcardTypeImpl implements WildcardType {
private final Type[] upperBounds;
private final Type[] lowerBounds;
protected WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
this.upperBounds = copyOf(upperBounds);
this.lowerBounds = copyOf(lowerBounds);
}
@Override
public Type[] getLowerBounds() {
return copyOf(lowerBounds);
}
@Override
public Type[] getUpperBounds() {
return copyOf(upperBounds);
}
@Override
public String toString() {
return format(this);
}
}
private static TypeFormatter staticTypeFormatter = new DefaultTypeFormatter();
private static void addStaticFields(Class<?> clazz, boolean includeSuperclasses, Collection<Field> out) {
if (includeSuperclasses && clazz != Object.class) {
addStaticFields(clazz.getSuperclass(), true, out);
}
for (Field field : clazz.getDeclaredFields()) {
if (Modifier.isStatic(field.getModifiers())) {
out.add(field);
}
}
}
public static Type extendsWildcard(Type basetype) {
if (basetype instanceof WildcardType) {
throw new IllegalArgumentException("can't extend a wildcard type");
}
return new WildcardTypeImpl(new Type[] { basetype }, new Type[0]);
}
public static Type findBestUpperBound(Collection<Type> types, Type toBound) {
Type best = null;
for (Type type : types) {
if (isAssignableFrom(type, toBound)) {
if (best == null || isAssignableFrom(best, type)) {
best = type;
}
}
}
return best;
}
@SuppressWarnings("unchecked")
public static <T> List<T> findConstants(Class<T> type, Class<?>... declaringClasses) {
List<T> result = new ArrayList<T>();
for (Class<?> declClass : declaringClasses) {
for (Field field : declClass.getDeclaredFields()) {
if (type.isAssignableFrom(field.getType()) &&
Modifier.isStatic(field.getModifiers()) &&
Modifier.isFinal(field.getModifiers())) {
try {
field.setAccessible(true);
result.add((T) field.get(null));
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
return result;
}
/**
* Creates a string representation of the given type that would be valid in
* java code.
*/
public static String format(Type t) {
return staticTypeFormatter.format(t);
}
public static Type getAddAllableType(Type collectionType) {
if (!Collection.class.isAssignableFrom(getRawType(collectionType))) {
throw new IllegalArgumentException(format(collectionType) + " is not a Collection");
}
Type parameter = getTypeParameter(collectionType, 0);
if (parameter == null) {
return parameterize(Collection.class, new WildcardTypeImpl(new Type[0], new Type[0]));
}
Type elemType = parameter;
if (!Modifier.isFinal(getRawType(elemType).getModifiers())) {
elemType = extendsWildcard(elemType);
}
return parameterize(Collection.class, elemType);
}
public static List<Method> getAllMethods(Class<?> clazz) {
List<Method> result = new ArrayList<Method>();
while (clazz != null) {
result.addAll(Arrays.asList(clazz.getDeclaredMethods()));
clazz = clazz.getSuperclass();
}
return result;
}
public static Class<?> getBox(Class<?> primitiveType) {
if (primitiveType.equals(boolean.class)) {
return Boolean.class;
}
if (primitiveType.equals(byte.class)) {
return Byte.class;
}
if (primitiveType.equals(short.class)) {
return Short.class;
}
if (primitiveType.equals(char.class)) {
return Character.class;
}
if (primitiveType.equals(int.class)) {
return Integer.class;
}
if (primitiveType.equals(float.class)) {
return Float.class;
}
if (primitiveType.equals(long.class)) {
return Long.class;
}
if (primitiveType.equals(double.class)) {
return Double.class;
}
throw new IllegalArgumentException("Invalid type: " + primitiveType);
}
public static Type getComponentType(Type type) {
if (type instanceof GenericArrayType) {
return ((GenericArrayType) type).getGenericComponentType();
}
if (type instanceof Class<?>) {
return ((Class<?>) type).getComponentType();
}
return null;
}
/**
* Searches all public, protected, package access, and private fields of a
* class and its superclasses for a field with the given name.
* {@link Class#getField(String)} only searches public members declared in
* the class.
*/
public static Field getField(Class<?> clazz, String name) {
while (clazz != null) {
for (Field field : clazz.getDeclaredFields()) {
if (field.getName().equals(name)) {
return field;
}
}
clazz = clazz.getSuperclass();
}
return null;
}
public static Object getFieldValue(Object obj, String fieldName)
throws IllegalArgumentException, IllegalAccessException {
return getField(obj.getClass(), fieldName).get(obj);
}
public static Type getGenericSupertype(Type t) {
if (t instanceof Class<?>) {
Class<?> c = (Class<?>) t;
return c.getGenericSuperclass();
}
if (t instanceof WildcardType) {
WildcardType wt = (WildcardType) t;
Type[] lowerBounds = wt.getLowerBounds();
Type[] upperBounds = wt.getUpperBounds();
if (lowerBounds == null && upperBounds != null && upperBounds.length == 1) {
return new WildcardTypeImpl(new Type[] { getGenericSupertype(upperBounds[0]) },
new Type[0]);
}
return Object.class;
}
if (t instanceof GenericArrayType) {
return new GenericArrayTypeImpl(getGenericSupertype(((GenericArrayType) t).getGenericComponentType()));
}
if (t instanceof TypeVariable<?>) {
TypeVariable<?> tv = (TypeVariable<?>) t;
Type[] bounds = tv.getBounds();
if (bounds != null && bounds.length == 1) {
return new TypeVariableImpl<GenericDeclaration>(tv.getGenericDeclaration(),
new Type[] { getGenericSupertype(bounds[0]) }, tv.getName());
}
}
if (t instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) t;
Class<?> c = (Class<?>) pt.getRawType();
return replaceTypeVariables(c.getGenericSuperclass(), c.getTypeParameters(), pt.getActualTypeArguments());
}
return t;
}
public static List<Field> getInstanceFieldList(Class<?> clazz) {
List<Field> result = new ArrayList<Field>();
while (clazz != null) {
for (Field field : clazz.getDeclaredFields()) {
if (!Modifier.isStatic(field.getModifiers())) {
result.add(field);
}
}
clazz = clazz.getSuperclass();
}
return result;
}
public static Map<String, Field> getInstanceFields(Class<?> clazz) {
Map<String, Field> result = new HashMap<String, Field>();
while (clazz != null) {
for (Field field : clazz.getDeclaredFields()) {
if (!Modifier.isStatic(field.getModifiers()) && !result.containsKey(field.getName())) {
result.put(field.getName(), field);
}
}
clazz = clazz.getSuperclass();
}
return result;
}
public static List<Method> getInstanceMethodList(Class<?> clazz) {
List<Method> result = new ArrayList<Method>();
while (clazz != null) {
for (Method method : clazz.getDeclaredMethods()) {
if (!Modifier.isStatic(method.getModifiers())) {
result.add(method);
}
}
clazz = clazz.getSuperclass();
}
return result;
}
/**
* Searches all public, protected, package access, and private methods of a
* class and its superclasses for a method with the given name and parameter
* types. {@link Class#getMethod(String, Class...)} only searches public
* members declared in the class.
*/
public static Method getMethod(Class<?> clazz, String name, Class<?>... parameterTypes) {
while (clazz != null) {
for (Method method : clazz.getDeclaredMethods()) {
if (method.getName().equals(name) && Arrays.deepEquals(method.getParameterTypes(), parameterTypes)) {
return method;
}
}
clazz = clazz.getSuperclass();
}
return null;
}
public static Class<?> getOwnerType(Type t) {
if (t instanceof Class<?>) {
return ((Class<?>) t).getEnclosingClass();
}
if (t instanceof ParameterizedType) {
return (Class<?>) ((ParameterizedType) t).getOwnerType();
}
return null;
}
public static Class<?> getRawType(Type t) {
if (t instanceof Class<?>) {
return (Class<?>) t;
}
if (t instanceof WildcardType) {
WildcardType wt = (WildcardType) t;
Type[] upperBounds = wt.getUpperBounds();
if (upperBounds != null && upperBounds.length == 1) {
return getRawType(upperBounds[0]);
}
}
if (t instanceof GenericArrayType) {
try {
return Class
.forName("[L" + getRawType(((GenericArrayType) t).getGenericComponentType()).getName() + ";");
} catch (ClassNotFoundException e) {
return new Object[0].getClass();
}
}
if (t instanceof ParameterizedType) {
return (Class<?>) ((ParameterizedType) t).getRawType();
}
if (t instanceof TypeVariable<?>) {
TypeVariable<?> tv = (TypeVariable<?>) t;
Type[] bounds = tv.getBounds();
if (bounds != null && bounds.length == 1) {
return getRawType(bounds[0]);
}
}
return Object.class;
}
/**
* Gets all static fields declared in the given class.
*
* @param clazz
* the class to get static fields of.
* @param includeSuperclasses
* whether to include static fields declared in superclasses.
* They will come before the {@code clazz}'s fields in the result
* list.
* @return a {@link List} of {@link Field}s.
*/
public static List<Field> getStaticFieldList(Class<?> clazz, boolean includeSuperclasses) {
List<Field> result = new ArrayList<Field>();
addStaticFields(clazz, includeSuperclasses, result);
return result;
}
public static Type[] getSupertypeParameters(Class<?> supertype, Type resolvedSubtype) {
Type lastType = resolvedSubtype;
while (resolvedSubtype != null && isAssignableFrom(supertype, resolvedSubtype)) {
lastType = resolvedSubtype;
resolvedSubtype = getGenericSupertype(resolvedSubtype);
}
Class<?> rawType = getRawType(lastType);
TypeVariable<?>[] variables = rawType.getTypeParameters();
Type[] replacements = getTypeParameters(lastType);
if (supertype.isInterface()) {
Type[] interfaces = rawType.getGenericInterfaces();
int ifaceIndex;
for (ifaceIndex = interfaces.length - 1; ifaceIndex >= 0; ifaceIndex--) {
if (getRawType(interfaces[ifaceIndex]) == supertype) {
break;
}
}
if (ifaceIndex >= 0) {
Type[] ifaceParams = getTypeParameters(interfaces[ifaceIndex]);
for (int i = 0; i < ifaceParams.length; i++) {
ifaceParams[i] = replaceTypeVariables(ifaceParams[i], variables, replacements);
}
return ifaceParams;
}
} else if (rawType == supertype) {
return replacements;
}
return lastType == null ? new Type[0] : getTypeParameters(lastType);
}
public static Type getTypeParameter(Type type, int index) {
if (type instanceof ParameterizedType) {
try {
return ((ParameterizedType) type).getActualTypeArguments()[index];
} catch (Exception ex) {
System.out.println("TEST");
}
}
if (type instanceof WildcardType) {
WildcardType wt = (WildcardType) type;
Type[] upperBounds = wt.getUpperBounds();
if (upperBounds.length == 1) {
return getTypeParameter(upperBounds[0], index);
}
}
if (type instanceof Class<?>) {
try {
return ((Class<?>) type).getTypeParameters()[index];
} catch (Exception e) {
}
}
return null;
}
public static Type getTypeParameterOrObject(Type type, int index) {
Type result = getTypeParameter(type, index);
return result == null ? Object.class : result;
}
public static Type[] getTypeParameters(Type type) {
if (type instanceof ParameterizedType) {
return ((ParameterizedType) type).getActualTypeArguments();
}
if (type instanceof Class<?>) {
return ((Class<?>) type).getTypeParameters();
}
return new Type[0];
}
public static boolean isAssignableFrom(Class<?> c, Type t) {
if (t instanceof WildcardType) {
for (Type bound : ((WildcardType) t).getUpperBounds()) {
if (!isAssignableFrom(c, bound)) {
return false;
}
}
return true;
} else if (t instanceof GenericArrayType) {
return isAssignableFrom(c, getRawType(t));
} else if (t instanceof ParameterizedType) {
return isAssignableFrom(c, getRawType(t));
} else if (t instanceof TypeVariable<?>) {
for (Type bound : ((TypeVariable<?>) t).getBounds()) {
if (isAssignableFrom(c, bound)) {
return true;
}
}
} else if (t instanceof Class<?>) {
return c.isAssignableFrom((Class<?>) t);
}
return false;
}
public static boolean isAssignableFrom(Type a, Type b) {
if (a instanceof WildcardType) {
WildcardType wt = (WildcardType) a;
Type[] lower = wt.getLowerBounds();
if (lower != null && lower.length > 0) {
return lower.length == 1;
}
Type[] upper = wt.getUpperBounds();
if (upper != null && upper.length == 1) {
return isAssignableFrom(upper[0], b);
}
} else if (a instanceof GenericArrayType) {
return getComponentType(b) != null && isAssignableFrom(getComponentType(a), getComponentType(b));
} else if (a instanceof ParameterizedType) {
if (b instanceof WildcardType) {
WildcardType wt = (WildcardType) b;
Type[] lower = wt.getLowerBounds();
if (lower != null && lower.length > 0) {
return false;
}
Type[] upper = wt.getUpperBounds();
if (upper != null && upper.length == 0) {
return isAssignableFrom(a, upper[0]);
}
} else if (b instanceof GenericArrayType) {
return false;
} else if (b instanceof ParameterizedType) {
if (!isAssignableFrom(getRawType(a), b)) {
return false;
}
Type[] aParams = ((ParameterizedType) a).getActualTypeArguments();
b = parameterize(getRawType(b), getSupertypeParameters(getRawType(a), b));
Type[] bParams = ((ParameterizedType) b).getActualTypeArguments();
if (aParams.length != bParams.length) {
return false;
}
for (int i = 0; i < aParams.length; i++) {
if (!isAssignableFrom(aParams[i], bParams[i])) {
return false;
}
}
return true;
} else if (b instanceof TypeVariable<?>) {
TypeVariable<?> tv = (TypeVariable<?>) b;
Type[] bounds = tv.getBounds();
return bounds.length == 1 && isAssignableFrom(a, bounds[0]);
} else if (b instanceof Class<?>) {
return false;
}
} else if (a instanceof TypeVariable<?>) {
TypeVariable<?> tv = (TypeVariable<?>) a;
Type[] bounds = tv.getBounds();
return bounds.length == 1 && isAssignableFrom(bounds[0], b);
} else if (a instanceof Class<?>) {
return ((Class<?>) a).isAssignableFrom(getRawType(b));
}
return false;
}
public static boolean isFieldSettableFrom(Class<?> fieldType, Class<?> cls) {
if (fieldType.isPrimitive()) {
return getBox(fieldType).isAssignableFrom(cls);
}
return fieldType.isAssignableFrom(cls);
}
public static void main(String[] args) {
List<Type> types = new ArrayList<Type>();
types.add(Map.class);
types.add(HashMap.class);
types.add(new ParameterizedTypeImpl(Map.class, null, Number.class, Number.class));
types.add(new ParameterizedTypeImpl(Map.class, null, Double.class, Number.class));
types.add(new ParameterizedTypeImpl(Map.class, null, Number.class, Number.class));
types.add(new ParameterizedTypeImpl(HashMap.class, null, Number.class, Double.class));
System.out.println(staticTypeFormatter.format(
findBestUpperBound(types, new ParameterizedTypeImpl(Map.class, null, Integer.class, Double.class))));
System.out.println(staticTypeFormatter.format(
findBestUpperBound(types, new ParameterizedTypeImpl(Map.class, null, Double.class, Double.class))));
System.out.println(staticTypeFormatter.format(findBestUpperBound(types,
new ParameterizedTypeImpl(LinkedHashMap.class, null, Double.class, Double.class))));
System.out.println(staticTypeFormatter.format(findBestUpperBound(types, LinkedHashMap.class)));
}
public static ParameterizedType parameterize(Class<?> clazz, final Type... parameters) {
return new ParameterizedTypeImpl(clazz, clazz.getEnclosingClass(), parameters);
}
/**
* Replaces any occurrences of the given {@link TypeVariable}s inside the
* given type (or the type itself, if it is one of the given
* {@code TypeVariable}s) with the given replacement types.
*
* @param t
* the type to replace {@code TypeVariable}s within.
* @param variables
* the {@code TypeVariable}s to replace.
* @param replacements
* the replacement {@code Type}s corresponding to
* {@code variables}.
*/
public static Type replaceTypeVariables(Type t, TypeVariable<?>[] variables, Type[] replacements) {
if (t instanceof WildcardType) {
WildcardType wt = (WildcardType) t;
Type[] lowerBounds = wt.getLowerBounds();
final Type[] replacedLowerBounds = new Type[lowerBounds.length];
for (int i = 0; i < lowerBounds.length; i++) {
replacedLowerBounds[i] = replaceTypeVariables(lowerBounds[i], variables, replacements);
}
Type[] upperBounds = wt.getUpperBounds();
final Type[] replacedUpperBounds = new Type[upperBounds.length];
for (int i = 0; i < upperBounds.length; i++) {
replacedUpperBounds[i] = replaceTypeVariables(upperBounds[i], variables, replacements);
}
return new WildcardTypeImpl(replacedUpperBounds, replacedLowerBounds);
}
if (t instanceof TypeVariable<?>) {
int index = ArrayUtils.indexOf(variables, t);
return index < 0 ? t : replacements[index];
}
if (t instanceof GenericArrayType) {
GenericArrayType gat = (GenericArrayType) t;
return new GenericArrayTypeImpl(replaceTypeVariables(
gat.getGenericComponentType(), variables, replacements));
}
if (t instanceof ParameterizedType) {
final ParameterizedType pt = (ParameterizedType) t;
Type[] arguments = pt.getActualTypeArguments();
final Type[] replacedArguments = new Type[arguments.length];
for (int i = 0; i < arguments.length; i++) {
replacedArguments[i] = replaceTypeVariables(arguments[i], variables, replacements);
}
return new ParameterizedTypeImpl(pt.getRawType(), pt.getOwnerType(), replacedArguments);
}
return t;
}
public static Type resolveType(final Type toResolve, Type resolvedEnclosingType) {
if (toResolve instanceof WildcardType) {
WildcardType wt = (WildcardType) toResolve;
Type[] lowerBounds = wt.getLowerBounds();
final Type[] resolvedLowerBounds = new Type[lowerBounds.length];
for (int i = 0; i < lowerBounds.length; i++) {
resolvedLowerBounds[i] = resolveType(lowerBounds[i], resolvedEnclosingType);
}
Type[] upperBounds = wt.getUpperBounds();
final Type[] resolvedUpperBounds = new Type[upperBounds.length];
for (int i = 0; i < upperBounds.length; i++) {
resolvedUpperBounds[i] = resolveType(upperBounds[i], resolvedEnclosingType);
}
return new WildcardTypeImpl(resolvedUpperBounds, resolvedLowerBounds);
}
if (toResolve instanceof TypeVariable<?>) {
TypeVariable<?> tv = (TypeVariable<?>) toResolve;
if (tv.getGenericDeclaration() instanceof Class) {
Class<?> declClass = (Class<?>) tv.getGenericDeclaration();
int varIndex = ArrayUtils.indexOf(declClass.getTypeParameters(), tv);
Type[] superParams = getSupertypeParameters(declClass, resolvedEnclosingType);
if (varIndex < superParams.length) {
return superParams[varIndex];
}
}
return toResolve;
}
if (toResolve instanceof GenericArrayType) {
GenericArrayType gat = (GenericArrayType) toResolve;
return new GenericArrayTypeImpl(resolveType(
gat.getGenericComponentType(), resolvedEnclosingType));
}
if (toResolve instanceof ParameterizedType || toResolve instanceof Class<?>) {
Type[] args = getTypeParameters(toResolve);
if (args.length == 0) {
return toResolve;
}
final Type[] resolvedTypeArguments = new Type[args.length];
for (int i = 0; i < args.length; i++) {
resolvedTypeArguments[i] = resolveType(args[i], resolvedEnclosingType);
}
return new ParameterizedTypeImpl(getRawType(toResolve), getOwnerType(toResolve), resolvedTypeArguments);
}
return toResolve;
}
}