package com.kaching.platform.common.types;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
public class Types {
/**
* Verifies that {@code b} is an instance of the type scheme {@code a}.
*/
public static boolean isInstance(Type a, Type b) {
if (a instanceof Class<?>) {
return a.equals(b);
} else if (a instanceof GenericArrayType) {
if (b instanceof GenericArrayType) {
return isInstance(
((GenericArrayType) a).getGenericComponentType(),
((GenericArrayType) b).getGenericComponentType());
} else {
return false;
}
} else if (a instanceof ParameterizedType) {
if (!(b instanceof ParameterizedType)) {
return false;
}
ParameterizedType parameterizedTypeA = (ParameterizedType) a;
ParameterizedType parameterizedTypeB = (ParameterizedType) b;
Type[] actualTypeArgumentsA = parameterizedTypeA.getActualTypeArguments();
Type[] actualTypeArgumentsB = parameterizedTypeB.getActualTypeArguments();
if (!parameterizedTypeA.getRawType().equals(parameterizedTypeB.getRawType()) ||
actualTypeArgumentsA.length != actualTypeArgumentsB.length) {
return false;
}
for (int i = 0; i < actualTypeArgumentsA.length; i++) {
if (!isInstance(actualTypeArgumentsA[i], actualTypeArgumentsB[i])) {
return false;
}
}
return true;
} else if (a instanceof TypeVariable<?>) {
TypeVariable<?> typeVariable = (TypeVariable<?>) a;
for (Type bound : typeVariable.getBounds()) {
return isAssignableFrom(bound, b);
}
return true;
} else if (a instanceof WildcardType) {
WildcardType wildcardType = (WildcardType) a;
for (Type lowerBound : wildcardType.getLowerBounds()) {
if (!isAssignableFrom(b, lowerBound)) {
return false;
}
}
for (Type upperBound : wildcardType.getUpperBounds()) {
if (!isAssignableFrom(upperBound, b)) {
return false;
}
}
return true;
}
throw new IllegalStateException();
}
/**
* Equivalent of {@link Class#isAssignableFrom(Class)} at the {@link Type}
* level.
*/
public static boolean isAssignableFrom(Type a, Type b) {
if (a instanceof Class<?>) {
Class<?> classA = (Class<?>) a;
if (b instanceof Class<?>) {
return classA.isAssignableFrom((Class<?>) b);
} else if (b instanceof GenericArrayType) {
return classA.isArray() && isAssignableFrom(
classA.getComponentType(), ((GenericArrayType) b).getGenericComponentType());
} else if (b instanceof ParameterizedType) {
return classA.isAssignableFrom((Class<?>) ((ParameterizedType) b).getRawType());
} else if (b instanceof TypeVariable<?>) {
TypeVariable<?> typeVariableB = (TypeVariable<?>) b;
for (Type upperBound : typeVariableB.getBounds()) {
if (!isAssignableFrom(a, upperBound)) {
return false;
}
}
return true;
} else if (b instanceof WildcardType) {
WildcardType wildcardTypeB = (WildcardType) b;
for (Type upperBound : wildcardTypeB.getUpperBounds()) {
if (!isAssignableFrom(a, upperBound)) {
return false;
}
}
return true;
}
} else if (a instanceof GenericArrayType) {
if (b instanceof GenericArrayType) {
return isAssignableFrom(
((GenericArrayType) a).getGenericComponentType(),
((GenericArrayType) b).getGenericComponentType());
}
} else if (a instanceof ParameterizedType) {
ParameterizedType parameterizedTypeA = (ParameterizedType) a;
if (b instanceof Class<?>) {
return isAssignableFrom(parameterizedTypeA.getRawType(), b);
} else if (b instanceof ParameterizedType) {
ParameterizedType parameterizedTypeB = (ParameterizedType) b;
Type[] actualTypeArgumentsA = parameterizedTypeA.getActualTypeArguments();
Type[] actualTypeArgumentsB = parameterizedTypeB.getActualTypeArguments();
if (actualTypeArgumentsA.length != actualTypeArgumentsB.length) {
return false;
}
for (int i = 0; i < actualTypeArgumentsA.length; i++) {
if (!isInstance(actualTypeArgumentsA[i], actualTypeArgumentsB[i])) {
return false;
}
}
return isAssignableFrom(
parameterizedTypeA.getRawType(),
parameterizedTypeB.getRawType());
}
} else if (a instanceof TypeVariable<?>) {
for (Type bound : ((TypeVariable<?>) a).getBounds()) {
if (!isAssignableFrom(bound, b)) {
return false;
}
}
return true;
} else if (a instanceof WildcardType) {
WildcardType wildcardType = (WildcardType) a;
Type[] lowerBounds = wildcardType.getLowerBounds();
for (Type lowerBound : lowerBounds) {
if (!isAssignableFrom(lowerBound, b)) {
return false;
}
}
return lowerBounds.length != 0;
}
return false;
}
}