/* * 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.aggregated; import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; import java.lang.annotation.Annotation; import java.util.Map; import java.util.Set; import org.hibernate.validator.internal.engine.cascading.ValueExtractorManager; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.ConstraintOrigin; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.core.MetaConstraints; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; import org.hibernate.validator.internal.metadata.raw.ConstrainedElement.ConstrainedElementKind; import org.hibernate.validator.internal.util.TypeResolutionHelper; /** * Builds {@link ConstraintMetaData} instances for the * {@link ConstrainedElement} objects representing one method or property in a * type's inheritance hierarchy. * * @author Gunnar Morling */ public abstract class MetaDataBuilder { protected final ConstraintHelper constraintHelper; protected final TypeResolutionHelper typeResolutionHelper; protected final ValueExtractorManager valueExtractorManager; private final Class<?> beanClass; private final Set<MetaConstraint<?>> directConstraints = newHashSet(); private final Set<MetaConstraint<?>> containerElementsConstraints = newHashSet(); private final Map<Class<?>, Class<?>> groupConversions = newHashMap(); private boolean isCascading = false; protected MetaDataBuilder(Class<?> beanClass, ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager) { this.beanClass = beanClass; this.constraintHelper = constraintHelper; this.typeResolutionHelper = typeResolutionHelper; this.valueExtractorManager = valueExtractorManager; } /** * Whether this builder allows to add the given element or not. This is the * case if the specified element relates to the same property or method with * which this builder was instantiated. * * @param constrainedElement The element to check. * * @return <code>true</code> if the given element can be added to this * builder, <code>false</code> otherwise. */ public abstract boolean accepts(ConstrainedElement constrainedElement); /** * Adds the given element to this builder. It must be checked with * {@link #accepts(ConstrainedElement)} before, whether this is allowed or * not. * * @param constrainedElement The element to add. */ public void add(ConstrainedElement constrainedElement) { directConstraints.addAll( adaptConstraints( constrainedElement.getKind(), constrainedElement.getConstraints() ) ); containerElementsConstraints.addAll( adaptConstraints( constrainedElement.getKind(), constrainedElement.getTypeArgumentConstraints() ) ); isCascading = isCascading || constrainedElement.getCascadingMetaData().isMarkedForCascadingOnElementOrContainerElements(); } /** * Creates a new, read-only {@link ConstraintMetaData} object with all * constraint information related to the method or property represented by * this builder. * * @return A {@link ConstraintMetaData} object. */ public abstract ConstraintMetaData build(); protected Map<Class<?>, Class<?>> getGroupConversions() { return groupConversions; } protected Set<MetaConstraint<?>> getDirectConstraints() { return directConstraints; } public Set<MetaConstraint<?>> getContainerElementConstraints() { return containerElementsConstraints; } protected boolean isCascading() { return isCascading; } protected Class<?> getBeanClass() { return beanClass; } /** * Adapts the given constraints to the given bean type. In case a constraint * is defined locally at the bean class the original constraint will be * returned without any modifications. If a constraint is defined in the * hierarchy (interface or super class) a new constraint will be returned * with an origin of {@link org.hibernate.validator.internal.metadata.core.ConstraintOrigin#DEFINED_IN_HIERARCHY}. If a * constraint is defined on an interface, the interface type will * additionally be part of the constraint's groups (implicit grouping). * * @param constraints The constraints that shall be adapted. The constraints themselves * will not be altered. * * @return A constraint adapted to the given bean type. */ protected Set<MetaConstraint<?>> adaptOriginsAndImplicitGroups(Set<MetaConstraint<?>> constraints) { Set<MetaConstraint<?>> adaptedConstraints = newHashSet(); for ( MetaConstraint<?> oneConstraint : constraints ) { adaptedConstraints.add( adaptOriginAndImplicitGroup( oneConstraint ) ); } return adaptedConstraints; } private <A extends Annotation> MetaConstraint<A> adaptOriginAndImplicitGroup(MetaConstraint<A> constraint) { ConstraintOrigin definedIn = definedIn( beanClass, constraint.getLocation().getDeclaringClass() ); if ( definedIn == ConstraintOrigin.DEFINED_LOCALLY ) { return constraint; } Class<?> constraintClass = constraint.getLocation().getDeclaringClass(); ConstraintDescriptorImpl<A> descriptor = new ConstraintDescriptorImpl<>( constraintHelper, constraint.getLocation().getMember(), constraint.getDescriptor().getAnnotation(), constraint.getElementType(), constraintClass.isInterface() ? constraintClass : null, definedIn, constraint.getDescriptor().getConstraintType() ); return MetaConstraints.create( typeResolutionHelper, valueExtractorManager, descriptor, constraint.getLocation() ); } /** * Allows specific sub-classes to customize the retrieved constraints. */ protected Set<MetaConstraint<?>> adaptConstraints(ConstrainedElementKind constrainedElementKind, Set<MetaConstraint<?>> constraints) { return constraints; } /** * @param rootClass The root class. That is the class for which we currently * create a {@code BeanMetaData} * @param hierarchyClass The class on which the current constraint is defined on * * @return Returns {@code ConstraintOrigin.DEFINED_LOCALLY} if the * constraint was defined on the root bean, * {@code ConstraintOrigin.DEFINED_IN_HIERARCHY} otherwise. */ private ConstraintOrigin definedIn(Class<?> rootClass, Class<?> hierarchyClass) { if ( hierarchyClass.equals( rootClass ) ) { return ConstraintOrigin.DEFINED_LOCALLY; } else { return ConstraintOrigin.DEFINED_IN_HIERARCHY; } } }