1
7 package org.hibernate.validator.internal.metadata.core;
8
9 import java.lang.annotation.Annotation;
10 import java.lang.invoke.MethodHandles;
11 import java.lang.reflect.Type;
12 import java.lang.reflect.TypeVariable;
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Objects;
18 import java.util.Set;
19 import java.util.stream.Collectors;
20
21 import javax.validation.metadata.ValidateUnwrappedValue;
22
23 import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager;
24 import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor;
25 import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorHelper;
26 import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager;
27 import org.hibernate.validator.internal.metadata.core.MetaConstraint.ContainerClassTypeParameterAndExtractor;
28 import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl;
29 import org.hibernate.validator.internal.metadata.location.ConstraintLocation;
30 import org.hibernate.validator.internal.metadata.location.TypeArgumentConstraintLocation;
31 import org.hibernate.validator.internal.util.TypeHelper;
32 import org.hibernate.validator.internal.util.TypeResolutionHelper;
33 import org.hibernate.validator.internal.util.TypeVariableBindings;
34 import org.hibernate.validator.internal.util.TypeVariables;
35 import org.hibernate.validator.internal.util.logging.Log;
36 import org.hibernate.validator.internal.util.logging.LoggerFactory;
37
38 import com.fasterxml.classmate.ResolvedType;
39
40
45 public class MetaConstraints {
46
47 private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
48
49 private MetaConstraints() {
50 }
51
52 public static <A extends Annotation> MetaConstraint<A> create(TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager,
53 ConstraintValidatorManager constraintValidatorManager,
54 ConstraintDescriptorImpl<A> constraintDescriptor, ConstraintLocation location) {
55 List<ContainerClassTypeParameterAndExtractor> valueExtractionPath = new ArrayList<>();
56
57 Type typeOfValidatedElement = addValueExtractorDescriptorForWrappedValue( typeResolutionHelper, valueExtractorManager, constraintDescriptor,
58 valueExtractionPath, location );
59
60 ConstraintLocation current = location;
61 do {
62 if ( current instanceof TypeArgumentConstraintLocation ) {
63 addValueExtractorDescriptorForTypeArgumentLocation( valueExtractorManager, valueExtractionPath, (TypeArgumentConstraintLocation) current );
64 current = ( (TypeArgumentConstraintLocation) current ).getDelegate();
65 }
66 else {
67 current = null;
68 }
69 }
70 while ( current != null );
71
72 Collections.reverse( valueExtractionPath );
73
74 return new MetaConstraint<>( constraintValidatorManager, constraintDescriptor, location, valueExtractionPath, typeOfValidatedElement );
75 }
76
77 private static <A extends Annotation> Type addValueExtractorDescriptorForWrappedValue(TypeResolutionHelper typeResolutionHelper,
78 ValueExtractorManager valueExtractorManager, ConstraintDescriptorImpl<A> constraintDescriptor,
79 List<ContainerClassTypeParameterAndExtractor> valueExtractionPath, ConstraintLocation location) {
80 if ( ValidateUnwrappedValue.SKIP.equals( constraintDescriptor.getValueUnwrapping() ) ) {
81 return location.getTypeForValidatorResolution();
82 }
83
84 Class<?> declaredType = TypeHelper.getErasedReferenceType( location.getTypeForValidatorResolution() );
85 Set<ValueExtractorDescriptor> valueExtractorDescriptorCandidates = valueExtractorManager.getResolver().getMaximallySpecificValueExtractors( declaredType );
86 ValueExtractorDescriptor selectedValueExtractorDescriptor;
87
88
89 if ( ValidateUnwrappedValue.UNWRAP.equals( constraintDescriptor.getValueUnwrapping() ) ) {
90 switch ( valueExtractorDescriptorCandidates.size() ) {
91 case 0:
92 throw LOG.getNoValueExtractorFoundForUnwrapException( declaredType );
93 case 1:
94 selectedValueExtractorDescriptor = valueExtractorDescriptorCandidates.iterator().next();
95 break;
96 default:
97 throw LOG.getUnableToGetMostSpecificValueExtractorDueToSeveralMaximallySpecificValueExtractorsDeclaredException(
98 declaredType,
99 ValueExtractorHelper.toValueExtractorClasses( valueExtractorDescriptorCandidates ) );
100 }
101 }
102
103
104
105
106 else {
107 Set<ValueExtractorDescriptor> unwrapByDefaultValueExtractorDescriptorCandidates = valueExtractorDescriptorCandidates.stream()
108 .filter( ved -> ved.isUnwrapByDefault() )
109 .collect( Collectors.toSet() );
110
111 switch ( unwrapByDefaultValueExtractorDescriptorCandidates.size() ) {
112 case 0:
113 return location.getTypeForValidatorResolution();
114 case 1:
115 selectedValueExtractorDescriptor = unwrapByDefaultValueExtractorDescriptorCandidates.iterator().next();
116 break;
117 default:
118 throw LOG.getImplicitUnwrappingNotAllowedWhenSeveralMaximallySpecificValueExtractorsMarkedWithUnwrapByDefaultDeclaredException(
119 declaredType,
120 ValueExtractorHelper.toValueExtractorClasses( unwrapByDefaultValueExtractorDescriptorCandidates ) );
121 }
122 }
123
124 if ( selectedValueExtractorDescriptor.getExtractedType().isPresent() ) {
125 valueExtractionPath.add( new ContainerClassTypeParameterAndExtractor( declaredType, null, null, selectedValueExtractorDescriptor ) );
126 return selectedValueExtractorDescriptor.getExtractedType().get();
127 }
128 else {
129 Class<?> wrappedValueType = getWrappedValueType( typeResolutionHelper, location.getTypeForValidatorResolution(), selectedValueExtractorDescriptor );
130 TypeVariable<?> typeParameter = getContainerClassTypeParameter( declaredType, selectedValueExtractorDescriptor );
131
132 valueExtractionPath.add( new ContainerClassTypeParameterAndExtractor( declaredType, typeParameter, TypeVariables.getTypeParameterIndex( typeParameter ), selectedValueExtractorDescriptor ) );
133
134 return wrappedValueType;
135 }
136 }
137
138 private static void addValueExtractorDescriptorForTypeArgumentLocation( ValueExtractorManager valueExtractorManager,
139 List<ContainerClassTypeParameterAndExtractor> valueExtractionPath, TypeArgumentConstraintLocation typeArgumentConstraintLocation ) {
140 Class<?> declaredType = typeArgumentConstraintLocation.getContainerClass();
141 TypeVariable<?> typeParameter = typeArgumentConstraintLocation.getTypeParameter();
142
143 ValueExtractorDescriptor valueExtractorDescriptor = valueExtractorManager.getResolver()
144 .getMaximallySpecificAndContainerElementCompliantValueExtractor( declaredType, typeParameter );
145
146 if ( valueExtractorDescriptor == null ) {
147 throw LOG.getNoValueExtractorFoundForTypeException( declaredType, typeParameter );
148 }
149
150 TypeVariable<?> actualTypeParameter = TypeVariables.getActualTypeParameter( typeParameter );
151 valueExtractionPath.add( new ContainerClassTypeParameterAndExtractor(
152 TypeVariables.getContainerClass( typeParameter ),
153 actualTypeParameter,
154 TypeVariables.getTypeParameterIndex( actualTypeParameter ),
155 valueExtractorDescriptor ) );
156 }
157
158
162 private static Class<?> getWrappedValueType(TypeResolutionHelper typeResolutionHelper, Type declaredType, ValueExtractorDescriptor valueExtractorDescriptor) {
163 ResolvedType resolvedType = typeResolutionHelper.getTypeResolver().resolve( declaredType );
164
165 List<ResolvedType> resolvedTypeParameters = resolvedType.typeParametersFor( valueExtractorDescriptor.getContainerType() );
166
167 if ( resolvedTypeParameters == null || resolvedTypeParameters.isEmpty() ) {
168 throw LOG.getNoValueExtractorFoundForUnwrapException( declaredType );
169 }
170
171 return resolvedTypeParameters.get( TypeVariables.getTypeParameterIndex( valueExtractorDescriptor.getExtractedTypeParameter() ) ).getErasedType();
172 }
173
174 private static TypeVariable<?> getContainerClassTypeParameter(Class<?> declaredType, ValueExtractorDescriptor selectedValueExtractorDescriptor) {
175 if ( selectedValueExtractorDescriptor.getExtractedTypeParameter() == null ) {
176 return null;
177 }
178
179 Map<Class<?>, Map<TypeVariable<?>, TypeVariable<?>>> allBindings = TypeVariableBindings.getTypeVariableBindings( declaredType );
180 Map<TypeVariable<?>, TypeVariable<?>> extractorTypeBindings = allBindings.get( selectedValueExtractorDescriptor.getContainerType() );
181 if ( extractorTypeBindings == null ) {
182 return null;
183 }
184 return extractorTypeBindings.entrySet().stream()
185 .filter( e -> Objects.equals( e.getKey().getGenericDeclaration(), declaredType ) )
186 .collect( Collectors.toMap( Map.Entry::getValue, Map.Entry::getKey ) )
187 .get( selectedValueExtractorDescriptor.getExtractedTypeParameter() );
188 }
189 }
190