/*
* JBoss, Home of Professional Open Source
* Copyright 2014, Red Hat, Inc., and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* Licensed 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.
*/
package org.jboss.weld.resolution;
import static org.jboss.weld.util.Types.boxedClass;
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;
import org.jboss.weld.logging.ReflectionLogger;
import org.jboss.weld.util.reflection.Reflections;
/**
* Utility class that captures invariant Java assignability rules (used with generics).
*
* This class operates on all the possible Type subtypes: Class, ParameterizedType, TypeVariable, WildcardType, GenericArrayType.
* To make this class easier to understand and maintain, there is a separate isAssignableFrom method for each combination
* of possible types. Each of these methods compares two type instances and determines whether the first one is assignable from
* the other.
*
* Since Java wildcards are by definition covariant, this class does not operate on wildcards and instead delegates to {@link CovariantTypes}.
*
* TypeVariables are considered a specific unknown type restricted by the upper bound. No inference of type variables is performed.
*
* @author Jozef Hartinger
*
*/
public class InvariantTypes {
private InvariantTypes() {
}
public static boolean isAssignableFrom(Type type1, Type type2) {
if (type1 instanceof WildcardType || type2 instanceof WildcardType) {
// Wildcards are by definition covariant
return CovariantTypes.isAssignableFrom(type1, type2);
}
if (type1 instanceof Class<?>) {
if (type2 instanceof Class<?>) {
return isAssignableFrom((Class<?>) type1, (Class<?>) type2);
}
if (type2 instanceof ParameterizedType) {
return isAssignableFrom((Class<?>) type1, (ParameterizedType) type2);
}
if (type2 instanceof TypeVariable<?>) {
return isAssignableFrom((Class<?>) type1, (TypeVariable<?>) type2);
}
if (type2 instanceof GenericArrayType) {
return isAssignableFrom((Class<?>) type1, (GenericArrayType) type2);
}
throw ReflectionLogger.LOG.unknownType(type2);
}
if (type1 instanceof ParameterizedType) {
if (type2 instanceof Class<?>) {
return isAssignableFrom((ParameterizedType) type1, (Class<?>) type2);
}
if (type2 instanceof ParameterizedType) {
return isAssignableFrom((ParameterizedType) type1, (ParameterizedType) type2);
}
if (type2 instanceof TypeVariable<?>) {
return isAssignableFrom((ParameterizedType) type1, (TypeVariable<?>) type2);
}
if (type2 instanceof GenericArrayType) {
return isAssignableFrom((ParameterizedType) type1, (GenericArrayType) type2);
}
throw ReflectionLogger.LOG.unknownType(type2);
}
if (type1 instanceof TypeVariable<?>) {
if (type2 instanceof Class<?>) {
return isAssignableFrom((TypeVariable<?>) type1, (Class<?>) type2);
}
if (type2 instanceof ParameterizedType) {
return isAssignableFrom((TypeVariable<?>) type1, (ParameterizedType) type2);
}
if (type2 instanceof TypeVariable<?>) {
return isAssignableFrom((TypeVariable<?>) type1, (TypeVariable<?>) type2);
}
if (type2 instanceof GenericArrayType) {
return isAssignableFrom((TypeVariable<?>) type1, (GenericArrayType) type2);
}
throw ReflectionLogger.LOG.unknownType(type2);
}
if (type1 instanceof GenericArrayType) {
if (type2 instanceof Class<?>) {
return isAssignableFrom((GenericArrayType) type1, (Class<?>) type2);
}
if (type2 instanceof ParameterizedType) {
return isAssignableFrom((GenericArrayType) type1, (ParameterizedType) type2);
}
if (type2 instanceof TypeVariable<?>) {
return isAssignableFrom((GenericArrayType) type1, (TypeVariable<?>) type2);
}
if (type2 instanceof GenericArrayType) {
return isAssignableFrom((GenericArrayType) type1, (GenericArrayType) type2);
}
throw ReflectionLogger.LOG.unknownType(type2);
}
throw ReflectionLogger.LOG.unknownType(type1);
}
/*
* Raw type
*/
private static boolean isAssignableFrom(Class<?> type1, Class<?> type2) {
return boxedClass(type1).equals(boxedClass(type2));
}
private static boolean isAssignableFrom(Class<?> type1, ParameterizedType type2) {
return false;
}
private static boolean isAssignableFrom(Class<?> type1, TypeVariable<?> type2) {
return false;
}
private static boolean isAssignableFrom(Class<?> type1, GenericArrayType type2) {
return false;
}
/*
* ParameterizedType
*/
private static boolean isAssignableFrom(ParameterizedType type1, Class<?> type2) {
return false;
}
private static boolean isAssignableFrom(ParameterizedType type1, ParameterizedType type2) {
// first, raw types have to be equal
if (!Reflections.getRawType(type1).equals(Reflections.getRawType(type2))) {
return false;
}
final Type[] types1 = type1.getActualTypeArguments();
final Type[] types2 = type2.getActualTypeArguments();
if (types1.length != types2.length) {
throw ReflectionLogger.LOG.invalidTypeArgumentCombination(type1, type2);
}
for (int i = 0; i < types1.length; i++) {
if (!isAssignableFrom(types1[i], types2[i])) {
return false;
}
}
return true;
}
private static boolean isAssignableFrom(ParameterizedType type1, TypeVariable<?> type2) {
return false;
}
private static boolean isAssignableFrom(ParameterizedType type1, GenericArrayType type2) {
return false;
}
/*
* Type variable
*/
private static boolean isAssignableFrom(TypeVariable<?> type1, Class<?> type2) {
return false;
}
private static boolean isAssignableFrom(TypeVariable<?> type1, ParameterizedType type2) {
return false;
}
private static boolean isAssignableFrom(TypeVariable<?> type1, TypeVariable<?> type2) {
return type1.equals(type2);
}
private static boolean isAssignableFrom(TypeVariable<?> type1, GenericArrayType type2) {
return false;
}
/*
* GenericArrayType
*/
private static boolean isAssignableFrom(GenericArrayType type1, Class<?> type2) {
return false;
}
private static boolean isAssignableFrom(GenericArrayType type1, ParameterizedType type2) {
return false;
}
private static boolean isAssignableFrom(GenericArrayType type1, TypeVariable<?> type2) {
return false;
}
private static boolean isAssignableFrom(GenericArrayType type1, GenericArrayType type2) {
return isAssignableFrom(type1.getGenericComponentType(), type2.getGenericComponentType());
}
}