/*
* 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.logging.ReflectionLogger;
import org.jboss.weld.util.Types;
import org.jboss.weld.util.reflection.HierarchyDiscovery;
import org.jboss.weld.util.reflection.Reflections;
/**
* This class implements Section 10.3.1 of the CDI specification.
*
* @author Jozef Hartinger
*
*/
public class EventTypeAssignabilityRules extends AbstractAssignabilityRules {
private static final AssignabilityRules INSTANCE = new EventTypeAssignabilityRules();
public static AssignabilityRules instance() {
return INSTANCE;
}
private EventTypeAssignabilityRules() {
}
@Override
public boolean matches(Type observedType, Type eventType) {
return matchesNoBoxing(Types.boxedType(observedType), Types.boxedType(eventType));
}
public boolean matchesNoBoxing(Type observedType, Type eventType) {
/*
* 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(observedType) && Types.isArray(eventType)) {
final Type observedComponentType = Types.getArrayComponentType(observedType);
for (Type type : new HierarchyDiscovery(Types.getArrayComponentType(eventType)).getTypeClosure()) {
if (matchesNoBoxing(observedComponentType, type)) {
return true;
}
}
return false;
}
if (observedType instanceof TypeVariable<?>) {
/*
* An event type is considered assignable to a type variable if the event type is assignable to the
* upper bound, if any.
*/
return matches((TypeVariable<?>) observedType, eventType);
}
if (observedType instanceof Class<?> && eventType instanceof ParameterizedType) {
/*
* A parameterized event type is considered assignable to a raw observed event type if the raw
* types are identical.
*/
return observedType.equals(Reflections.getRawType(eventType));
}
if (observedType instanceof ParameterizedType && eventType instanceof ParameterizedType) {
/*
* A parameterized event type is considered assignable to a parameterized observed event type if
* they have identical raw type and for each parameter:
*/
return matches((ParameterizedType) observedType, (ParameterizedType) eventType);
}
/*
* Not explicitly said in the spec but obvious.
*/
if (observedType instanceof Class<?> && eventType instanceof Class<?>) {
return observedType.equals(eventType);
}
return false;
}
private boolean matches(TypeVariable<?> observedType, Type eventType) {
for (Type bound : getUppermostTypeVariableBounds(observedType)) {
if (!CovariantTypes.isAssignableFrom(bound, eventType)) {
return false;
}
}
return true;
}
private boolean matches(ParameterizedType observedType, ParameterizedType eventType) {
if (!observedType.getRawType().equals(eventType.getRawType())) {
return false;
}
if (observedType.getActualTypeArguments().length != eventType.getActualTypeArguments().length) {
throw ReflectionLogger.LOG.invalidTypeArgumentCombination(observedType, eventType);
}
for (int i = 0; i < observedType.getActualTypeArguments().length; i++) {
if (!parametersMatch(observedType.getActualTypeArguments()[i], eventType.getActualTypeArguments()[i])) {
return false;
}
}
return true;
}
/**
* A parameterized event type is considered assignable to a parameterized observed event type if
* they have identical raw type and for each parameter:
*/
private boolean parametersMatch(Type observedParameter, Type eventParameter) {
if (Types.isActualType(observedParameter) && Types.isActualType(eventParameter)) {
/*
* the observed event type parameter is an actual type with identical raw type to the event type
* parameter, and, if the type is parameterized, the event type parameter is assignable to the
* observed event type parameter according to these rules, or
*/
return matches(observedParameter, eventParameter);
}
if (observedParameter instanceof WildcardType && eventParameter instanceof WildcardType) {
/*
* both the observed event type parameter and the event type parameter are wildcards, and the event type parameter is assignable to the observed event
* type
*/
return CovariantTypes.isAssignableFrom(observedParameter, eventParameter);
}
if (observedParameter instanceof WildcardType) {
/*
* the observed event type parameter is a wildcard and the event type parameter 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) observedParameter, eventParameter);
}
if (observedParameter instanceof TypeVariable<?>) {
/*
* the observed event type parameter is a type variable and the event type parameter is assignable
* to the upper bound, if any, of the type variable.
*/
return parametersMatch((TypeVariable<?>) observedParameter, eventParameter);
}
return false;
}
private boolean parametersMatch(TypeVariable<?> observedParameter, Type eventParameter) {
for (Type bound : getUppermostTypeVariableBounds(observedParameter)) {
if (!CovariantTypes.isAssignableFrom(bound, eventParameter)) {
return false;
}
}
return true;
}
private boolean parametersMatch(WildcardType observedParameter, Type eventParameter) {
return (lowerBoundsOfWildcardMatch(eventParameter, observedParameter)
&& upperBoundsOfWildcardMatch(observedParameter, eventParameter));
}
}