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

org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl Maven / Gradle / Ivy

Go to download

JSR 380's RI, Hibernate Validator version ${hibernate-validator.version} and its dependencies repackaged as OSGi bundle

There is a newer version: 5.1.0
Show newest version
/*
 * Hibernate Validator, declare and validate application constraints
 *
 * License: Apache License, Version 2.0
 * See the license.txt file in the root directory or .
 */
package org.hibernate.validator.internal.metadata.descriptor;

import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap;
import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import javax.validation.Constraint;
import javax.validation.ConstraintTarget;
import javax.validation.ConstraintValidator;
import javax.validation.OverridesAttribute;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.constraintvalidation.SupportedValidationTarget;
import javax.validation.constraintvalidation.ValidationTarget;
import javax.validation.groups.Default;
import javax.validation.metadata.ConstraintDescriptor;
import javax.validation.metadata.ValidateUnwrappedValue;
import javax.validation.valueextraction.Unwrapping;

import org.hibernate.validator.constraints.CompositionType;
import org.hibernate.validator.constraints.ConstraintComposition;
import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorDescriptor;
import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
import org.hibernate.validator.internal.metadata.core.ConstraintOrigin;
import org.hibernate.validator.internal.util.CollectionHelper;
import org.hibernate.validator.internal.util.StringHelper;
import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor;
import org.hibernate.validator.internal.util.logging.Log;
import org.hibernate.validator.internal.util.logging.LoggerFactory;
import org.hibernate.validator.internal.util.privilegedactions.GetAnnotationAttributes;
import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredMethods;
import org.hibernate.validator.internal.util.privilegedactions.GetMethod;
import org.hibernate.validator.internal.util.stereotypes.Immutable;

/**
 * Describes a single constraint (including its composing constraints).
 *
 * @author Emmanuel Bernard
 * @author Hardy Ferentschik
 * @author Federico Mancini
 * @author Dag Hovland
 * @author Guillaume Smet
 */
public class ConstraintDescriptorImpl implements ConstraintDescriptor, Serializable {

	private static final long serialVersionUID = -2563102960314069246L;
	private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
	private static final int OVERRIDES_PARAMETER_DEFAULT_INDEX = -1;

	/**
	 * A list of annotations which can be ignored when investigating for composing constraints.
	 */
	private static final List NON_COMPOSING_CONSTRAINT_ANNOTATIONS = Arrays.asList(
			Documented.class.getName(),
			Retention.class.getName(),
			Target.class.getName(),
			Constraint.class.getName(),
			ReportAsSingleViolation.class.getName(),
			Repeatable.class.getName(),
			Deprecated.class.getName()
	);

	/**
	 * The annotation descriptor - accessing the annotation information has a cost so we do it only once.
	 */
	private final ConstraintAnnotationDescriptor annotationDescriptor;

	/**
	 * The set of classes implementing the validation for this constraint. See also
	 * {@code ConstraintValidator} resolution algorithm.
	 */
	@Immutable
	private final List>> constraintValidatorClasses;

	/**
	 * This field is transient as the implementations of  {@link ConstraintValidatorDescriptor} might not be serializable.
	 * Typically {@code ClassBasedValidatorDescriptor} might contain a {@code ParameterizedTypeImpl}, which is not serializable.
	 */
	@Immutable
	private final transient List> matchingConstraintValidatorDescriptors;

	/**
	 * The groups for which to apply this constraint.
	 */
	@Immutable
	private final Set> groups;

	/**
	 * The specified payload of the constraint.
	 */
	@Immutable
	private final Set> payloads;

	/**
	 * The composing constraints for this constraint.
	 */
	@Immutable
	private final Set> composingConstraints;

	/**
	 * Flag indicating if in case of a composing constraint a single error or multiple errors should be raised.
	 */
	private final boolean isReportAsSingleInvalidConstraint;

	/**
	 * Describes on which level ({@code TYPE}, {@code METHOD}, {@code FIELD}) the constraint was
	 * defined on.
	 */
	private final ElementType elementType;

	/**
	 * The origin of the constraint. Defined on the actual root class or somewhere in the class hierarchy
	 */
	private final ConstraintOrigin definedOn;

	/**
	 * The type of this constraint.
	 */
	private final ConstraintType constraintType;

	/**
	 * The unwrapping behavior defined on the constraint.
	 */
	private final ValidateUnwrappedValue valueUnwrapping;

	/**
	 * The target of the constraint.
	 */
	private final ConstraintTarget validationAppliesTo;

	/**
	 * Type indicating how composing constraints should be combined. By default this is set to
	 * {@code ConstraintComposition.CompositionType.AND}.
	 */
	private final CompositionType compositionType;

	private final int hashCode;

	public ConstraintDescriptorImpl(ConstraintHelper constraintHelper,
			Member member,
			ConstraintAnnotationDescriptor annotationDescriptor,
			ElementType type,
			Class implicitGroup,
			ConstraintOrigin definedOn,
			ConstraintType externalConstraintType) {
		this.annotationDescriptor = annotationDescriptor;
		this.elementType = type;
		this.definedOn = definedOn;
		this.isReportAsSingleInvalidConstraint = annotationDescriptor.getType().isAnnotationPresent(
				ReportAsSingleViolation.class
		);

		// HV-181 - To avoid any thread visibility issues we are building the different data structures in tmp variables and
		// then assign them to the final variables
		this.groups = buildGroupSet( annotationDescriptor, implicitGroup );
		this.payloads = buildPayloadSet( annotationDescriptor );

		this.valueUnwrapping = determineValueUnwrapping( this.payloads, member, annotationDescriptor.getType() );

		this.validationAppliesTo = determineValidationAppliesTo( annotationDescriptor );

		this.constraintValidatorClasses = constraintHelper.getAllValidatorDescriptors( annotationDescriptor.getType() )
				.stream()
				.map( ConstraintValidatorDescriptor::getValidatorClass )
				.collect( Collectors.collectingAndThen( Collectors.toList(), CollectionHelper::toImmutableList ) );

		List> crossParameterValidatorDescriptors = CollectionHelper.toImmutableList( constraintHelper.findValidatorDescriptors(
				annotationDescriptor.getType(),
				ValidationTarget.PARAMETERS
		) );
		List> genericValidatorDescriptors = CollectionHelper.toImmutableList( constraintHelper.findValidatorDescriptors(
				annotationDescriptor.getType(),
				ValidationTarget.ANNOTATED_ELEMENT
		) );

		if ( crossParameterValidatorDescriptors.size() > 1 ) {
			throw LOG.getMultipleCrossParameterValidatorClassesException( annotationDescriptor.getType() );
		}

		this.constraintType = determineConstraintType(
				annotationDescriptor.getType(),
				member,
				type,
				!genericValidatorDescriptors.isEmpty(),
				!crossParameterValidatorDescriptors.isEmpty(),
				externalConstraintType
		);
		this.composingConstraints = parseComposingConstraints( constraintHelper, member, constraintType );
		this.compositionType = parseCompositionType( constraintHelper );
		validateComposingConstraintTypes();

		if ( constraintType == ConstraintType.GENERIC ) {
			this.matchingConstraintValidatorDescriptors = CollectionHelper.toImmutableList( genericValidatorDescriptors );
		}
		else {
			this.matchingConstraintValidatorDescriptors = CollectionHelper.toImmutableList( crossParameterValidatorDescriptors );
		}

		this.hashCode = annotationDescriptor.hashCode();
	}

	public ConstraintDescriptorImpl(ConstraintHelper constraintHelper,
			Member member,
			ConstraintAnnotationDescriptor annotationDescriptor,
			ElementType type) {
		this( constraintHelper, member, annotationDescriptor, type, null, ConstraintOrigin.DEFINED_LOCALLY, null );
	}

	public ConstraintDescriptorImpl(ConstraintHelper constraintHelper,
			Member member,
			ConstraintAnnotationDescriptor annotationDescriptor,
			ElementType type,
			ConstraintType constraintType) {
		this( constraintHelper, member, annotationDescriptor, type, null, ConstraintOrigin.DEFINED_LOCALLY, constraintType );
	}

	public ConstraintAnnotationDescriptor getAnnotationDescriptor() {
		return annotationDescriptor;
	}

	@Override
	public T getAnnotation() {
		return annotationDescriptor.getAnnotation();
	}

	public Class getAnnotationType() {
		return annotationDescriptor.getType();
	}

	@Override
	public String getMessageTemplate() {
		return annotationDescriptor.getMessage();
	}

	@Override
	public Set> getGroups() {
		return groups;
	}

	@Override
	public Set> getPayload() {
		return payloads;
	}

	@Override
	public ConstraintTarget getValidationAppliesTo() {
		return validationAppliesTo;
	}

	@Override
	public ValidateUnwrappedValue getValueUnwrapping() {
		return valueUnwrapping;
	}

	@Override
	public List>> getConstraintValidatorClasses() {
		return constraintValidatorClasses;
	}

	/**
	 * Return all constraint validator descriptors (either generic or cross-parameter) which are registered for the
	 * constraint of this descriptor.
	 *
	 * @return The constraint validator descriptors applying to type of this constraint.
	 */
	public List> getMatchingConstraintValidatorDescriptors() {
		return matchingConstraintValidatorDescriptors;
	}

	@Override
	public Map getAttributes() {
		return annotationDescriptor.getAttributes();
	}

	@SuppressWarnings("unchecked")
	@Override
	public Set> getComposingConstraints() {
		return (Set>) (Object) composingConstraints;
	}

	public Set> getComposingConstraintImpls() {
		return composingConstraints;
	}

	@Override
	public boolean isReportAsSingleViolation() {
		return isReportAsSingleInvalidConstraint;
	}

	public ElementType getElementType() {
		return elementType;
	}

	public ConstraintOrigin getDefinedOn() {
		return definedOn;
	}

	public ConstraintType getConstraintType() {
		return constraintType;
	}

	@Override
	public  U unwrap(Class type) {
		throw LOG.getUnwrappingOfConstraintDescriptorNotSupportedYetException();
	}

	@Override
	public boolean equals(Object o) {
		if ( this == o ) {
			return true;
		}
		if ( o == null || getClass() != o.getClass() ) {
			return false;
		}

		ConstraintDescriptorImpl that = (ConstraintDescriptorImpl) o;

		if ( annotationDescriptor != null ? !annotationDescriptor.equals( that.annotationDescriptor ) : that.annotationDescriptor != null ) {
			return false;
		}

		return true;
	}

	@Override
	public int hashCode() {
		return hashCode;
	}

	@Override
	public String toString() {
		final StringBuilder sb = new StringBuilder();
		sb.append( "ConstraintDescriptorImpl" );
		sb.append( "{annotation=" ).append( StringHelper.toShortString( annotationDescriptor.getType() ) );
		sb.append( ", payloads=" ).append( payloads );
		sb.append( ", hasComposingConstraints=" ).append( composingConstraints.isEmpty() );
		sb.append( ", isReportAsSingleInvalidConstraint=" ).append( isReportAsSingleInvalidConstraint );
		sb.append( ", elementType=" ).append( elementType );
		sb.append( ", definedOn=" ).append( definedOn );
		sb.append( ", groups=" ).append( groups );
		sb.append( ", attributes=" ).append( annotationDescriptor.getAttributes() );
		sb.append( ", constraintType=" ).append( constraintType );
		sb.append( ", valueUnwrapping=" ).append( valueUnwrapping );
		sb.append( '}' );
		return sb.toString();
	}

	/**
	 * Determines the type of this constraint. The following rules apply in
	 * descending order:
	 * 
    *
  • If {@code validationAppliesTo()} is set to either * {@link ConstraintTarget#RETURN_VALUE} or * {@link ConstraintTarget#PARAMETERS}, this value will be considered.
  • *
  • Otherwise, if the constraint is either purely generic or purely * cross-parameter as per its validators, that value will be considered.
  • *
  • Otherwise, if the constraint is not on an executable, it is * considered generic.
  • *
  • Otherwise, the type will be determined based on exclusive existence * of parameters and return value.
  • *
  • If that also is not possible, determination fails (i.e. the user must * specify the target explicitly).
  • *
* * @param member The annotated member * @param elementType The type of the annotated element * @param hasGenericValidators Whether the constraint has at least one generic validator or * not * @param hasCrossParameterValidator Whether the constraint has a cross-parameter validator * @param externalConstraintType constraint type as derived from external context, e.g. for * constraints declared in XML via {@code <return-value/gt;} * * @return The type of this constraint */ private ConstraintType determineConstraintType(Class constraintAnnotationType, Member member, ElementType elementType, boolean hasGenericValidators, boolean hasCrossParameterValidator, ConstraintType externalConstraintType) { ConstraintTarget constraintTarget = validationAppliesTo; ConstraintType constraintType = null; boolean isExecutable = isExecutable( elementType ); //target explicitly set to RETURN_VALUE if ( constraintTarget == ConstraintTarget.RETURN_VALUE ) { if ( !isExecutable ) { throw LOG.getParametersOrReturnValueConstraintTargetGivenAtNonExecutableException( annotationDescriptor.getType(), ConstraintTarget.RETURN_VALUE ); } constraintType = ConstraintType.GENERIC; } //target explicitly set to PARAMETERS else if ( constraintTarget == ConstraintTarget.PARAMETERS ) { if ( !isExecutable ) { throw LOG.getParametersOrReturnValueConstraintTargetGivenAtNonExecutableException( annotationDescriptor.getType(), ConstraintTarget.PARAMETERS ); } constraintType = ConstraintType.CROSS_PARAMETER; } //target set by external context (e.g. element in XML or returnValue() method in prog. API) else if ( externalConstraintType != null ) { constraintType = externalConstraintType; } //target set to IMPLICIT or not set at all else { //try to derive the type from the existing validators if ( hasGenericValidators && !hasCrossParameterValidator ) { constraintType = ConstraintType.GENERIC; } else if ( !hasGenericValidators && hasCrossParameterValidator ) { constraintType = ConstraintType.CROSS_PARAMETER; } else if ( !isExecutable ) { constraintType = ConstraintType.GENERIC; } else if ( constraintAnnotationType.isAnnotationPresent( SupportedValidationTarget.class ) ) { SupportedValidationTarget supportedValidationTarget = constraintAnnotationType.getAnnotation( SupportedValidationTarget.class ); if ( supportedValidationTarget.value().length == 1 ) { constraintType = supportedValidationTarget.value()[0] == ValidationTarget.ANNOTATED_ELEMENT ? ConstraintType.GENERIC : ConstraintType.CROSS_PARAMETER; } } //try to derive from existence of parameters/return value else { boolean hasParameters = hasParameters( member ); boolean hasReturnValue = hasReturnValue( member ); if ( !hasParameters && hasReturnValue ) { constraintType = ConstraintType.GENERIC; } else if ( hasParameters && !hasReturnValue ) { constraintType = ConstraintType.CROSS_PARAMETER; } } } // Now we are out of luck if ( constraintType == null ) { throw LOG.getImplicitConstraintTargetInAmbiguousConfigurationException( annotationDescriptor.getType() ); } if ( constraintType == ConstraintType.CROSS_PARAMETER ) { validateCrossParameterConstraintType( member, hasCrossParameterValidator ); } return constraintType; } private static ValidateUnwrappedValue determineValueUnwrapping(Set> payloads, Member member, Class annotationType) { if ( payloads.contains( Unwrapping.Unwrap.class ) ) { if ( payloads.contains( Unwrapping.Skip.class ) ) { throw LOG.getInvalidUnwrappingConfigurationForConstraintException( member, annotationType ); } return ValidateUnwrappedValue.UNWRAP; } if ( payloads.contains( Unwrapping.Skip.class ) ) { return ValidateUnwrappedValue.SKIP; } return ValidateUnwrappedValue.DEFAULT; } private static ConstraintTarget determineValidationAppliesTo(ConstraintAnnotationDescriptor annotationDescriptor) { return annotationDescriptor.getValidationAppliesTo(); } private void validateCrossParameterConstraintType(Member member, boolean hasCrossParameterValidator) { if ( !hasCrossParameterValidator ) { throw LOG.getCrossParameterConstraintHasNoValidatorException( annotationDescriptor.getType() ); } else if ( member == null ) { throw LOG.getCrossParameterConstraintOnClassException( annotationDescriptor.getType() ); } else if ( member instanceof Field ) { throw LOG.getCrossParameterConstraintOnFieldException( annotationDescriptor.getType(), member ); } else if ( !hasParameters( member ) ) { throw LOG.getCrossParameterConstraintOnMethodWithoutParametersException( annotationDescriptor.getType(), (Executable) member ); } } /** * Asserts that this constraint and all its composing constraints share the * same constraint type (generic or cross-parameter). */ private void validateComposingConstraintTypes() { for ( ConstraintDescriptorImpl composingConstraint : getComposingConstraintImpls() ) { if ( composingConstraint.constraintType != constraintType ) { throw LOG.getComposedAndComposingConstraintsHaveDifferentTypesException( annotationDescriptor.getType(), composingConstraint.annotationDescriptor.getType(), constraintType, composingConstraint.constraintType ); } } } private boolean hasParameters(Member member) { boolean hasParameters = false; if ( member instanceof Constructor ) { Constructor constructor = (Constructor) member; hasParameters = constructor.getParameterTypes().length > 0; } else if ( member instanceof Method ) { Method method = (Method) member; hasParameters = method.getParameterTypes().length > 0; } return hasParameters; } private boolean hasReturnValue(Member member) { boolean hasReturnValue; if ( member instanceof Constructor ) { hasReturnValue = true; } else if ( member instanceof Method ) { Method method = (Method) member; hasReturnValue = method.getGenericReturnType() != void.class; } else { // field or type hasReturnValue = false; } return hasReturnValue; } private boolean isExecutable(ElementType elementType) { return elementType == ElementType.METHOD || elementType == ElementType.CONSTRUCTOR; } @SuppressWarnings("unchecked") private static Set> buildPayloadSet(ConstraintAnnotationDescriptor annotationDescriptor) { Set> payloadSet = newHashSet(); Class[] payloadFromAnnotation = annotationDescriptor.getPayload(); if ( payloadFromAnnotation != null ) { payloadSet.addAll( Arrays.asList( payloadFromAnnotation ) ); } return CollectionHelper.toImmutableSet( payloadSet ); } private static Set> buildGroupSet(ConstraintAnnotationDescriptor annotationDescriptor, Class implicitGroup) { Set> groupSet = newHashSet(); final Class[] groupsFromAnnotation = annotationDescriptor.getGroups(); if ( groupsFromAnnotation.length == 0 ) { groupSet.add( Default.class ); } else { groupSet.addAll( Arrays.asList( groupsFromAnnotation ) ); } // if the constraint is part of the Default group it is automatically part of the implicit group as well if ( implicitGroup != null && groupSet.contains( Default.class ) ) { groupSet.add( implicitGroup ); } return CollectionHelper.toImmutableSet( groupSet ); } private Map> parseOverrideParameters() { Map> overrideParameters = newHashMap(); final Method[] methods = run( GetDeclaredMethods.action( annotationDescriptor.getType() ) ); for ( Method m : methods ) { if ( m.getAnnotation( OverridesAttribute.class ) != null ) { addOverrideAttributes( overrideParameters, m, m.getAnnotation( OverridesAttribute.class ) ); } else if ( m.getAnnotation( OverridesAttribute.List.class ) != null ) { addOverrideAttributes( overrideParameters, m, m.getAnnotation( OverridesAttribute.List.class ).value() ); } } return overrideParameters; } private void addOverrideAttributes(Map> overrideParameters, Method m, OverridesAttribute... attributes) { Object value = annotationDescriptor.getAttribute( m.getName() ); for ( OverridesAttribute overridesAttribute : attributes ) { String overridesAttributeName = overridesAttribute.name().length() > 0 ? overridesAttribute.name() : m.getName(); ensureAttributeIsOverridable( m, overridesAttribute, overridesAttributeName ); ClassIndexWrapper wrapper = new ClassIndexWrapper( overridesAttribute.constraint(), overridesAttribute.constraintIndex() ); Map map = overrideParameters.get( wrapper ); if ( map == null ) { map = newHashMap(); overrideParameters.put( wrapper, map ); } map.put( overridesAttributeName, value ); } } private void ensureAttributeIsOverridable(Method m, OverridesAttribute overridesAttribute, String overridesAttributeName) { final Method method = run( GetMethod.action( overridesAttribute.constraint(), overridesAttributeName ) ); if ( method == null ) { throw LOG.getOverriddenConstraintAttributeNotFoundException( overridesAttributeName ); } Class returnTypeOfOverriddenConstraint = method.getReturnType(); if ( !returnTypeOfOverriddenConstraint.equals( m.getReturnType() ) ) { throw LOG.getWrongAttributeTypeForOverriddenConstraintException( returnTypeOfOverriddenConstraint, m.getReturnType() ); } } private Set> parseComposingConstraints(ConstraintHelper constraintHelper, Member member, ConstraintType constraintType) { Set> composingConstraintsSet = newHashSet(); Map> overrideParameters = parseOverrideParameters(); Map, ComposingConstraintAnnotationLocation> composingConstraintLocations = new HashMap<>(); for ( Annotation declaredAnnotation : annotationDescriptor.getType().getDeclaredAnnotations() ) { Class declaredAnnotationType = declaredAnnotation.annotationType(); if ( NON_COMPOSING_CONSTRAINT_ANNOTATIONS.contains( declaredAnnotationType.getName() ) ) { // ignore the usual suspects which will be in almost any constraint, but are no composing constraint continue; } if ( constraintHelper.isConstraintAnnotation( declaredAnnotationType ) ) { if ( composingConstraintLocations.containsKey( declaredAnnotationType ) && !ComposingConstraintAnnotationLocation.DIRECT.equals( composingConstraintLocations.get( declaredAnnotationType ) ) ) { throw LOG.getCannotMixDirectAnnotationAndListContainerOnComposedConstraintException( annotationDescriptor.getType(), declaredAnnotationType ); } ConstraintDescriptorImpl descriptor = createComposingConstraintDescriptor( constraintHelper, member, overrideParameters, OVERRIDES_PARAMETER_DEFAULT_INDEX, declaredAnnotation, constraintType ); composingConstraintsSet.add( descriptor ); composingConstraintLocations.put( declaredAnnotationType, ComposingConstraintAnnotationLocation.DIRECT ); LOG.debugf( "Adding composing constraint: %s.", descriptor ); } else if ( constraintHelper.isMultiValueConstraint( declaredAnnotationType ) ) { List multiValueConstraints = constraintHelper.getConstraintsFromMultiValueConstraint( declaredAnnotation ); int index = 0; for ( Annotation constraintAnnotation : multiValueConstraints ) { if ( composingConstraintLocations.containsKey( constraintAnnotation.annotationType() ) && !ComposingConstraintAnnotationLocation.IN_CONTAINER.equals( composingConstraintLocations.get( constraintAnnotation.annotationType() ) ) ) { throw LOG.getCannotMixDirectAnnotationAndListContainerOnComposedConstraintException( annotationDescriptor.getType(), constraintAnnotation.annotationType() ); } ConstraintDescriptorImpl descriptor = createComposingConstraintDescriptor( constraintHelper, member, overrideParameters, index, constraintAnnotation, constraintType ); composingConstraintsSet.add( descriptor ); composingConstraintLocations.put( constraintAnnotation.annotationType(), ComposingConstraintAnnotationLocation.IN_CONTAINER ); LOG.debugf( "Adding composing constraint: %s.", descriptor ); index++; } } } return CollectionHelper.toImmutableSet( composingConstraintsSet ); } private CompositionType parseCompositionType(ConstraintHelper constraintHelper) { for ( Annotation declaredAnnotation : annotationDescriptor.getType().getDeclaredAnnotations() ) { Class declaredAnnotationType = declaredAnnotation.annotationType(); if ( NON_COMPOSING_CONSTRAINT_ANNOTATIONS.contains( declaredAnnotationType.getName() ) ) { // ignore the usual suspects which will be in almost any constraint, but are no composing constraint continue; } if ( constraintHelper.isConstraintComposition( declaredAnnotationType ) ) { if ( LOG.isDebugEnabled() ) { LOG.debugf( "Adding Bool %s.", declaredAnnotationType.getName() ); } return ( (ConstraintComposition) declaredAnnotation ).value(); } } return CompositionType.AND; } private ConstraintDescriptorImpl createComposingConstraintDescriptor( ConstraintHelper constraintHelper, Member member, Map> overrideParameters, int index, U constraintAnnotation, ConstraintType constraintType) { @SuppressWarnings("unchecked") final Class annotationType = (Class) constraintAnnotation.annotationType(); // use a annotation proxy ConstraintAnnotationDescriptor.Builder annotationDescriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( annotationType, run( GetAnnotationAttributes.action( constraintAnnotation ) ) ); // get the right override parameters Map overrides = overrideParameters.get( new ClassIndexWrapper( annotationType, index ) ); if ( overrides != null ) { for ( Map.Entry entry : overrides.entrySet() ) { annotationDescriptorBuilder.setAttribute( entry.getKey(), entry.getValue() ); } } //propagate inherited attributes to composing constraints annotationDescriptorBuilder.setGroups( groups.toArray( new Class[groups.size()] ) ); annotationDescriptorBuilder.setPayload( payloads.toArray( new Class[payloads.size()] ) ); if ( annotationDescriptorBuilder.hasAttribute( ConstraintHelper.VALIDATION_APPLIES_TO ) ) { ConstraintTarget validationAppliesTo = getValidationAppliesTo(); // composed constraint does not declare validationAppliesTo() itself if ( validationAppliesTo == null ) { if ( constraintType == ConstraintType.CROSS_PARAMETER ) { validationAppliesTo = ConstraintTarget.PARAMETERS; } else { validationAppliesTo = ConstraintTarget.IMPLICIT; } } annotationDescriptorBuilder.setAttribute( ConstraintHelper.VALIDATION_APPLIES_TO, validationAppliesTo ); } return new ConstraintDescriptorImpl<>( constraintHelper, member, annotationDescriptorBuilder.build(), elementType, null, definedOn, constraintType ); } /** * Runs the given privileged action, using a privileged block if required. *

* NOTE: This must never be changed into a publicly available method to avoid execution of arbitrary * privileged actions within HV's protection domain. */ private static

P run(PrivilegedAction

action) { return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); } /** * @return the compositionType */ public CompositionType getCompositionType() { return compositionType; } /** * A wrapper class to keep track for which composing constraints (class and index) a given attribute override applies to. */ private static class ClassIndexWrapper { final Class clazz; final int index; ClassIndexWrapper(Class clazz, int index) { this.clazz = clazz; this.index = index; } @Override public boolean equals(Object o) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } @SuppressWarnings("unchecked") // safe due to the check above ClassIndexWrapper that = (ClassIndexWrapper) o; if ( index != that.index ) { return false; } return clazz.equals( that.clazz ); } @Override public int hashCode() { int result = clazz != null ? clazz.hashCode() : 0; result = 31 * result + index; return result; } @Override public String toString() { return "ClassIndexWrapper [clazz=" + StringHelper.toShortString( clazz ) + ", index=" + index + "]"; } } /** * The type of a constraint. */ public enum ConstraintType { /** * A non cross parameter constraint. */ GENERIC, /** * A cross parameter constraint. */ CROSS_PARAMETER } /** * The location of a composing constraint. */ private enum ComposingConstraintAnnotationLocation { /** * The annotation is located directly on the class. */ DIRECT, /** * The annotation is defined in a container, typically a List container. */ IN_CONTAINER } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy