/* * Hibernate Validator, declare and validate application constraints * * License: Apache License, Version 2.0 * See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>. */ package org.hibernate.validator.internal.metadata.core; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.validation.metadata.ValidateUnwrappedValue; import org.hibernate.validator.internal.engine.cascading.ValueExtractorDescriptor; import org.hibernate.validator.internal.engine.cascading.ValueExtractorManager; import org.hibernate.validator.internal.metadata.core.MetaConstraint.TypeParameterAndExtractor; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; import org.hibernate.validator.internal.metadata.location.ConstraintLocation; import org.hibernate.validator.internal.metadata.location.TypeArgumentConstraintLocation; import org.hibernate.validator.internal.util.TypeHelper; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; import com.fasterxml.classmate.ResolvedType; /** * Helper used to create {@link MetaConstraint}s. * * @author Guillaume Smet */ public class MetaConstraints { private static final Log LOG = LoggerFactory.make(); private MetaConstraints() { } public static <A extends Annotation> MetaConstraint<A> create(TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, ConstraintDescriptorImpl<A> constraintDescriptor, ConstraintLocation location) { List<TypeParameterAndExtractor> valueExtractionPath = new ArrayList<>(); Type typeOfValidatedElement = addValueExtractorDescriptorForWrappedValue( typeResolutionHelper, valueExtractorManager, constraintDescriptor, valueExtractionPath, location ); ConstraintLocation current = location; do { if ( current instanceof TypeArgumentConstraintLocation ) { addValueExtractorDescriptorForTypeArgumentLocation( valueExtractorManager, valueExtractionPath, (TypeArgumentConstraintLocation) current ); current = ( (TypeArgumentConstraintLocation) current ).getDelegate(); } else { current = null; } } while ( current != null ); Collections.reverse( valueExtractionPath ); return new MetaConstraint<>( constraintDescriptor, location, valueExtractionPath, typeOfValidatedElement ); } private static <A extends Annotation> Type addValueExtractorDescriptorForWrappedValue(TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, ConstraintDescriptorImpl<A> constraintDescriptor, List<TypeParameterAndExtractor> valueExtractionPath, ConstraintLocation location) { if ( ValidateUnwrappedValue.NO.equals( constraintDescriptor.validateUnwrappedValue() ) ) { return location.getTypeForValidatorResolution(); } Class<?> declaredType = TypeHelper.getErasedReferenceType( location.getTypeForValidatorResolution() ); ValueExtractorDescriptor valueExtractorDescriptorCandidate = valueExtractorManager.getValueExtractor( declaredType ); if ( ValidateUnwrappedValue.DEFAULT.equals( constraintDescriptor.validateUnwrappedValue() ) && ( valueExtractorDescriptorCandidate == null || !valueExtractorDescriptorCandidate.isUnwrapByDefault() ) ) { return location.getTypeForValidatorResolution(); } else { if ( valueExtractorDescriptorCandidate == null ) { throw LOG.getNoValueExtractorFoundForTypeException( declaredType, null ); } valueExtractionPath.add( TypeParameterAndExtractor.of( valueExtractorDescriptorCandidate ) ); return getSingleTypeParameterBind( typeResolutionHelper, location.getTypeForValidatorResolution(), valueExtractorDescriptorCandidate.getExtractedType() ); } } private static void addValueExtractorDescriptorForTypeArgumentLocation( ValueExtractorManager valueExtractorManager, List<TypeParameterAndExtractor> valueExtractionPath, TypeArgumentConstraintLocation typeArgumentConstraintLocation ) { Class<?> declaredType = TypeHelper.getErasedReferenceType( typeArgumentConstraintLocation.getContainerType() ); TypeVariable<?> typeParameter = typeArgumentConstraintLocation.getTypeParameter(); ValueExtractorDescriptor valueExtractorDescriptor = valueExtractorManager.getValueExtractor( declaredType, typeParameter ); if ( valueExtractorDescriptor == null ) { throw LOG.getNoValueExtractorFoundForTypeException( declaredType, typeParameter ); } valueExtractionPath.add( TypeParameterAndExtractor.of( typeParameter, valueExtractorDescriptor ) ); } /** * Returns the sub-types binding for the single type parameter of the super-type. E.g. for {@code IntegerProperty} * and {@code Property<T>}, {@code Integer} would be returned. */ static Class<?> getSingleTypeParameterBind(TypeResolutionHelper typeResolutionHelper, Type subType, Class<?> superType) { ResolvedType resolvedType = typeResolutionHelper.getTypeResolver().resolve( subType ); List<ResolvedType> resolvedTypeParameters = resolvedType.typeParametersFor( superType ); if ( resolvedTypeParameters.isEmpty() ) { throw LOG.getNoValueExtractorFoundForUnwrapException( subType ); } else if ( resolvedTypeParameters.size() > 1 ) { throw LOG.getUnableToExtractValueForTypeWithMultipleTypeParametersException( subType ); } else { return resolvedTypeParameters.iterator().next().getErasedType(); } } }