/** * */ package org.beanfabrics.util; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.HashMap; import java.util.Map; /** * The {@link VariableMapping} maps type variables to type values. * * @author Michael Karneim */ public class VariableMapping { private final Map<TypeVariable<?>, Type> mapping = new HashMap<TypeVariable<?>, Type>(); /** * Creates an empty variable mapping. */ public VariableMapping() { this(null); } /** * Creates a copy of the given variable mapping. * * @param aParentMapping */ public VariableMapping(VariableMapping aParentMapping) { if (aParentMapping != null) { mapping.putAll(aParentMapping.mapping); } } /** * Creates a copy of the given variable mapping and populates it with the * type variable mapping of the given parameterized type. * * @param aParentMapping * @param parameterizedType */ public VariableMapping(VariableMapping aParentMapping, ParameterizedType parameterizedType) { this(aParentMapping); this.addTypeArgumentsOfParameterizedType(parameterizedType); } /** * Returns <code>true</code> if the given variable is mapped to some value. * * @param variable * @return <code>true</code> if the given variable is mapped to some value */ public boolean isMapped(TypeVariable<?> variable) { return mapping.containsKey(variable); } /** * Resolves the mapped value of the given variable. Returns the mapped * value. * * @param variable * the variable to resolve * @return the value that is mapped to this variable */ public Type resolve(TypeVariable<?> variable) { Type value = mapping.get(variable); if (value instanceof TypeVariable<?> && isMapped((TypeVariable<?>) value)) { return resolve((TypeVariable<?>) value); } return value; } /** * Tries to resolve the mapped value of the given variable. Returns the * mapped value if a mapping is found, or the variable itself, if it is not * found. * * @param var * @return */ public Type tryResolve(TypeVariable<?> var) { Type resolvedType = resolve(var); if (resolvedType != null) { return resolvedType; } else { return var; } } /** * Maps the given variable to the given value. * * @param variable * @param toValue * @throws IllegalArgumentException * if the variable is already mapped */ private void add(TypeVariable<?> variable, Type toValue) throws IllegalArgumentException { if (variable != toValue) { Type old = mapping.put(variable, toValue); if (old != null && old != toValue) { throw new IllegalArgumentException("var already mapped! var=" + variable); } } // throw new IllegalArgumentException(); } /** * Maps the given variables to the given values. * * @param variables * @param toValues */ private void add(TypeVariable<?>[] variables, Type[] toValues) { for (int i = 0; i < variables.length; i++) { add(variables[i], toValues[i]); } } /** * Maps the type parameters of the given parameterized type to their actual * type arguments. * * @param parameterizedType */ private void addTypeArgumentsOfParameterizedType(ParameterizedType parameterizedType) { if (parameterizedType.getOwnerType() instanceof ParameterizedType) { addTypeArgumentsOfParameterizedType((ParameterizedType) parameterizedType.getOwnerType()); } // .... extends MyClass<X,String> Type[] args = parameterizedType.getActualTypeArguments(); // public class MyClass<X,Y> .... Class<?> forRawClass = (Class<?>) parameterizedType.getRawType(); TypeVariable<?>[] params = forRawClass.getTypeParameters(); this.add(params, args); } }