co.paralleluniverse.common.reflection.ReflectionUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of quasar-core Show documentation
Show all versions of quasar-core Show documentation
Fibers, Channels and Actors for the JVM
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Based, in part, on code from apache.commons-lang, released under the Apache License 2.0
*/
package co.paralleluniverse.common.reflection;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;
/**
*
* @author pron
*/
public final class ReflectionUtil {
private ReflectionUtil() {
}
public static T accessible(T obj) {
if (obj == null)
return null;
obj.setAccessible(true);
return obj;
}
public static Class>[] getTypes(Object... vals) {
Class>[] types = new Class[vals.length];
for (int i = 0; i < vals.length; i++)
types[i] = vals[i] != null ? vals[i].getClass() : null;
return types;
}
public static boolean isAssignable(Class>[] classArray, Class>[] toClassArray, final boolean autoboxing) {
if (classArray.length != toClassArray.length)
return false;
for (int i = 0; i < classArray.length; i++) {
if (!isAssignable(classArray[i], toClassArray[i], autoboxing))
return false;
}
return true;
}
public static Constructor getMatchingConstructor(final Class cls, final Class>... parameterTypes) {
// see if we can find the constructor directly
// most of the time this works and it's much faster
try {
final Constructor ctor = cls.getConstructor(parameterTypes);
return ctor;
} catch (final NoSuchMethodException e) {
}
// return best match:
Constructor result = null;
final Constructor>[] ctors = cls.getConstructors();
for (Constructor> ctor : ctors) {
// compare parameters
if (isAssignable(parameterTypes, ctor.getParameterTypes(), true)) {
if (result == null || compareParameterTypes(ctor.getParameterTypes(), result.getParameterTypes(), parameterTypes) < 0)
result = (Constructor) ctor;
}
}
return result;
}
/**
* Compares the relative fitness of two sets of parameter types in terms of
* matching a third set of runtime parameter types, such that a list ordered
* by the results of the comparison would return the best match first
* (least).
*
* @param left the "left" parameter set
* @param right the "right" parameter set
* @param actual the runtime parameter types to match against
* left
/right
* @return int consistent with compare
semantics
*/
static int compareParameterTypes(final Class>[] left, final Class>[] right, final Class>[] actual) {
final float leftCost = getTotalTransformationCost(actual, left);
final float rightCost = getTotalTransformationCost(actual, right);
return leftCost < rightCost ? -1 : rightCost < leftCost ? 1 : 0;
}
private static float getTotalTransformationCost(final Class>[] srcArgs, final Class>[] destArgs) {
float totalCost = 0.0f;
for (int i = 0; i < srcArgs.length; i++) {
Class> srcClass, destClass;
srcClass = srcArgs[i];
destClass = destArgs[i];
totalCost += getObjectTransformationCost(srcClass, destClass);
}
return totalCost;
}
private static float getObjectTransformationCost(Class> srcClass, final Class> destClass) {
if (destClass.isPrimitive()) {
return getPrimitivePromotionCost(srcClass, destClass);
}
float cost = 0.0f;
while (srcClass != null && !destClass.equals(srcClass)) {
if (destClass.isInterface() && isAssignable(srcClass, destClass, true)) {
// slight penalty for interface match.
// we still want an exact match to override an interface match,
// but
// an interface match should override anything where we have to
// get a superclass.
cost += 0.25f;
break;
}
cost++;
srcClass = srcClass.getSuperclass();
}
/*
* If the destination class is null, we've travelled all the way up to
* an Object match. We'll penalize this by adding 1.5 to the cost.
*/
if (srcClass == null) {
cost += 1.5f;
}
return cost;
}
private static float getPrimitivePromotionCost(final Class> srcClass, final Class> destClass) {
float cost = 0.0f;
Class> cls = srcClass;
if (!cls.isPrimitive()) {
// slight unwrapping penalty
cost += 0.1f;
cls = wrapperToPrimitive(cls);
}
for (int i = 0; cls != destClass && i < ORDERED_PRIMITIVE_TYPES.length; i++) {
if (cls == ORDERED_PRIMITIVE_TYPES[i]) {
cost += 0.1f;
if (i < ORDERED_PRIMITIVE_TYPES.length - 1) {
cls = ORDERED_PRIMITIVE_TYPES[i + 1];
}
}
}
return cost;
}
public static boolean isAssignable(Class> cls, final Class> toClass, final boolean autoboxing) {
if (toClass == null)
return false;
// have to check for null, as isAssignableFrom doesn't
if (cls == null)
return !toClass.isPrimitive();
//autoboxing:
if (autoboxing) {
if (cls.isPrimitive() && !toClass.isPrimitive()) {
cls = primitiveToWrapper(cls);
if (cls == null) {
return false;
}
}
if (toClass.isPrimitive() && !cls.isPrimitive()) {
cls = wrapperToPrimitive(cls);
if (cls == null) {
return false;
}
}
}
if (cls.equals(toClass))
return true;
if (cls.isPrimitive()) {
if (toClass.isPrimitive() == false)
return false;
if (Integer.TYPE.equals(cls)) {
return Long.TYPE.equals(toClass)
|| Float.TYPE.equals(toClass)
|| Double.TYPE.equals(toClass);
}
if (Long.TYPE.equals(cls)) {
return Float.TYPE.equals(toClass)
|| Double.TYPE.equals(toClass);
}
if (Boolean.TYPE.equals(cls)) {
return false;
}
if (Double.TYPE.equals(cls)) {
return false;
}
if (Float.TYPE.equals(cls)) {
return Double.TYPE.equals(toClass);
}
if (Character.TYPE.equals(cls)) {
return Integer.TYPE.equals(toClass)
|| Long.TYPE.equals(toClass)
|| Float.TYPE.equals(toClass)
|| Double.TYPE.equals(toClass);
}
if (Short.TYPE.equals(cls)) {
return Integer.TYPE.equals(toClass)
|| Long.TYPE.equals(toClass)
|| Float.TYPE.equals(toClass)
|| Double.TYPE.equals(toClass);
}
if (Byte.TYPE.equals(cls)) {
return Short.TYPE.equals(toClass)
|| Integer.TYPE.equals(toClass)
|| Long.TYPE.equals(toClass)
|| Float.TYPE.equals(toClass)
|| Double.TYPE.equals(toClass);
}
// should never get here
return false;
}
return toClass.isAssignableFrom(cls);
}
public static Class> primitiveToWrapper(final Class> cls) {
Class> convertedClass = cls;
if (cls != null && cls.isPrimitive()) {
convertedClass = primitiveWrapperMap.get(cls);
}
return convertedClass;
}
public static Class> wrapperToPrimitive(final Class> cls) {
return wrapperPrimitiveMap.get(cls);
}
public static Type getGenericParameterType(Class> cls, Class> genericSuper, int paramIndex) {
if (!genericSuper.isAssignableFrom(cls))
throw new IllegalArgumentException("Class " + cls.getName() + " does not implement or extend " + genericSuper.getName());
Type res = getGenericParameterType0(cls, genericSuper, paramIndex);
return !(res instanceof TypeVariable) ? res : null;
}
private static Type getGenericParameterType0(Class> cls, Class> genericSuper, int paramIndex) {
if (!genericSuper.isAssignableFrom(cls))
return null;
if (genericSuper.isInterface()) {
for (Type type : cls.getGenericInterfaces()) {
final Class> clazz = getClass(type);
if (genericSuper.isAssignableFrom(clazz)) {
if (genericSuper.equals(clazz))
return type instanceof ParameterizedType ? ((ParameterizedType) type).getActualTypeArguments()[paramIndex] : null;
else {
for (Class> iface : cls.getInterfaces()) {
final Type res = getGenericParameterType0(iface, genericSuper, paramIndex);
if (res != null)
return res;
}
}
}
}
return null;
} else {
Type type = cls.getGenericSuperclass();
assert genericSuper.isAssignableFrom(getClass(type));
if (genericSuper.equals(getClass(type)))
return type instanceof ParameterizedType ? ((ParameterizedType) type).getActualTypeArguments()[paramIndex] : null;
else
return getGenericParameterType0(cls.getSuperclass(), genericSuper, paramIndex);
}
}
public static Class> getClass(Type type) {
if (type instanceof Class)
return (Class>) type;
if (type instanceof ParameterizedType)
return (Class>) ((ParameterizedType) type).getRawType();
if (type instanceof GenericArrayType)
return Array.newInstance((Class>) ((GenericArrayType) type).getGenericComponentType(), 0).getClass();
return null;
}
public static Class>[] getParameterTypes(Member m) {
// necessary prior to Java 8's Executable
if (m instanceof Method)
return ((Method) m).getParameterTypes();
if (m instanceof Constructor)
return ((Constructor) m).getParameterTypes();
throw new IllegalArgumentException("Not an executable: " + m);
}
private static final Map, Class>> primitiveWrapperMap = new HashMap, Class>>();
static {
primitiveWrapperMap.put(Boolean.TYPE, Boolean.class);
primitiveWrapperMap.put(Byte.TYPE, Byte.class);
primitiveWrapperMap.put(Character.TYPE, Character.class);
primitiveWrapperMap.put(Short.TYPE, Short.class);
primitiveWrapperMap.put(Integer.TYPE, Integer.class);
primitiveWrapperMap.put(Long.TYPE, Long.class);
primitiveWrapperMap.put(Double.TYPE, Double.class);
primitiveWrapperMap.put(Float.TYPE, Float.class);
primitiveWrapperMap.put(Void.TYPE, Void.TYPE);
}
private static final Map, Class>> wrapperPrimitiveMap = new HashMap, Class>>();
static {
for (final Class> primitiveClass : primitiveWrapperMap.keySet()) {
final Class> wrapperClass = primitiveWrapperMap.get(primitiveClass);
if (!primitiveClass.equals(wrapperClass)) {
wrapperPrimitiveMap.put(wrapperClass, primitiveClass);
}
}
}
private static final Class>[] ORDERED_PRIMITIVE_TYPES = {Byte.TYPE, Short.TYPE,
Character.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE};
}