1
7 package org.hibernate.validator.internal.metadata.core;
8
9 import java.lang.annotation.Annotation;
10 import java.lang.reflect.Type;
11 import java.lang.reflect.TypeVariable;
12 import java.util.List;
13 import java.util.NoSuchElementException;
14 import java.util.Set;
15
16 import javax.validation.valueextraction.ValueExtractor;
17
18 import org.hibernate.validator.internal.engine.valuecontext.ValueContext;
19 import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree;
20 import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager;
21 import org.hibernate.validator.internal.engine.validationcontext.ValidationContext;
22 import org.hibernate.validator.internal.engine.valuecontext.BeanValueContext;
23 import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor;
24 import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorHelper;
25 import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl;
26 import org.hibernate.validator.internal.metadata.location.ConstraintLocation;
27 import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind;
28 import org.hibernate.validator.internal.util.StringHelper;
29 import org.hibernate.validator.internal.util.stereotypes.Immutable;
30
31
39 public class MetaConstraint<A extends Annotation> {
40
41
44 private final ConstraintTree<A> constraintTree;
45
46
49 private final ConstraintLocation location;
50
51
55 @Immutable
56 private final ValueExtractionPathNode valueExtractionPath;
57
58 private final int hashCode;
59
60
64 private final boolean isDefinedForOneGroupOnly;
65
66
72 MetaConstraint(ConstraintValidatorManager constraintValidatorManager, ConstraintDescriptorImpl<A> constraintDescriptor,
73 ConstraintLocation location, List<ContainerClassTypeParameterAndExtractor> valueExtractionPath,
74 Type validatedValueType) {
75 this.constraintTree = ConstraintTree.of( constraintValidatorManager, constraintDescriptor, validatedValueType );
76 this.location = location;
77 this.valueExtractionPath = getValueExtractionPath( valueExtractionPath );
78 this.hashCode = buildHashCode( constraintDescriptor, location );
79 this.isDefinedForOneGroupOnly = constraintDescriptor.getGroups().size() <= 1;
80 }
81
82 private static ValueExtractionPathNode getValueExtractionPath(List<ContainerClassTypeParameterAndExtractor> valueExtractionPath) {
83 switch ( valueExtractionPath.size() ) {
84 case 0: return null;
85 case 1: return new SingleValueExtractionPathNode( valueExtractionPath.iterator().next() );
86 default: return new LinkedValueExtractionPathNode( null, valueExtractionPath );
87 }
88 }
89
90
94 public final Set<Class<?>> getGroupList() {
95 return constraintTree.getDescriptor().getGroups();
96 }
97
98 public final boolean isDefinedForOneGroupOnly() {
99 return isDefinedForOneGroupOnly;
100 }
101
102 public final ConstraintDescriptorImpl<A> getDescriptor() {
103 return constraintTree.getDescriptor();
104 }
105
106 public final ConstraintLocationKind getConstraintLocationKind() {
107 return constraintTree.getDescriptor().getConstraintLocationKind();
108 }
109
110 public boolean validateConstraint(ValidationContext<?> validationContext, ValueContext<?, Object> valueContext) {
111 boolean success = true;
112
113 if ( valueExtractionPath != null ) {
114 Object valueToValidate = valueContext.getCurrentValidatedValue();
115 if ( valueToValidate != null ) {
116 TypeParameterValueReceiver receiver = new TypeParameterValueReceiver( validationContext, valueContext, valueExtractionPath );
117 ValueExtractorHelper.extractValues( valueExtractionPath.getValueExtractorDescriptor(), valueToValidate, receiver );
118 success = receiver.isSuccess();
119 }
120 }
121
122 else {
123 success = doValidateConstraint( validationContext, valueContext );
124 }
125 return success;
126 }
127
128 private boolean doValidateConstraint(ValidationContext<?> executionContext, ValueContext<?, ?> valueContext) {
129 valueContext.setConstraintLocationKind( getConstraintLocationKind() );
130 boolean validationResult = constraintTree.validateConstraints( executionContext, valueContext );
131
132 return validationResult;
133 }
134
135 public ConstraintLocation getLocation() {
136 return location;
137 }
138
139 @Override
140 public boolean equals(Object o) {
141 if ( this == o ) {
142 return true;
143 }
144 if ( o == null || getClass() != o.getClass() ) {
145 return false;
146 }
147
148 MetaConstraint<?> that = (MetaConstraint<?>) o;
149
150 if ( !constraintTree.getDescriptor().equals( that.constraintTree.getDescriptor() ) ) {
151 return false;
152 }
153
154 if ( !location.equals( that.location ) ) {
155 return false;
156 }
157
158 return true;
159 }
160
161 private static int buildHashCode(ConstraintDescriptorImpl<?> constraintDescriptor, ConstraintLocation location) {
162 final int prime = 31;
163 int result = 1;
164 result = prime * result + constraintDescriptor.hashCode();
165 result = prime * result + location.hashCode();
166 return result;
167 }
168
169 @Override
170 public int hashCode() {
171 return hashCode;
172 }
173
174 @Override
175 public String toString() {
176 final StringBuilder sb = new StringBuilder();
177 sb.append( "MetaConstraint" );
178 sb.append( "{constraintType=" ).append( StringHelper.toShortString( constraintTree.getDescriptor().getAnnotation().annotationType() ) );
179 sb.append( ", location=" ).append( location );
180 sb.append( ", valueExtractionPath=" ).append( valueExtractionPath );
181 sb.append( "}" );
182 return sb.toString();
183 }
184
185 private final class TypeParameterValueReceiver implements ValueExtractor.ValueReceiver {
186
187 private final ValidationContext<?> validationContext;
188 private final ValueContext<?, Object> valueContext;
189 private boolean success = true;
190 private ValueExtractionPathNode currentValueExtractionPathNode;
191
192 public TypeParameterValueReceiver(ValidationContext<?> validationContext, ValueContext<?, Object> valueContext, ValueExtractionPathNode currentValueExtractionPathNode) {
193 this.validationContext = validationContext;
194 this.valueContext = valueContext;
195 this.currentValueExtractionPathNode = currentValueExtractionPathNode;
196 }
197
198 @Override
199 public void value(String nodeName, Object object) {
200 doValidate( object, nodeName );
201 }
202
203 @Override
204 public void iterableValue(String nodeName, Object value) {
205 valueContext.markCurrentPropertyAsIterable();
206 doValidate( value, nodeName );
207 }
208
209 @Override
210 public void indexedValue(String nodeName, int index, Object value) {
211 valueContext.markCurrentPropertyAsIterableAndSetIndex( index );
212 doValidate( value, nodeName );
213 }
214
215 @Override
216 public void keyedValue(String nodeName, Object key, Object value) {
217 valueContext.markCurrentPropertyAsIterableAndSetKey( key );
218 doValidate( value, nodeName );
219 }
220
221 private void doValidate(Object value, String nodeName) {
222 BeanValueContext.ValueState<Object> originalValueState = valueContext.getCurrentValueState();
223
224 Class<?> containerClass = currentValueExtractionPathNode.getContainerClass();
225 if ( containerClass != null ) {
226 valueContext.setTypeParameter( containerClass, currentValueExtractionPathNode.getTypeParameterIndex() );
227 }
228
229 if ( nodeName != null ) {
230 valueContext.appendTypeParameterNode( nodeName );
231 }
232
233 valueContext.setCurrentValidatedValue( value );
234
235 if ( currentValueExtractionPathNode.hasNext() ) {
236 if ( value != null ) {
237 currentValueExtractionPathNode = currentValueExtractionPathNode.getNext();
238
239 ValueExtractorDescriptor valueExtractorDescriptor = currentValueExtractionPathNode.getValueExtractorDescriptor();
240 ValueExtractorHelper.extractValues( valueExtractorDescriptor, value, this );
241
242 currentValueExtractionPathNode = currentValueExtractionPathNode.getPrevious();
243 }
244 }
245 else {
246 success &= doValidateConstraint( validationContext, valueContext );
247 }
248
249
250 valueContext.resetValueState( originalValueState );
251 }
252
253 public boolean isSuccess() {
254 return success;
255 }
256 }
257
258 static final class ContainerClassTypeParameterAndExtractor {
259
260 private final Class<?> containerClass;
261 private final TypeVariable<?> typeParameter;
262 private final Integer typeParameterIndex;
263 private final ValueExtractorDescriptor valueExtractorDescriptor;
264
265 ContainerClassTypeParameterAndExtractor(Class<?> containerClass, TypeVariable<?> typeParameter, Integer typeParameterIndex, ValueExtractorDescriptor valueExtractorDescriptor) {
266 this.containerClass = containerClass;
267 this.typeParameter = typeParameter;
268 this.typeParameterIndex = typeParameterIndex;
269 this.valueExtractorDescriptor = valueExtractorDescriptor;
270 }
271
272 @Override
273 public String toString() {
274 return "ContainerClassTypeParameterAndExtractor [containerClass=" + containerClass +
275 ", typeParameter=" + typeParameter +
276 ", typeParameterIndex=" + typeParameterIndex +
277 ", valueExtractorDescriptor=" + valueExtractorDescriptor + "]";
278 }
279 }
280
281 private interface ValueExtractionPathNode {
282 boolean hasNext();
283 ValueExtractionPathNode getPrevious();
284 ValueExtractionPathNode getNext();
285 Class<?> getContainerClass();
286 TypeVariable<?> getTypeParameter();
287 Integer getTypeParameterIndex();
288 ValueExtractorDescriptor getValueExtractorDescriptor();
289 }
290
291 private static final class SingleValueExtractionPathNode implements ValueExtractionPathNode {
292
293 private final Class<?> containerClass;
294 private final TypeVariable<?> typeParameter;
295 private final Integer typeParameterIndex;
296 private final ValueExtractorDescriptor valueExtractorDescriptor;
297
298 public SingleValueExtractionPathNode(ContainerClassTypeParameterAndExtractor typeParameterAndExtractor) {
299 this.containerClass = typeParameterAndExtractor.containerClass;
300 this.typeParameter = typeParameterAndExtractor.typeParameter;
301 this.typeParameterIndex = typeParameterAndExtractor.typeParameterIndex;
302 this.valueExtractorDescriptor = typeParameterAndExtractor.valueExtractorDescriptor;
303 }
304
305 @Override
306 public boolean hasNext() {
307 return false;
308 }
309
310 @Override
311 public ValueExtractionPathNode getPrevious() {
312 throw new NoSuchElementException();
313 }
314
315 @Override
316 public ValueExtractionPathNode getNext() {
317 throw new NoSuchElementException();
318 }
319
320 @Override
321 public Class<?> getContainerClass() {
322 return containerClass;
323 }
324
325 @Override
326 public TypeVariable<?> getTypeParameter() {
327 return typeParameter;
328 }
329
330 @Override
331 public Integer getTypeParameterIndex() {
332 return typeParameterIndex;
333 }
334
335 @Override
336 public ValueExtractorDescriptor getValueExtractorDescriptor() {
337 return valueExtractorDescriptor;
338 }
339
340 @Override
341 public String toString() {
342 return "SingleValueExtractionPathNode [containerClass=" + containerClass +
343 ", typeParameter=" + typeParameter +
344 ", valueExtractorDescriptor=" + valueExtractorDescriptor + "]";
345 }
346 }
347
348 private static final class LinkedValueExtractionPathNode implements ValueExtractionPathNode {
349
350 private final ValueExtractionPathNode previous;
351 private final ValueExtractionPathNode next;
352 private final Class<?> containerClass;
353 private final TypeVariable<?> typeParameter;
354 private final Integer typeParameterIndex;
355 private final ValueExtractorDescriptor valueExtractorDescriptor;
356
357 private LinkedValueExtractionPathNode( ValueExtractionPathNode previous, List<ContainerClassTypeParameterAndExtractor> elements) {
358 ContainerClassTypeParameterAndExtractor first = elements.get( 0 );
359 this.containerClass = first.containerClass;
360 this.typeParameter = first.typeParameter;
361 this.typeParameterIndex = first.typeParameterIndex;
362 this.valueExtractorDescriptor = first.valueExtractorDescriptor;
363 this.previous = previous;
364
365 if ( elements.size() == 1 ) {
366 this.next = null;
367 }
368 else {
369 this.next = new LinkedValueExtractionPathNode( this, elements.subList( 1, elements.size() ) );
370 }
371 }
372
373 @Override
374 public boolean hasNext() {
375 return next != null;
376 }
377
378 @Override
379 public ValueExtractionPathNode getPrevious() {
380 return previous;
381 }
382
383 @Override
384 public ValueExtractionPathNode getNext() {
385 return next;
386 }
387
388 @Override
389 public Class<?> getContainerClass() {
390 return containerClass;
391 }
392
393 @Override
394 public TypeVariable<?> getTypeParameter() {
395 return typeParameter;
396 }
397
398 @Override
399 public Integer getTypeParameterIndex() {
400 return typeParameterIndex;
401 }
402
403 @Override
404 public ValueExtractorDescriptor getValueExtractorDescriptor() {
405 return valueExtractorDescriptor;
406 }
407
408 @Override
409 public String toString() {
410 return "LinkedValueExtractionPathNode [containerClass=" + containerClass +
411 ", typeParameter=" + typeParameter +
412 ", valueExtractorDescriptor=" + valueExtractorDescriptor + "]";
413 }
414 }
415 }
416