All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.bval.jsr303.util.TypeUtils Maven / Gradle / Ivy

There is a newer version: 4.0.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.bval.jsr303.util;

import javax.validation.ConstraintValidator;
import javax.validation.ValidationException;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;


/**
 * Description: Helper methods to determine the type for a {@link ConstraintValidator} class.
 * Alternative: could also do this by using 
 *  <groupId>com.googlecode.jtype</groupId>
 *  <artifactId>jtype</artifactId>
 *  <version>0.1.1</version>
 * 
but tried to reduce dependencies, so here is some code, that * handles java5 generic types to find suitable implementations for * ConstraintValidators. *
*/ public class TypeUtils { private static final Map, Set>> SUBTYPES_BY_PRIMITIVE; private static final int VALIDATOR_TYPE_INDEX = 1; static { Map, Set>> subtypesByPrimitive = new HashMap, Set>>(); putPrimitiveSubtypes(subtypesByPrimitive, Void.TYPE); putPrimitiveSubtypes(subtypesByPrimitive, Boolean.TYPE); putPrimitiveSubtypes(subtypesByPrimitive, Byte.TYPE); putPrimitiveSubtypes(subtypesByPrimitive, Character.TYPE); putPrimitiveSubtypes(subtypesByPrimitive, Short.TYPE, Byte.TYPE); putPrimitiveSubtypes(subtypesByPrimitive, Integer.TYPE, Character.TYPE, Short.TYPE); putPrimitiveSubtypes(subtypesByPrimitive, Long.TYPE, Integer.TYPE); putPrimitiveSubtypes(subtypesByPrimitive, Float.TYPE, Long.TYPE); putPrimitiveSubtypes(subtypesByPrimitive, Double.TYPE, Float.TYPE); SUBTYPES_BY_PRIMITIVE = subtypesByPrimitive; } private static void putPrimitiveSubtypes(Map, Set>> subtypesByPrimitive, Class primitiveType, Class... directSubtypes) { Set> subtypes = new HashSet>(); for (Class directSubtype : directSubtypes) { subtypes.add(directSubtype); subtypes.addAll(subtypesByPrimitive.get(directSubtype)); } subtypesByPrimitive.put(primitiveType, subtypes); } private static Type getArrayType(final Type componentType) { if (componentType instanceof Class) { return getClassArrayType((Class) componentType); } else { return getGenericArrayType(componentType); } } private static Type getGenericArrayType(final Type componentType) { return new GenericArrayType() { public Type getGenericComponentType() { return componentType; } public int hashCode() { return componentType.hashCode(); } public boolean equals(Object object) { return object instanceof GenericArrayType && componentType .equals(((GenericArrayType) object).getGenericComponentType()); } }; } private static Class getClassArrayType(Class componentType) { return Array.newInstance(componentType, 0).getClass(); } private static Type getComponentType(Type type) { if (type instanceof Class) { Class clazz = (Class) type; return clazz.isArray() ? clazz.getComponentType() : null; } else if (type instanceof GenericArrayType) { return ((GenericArrayType) type).getGenericComponentType(); } else { return null; } } /** * Given a set of {@link ConstraintValidator} implementation classes, map those * to their target types. * @param validators * @return {@link Map} of {@link Type} : {@link ConstraintValidator} subtype */ public static Map>> getValidatorsTypes( Class>[] validators) { Map>> validatorsTypes = new HashMap>>(); for (Class> validator : validators) { validatorsTypes.put(getValidatorType(validator), validator); } return validatorsTypes; } // ((ParameterizedType)validator.getGenericInterfaces()[0]).getActualTypeArguments()[1] private static Type getValidatorType( Class> validator) { Map resolvedTypes = new HashMap(); Type constraintValidatorType = resolveTypes(resolvedTypes, validator); Type validatorType = ((ParameterizedType) constraintValidatorType) .getActualTypeArguments()[VALIDATOR_TYPE_INDEX]; if (validatorType == null) { throw new ValidationException("null is an invalid type for a ConstraintValidator"); } else if (validatorType instanceof GenericArrayType) { validatorType = getArrayType(getComponentType(validatorType)); } while (resolvedTypes.containsKey(validatorType)) { validatorType = resolvedTypes.get(validatorType); } return validatorType; } private static Type resolveTypes(Map resolvedTypes, Type type) { if (type == null) { return null; } else if (type instanceof Class) { Class clazz = (Class) type; final Type returnedType = resolveTypeForHierarchy(resolvedTypes, clazz); if (returnedType != null) { return returnedType; } } else if (type instanceof ParameterizedType) { ParameterizedType paramType = (ParameterizedType) type; if (!(paramType.getRawType() instanceof Class)) { return null; } Class rawType = (Class) paramType.getRawType(); TypeVariable[] originalTypes = rawType.getTypeParameters(); Type[] partiallyResolvedTypes = paramType.getActualTypeArguments(); int nbrOfParams = originalTypes.length; for (int i = 0; i < nbrOfParams; i++) { resolvedTypes.put(originalTypes[i], partiallyResolvedTypes[i]); } if (rawType.equals(ConstraintValidator.class)) { return type; // we found what we were looking for } else { Type returnedType = resolveTypeForHierarchy(resolvedTypes, rawType); if (returnedType != null) { return returnedType; } } } return null; } private static Type resolveTypeForHierarchy(Map resolvedTypes, Class clazz) { Type returnedType = resolveTypes(resolvedTypes, clazz.getGenericSuperclass()); if (returnedType != null) { return returnedType; } for (Type genericInterface : clazz.getGenericInterfaces()) { returnedType = resolveTypes(resolvedTypes, genericInterface); if (returnedType != null) { return returnedType; } } return null; } /** * Learn whether type is assignable to supertype. * @param supertype * @param type * @return boolean */ public static boolean isAssignable(Type supertype, Type type) { if (supertype.equals(type)) { return true; } if (supertype instanceof Class) { if (type instanceof Class) { return isClassAssignable((Class) supertype, (Class) type); } if (type instanceof ParameterizedType) { return isAssignable(supertype, ((ParameterizedType) type).getRawType()); } if (type instanceof TypeVariable) { return isTypeVariableAssignable(supertype, (TypeVariable) type); } if (type instanceof GenericArrayType) { if (((Class) supertype).isArray()) { return isAssignable(getComponentType(supertype), getComponentType(type)); } return isArraySupertype((Class) supertype); } return false; } if (supertype instanceof ParameterizedType) { if (type instanceof Class) { return isSuperAssignable(supertype, type); } return type instanceof ParameterizedType && isParameterizedTypeAssignable( (ParameterizedType) supertype, (ParameterizedType) type); } if (type instanceof TypeVariable) { return isTypeVariableAssignable(supertype, (TypeVariable) type); } if (supertype instanceof GenericArrayType) { return isArray(type) && isAssignable(getComponentType(supertype), getComponentType(type)); } return supertype instanceof WildcardType && isWildcardTypeAssignable((WildcardType) supertype, type); } private static boolean isClassAssignable(Class supertype, Class type) { if (supertype.isPrimitive() && type.isPrimitive()) { return SUBTYPES_BY_PRIMITIVE.get(supertype).contains(type); } return supertype.isAssignableFrom(type); } private static boolean isParameterizedTypeAssignable(ParameterizedType supertype, ParameterizedType type) { Type rawSupertype = supertype.getRawType(); Type rawType = type.getRawType(); if (!rawSupertype.equals(rawType)) { return !(rawSupertype instanceof Class && rawType instanceof Class && !(((Class) rawSupertype).isAssignableFrom((Class) rawType))) && isSuperAssignable(supertype, type); } Type[] supertypeArgs = supertype.getActualTypeArguments(); Type[] typeArgs = type.getActualTypeArguments(); if (supertypeArgs.length != typeArgs.length) { return false; } for (int i = 0; i < supertypeArgs.length; i++) { Type supertypeArg = supertypeArgs[i]; Type typeArg = typeArgs[i]; if (supertypeArg instanceof WildcardType) { if (!isWildcardTypeAssignable((WildcardType) supertypeArg, typeArg)) { return false; } } else if (!supertypeArg.equals(typeArg)) { return false; } } return true; } private static boolean isTypeVariableAssignable(Type supertype, TypeVariable type) { for (Type bound : type.getBounds()) { if (isAssignable(supertype, bound)) { return true; } } return false; } private static boolean isWildcardTypeAssignable(WildcardType supertype, Type type) { for (Type upperBound : supertype.getUpperBounds()) { if (!isAssignable(upperBound, type)) { return false; } } for (Type lowerBound : supertype.getLowerBounds()) { if (!isAssignable(type, lowerBound)) { return false; } } return true; } private static boolean isSuperAssignable(Type supertype, Type type) { Type superclass = getResolvedSuperclass(type); if (superclass != null && isAssignable(supertype, superclass)) { return true; } for (Type interphace : getResolvedInterfaces(type)) { if (isAssignable(supertype, interphace)) { return true; } } return false; } private static boolean isArraySupertype(Class type) { return Object.class.equals(type) || Cloneable.class.equals(type) || Serializable.class.equals(type); } private static Type getResolvedSuperclass(Type type) { Class rawType = getErasedReferenceType(type); Type supertype = rawType.getGenericSuperclass(); if (supertype == null) { return null; } return resolveTypeVariables(supertype, type); } private static Class getErasedReferenceType(Type type) { return (Class) getErasedType(type); } private static Type getErasedType(Type type) { if (type instanceof ParameterizedType) { Type rawType = ((ParameterizedType) type).getRawType(); return getErasedType(rawType); } if (isArray(type)) { Type componentType = getComponentType(type); Type erasedComponentType = getErasedType(componentType); return getArrayType(erasedComponentType); } if (type instanceof TypeVariable) { Type[] bounds = ((TypeVariable) type).getBounds(); return getErasedType(bounds[0]); } return type; } private static boolean isArray(Type type) { return (type instanceof Class && ((Class) type).isArray()) || (type instanceof GenericArrayType); } private static Type[] getResolvedInterfaces(Type type) { Class rawType = getErasedReferenceType(type); Type[] interfaces = rawType.getGenericInterfaces(); Type[] resolvedInterfaces = new Type[interfaces.length]; for (int i = 0; i < interfaces.length; i++) { resolvedInterfaces[i] = resolveTypeVariables(interfaces[i], type); } return resolvedInterfaces; } private static Type resolveTypeVariables(Type type, Type subtype) { if (!(type instanceof ParameterizedType)) { return type; } Map actualTypeArgumentsByParameter = getActualTypeArgumentsByParameter(type, subtype); Class rawType = getErasedReferenceType(type); return parameterizeClass(rawType, actualTypeArgumentsByParameter); } private static Map getActualTypeArgumentsByParameter(Type... types) { Map actualTypeArgumentsByParameter = new LinkedHashMap(); for (Type type : types) { actualTypeArgumentsByParameter .putAll(getActualTypeArgumentsByParameterInternal(type)); } return normalize(actualTypeArgumentsByParameter); } private static Map normalize(Map map) { for (Map.Entry entry : map.entrySet()) { K key = entry.getKey(); K value = entry.getValue(); while (map.containsKey(value)) { value = map.get(value); } map.put(key, value); } return map; } private static Map getActualTypeArgumentsByParameterInternal(Type type) { if (!(type instanceof ParameterizedType)) { return Collections.emptyMap(); } TypeVariable[] typeParameters = getErasedReferenceType(type).getTypeParameters(); Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments(); if (typeParameters.length != typeArguments.length) { throw new MalformedParameterizedTypeException(); } Map actualTypeArgumentsByParameter = new LinkedHashMap(); for (int i = 0; i < typeParameters.length; i++) { actualTypeArgumentsByParameter.put(typeParameters[i], typeArguments[i]); } return actualTypeArgumentsByParameter; } private static ParameterizedType parameterizeClass(Class type, Map actualTypeArgumentsByParameter) { return parameterizeClassCapture(type, actualTypeArgumentsByParameter); } private static ParameterizedType parameterizeClassCapture(Class type, Map actualTypeArgumentsByParameter) { TypeVariable>[] typeParameters = type.getTypeParameters(); Type[] actualTypeArguments = new Type[typeParameters.length]; for (int i = 0; i < typeParameters.length; i++) { TypeVariable> typeParameter = typeParameters[i]; Type actualTypeArgument = actualTypeArgumentsByParameter.get(typeParameter); if (actualTypeArgument == null) { throw new IllegalArgumentException( "Missing actual type argument for type parameter: " + typeParameter); } actualTypeArguments[i] = actualTypeArgument; } return parameterizedType(getErasedReferenceType(type), actualTypeArguments); } private static ParameterizedType parameterizedType(final Class rawType, final Type... actualTypeArguments) { return new ParameterizedType() { public Type getOwnerType() { return null; } public Type getRawType() { return rawType; } public Type[] getActualTypeArguments() { return actualTypeArguments.clone(); } }; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy