1
7 package org.hibernate.validator.internal.metadata.descriptor;
8
9 import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap;
10 import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet;
11
12 import java.io.Serializable;
13 import java.lang.annotation.Annotation;
14 import java.lang.annotation.Documented;
15 import java.lang.annotation.Repeatable;
16 import java.lang.annotation.Retention;
17 import java.lang.annotation.Target;
18 import java.lang.invoke.MethodHandles;
19 import java.lang.reflect.Method;
20 import java.security.AccessController;
21 import java.security.PrivilegedAction;
22 import java.util.Arrays;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Set;
27 import java.util.stream.Collectors;
28
29 import javax.validation.Constraint;
30 import javax.validation.ConstraintTarget;
31 import javax.validation.ConstraintValidator;
32 import javax.validation.OverridesAttribute;
33 import javax.validation.Payload;
34 import javax.validation.ReportAsSingleViolation;
35 import javax.validation.constraintvalidation.SupportedValidationTarget;
36 import javax.validation.constraintvalidation.ValidationTarget;
37 import javax.validation.groups.Default;
38 import javax.validation.metadata.ConstraintDescriptor;
39 import javax.validation.metadata.ValidateUnwrappedValue;
40 import javax.validation.valueextraction.Unwrapping;
41
42 import org.hibernate.validator.constraints.CompositionType;
43 import org.hibernate.validator.constraints.ConstraintComposition;
44 import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorDescriptor;
45 import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
46 import org.hibernate.validator.internal.metadata.core.ConstraintOrigin;
47 import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind;
48 import org.hibernate.validator.internal.properties.Callable;
49 import org.hibernate.validator.internal.properties.Constrainable;
50 import org.hibernate.validator.internal.properties.Property;
51 import org.hibernate.validator.internal.util.CollectionHelper;
52 import org.hibernate.validator.internal.util.StringHelper;
53 import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor;
54 import org.hibernate.validator.internal.util.logging.Log;
55 import org.hibernate.validator.internal.util.logging.LoggerFactory;
56 import org.hibernate.validator.internal.util.privilegedactions.GetAnnotationAttributes;
57 import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredMethods;
58 import org.hibernate.validator.internal.util.privilegedactions.GetMethod;
59 import org.hibernate.validator.internal.util.stereotypes.Immutable;
60
61
70 public class ConstraintDescriptorImpl<T extends Annotation> implements ConstraintDescriptor<T>, Serializable {
71
72 private static final long serialVersionUID = -2563102960314069246L;
73 private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
74 private static final int OVERRIDES_PARAMETER_DEFAULT_INDEX = -1;
75
76
79 private static final List<String> NON_COMPOSING_CONSTRAINT_ANNOTATIONS = Arrays.asList(
80 Documented.class.getName(),
81 Retention.class.getName(),
82 Target.class.getName(),
83 Constraint.class.getName(),
84 ReportAsSingleViolation.class.getName(),
85 Repeatable.class.getName(),
86 Deprecated.class.getName()
87 );
88
89
92 private final ConstraintAnnotationDescriptor<T> annotationDescriptor;
93
94
98 @Immutable
99 private final List<Class<? extends ConstraintValidator<T, ?>>> constraintValidatorClasses;
100
101
105 @Immutable
106 private final transient List<ConstraintValidatorDescriptor<T>> matchingConstraintValidatorDescriptors;
107
108
111 @Immutable
112 private final Set<Class<?>> groups;
113
114
117 @Immutable
118 private final Set<Class<? extends Payload>> payloads;
119
120
123 @Immutable
124 private final Set<ConstraintDescriptorImpl<?>> composingConstraints;
125
126
129 private final boolean isReportAsSingleInvalidConstraint;
130
131
135 private final ConstraintLocationKind constraintLocationKind;
136
137
140 private final ConstraintOrigin definedOn;
141
142
145 private final ConstraintType constraintType;
146
147
150 private final ValidateUnwrappedValue valueUnwrapping;
151
152
155 private final ConstraintTarget validationAppliesTo;
156
157
161 private final CompositionType compositionType;
162
163 private final int hashCode;
164
165 public ConstraintDescriptorImpl(ConstraintHelper constraintHelper,
166 Constrainable constrainable,
167 ConstraintAnnotationDescriptor<T> annotationDescriptor,
168 ConstraintLocationKind constraintLocationKind,
169 Class<?> implicitGroup,
170 ConstraintOrigin definedOn,
171 ConstraintType externalConstraintType) {
172 this.annotationDescriptor = annotationDescriptor;
173 this.constraintLocationKind = constraintLocationKind;
174 this.definedOn = definedOn;
175 this.isReportAsSingleInvalidConstraint = annotationDescriptor.getType().isAnnotationPresent(
176 ReportAsSingleViolation.class
177 );
178
179
180
181 this.groups = buildGroupSet( annotationDescriptor, implicitGroup );
182 this.payloads = buildPayloadSet( annotationDescriptor );
183
184 this.valueUnwrapping = determineValueUnwrapping( this.payloads, constrainable, annotationDescriptor.getType() );
185
186 this.validationAppliesTo = determineValidationAppliesTo( annotationDescriptor );
187
188 this.constraintValidatorClasses = constraintHelper.getAllValidatorDescriptors( annotationDescriptor.getType() )
189 .stream()
190 .map( ConstraintValidatorDescriptor::getValidatorClass )
191 .collect( Collectors.collectingAndThen( Collectors.toList(), CollectionHelper::toImmutableList ) );
192
193 List<ConstraintValidatorDescriptor<T>> crossParameterValidatorDescriptors = CollectionHelper.toImmutableList( constraintHelper.findValidatorDescriptors(
194 annotationDescriptor.getType(),
195 ValidationTarget.PARAMETERS
196 ) );
197 List<ConstraintValidatorDescriptor<T>> genericValidatorDescriptors = CollectionHelper.toImmutableList( constraintHelper.findValidatorDescriptors(
198 annotationDescriptor.getType(),
199 ValidationTarget.ANNOTATED_ELEMENT
200 ) );
201
202 if ( crossParameterValidatorDescriptors.size() > 1 ) {
203 throw LOG.getMultipleCrossParameterValidatorClassesException( annotationDescriptor.getType() );
204 }
205
206 this.constraintType = determineConstraintType(
207 annotationDescriptor.getType(),
208 constrainable,
209 !genericValidatorDescriptors.isEmpty(),
210 !crossParameterValidatorDescriptors.isEmpty(),
211 externalConstraintType
212 );
213 this.composingConstraints = parseComposingConstraints( constraintHelper, constrainable, constraintType );
214 this.compositionType = parseCompositionType( constraintHelper );
215 validateComposingConstraintTypes();
216
217 if ( constraintType == ConstraintType.GENERIC ) {
218 this.matchingConstraintValidatorDescriptors = CollectionHelper.toImmutableList( genericValidatorDescriptors );
219 }
220 else {
221 this.matchingConstraintValidatorDescriptors = CollectionHelper.toImmutableList( crossParameterValidatorDescriptors );
222 }
223
224 this.hashCode = annotationDescriptor.hashCode();
225 }
226
227 public ConstraintDescriptorImpl(ConstraintHelper constraintHelper,
228 Constrainable constrainable,
229 ConstraintAnnotationDescriptor<T> annotationDescriptor,
230 ConstraintLocationKind constraintLocationKind) {
231 this( constraintHelper, constrainable, annotationDescriptor, constraintLocationKind, null, ConstraintOrigin.DEFINED_LOCALLY, null );
232 }
233
234 public ConstraintDescriptorImpl(ConstraintHelper constraintHelper,
235 Constrainable constrainable,
236 ConstraintAnnotationDescriptor<T> annotationDescriptor,
237 ConstraintLocationKind constraintLocationKind,
238 ConstraintType constraintType) {
239 this( constraintHelper, constrainable, annotationDescriptor, constraintLocationKind, null, ConstraintOrigin.DEFINED_LOCALLY, constraintType );
240 }
241
242 public ConstraintAnnotationDescriptor<T> getAnnotationDescriptor() {
243 return annotationDescriptor;
244 }
245
246 @Override
247 public T getAnnotation() {
248 return annotationDescriptor.getAnnotation();
249 }
250
251 public Class<T> getAnnotationType() {
252 return annotationDescriptor.getType();
253 }
254
255 @Override
256 public String getMessageTemplate() {
257 return annotationDescriptor.getMessage();
258 }
259
260 @Override
261 public Set<Class<?>> getGroups() {
262 return groups;
263 }
264
265 @Override
266 public Set<Class<? extends Payload>> getPayload() {
267 return payloads;
268 }
269
270 @Override
271 public ConstraintTarget getValidationAppliesTo() {
272 return validationAppliesTo;
273 }
274
275 @Override
276 public ValidateUnwrappedValue getValueUnwrapping() {
277 return valueUnwrapping;
278 }
279
280 @Override
281 public List<Class<? extends ConstraintValidator<T, ?>>> getConstraintValidatorClasses() {
282 return constraintValidatorClasses;
283 }
284
285
291 public List<ConstraintValidatorDescriptor<T>> getMatchingConstraintValidatorDescriptors() {
292 return matchingConstraintValidatorDescriptors;
293 }
294
295 @Override
296 public Map<String, Object> getAttributes() {
297 return annotationDescriptor.getAttributes();
298 }
299
300 @SuppressWarnings("unchecked")
301 @Override
302 public Set<ConstraintDescriptor<?>> getComposingConstraints() {
303 return (Set<ConstraintDescriptor<?>>) (Object) composingConstraints;
304 }
305
306 public Set<ConstraintDescriptorImpl<?>> getComposingConstraintImpls() {
307 return composingConstraints;
308 }
309
310 @Override
311 public boolean isReportAsSingleViolation() {
312 return isReportAsSingleInvalidConstraint;
313 }
314
315 public ConstraintLocationKind getConstraintLocationKind() {
316 return constraintLocationKind;
317 }
318
319 public ConstraintOrigin getDefinedOn() {
320 return definedOn;
321 }
322
323 public ConstraintType getConstraintType() {
324 return constraintType;
325 }
326
327 @Override
328 public <U> U unwrap(Class<U> type) {
329 throw LOG.getUnwrappingOfConstraintDescriptorNotSupportedYetException();
330 }
331
332 @Override
333 public boolean equals(Object o) {
334 if ( this == o ) {
335 return true;
336 }
337 if ( o == null || getClass() != o.getClass() ) {
338 return false;
339 }
340
341 ConstraintDescriptorImpl<?> that = (ConstraintDescriptorImpl<?>) o;
342
343 if ( annotationDescriptor != null ? !annotationDescriptor.equals( that.annotationDescriptor ) : that.annotationDescriptor != null ) {
344 return false;
345 }
346
347 return true;
348 }
349
350 @Override
351 public int hashCode() {
352 return hashCode;
353 }
354
355 @Override
356 public String toString() {
357 final StringBuilder sb = new StringBuilder();
358 sb.append( "ConstraintDescriptorImpl" );
359 sb.append( "{annotation=" ).append( StringHelper.toShortString( annotationDescriptor.getType() ) );
360 sb.append( ", payloads=" ).append( payloads );
361 sb.append( ", hasComposingConstraints=" ).append( composingConstraints.isEmpty() );
362 sb.append( ", isReportAsSingleInvalidConstraint=" ).append( isReportAsSingleInvalidConstraint );
363 sb.append( ", constraintLocationKind=" ).append( constraintLocationKind );
364 sb.append( ", definedOn=" ).append( definedOn );
365 sb.append( ", groups=" ).append( groups );
366 sb.append( ", attributes=" ).append( annotationDescriptor.getAttributes() );
367 sb.append( ", constraintType=" ).append( constraintType );
368 sb.append( ", valueUnwrapping=" ).append( valueUnwrapping );
369 sb.append( '}' );
370 return sb.toString();
371 }
372
373
399 private ConstraintType determineConstraintType(Class<? extends Annotation> constraintAnnotationType,
400 Constrainable constrainable,
401 boolean hasGenericValidators,
402 boolean hasCrossParameterValidator,
403 ConstraintType externalConstraintType) {
404 ConstraintTarget constraintTarget = validationAppliesTo;
405 ConstraintType constraintType = null;
406 boolean isExecutable = constraintLocationKind.isExecutable();
407
408
409 if ( constraintTarget == ConstraintTarget.RETURN_VALUE ) {
410 if ( !isExecutable ) {
411 throw LOG.getParametersOrReturnValueConstraintTargetGivenAtNonExecutableException(
412 annotationDescriptor.getType(),
413 ConstraintTarget.RETURN_VALUE
414 );
415 }
416 constraintType = ConstraintType.GENERIC;
417 }
418
419 else if ( constraintTarget == ConstraintTarget.PARAMETERS ) {
420 if ( !isExecutable ) {
421 throw LOG.getParametersOrReturnValueConstraintTargetGivenAtNonExecutableException(
422 annotationDescriptor.getType(),
423 ConstraintTarget.PARAMETERS
424 );
425 }
426 constraintType = ConstraintType.CROSS_PARAMETER;
427 }
428
429 else if ( externalConstraintType != null ) {
430 constraintType = externalConstraintType;
431 }
432
433 else {
434
435 if ( hasGenericValidators && !hasCrossParameterValidator ) {
436 constraintType = ConstraintType.GENERIC;
437 }
438 else if ( !hasGenericValidators && hasCrossParameterValidator ) {
439 constraintType = ConstraintType.CROSS_PARAMETER;
440 }
441 else if ( !isExecutable ) {
442 constraintType = ConstraintType.GENERIC;
443 }
444 else if ( constraintAnnotationType.isAnnotationPresent( SupportedValidationTarget.class ) ) {
445 SupportedValidationTarget supportedValidationTarget = constraintAnnotationType.getAnnotation( SupportedValidationTarget.class );
446 if ( supportedValidationTarget.value().length == 1 ) {
447 constraintType = supportedValidationTarget.value()[0] == ValidationTarget.ANNOTATED_ELEMENT ? ConstraintType.GENERIC : ConstraintType.CROSS_PARAMETER;
448 }
449 }
450
451
452
453 else if ( constrainable instanceof Callable ) {
454 boolean hasParameters = constrainable.as( Callable.class ).hasParameters();
455 boolean hasReturnValue = constrainable.as( Callable.class ).hasReturnValue();
456
457 if ( !hasParameters && hasReturnValue ) {
458 constraintType = ConstraintType.GENERIC;
459 }
460 else if ( hasParameters && !hasReturnValue ) {
461 constraintType = ConstraintType.CROSS_PARAMETER;
462 }
463 }
464 }
465
466
467 if ( constraintType == null ) {
468 throw LOG.getImplicitConstraintTargetInAmbiguousConfigurationException( annotationDescriptor.getType() );
469 }
470
471 if ( constraintType == ConstraintType.CROSS_PARAMETER ) {
472 validateCrossParameterConstraintType( constrainable, hasCrossParameterValidator );
473 }
474
475 return constraintType;
476 }
477
478 private static ValidateUnwrappedValue determineValueUnwrapping(Set<Class<? extends Payload>> payloads, Constrainable constrainable, Class<? extends Annotation> annotationType) {
479 if ( payloads.contains( Unwrapping.Unwrap.class ) ) {
480 if ( payloads.contains( Unwrapping.Skip.class ) ) {
481 throw LOG.getInvalidUnwrappingConfigurationForConstraintException( constrainable, annotationType );
482 }
483
484 return ValidateUnwrappedValue.UNWRAP;
485 }
486
487 if ( payloads.contains( Unwrapping.Skip.class ) ) {
488 return ValidateUnwrappedValue.SKIP;
489 }
490
491 return ValidateUnwrappedValue.DEFAULT;
492 }
493
494 private static ConstraintTarget determineValidationAppliesTo(ConstraintAnnotationDescriptor<?> annotationDescriptor) {
495 return annotationDescriptor.getValidationAppliesTo();
496 }
497
498 private void validateCrossParameterConstraintType(Constrainable constrainable, boolean hasCrossParameterValidator) {
499 if ( !hasCrossParameterValidator ) {
500 throw LOG.getCrossParameterConstraintHasNoValidatorException( annotationDescriptor.getType() );
501 }
502 else if ( constrainable == null ) {
503 throw LOG.getCrossParameterConstraintOnClassException( annotationDescriptor.getType() );
504 }
505 else if ( constrainable instanceof Property ) {
506 throw LOG.getCrossParameterConstraintOnFieldException( annotationDescriptor.getType(), constrainable );
507 }
508 else if ( !constrainable.as( Callable.class ).hasParameters() ) {
509 throw LOG.getCrossParameterConstraintOnMethodWithoutParametersException(
510 annotationDescriptor.getType(),
511 constrainable
512 );
513 }
514 }
515
516
520 private void validateComposingConstraintTypes() {
521 for ( ConstraintDescriptorImpl<?> composingConstraint : getComposingConstraintImpls() ) {
522 if ( composingConstraint.constraintType != constraintType ) {
523 throw LOG.getComposedAndComposingConstraintsHaveDifferentTypesException(
524 annotationDescriptor.getType(),
525 composingConstraint.annotationDescriptor.getType(),
526 constraintType,
527 composingConstraint.constraintType
528 );
529 }
530 }
531 }
532
533 @SuppressWarnings("unchecked")
534 private static Set<Class<? extends Payload>> buildPayloadSet(ConstraintAnnotationDescriptor<?> annotationDescriptor) {
535 Set<Class<? extends Payload>> payloadSet = newHashSet();
536
537 Class<? extends Payload>[] payloadFromAnnotation = annotationDescriptor.getPayload();
538
539 if ( payloadFromAnnotation != null ) {
540 payloadSet.addAll( Arrays.asList( payloadFromAnnotation ) );
541 }
542 return CollectionHelper.toImmutableSet( payloadSet );
543 }
544
545 private static Set<Class<?>> buildGroupSet(ConstraintAnnotationDescriptor<?> annotationDescriptor, Class<?> implicitGroup) {
546 Set<Class<?>> groupSet = newHashSet();
547 final Class<?>[] groupsFromAnnotation = annotationDescriptor.getGroups();
548 if ( groupsFromAnnotation.length == 0 ) {
549 groupSet.add( Default.class );
550 }
551 else {
552 groupSet.addAll( Arrays.asList( groupsFromAnnotation ) );
553 }
554
555
556 if ( implicitGroup != null && groupSet.contains( Default.class ) ) {
557 groupSet.add( implicitGroup );
558 }
559 return CollectionHelper.toImmutableSet( groupSet );
560 }
561
562 private Map<ClassIndexWrapper, Map<String, Object>> parseOverrideParameters() {
563 Map<ClassIndexWrapper, Map<String, Object>> overrideParameters = newHashMap();
564 final Method[] methods = run( GetDeclaredMethods.action( annotationDescriptor.getType() ) );
565 for ( Method m : methods ) {
566 if ( m.getAnnotation( OverridesAttribute.class ) != null ) {
567 addOverrideAttributes(
568 overrideParameters, m, m.getAnnotation( OverridesAttribute.class )
569 );
570 }
571 else if ( m.getAnnotation( OverridesAttribute.List.class ) != null ) {
572 addOverrideAttributes(
573 overrideParameters,
574 m,
575 m.getAnnotation( OverridesAttribute.List.class ).value()
576 );
577 }
578 }
579 return overrideParameters;
580 }
581
582 private void addOverrideAttributes(Map<ClassIndexWrapper, Map<String, Object>> overrideParameters, Method m, OverridesAttribute... attributes) {
583 Object value = annotationDescriptor.getAttribute( m.getName() );
584 for ( OverridesAttribute overridesAttribute : attributes ) {
585 String overridesAttributeName = overridesAttribute.name().length() > 0 ? overridesAttribute.name() : m.getName();
586
587 ensureAttributeIsOverridable( m, overridesAttribute, overridesAttributeName );
588
589 ClassIndexWrapper wrapper = new ClassIndexWrapper(
590 overridesAttribute.constraint(), overridesAttribute.constraintIndex()
591 );
592 Map<String, Object> map = overrideParameters.get( wrapper );
593 if ( map == null ) {
594 map = newHashMap();
595 overrideParameters.put( wrapper, map );
596 }
597 map.put( overridesAttributeName, value );
598 }
599 }
600
601 private void ensureAttributeIsOverridable(Method m, OverridesAttribute overridesAttribute, String overridesAttributeName) {
602 final Method method = run( GetMethod.action( overridesAttribute.constraint(), overridesAttributeName ) );
603 if ( method == null ) {
604 throw LOG.getOverriddenConstraintAttributeNotFoundException( overridesAttributeName );
605 }
606 Class<?> returnTypeOfOverriddenConstraint = method.getReturnType();
607 if ( !returnTypeOfOverriddenConstraint.equals( m.getReturnType() ) ) {
608 throw LOG.getWrongAttributeTypeForOverriddenConstraintException(
609 returnTypeOfOverriddenConstraint,
610 m.getReturnType()
611 );
612 }
613 }
614
615 private Set<ConstraintDescriptorImpl<?>> parseComposingConstraints(ConstraintHelper constraintHelper, Constrainable constrainable,
616 ConstraintType constraintType) {
617 Set<ConstraintDescriptorImpl<?>> composingConstraintsSet = newHashSet();
618 Map<ClassIndexWrapper, Map<String, Object>> overrideParameters = parseOverrideParameters();
619 Map<Class<? extends Annotation>, ComposingConstraintAnnotationLocation> composingConstraintLocations = new HashMap<>();
620
621 for ( Annotation declaredAnnotation : annotationDescriptor.getType().getDeclaredAnnotations() ) {
622 Class<? extends Annotation> declaredAnnotationType = declaredAnnotation.annotationType();
623 if ( NON_COMPOSING_CONSTRAINT_ANNOTATIONS.contains( declaredAnnotationType.getName() ) ) {
624
625 continue;
626 }
627
628 if ( constraintHelper.isConstraintAnnotation( declaredAnnotationType ) ) {
629 if ( composingConstraintLocations.containsKey( declaredAnnotationType )
630 && !ComposingConstraintAnnotationLocation.DIRECT.equals( composingConstraintLocations.get( declaredAnnotationType ) ) ) {
631 throw LOG.getCannotMixDirectAnnotationAndListContainerOnComposedConstraintException( annotationDescriptor.getType(), declaredAnnotationType );
632 }
633
634 ConstraintDescriptorImpl<?> descriptor = createComposingConstraintDescriptor(
635 constraintHelper,
636 constrainable,
637 overrideParameters,
638 OVERRIDES_PARAMETER_DEFAULT_INDEX,
639 declaredAnnotation,
640 constraintType
641 );
642 composingConstraintsSet.add( descriptor );
643 composingConstraintLocations.put( declaredAnnotationType, ComposingConstraintAnnotationLocation.DIRECT );
644 LOG.debugf( "Adding composing constraint: %s.", descriptor );
645 }
646 else if ( constraintHelper.isMultiValueConstraint( declaredAnnotationType ) ) {
647 List<Annotation> multiValueConstraints = constraintHelper.getConstraintsFromMultiValueConstraint( declaredAnnotation );
648 int index = 0;
649 for ( Annotation constraintAnnotation : multiValueConstraints ) {
650 if ( composingConstraintLocations.containsKey( constraintAnnotation.annotationType() )
651 && !ComposingConstraintAnnotationLocation.IN_CONTAINER.equals( composingConstraintLocations.get( constraintAnnotation.annotationType() ) ) ) {
652 throw LOG.getCannotMixDirectAnnotationAndListContainerOnComposedConstraintException( annotationDescriptor.getType(),
653 constraintAnnotation.annotationType() );
654 }
655
656 ConstraintDescriptorImpl<?> descriptor = createComposingConstraintDescriptor(
657 constraintHelper,
658 constrainable,
659 overrideParameters,
660 index,
661 constraintAnnotation,
662 constraintType
663 );
664 composingConstraintsSet.add( descriptor );
665 composingConstraintLocations.put( constraintAnnotation.annotationType(), ComposingConstraintAnnotationLocation.IN_CONTAINER );
666 LOG.debugf( "Adding composing constraint: %s.", descriptor );
667 index++;
668 }
669 }
670 }
671 return CollectionHelper.toImmutableSet( composingConstraintsSet );
672 }
673
674 private CompositionType parseCompositionType(ConstraintHelper constraintHelper) {
675 for ( Annotation declaredAnnotation : annotationDescriptor.getType().getDeclaredAnnotations() ) {
676 Class<? extends Annotation> declaredAnnotationType = declaredAnnotation.annotationType();
677 if ( NON_COMPOSING_CONSTRAINT_ANNOTATIONS.contains( declaredAnnotationType.getName() ) ) {
678
679 continue;
680 }
681
682 if ( constraintHelper.isConstraintComposition( declaredAnnotationType ) ) {
683 if ( LOG.isDebugEnabled() ) {
684 LOG.debugf( "Adding Bool %s.", declaredAnnotationType.getName() );
685 }
686 return ( (ConstraintComposition) declaredAnnotation ).value();
687 }
688 }
689 return CompositionType.AND;
690 }
691
692 private <U extends Annotation> ConstraintDescriptorImpl<U> createComposingConstraintDescriptor(
693 ConstraintHelper constraintHelper,
694 Constrainable constrainable,
695 Map<ClassIndexWrapper, Map<String, Object>> overrideParameters,
696 int index,
697 U constraintAnnotation,
698 ConstraintType constraintType) {
699
700 @SuppressWarnings("unchecked")
701 final Class<U> annotationType = (Class<U>) constraintAnnotation.annotationType();
702
703
704 ConstraintAnnotationDescriptor.Builder<U> annotationDescriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>(
705 annotationType, run( GetAnnotationAttributes.action( constraintAnnotation ) )
706 );
707
708
709 Map<String, Object> overrides = overrideParameters.get(
710 new ClassIndexWrapper(
711 annotationType, index
712 )
713 );
714 if ( overrides != null ) {
715 for ( Map.Entry<String, Object> entry : overrides.entrySet() ) {
716 annotationDescriptorBuilder.setAttribute( entry.getKey(), entry.getValue() );
717 }
718 }
719
720
721 annotationDescriptorBuilder.setGroups( groups.toArray( new Class<?>[groups.size()] ) );
722 annotationDescriptorBuilder.setPayload( payloads.toArray( new Class<?>[payloads.size()] ) );
723 if ( annotationDescriptorBuilder.hasAttribute( ConstraintHelper.VALIDATION_APPLIES_TO ) ) {
724 ConstraintTarget validationAppliesTo = getValidationAppliesTo();
725
726
727 if ( validationAppliesTo == null ) {
728 if ( constraintType == ConstraintType.CROSS_PARAMETER ) {
729 validationAppliesTo = ConstraintTarget.PARAMETERS;
730 }
731 else {
732 validationAppliesTo = ConstraintTarget.IMPLICIT;
733 }
734 }
735
736 annotationDescriptorBuilder.setAttribute( ConstraintHelper.VALIDATION_APPLIES_TO, validationAppliesTo );
737 }
738
739 return new ConstraintDescriptorImpl<>(
740 constraintHelper, constrainable, annotationDescriptorBuilder.build(), constraintLocationKind, null, definedOn, constraintType
741 );
742 }
743
744
750 private static <P> P run(PrivilegedAction<P> action) {
751 return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run();
752 }
753
754
757 public CompositionType getCompositionType() {
758 return compositionType;
759 }
760
761
764 private static class ClassIndexWrapper {
765 final Class<?> clazz;
766 final int index;
767
768 ClassIndexWrapper(Class<?> clazz, int index) {
769 this.clazz = clazz;
770 this.index = index;
771 }
772
773 @Override
774 public boolean equals(Object o) {
775 if ( this == o ) {
776 return true;
777 }
778 if ( o == null || getClass() != o.getClass() ) {
779 return false;
780 }
781
782 @SuppressWarnings("unchecked")
783 ClassIndexWrapper that = (ClassIndexWrapper) o;
784
785 if ( index != that.index ) {
786 return false;
787 }
788
789 return clazz.equals( that.clazz );
790 }
791
792 @Override
793 public int hashCode() {
794 int result = clazz != null ? clazz.hashCode() : 0;
795 result = 31 * result + index;
796 return result;
797 }
798
799 @Override
800 public String toString() {
801 return "ClassIndexWrapper [clazz=" + StringHelper.toShortString( clazz ) + ", index=" + index + "]";
802 }
803 }
804
805
808 public enum ConstraintType {
809
812 GENERIC,
813
814
817 CROSS_PARAMETER
818 }
819
820
823 private enum ComposingConstraintAnnotationLocation {
824
827 DIRECT,
828
829
832 IN_CONTAINER
833 }
834 }
835