/*
* Copyright 2013 Cameron Beccario
*
* 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 net.nullschool.reflect;
import net.nullschool.collect.ConstMap;
import java.lang.reflect.*;
import java.util.Objects;
import static net.nullschool.reflect.TypeTools.buildArrayType;
import static net.nullschool.util.ObjectTools.coalesce;
/**
* 2013-03-29<p/>
*
* A type operator that resolves type variables. See {@link LateParameterizedType#resolve(Type)}.<p/>
*
* As an example, consider the type Map<String, Integer>. In this context, the type variable K is bound
* to String and V is bound to Integer. A resolver constructed with these bindings will deeply search any type
* for the variables K and V and substitute them for String and Integer, respectively.
*
* @author Cameron Beccario
*/
final class Resolver extends AbstractTypeOperator<Type> {
/**
* A mapping of type variables to the types they represent for this resolver.
*/
private final ConstMap<TypeVariable<?>, Type> variableBindings;
Resolver(ConstMap<TypeVariable<?>, Type> variableBindings) {
this.variableBindings = Objects.requireNonNull(variableBindings);
}
/**
* A class resolves to itself.
*/
@Override public Type apply(Class<?> clazz) {
return clazz;
}
/**
* A parameterized type may have type variables in both the owner type and type arguments, such as
* {@code Foo<T>.Bar<U>}, so resolve them first and build a new parameterized type from the results.
*/
@Override public Type apply(ParameterizedType pt) {
return new LateParameterizedType(
pt.getRawType(),
apply(pt.getOwnerType()),
TypeTools.apply(this, pt.getActualTypeArguments()));
}
/**
* A generic array type resolves to an array of whatever its component type resolves to. If the component type
* resolves to a concrete Class object C, then we must return the concrete Class object for C[]. Once the
* component type of an array is no longer generic, then the array itself is no longer generic.
*/
@Override public Type apply(GenericArrayType gat) {
Type resolvedComponentType = apply(gat.getGenericComponentType());
return resolvedComponentType instanceof Class ?
buildArrayType((Class<?>)resolvedComponentType) :
new LateGenericArrayType(resolvedComponentType);
}
/**
* A wildcard simply resolves to a wildcard of its resolved bounds.
*/
@Override public Type apply(WildcardType wt) {
return wt.getLowerBounds().length > 0 ?
new LateWildcardType("? super", TypeTools.apply(this, wt.getLowerBounds())) :
new LateWildcardType("? extends", TypeTools.apply(this, wt.getUpperBounds()));
}
/**
* A type variable T resolves to whatever T means in this context, as determined by the variable bindings
* provided to this resolver during construction. If T is not bound to anything in this context, then T is
* returned unchanged.
*/
@Override public Type apply(TypeVariable<?> tv) {
return coalesce(variableBindings.get(tv), tv);
}
}