/*
* 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 java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import org.jboss.weld.util.Types;
import org.jboss.weld.util.reflection.Reflections;
/**
* Implementation of the Section 5.2.4 of the CDI specification.
*
* @author Jozef Hartinger
* @author Matus Abaffy
*/
public class BeanTypeAssignabilityRules extends AbstractAssignabilityRules {
private static final AssignabilityRules INSTANCE = new BeanTypeAssignabilityRules();
public static AssignabilityRules instance() {
return INSTANCE;
}
protected BeanTypeAssignabilityRules() {
}
@Override
public boolean matches(Type requiredType, Type beanType) {
return matchesNoBoxing(Types.boxedType(requiredType), Types.boxedType(beanType));
}
public boolean matchesNoBoxing(Type requiredType, Type beanType) {
/*
* Special handling for array event types as eventType closure does not contain the type closure of array component type
* this is here for backwards compatibility - see ObserverMethodWithParametertizedTypeTest.testObserverMethodCanObserveArrayWildcard()
*/
if (Types.isArray(requiredType) && Types.isArray(beanType)) {
return matchesNoBoxing(Types.getArrayComponentType(requiredType), Types.getArrayComponentType(beanType));
}
if (requiredType instanceof Class<?>) {
if (beanType instanceof Class<?>) {
return matches((Class<?>) requiredType, (Class<?>) beanType);
}
if (beanType instanceof ParameterizedType) {
return matches((Class<?>) requiredType, (ParameterizedType) beanType);
}
} else if (requiredType instanceof ParameterizedType) {
if (beanType instanceof Class<?>) {
return matches((Class<?>) beanType, (ParameterizedType) requiredType);
}
if (beanType instanceof ParameterizedType) {
return matches((ParameterizedType) requiredType, (ParameterizedType) beanType);
}
}
return false;
}
private boolean matches(Class<?> requiredType, Class<?> beanType) {
return requiredType.equals(beanType);
}
/**
* A parameterized bean type is considered assignable to a raw required type if the raw types
* are identical and all type parameters of the bean type are either unbounded type variables or
* java.lang.Object.
* <p>
* A raw bean type is considered assignable to a parameterized required type if the raw types are
* identical and all type parameters of the required type are either unbounded type variables or
* java.lang.Object.
*
*/
private boolean matches(Class<?> type1, ParameterizedType type2) {
if (!type1.equals(Reflections.getRawType(type2))) {
return false;
}
return Types.isArrayOfUnboundedTypeVariablesOrObjects(type2.getActualTypeArguments());
}
/**
* A parameterized bean type is considered assignable to a parameterized required type if they have
* identical raw type and for each parameter:
*/
private boolean matches(ParameterizedType requiredType, ParameterizedType beanType) {
if (!requiredType.getRawType().equals(beanType.getRawType())) {
return false;
}
if (requiredType.getActualTypeArguments().length != beanType.getActualTypeArguments().length) {
throw new IllegalArgumentException("Invalid argument combination " + requiredType + "; " + beanType);
}
for (int i = 0; i < requiredType.getActualTypeArguments().length; i++) {
if (!parametersMatch(requiredType.getActualTypeArguments()[i], beanType.getActualTypeArguments()[i])) {
return false;
}
}
return true;
}
/*
* Actual type parameters
*/
protected boolean parametersMatch(Type requiredParameter, Type beanParameter) {
if (Types.isActualType(requiredParameter) && Types.isActualType(beanParameter)) {
/*
* the required type parameter and the bean type parameter are actual types with identical raw
* type, and, if the type is parameterized, the bean type parameter is assignable to the required
* type parameter according to these rules, or
*/
return matches(requiredParameter, beanParameter);
}
if (requiredParameter instanceof WildcardType && Types.isActualType(beanParameter)) {
/*
* the required type parameter is a wildcard, the bean type parameter is an actual type and the
* actual type is assignable to the upper bound, if any, of the wildcard and assignable from the
* lower bound, if any, of the wildcard, or
*/
return parametersMatch((WildcardType) requiredParameter, beanParameter);
}
if (requiredParameter instanceof WildcardType && beanParameter instanceof TypeVariable<?>) {
/*
* the required type parameter is a wildcard, the bean type parameter is a type variable and the
* upper bound of the type variable is assignable to or assignable from the upper bound, if any, of
* the wildcard and assignable from the lower bound, if any, of the wildcard, or
*/
return parametersMatch((WildcardType) requiredParameter, (TypeVariable<?>) beanParameter);
}
if (Types.isActualType(requiredParameter) && beanParameter instanceof TypeVariable<?>) {
/*
* the required type parameter is an actual type, the bean type parameter is a type variable and
* the actual type is assignable to the upper bound, if any, of the type variable, or
*/
return parametersMatch(requiredParameter, (TypeVariable<?>) beanParameter);
}
if (requiredParameter instanceof TypeVariable<?> && beanParameter instanceof TypeVariable<?>) {
/*
* the required type parameter and the bean type parameter are both type variables and the upper
* bound of the required type parameter is assignable to the upper bound, if any, of the bean type
* parameter
*/
return parametersMatch((TypeVariable<?>) requiredParameter, (TypeVariable<?>) beanParameter);
}
return false;
}
protected boolean parametersMatch(WildcardType requiredParameter, Type beanParameter) {
return (lowerBoundsOfWildcardMatch(beanParameter, requiredParameter)
&& upperBoundsOfWildcardMatch(requiredParameter, beanParameter));
}
protected boolean parametersMatch(WildcardType requiredParameter, TypeVariable<?> beanParameter) {
Type[] beanParameterBounds = getUppermostTypeVariableBounds(beanParameter);
if (!lowerBoundsOfWildcardMatch(beanParameterBounds, requiredParameter)) {
return false;
}
Type[] requiredUpperBounds = requiredParameter.getUpperBounds();
// upper bound of the type variable is assignable to OR assignable from the upper bound of the wildcard
return (boundsMatch(requiredUpperBounds, beanParameterBounds) || boundsMatch(beanParameterBounds, requiredUpperBounds));
}
protected boolean parametersMatch(Type requiredParameter, TypeVariable<?> beanParameter) {
for (Type bound : getUppermostTypeVariableBounds(beanParameter)) {
if (!CovariantTypes.isAssignableFrom(bound, requiredParameter)) {
return false;
}
}
return true;
}
protected boolean parametersMatch(TypeVariable<?> requiredParameter, TypeVariable<?> beanParameter) {
return boundsMatch(getUppermostTypeVariableBounds(beanParameter), getUppermostTypeVariableBounds(requiredParameter));
}
}