From f61a93dda3415e122baca95cb6cfcb509ddf5fc3 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Fri, 29 Jun 2018 12:07:57 +0200 Subject: [PATCH 01/14] [property-holder-preparation] Remove impossible case from ValidatorImpl#buildNewLocalExecutionContext() --- .../internal/engine/ValidatorImpl.java | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java index 9bbe13193e..3bcef46a56 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java @@ -739,23 +739,14 @@ private void validateCascadedContainerElementsInContext(Object value, BaseBeanVa private ValueContext buildNewLocalExecutionContext(ValueContext valueContext, Object value) { ValueContext newValueContext; - if ( value != null ) { - newValueContext = ValueContext.getLocalExecutionContext( - validatorScopedContext.getParameterNameProvider(), - value, - beanMetaDataManager.getBeanMetaData( value.getClass() ), - valueContext.getPropertyPath() - ); - newValueContext.setCurrentValidatedValue( value ); - } - else { - newValueContext = ValueContext.getLocalExecutionContext( - validatorScopedContext.getParameterNameProvider(), - valueContext.getCurrentBeanType(), - valueContext.getCurrentBeanMetaData(), - valueContext.getPropertyPath() - ); - } + Contracts.assertNotNull( value, "value cannot be null" ); + newValueContext = ValueContext.getLocalExecutionContext( + validatorScopedContext.getParameterNameProvider(), + value, + beanMetaDataManager.getBeanMetaData( value.getClass() ), + valueContext.getPropertyPath() + ); + newValueContext.setCurrentValidatedValue( value ); return newValueContext; } From 80a4bb8adc2c61948b0721914e8a72df75215a79 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Fri, 29 Jun 2018 13:18:46 +0200 Subject: [PATCH 02/14] [property-holder-preparation] Delegate retrieving bean metadata to CascadingMetaData As in case of property holders we would need to produce metadata not based on a class of a current object at runtime but based on a string representing the constraint mapping, we need to delegate this to cascading metadata as that's the place where this information will be stored. --- .../validator/internal/engine/ValidatorImpl.java | 10 +++++----- .../metadata/aggregated/CascadingMetaData.java | 5 +++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java index 3bcef46a56..54f52ebaf7 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java @@ -597,7 +597,7 @@ private void validateCascadedAnnotatedObjectForCurrentGroup(Object value, BaseBe // already and need only to pass the current element ValidationOrder validationOrder = validationOrderGenerator.getValidationOrder( currentGroup, currentGroup != originalGroup ); - ValueContext cascadedValueContext = buildNewLocalExecutionContext( valueContext, value ); + ValueContext cascadedValueContext = buildNewLocalExecutionContext( cascadingMetaData, valueContext, value ); validateInContext( validationContext, cascadedValueContext, validationOrder ); } @@ -675,7 +675,7 @@ private void doValidate(Object value, String nodeName) { // already and need only to pass the current element ValidationOrder validationOrder = validationOrderGenerator.getValidationOrder( currentGroup, currentGroup != originalGroup ); - ValueContext cascadedValueContext = buildNewLocalExecutionContext( valueContext, value ); + ValueContext cascadedValueContext = buildNewLocalExecutionContext( cascadingMetaData, valueContext, value ); if ( cascadingMetaData.getDeclaredContainerClass() != null ) { cascadedValueContext.setTypeParameter( cascadingMetaData.getDeclaredContainerClass(), cascadingMetaData.getDeclaredTypeParameterIndex() ); @@ -688,7 +688,7 @@ private void doValidate(Object value, String nodeName) { // Cascade validation to container elements if we are dealing with a container element if ( cascadingMetaData.hasContainerElementsMarkedForCascading() ) { - ValueContext cascadedTypeArgumentValueContext = buildNewLocalExecutionContext( valueContext, value ); + ValueContext cascadedTypeArgumentValueContext = buildNewLocalExecutionContext( cascadingMetaData, valueContext, value ); if ( cascadingMetaData.getTypeParameter() != null ) { cascadedValueContext.setTypeParameter( cascadingMetaData.getDeclaredContainerClass(), cascadingMetaData.getDeclaredTypeParameterIndex() ); } @@ -737,13 +737,13 @@ private void validateCascadedContainerElementsInContext(Object value, BaseBeanVa } } - private ValueContext buildNewLocalExecutionContext(ValueContext valueContext, Object value) { + private ValueContext buildNewLocalExecutionContext(CascadingMetaData cascadingMetaData, ValueContext valueContext, Object value) { ValueContext newValueContext; Contracts.assertNotNull( value, "value cannot be null" ); newValueContext = ValueContext.getLocalExecutionContext( validatorScopedContext.getParameterNameProvider(), value, - beanMetaDataManager.getBeanMetaData( value.getClass() ), + cascadingMetaData.getBeanMetaDataForCascadable( beanMetaDataManager, value ), valueContext.getPropertyPath() ); newValueContext.setCurrentValidatedValue( value ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaData.java index 86a03e770c..54f394a56e 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaData.java @@ -15,6 +15,7 @@ import org.hibernate.validator.internal.engine.valueextraction.AnnotatedObject; import org.hibernate.validator.internal.engine.valueextraction.ArrayElement; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.BeanMetaDataManager; /** * An aggregated view of the cascading validation metadata. Note that it also includes the cascading validation metadata @@ -59,4 +60,8 @@ public interface CascadingMetaData { * time. */ CascadingMetaData addRuntimeContainerSupport(ValueExtractorManager valueExtractorManager, Class valueClass); + + default BeanMetaData getBeanMetaDataForCascadable(BeanMetaDataManager beanMetaDataManager, Object value) { + return beanMetaDataManager.getBeanMetaData( value.getClass() ); + } } From 9418a358a9523e017914a89d6cbc7a73a5efa770 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Sun, 8 Jul 2018 22:02:38 +0200 Subject: [PATCH 03/14] [property-holder] Introduce property holder property and property holder accessor creator with simple a implementation for Map --- .../PropertyAccessorCreatorProvider.java | 113 ++++++++++++++++++ .../PropertyHolderProperty.java | 99 +++++++++++++++ .../map/MapPropertyAccessorCreator.java | 58 +++++++++ .../propertyholder/map/package-info.java | 11 ++ .../propertyholder/package-info.java | 12 ++ .../validator/internal/util/logging/Log.java | 12 ++ .../PropertyAccessorCreator.java | 35 ++++++ .../spi/propertyholder/package-info.java | 15 +++ 8 files changed, 355 insertions(+) create mode 100644 engine/src/main/java/org/hibernate/validator/internal/properties/propertyholder/PropertyAccessorCreatorProvider.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/properties/propertyholder/PropertyHolderProperty.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/properties/propertyholder/map/MapPropertyAccessorCreator.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/properties/propertyholder/map/package-info.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/properties/propertyholder/package-info.java create mode 100644 engine/src/main/java/org/hibernate/validator/spi/propertyholder/PropertyAccessorCreator.java create mode 100644 engine/src/main/java/org/hibernate/validator/spi/propertyholder/package-info.java diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/propertyholder/PropertyAccessorCreatorProvider.java b/engine/src/main/java/org/hibernate/validator/internal/properties/propertyholder/PropertyAccessorCreatorProvider.java new file mode 100644 index 0000000000..c1dae8294b --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/propertyholder/PropertyAccessorCreatorProvider.java @@ -0,0 +1,113 @@ +/* + * 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.properties.propertyholder; + +import java.lang.invoke.MethodHandles; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.hibernate.validator.internal.properties.propertyholder.map.MapPropertyAccessorCreator; +import org.hibernate.validator.internal.util.CollectionHelper; +import org.hibernate.validator.internal.util.TypeHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.util.privilegedactions.GetClassLoader; +import org.hibernate.validator.internal.util.privilegedactions.GetInstancesFromServiceLoader; +import org.hibernate.validator.spi.propertyholder.PropertyAccessorCreator; + +/** + * @author Marko Bekhta + */ +public class PropertyAccessorCreatorProvider { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private final Set> configuredPropertyCreators = new HashSet<>(); + + public PropertyAccessorCreatorProvider() { + //add default property creator for a Map + configuredPropertyCreators.add( new MapPropertyAccessorCreator() ); + + List propertyAccessorCreators = run( GetInstancesFromServiceLoader.action( + run( GetClassLoader.fromContext() ), + PropertyAccessorCreator.class + ) ); + for ( PropertyAccessorCreator propertyAccessorCreator : propertyAccessorCreators ) { + configuredPropertyCreators.add( propertyAccessorCreator ); + } + } + + @SuppressWarnings("unchecked") + public PropertyAccessorCreator getPropertyAccessorCreatorFor(Class propertyHolderType) { + + Set possibleCreators = configuredPropertyCreators + .stream() + .filter( el -> TypeHelper.isAssignable( el.getPropertyHolderType(), propertyHolderType ) ) + .collect( Collectors.toSet() ); + + Set creators = getMaximallySpecificPropertyAccessorCreators( possibleCreators ); + + if ( creators.isEmpty() ) { + throw LOG.getUnableToFindPropertyCreatorException( propertyHolderType ); + } + else if ( creators.size() > 1 ) { + throw LOG.getUnableToFinUniquedPropertyCreatorException( propertyHolderType ); + } + else { + return creators.iterator().next(); + } + } + + private Set getMaximallySpecificPropertyAccessorCreators(Set possiblePropertyAccessorCreators) { + Set propertyAccessorCreators = CollectionHelper.newHashSet( possiblePropertyAccessorCreators.size() ); + + for ( PropertyAccessorCreator creator : possiblePropertyAccessorCreators ) { + if ( propertyAccessorCreators.isEmpty() ) { + propertyAccessorCreators.add( creator ); + continue; + } + Iterator candidatesIterator = propertyAccessorCreators.iterator(); + boolean isNewRoot = true; + while ( candidatesIterator.hasNext() ) { + PropertyAccessorCreator candidate = candidatesIterator.next(); + + // we consider the strictly more specific value extractor so 2 value extractors for the same container + // type should throw an error in the end if no other more specific value extractor is found. + if ( candidate.getPropertyHolderType().equals( creator.getPropertyHolderType() ) ) { + continue; + } + + if ( TypeHelper.isAssignable( candidate.getPropertyHolderType(), creator.getPropertyHolderType() ) ) { + candidatesIterator.remove(); + } + else if ( TypeHelper.isAssignable( creator.getPropertyHolderType(), candidate.getPropertyHolderType() ) ) { + isNewRoot = false; + } + } + if ( isNewRoot ) { + propertyAccessorCreators.add( creator ); + } + } + return propertyAccessorCreators; + } + + /** + * 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 T run(PrivilegedAction action) { + return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); + } + +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/propertyholder/PropertyHolderProperty.java b/engine/src/main/java/org/hibernate/validator/internal/properties/propertyholder/PropertyHolderProperty.java new file mode 100644 index 0000000000..ab83f1ffe8 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/propertyholder/PropertyHolderProperty.java @@ -0,0 +1,99 @@ +/* + * 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.properties.propertyholder; + +import java.lang.reflect.Type; + +import org.hibernate.validator.internal.metadata.raw.ConstrainedElement.ConstrainedElementKind; +import org.hibernate.validator.internal.properties.Property; +import org.hibernate.validator.internal.properties.PropertyAccessor; + +/** + * @author Marko Bekhta + */ +public class PropertyHolderProperty implements Property { + + private final Class propertyHolderType; + private final PropertyAccessor propertyAccessor; + + private final String name; + private final Class type; + + public PropertyHolderProperty(Class propertyHolderType, PropertyAccessor propertyAccessor, String name, Class type) { + this.propertyHolderType = propertyHolderType; + this.propertyAccessor = propertyAccessor; + this.name = name; + this.type = type; + } + + @Override + public String getPropertyName() { + return getName(); + } + + @Override + public PropertyAccessor createAccessor() { + return propertyAccessor; + } + + @Override + public String getName() { + return name; + } + + @Override + public Class getDeclaringClass() { + return propertyHolderType; + } + + @Override + public Type getTypeForValidatorResolution() { + return getType(); + } + + @Override + public Type getType() { + return type; + } + + @Override + public ConstrainedElementKind getConstrainedElementKind() { + return ConstrainedElementKind.FIELD; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + + PropertyHolderProperty that = (PropertyHolderProperty) o; + + if ( !propertyHolderType.equals( that.propertyHolderType ) ) { + return false; + } + if ( !name.equals( that.name ) ) { + return false; + } + if ( !type.equals( that.type ) ) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int result = propertyHolderType.hashCode(); + result = 31 * result + name.hashCode(); + result = 31 * result + type.hashCode(); + return result; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/propertyholder/map/MapPropertyAccessorCreator.java b/engine/src/main/java/org/hibernate/validator/internal/properties/propertyholder/map/MapPropertyAccessorCreator.java new file mode 100644 index 0000000000..b34e5044ad --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/propertyholder/map/MapPropertyAccessorCreator.java @@ -0,0 +1,58 @@ +/* + * 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.properties.propertyholder.map; + +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Type; +import java.util.Map; + +import org.hibernate.validator.internal.properties.PropertyAccessor; +import org.hibernate.validator.internal.util.TypeHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.spi.propertyholder.PropertyAccessorCreator; + +/** + * @author Marko Bekhta + */ +public class MapPropertyAccessorCreator implements PropertyAccessorCreator { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + @Override + public Class getPropertyHolderType() { + return Map.class; + } + + @Override + public PropertyAccessor create(String propertyName, Type propertyType) { + return new MapPropertyAccessor( propertyName, propertyType ); + } + + private static class MapPropertyAccessor implements PropertyAccessor { + + private final String name; + private final Type type; + + private MapPropertyAccessor(String name, Type type) { + this.name = name; + this.type = type; + } + + @Override + public Object getValueFrom(Object bean) { + if ( !( bean instanceof Map ) ) { + throw LOG.getUnexpextedPropertyHolderTypeException( Map.class, bean.getClass() ); + } + Object value = ( (Map) bean ).get( name ); + if ( value != null && !TypeHelper.isAssignable( type, value.getClass() ) ) { + throw LOG.getUnexpextedPropertyTypeInPropertyHolderException( type, value.getClass(), name ); + } + return value; + } + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/propertyholder/map/package-info.java b/engine/src/main/java/org/hibernate/validator/internal/properties/propertyholder/map/package-info.java new file mode 100644 index 0000000000..08fa45c3f6 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/propertyholder/map/package-info.java @@ -0,0 +1,11 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +/** + * Contains default {@link org.hibernate.validator.spi.propertyholder.PropertyAccessorCreator} implementaion for {@link java.util.Map}, + * and related classes. + */ +package org.hibernate.validator.internal.properties.propertyholder.map; diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/propertyholder/package-info.java b/engine/src/main/java/org/hibernate/validator/internal/properties/propertyholder/package-info.java new file mode 100644 index 0000000000..7e074b01dd --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/propertyholder/package-info.java @@ -0,0 +1,12 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +/** + * Contains {@link org.hibernate.validator.internal.properties.propertyholder.PropertyAccessorCreatorProvider} + * and all related classes, as well as default {@link org.hibernate.validator.spi.propertyholder.PropertyAccessorCreator} + * implementaion for {@link java.util.Map}. + */ +package org.hibernate.validator.internal.properties.propertyholder; diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java b/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java index c79e7ca4ef..c04731319f 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java @@ -875,4 +875,16 @@ ConstraintDefinitionException getConstraintValidatorDefinitionConstraintMismatch @Message(id = 248, value = "Unable to get an XML schema named %s.") ValidationException unableToGetXmlSchema(String schemaResourceName); + + @Message(id = 249, value = "Unable to find property creator for property holder of %1$s type.") + ValidationException getUnableToFindPropertyCreatorException(@FormatWith(ClassObjectFormatter.class) Class clazz); + + @Message(id = 250, value = "Unable to find single unique property creator for property holder of %1$s type.") + ValidationException getUnableToFinUniquedPropertyCreatorException(@FormatWith(ClassObjectFormatter.class) Class clazz); + + @Message(id = 251, value = "Unexpected property holder type received. Expected %1$s type, but instead got %2$s.") + ValidationException getUnexpextedPropertyHolderTypeException(@FormatWith(ClassObjectFormatter.class) Class expecetedHolderType, @FormatWith(ClassObjectFormatter.class) Class realHolderType); + + @Message(id = 252, value = "Unexpected property type in a given property holder. Expected that '%3$s' will be of %1$s type, but instead it is %2$s.") + ValidationException getUnexpextedPropertyTypeInPropertyHolderException(Type expecetedType, @FormatWith(ClassObjectFormatter.class) Class realType, String propertyName); } diff --git a/engine/src/main/java/org/hibernate/validator/spi/propertyholder/PropertyAccessorCreator.java b/engine/src/main/java/org/hibernate/validator/spi/propertyholder/PropertyAccessorCreator.java new file mode 100644 index 0000000000..48198cdc2b --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/spi/propertyholder/PropertyAccessorCreator.java @@ -0,0 +1,35 @@ +/* + * 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.spi.propertyholder; + +import java.lang.reflect.Type; + +import org.hibernate.validator.internal.properties.PropertyAccessor; + +/** + * This interface expose the functionality of creating the property accessor based on + * a string representation of a property. + * + * @author Marko Bekhta + */ +public interface PropertyAccessorCreator { + + /** + * @return the type of the property holder this creator can be applied to. + */ + Class getPropertyHolderType(); + + /** + * Creates property accessor for a given name and type. + * + * @param propertyName property name + * @param propertyType property type + * + * @return created property + */ + PropertyAccessor create(String propertyName, Type propertyType); +} diff --git a/engine/src/main/java/org/hibernate/validator/spi/propertyholder/package-info.java b/engine/src/main/java/org/hibernate/validator/spi/propertyholder/package-info.java new file mode 100644 index 0000000000..e8018bd99e --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/spi/propertyholder/package-info.java @@ -0,0 +1,15 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ + +/** + *

+ * This package provides support for customization of the property holder validation by + * exposing a set of interfaces that helps creating properties for custom property holders. + *

+ *

This package is part of the public Hibernate Validator API.

+ */ +package org.hibernate.validator.spi.propertyholder; From 415abf7de31ba2176ba481205e80a242dee467a9 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Mon, 9 Jul 2018 20:15:08 +0200 Subject: [PATCH 04/14] [property-holder-preparation] Move metadata manager to its own package and rename it As metadata manager will serve as provider of bean metadata as well as property holder metadata, it'll be better to keep it in specific package --- .../internal/engine/ValidatorFactoryImpl.java | 14 +- .../internal/engine/ValidatorImpl.java | 32 +-- .../internal/engine/ValueContext.java | 10 +- .../ValidationContextBuilder.java | 20 +- .../aggregated/CascadingMetaData.java | 6 +- .../manager/ConstraintMetaDataManager.java | 239 ++++++++++++++++++ .../metadata/manager/package-info.java | 10 + .../cfg/ConfigurationFilePropertiesTest.java | 8 +- .../internal/engine/path/PathImplTest.java | 6 +- ...ava => ConstraintMetaDataManagerTest.java} | 8 +- .../aggregated/ExecutableMetaDataTest.java | 16 +- .../aggregated/ParameterMetaDataTest.java | 10 +- .../aggregated/PropertyMetaDataTest.java | 12 +- 13 files changed, 320 insertions(+), 71 deletions(-) create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/manager/ConstraintMetaDataManager.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/manager/package-info.java rename engine/src/test/java/org/hibernate/validator/test/internal/metadata/{BeanMetaDataManagerTest.java => ConstraintMetaDataManagerTest.java} (96%) diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java index 8da528f0be..fa909c020c 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java @@ -42,7 +42,7 @@ import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; import org.hibernate.validator.internal.engine.scripting.DefaultScriptEvaluatorFactory; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.BeanMetaDataManager; +import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; import org.hibernate.validator.internal.metadata.provider.ProgrammaticMetaDataProvider; @@ -131,7 +131,7 @@ public class ValidatorFactoryImpl implements HibernateValidatorFactory { * provider. See also HV-659. */ @ThreadSafe - private final ConcurrentMap beanMetaDataManagers; + private final ConcurrentMap beanMetaDataManagers; private final ValueExtractorManager valueExtractorManager; @@ -336,8 +336,8 @@ public HibernateValidatorContext usingContext() { public void close() { constraintValidatorManager.clear(); constraintHelper.clear(); - for ( BeanMetaDataManager beanMetaDataManager : beanMetaDataManagers.values() ) { - beanMetaDataManager.clear(); + for ( ConstraintMetaDataManager constraintMetaDataManager : beanMetaDataManagers.values() ) { + constraintMetaDataManager.clear(); } validatorFactoryScopedContext.getScriptEvaluatorFactory().clear(); valueExtractorManager.clear(); @@ -352,9 +352,9 @@ Validator createValidator(ConstraintValidatorFactory constraintValidatorFactory, ValidatorFactoryScopedContext validatorFactoryScopedContext, MethodValidationConfiguration methodValidationConfiguration) { - BeanMetaDataManager beanMetaDataManager = beanMetaDataManagers.computeIfAbsent( + ConstraintMetaDataManager constraintMetaDataManager = beanMetaDataManagers.computeIfAbsent( new BeanMetaDataManagerKey( validatorFactoryScopedContext.getParameterNameProvider(), valueExtractorManager, methodValidationConfiguration ), - key -> new BeanMetaDataManager( + key -> new ConstraintMetaDataManager( constraintHelper, executableHelper, typeResolutionHelper, @@ -369,7 +369,7 @@ Validator createValidator(ConstraintValidatorFactory constraintValidatorFactory, return new ValidatorImpl( constraintValidatorFactory, - beanMetaDataManager, + constraintMetaDataManager, valueExtractorManager, constraintValidatorManager, validationOrderGenerator, diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java index 54f52ebaf7..e4b197615a 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java @@ -51,7 +51,7 @@ import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorHelper; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.BeanMetaDataManager; +import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaData; import org.hibernate.validator.internal.metadata.aggregated.ContainerCascadingMetaData; @@ -108,7 +108,7 @@ public class ValidatorImpl implements Validator, ExecutableValidator { * Used to get access to the bean meta data. Used to avoid to parsing the constraint configuration for each call * of a given entity. */ - private final BeanMetaDataManager beanMetaDataManager; + private final ConstraintMetaDataManager constraintMetaDataManager; /** * Manages the life cycle of constraint validator instances @@ -129,13 +129,13 @@ public class ValidatorImpl implements Validator, ExecutableValidator { private final HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext; public ValidatorImpl(ConstraintValidatorFactory constraintValidatorFactory, - BeanMetaDataManager beanMetaDataManager, + ConstraintMetaDataManager constraintMetaDataManager, ValueExtractorManager valueExtractorManager, ConstraintValidatorManager constraintValidatorManager, ValidationOrderGenerator validationOrderGenerator, ValidatorFactoryScopedContext validatorFactoryScopedContext) { this.constraintValidatorFactory = constraintValidatorFactory; - this.beanMetaDataManager = beanMetaDataManager; + this.constraintMetaDataManager = constraintMetaDataManager; this.valueExtractorManager = valueExtractorManager; this.constraintValidatorManager = constraintValidatorManager; this.validationOrderGenerator = validationOrderGenerator; @@ -288,7 +288,7 @@ private Set> validateReturnValue(T object, Executable @Override public final BeanDescriptor getConstraintsForClass(Class clazz) { - return beanMetaDataManager.getBeanMetaData( clazz ).getBeanDescriptor(); + return constraintMetaDataManager.getBeanMetaData( clazz ).getBeanDescriptor(); } @Override @@ -310,7 +310,7 @@ public ExecutableValidator forExecutables() { private ValidationContextBuilder getValidationContextBuilder() { return new ValidationContextBuilder( - beanMetaDataManager, + constraintMetaDataManager, constraintValidatorManager, constraintValidatorFactory, validatorScopedContext, @@ -434,7 +434,7 @@ private void validateConstraintsForDefaultGroup(BaseBeanValidationContext // evaluating the constraints of a bean per class in hierarchy, this is necessary to detect potential default group re-definitions for ( Class clazz : beanMetaData.getClassHierarchy() ) { - BeanMetaData hostingBeanMetaData = beanMetaDataManager.getBeanMetaData( clazz ); + BeanMetaData hostingBeanMetaData = constraintMetaDataManager.getBeanMetaData( clazz ); boolean defaultGroupSequenceIsRedefined = hostingBeanMetaData.isDefaultGroupSequenceRedefined(); // if the current class redefined the default group sequence, this sequence has to be applied to all the class hierarchy. @@ -743,7 +743,7 @@ private ValueContext buildNewLocalExecutionContext(CascadingMetaData newValueContext = ValueContext.getLocalExecutionContext( validatorScopedContext.getParameterNameProvider(), value, - cascadingMetaData.getBeanMetaDataForCascadable( beanMetaDataManager, value ), + cascadingMetaData.getBeanMetaDataForCascadable( constraintMetaDataManager, value ), valueContext.getPropertyPath() ); newValueContext.setCurrentValidatedValue( value ); @@ -837,7 +837,7 @@ private void validateParametersInContext(ExecutableValidationContext vali } ValueContext cascadingValueContext = ValueContext.getLocalExecutionContext( - beanMetaDataManager, + constraintMetaDataManager, validatorScopedContext.getParameterNameProvider(), parameterValues, executableMetaData.getValidatableParametersMetaData(), @@ -971,7 +971,7 @@ private ValueContext getExecutableValueContext(T object, Executab if ( object != null ) { valueContext = ValueContext.getLocalExecutionContext( - beanMetaDataManager, + constraintMetaDataManager, validatorScopedContext.getParameterNameProvider(), object, validatable, @@ -980,7 +980,7 @@ private ValueContext getExecutableValueContext(T object, Executab } else { valueContext = ValueContext.getLocalExecutionContext( - beanMetaDataManager, + constraintMetaDataManager, validatorScopedContext.getParameterNameProvider(), (Class) null, //the type is not required in this case (only for cascaded validation) validatable, @@ -1023,7 +1023,7 @@ private void validateReturnValueInContext(ExecutableValidationContext if ( value != null ) { cascadingValueContext = ValueContext.getLocalExecutionContext( - beanMetaDataManager, + constraintMetaDataManager, validatorScopedContext.getParameterNameProvider(), value, executableMetaData.getReturnValueMetaData(), @@ -1179,11 +1179,11 @@ else if ( propertyPathNode.getKey() != null ) { } clazz = value.getClass(); - beanMetaData = beanMetaDataManager.getBeanMetaData( clazz ); + beanMetaData = constraintMetaDataManager.getBeanMetaData( clazz ); propertyMetaData = getBeanPropertyMetaData( beanMetaData, propertyPathNode ); } else { - beanMetaData = beanMetaDataManager.getBeanMetaData( clazz ); + beanMetaData = constraintMetaDataManager.getBeanMetaData( clazz ); } } } @@ -1220,7 +1220,7 @@ private ValueContext getValueContextForValueValidation(Class rootBe while ( propertyPathIter.hasNext() ) { // cast is ok, since we are dealing with engine internal classes NodeImpl propertyPathNode = (NodeImpl) propertyPathIter.next(); - beanMetaData = beanMetaDataManager.getBeanMetaData( clazz ); + beanMetaData = constraintMetaDataManager.getBeanMetaData( clazz ); propertyMetaData = getBeanPropertyMetaData( beanMetaData, propertyPathNode ); // if the property is not the leaf property, we set up the context for the next iteration @@ -1231,7 +1231,7 @@ private ValueContext getValueContextForValueValidation(Class rootBe propertyPathNode = (NodeImpl) propertyPathIter.next(); clazz = ReflectionHelper.getClassFromType( ReflectionHelper.getCollectionElementType( propertyMetaData.getType() ) ); - beanMetaData = beanMetaDataManager.getBeanMetaData( clazz ); + beanMetaData = constraintMetaDataManager.getBeanMetaData( clazz ); propertyMetaData = getBeanPropertyMetaData( beanMetaData, propertyPathNode ); } else { diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValueContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValueContext.java index 024b5419ec..fa1f834d61 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValueContext.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValueContext.java @@ -13,7 +13,7 @@ import org.hibernate.validator.internal.engine.path.PathImpl; import org.hibernate.validator.internal.engine.valueextraction.AnnotatedObject; import org.hibernate.validator.internal.engine.valueextraction.ArrayElement; -import org.hibernate.validator.internal.metadata.BeanMetaDataManager; +import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.facets.Cascadable; import org.hibernate.validator.internal.metadata.facets.Validatable; @@ -71,11 +71,11 @@ public class ValueContext { */ private ConstraintLocationKind constraintLocationKind; - public static ValueContext getLocalExecutionContext(BeanMetaDataManager beanMetaDataManager, + public static ValueContext getLocalExecutionContext(ConstraintMetaDataManager constraintMetaDataManager, ExecutableParameterNameProvider parameterNameProvider, T value, Validatable validatable, PathImpl propertyPath) { @SuppressWarnings("unchecked") Class rootBeanType = (Class) value.getClass(); - return new ValueContext<>( parameterNameProvider, value, rootBeanType, beanMetaDataManager.getBeanMetaData( rootBeanType ), validatable, propertyPath ); + return new ValueContext<>( parameterNameProvider, value, rootBeanType, constraintMetaDataManager.getBeanMetaData( rootBeanType ), validatable, propertyPath ); } @SuppressWarnings("unchecked") @@ -85,9 +85,9 @@ public static ValueContext getLocalExecutionContext(ExecutableParam return new ValueContext<>( parameterNameProvider, value, rootBeanType, (BeanMetaData) currentBeanMetaData, currentBeanMetaData, propertyPath ); } - public static ValueContext getLocalExecutionContext(BeanMetaDataManager beanMetaDataManager, + public static ValueContext getLocalExecutionContext(ConstraintMetaDataManager constraintMetaDataManager, ExecutableParameterNameProvider parameterNameProvider, Class rootBeanType, Validatable validatable, PathImpl propertyPath) { - BeanMetaData rootBeanMetaData = rootBeanType != null ? beanMetaDataManager.getBeanMetaData( rootBeanType ) : null; + BeanMetaData rootBeanMetaData = rootBeanType != null ? constraintMetaDataManager.getBeanMetaData( rootBeanType ) : null; return new ValueContext<>( parameterNameProvider, null, rootBeanType, rootBeanMetaData, validatable, propertyPath ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ValidationContextBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ValidationContextBuilder.java index 8db1873385..045aeeff3f 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ValidationContextBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ValidationContextBuilder.java @@ -14,7 +14,7 @@ import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; import org.hibernate.validator.internal.engine.path.PathImpl; -import org.hibernate.validator.internal.metadata.BeanMetaDataManager; +import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; /** @@ -25,7 +25,7 @@ */ public class ValidationContextBuilder { - private final BeanMetaDataManager beanMetaDataManager; + private final ConstraintMetaDataManager constraintMetaDataManager; private final ConstraintValidatorManager constraintValidatorManager; private final ConstraintValidatorFactory constraintValidatorFactory; private final TraversableResolver traversableResolver; @@ -33,13 +33,13 @@ public class ValidationContextBuilder { private final ValidatorScopedContext validatorScopedContext; public ValidationContextBuilder( - BeanMetaDataManager beanMetaDataManager, + ConstraintMetaDataManager constraintMetaDataManager, ConstraintValidatorManager constraintValidatorManager, ConstraintValidatorFactory constraintValidatorFactory, ValidatorScopedContext validatorScopedContext, TraversableResolver traversableResolver, HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext) { - this.beanMetaDataManager = beanMetaDataManager; + this.constraintMetaDataManager = constraintMetaDataManager; this.constraintValidatorManager = constraintValidatorManager; this.constraintValidatorFactory = constraintValidatorFactory; this.traversableResolver = traversableResolver; @@ -58,7 +58,7 @@ public BaseBeanValidationContext forValidate(T rootBean) { constraintValidatorInitializationContext, rootBean, rootBeanClass, - beanMetaDataManager.getBeanMetaData( rootBeanClass ) + constraintMetaDataManager.getBeanMetaData( rootBeanClass ) ); } @@ -73,7 +73,7 @@ public BaseBeanValidationContext forValidateProperty(T rootBean, PathImpl constraintValidatorInitializationContext, rootBean, rootBeanClass, - beanMetaDataManager.getBeanMetaData( rootBeanClass ), + constraintMetaDataManager.getBeanMetaData( rootBeanClass ), propertyPath.getLeafNode().getName() ); } @@ -87,7 +87,7 @@ public BaseBeanValidationContext forValidateValue(Class rootBeanClass, constraintValidatorInitializationContext, null, //root bean rootBeanClass, - beanMetaDataManager.getBeanMetaData( rootBeanClass ), + constraintMetaDataManager.getBeanMetaData( rootBeanClass ), propertyPath.getLeafNode().getName() ); } @@ -98,7 +98,7 @@ public ExecutableValidationContext forValidateParameters( Object[] executableParameters) { @SuppressWarnings("unchecked") Class rootBeanClass = rootBean != null ? (Class) rootBean.getClass() : (Class) executable.getDeclaringClass(); - BeanMetaData rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass ); + BeanMetaData rootBeanMetaData = constraintMetaDataManager.getBeanMetaData( rootBeanClass ); return new ParameterExecutableValidationContext<>( constraintValidatorManager, @@ -121,7 +121,7 @@ public ExecutableValidationContext forValidateReturnValue( Object executableReturnValue) { @SuppressWarnings("unchecked") Class rootBeanClass = rootBean != null ? (Class) rootBean.getClass() : (Class) executable.getDeclaringClass(); - BeanMetaData rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass ); + BeanMetaData rootBeanMetaData = constraintMetaDataManager.getBeanMetaData( rootBeanClass ); return new ReturnValueExecutableValidationContext<>( constraintValidatorManager, constraintValidatorFactory, @@ -130,7 +130,7 @@ public ExecutableValidationContext forValidateReturnValue( constraintValidatorInitializationContext, rootBean, rootBeanClass, - beanMetaDataManager.getBeanMetaData( rootBeanClass ), + constraintMetaDataManager.getBeanMetaData( rootBeanClass ), executable, rootBeanMetaData.getMetaDataFor( executable ), executableReturnValue diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaData.java index 54f394a56e..3b64e9d1c2 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaData.java @@ -15,7 +15,7 @@ import org.hibernate.validator.internal.engine.valueextraction.AnnotatedObject; import org.hibernate.validator.internal.engine.valueextraction.ArrayElement; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.BeanMetaDataManager; +import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; /** * An aggregated view of the cascading validation metadata. Note that it also includes the cascading validation metadata @@ -61,7 +61,7 @@ public interface CascadingMetaData { */ CascadingMetaData addRuntimeContainerSupport(ValueExtractorManager valueExtractorManager, Class valueClass); - default BeanMetaData getBeanMetaDataForCascadable(BeanMetaDataManager beanMetaDataManager, Object value) { - return beanMetaDataManager.getBeanMetaData( value.getClass() ); + default BeanMetaData getBeanMetaDataForCascadable(ConstraintMetaDataManager constraintMetaDataManager, Object value) { + return constraintMetaDataManager.getBeanMetaData( value.getClass() ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/ConstraintMetaDataManager.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/ConstraintMetaDataManager.java new file mode 100644 index 0000000000..a24b74ae70 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/ConstraintMetaDataManager.java @@ -0,0 +1,239 @@ +/* + * 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.manager; + +import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; +import static org.hibernate.validator.internal.util.ConcurrentReferenceHashMap.Option.IDENTITY_COMPARISONS; +import static org.hibernate.validator.internal.util.ConcurrentReferenceHashMap.ReferenceType.SOFT; +import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; + +import javax.validation.valueextraction.ValueExtractor; + +import org.hibernate.validator.internal.engine.MethodValidationConfiguration; +import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; +import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl; +import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl.BeanMetaDataBuilder; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider; +import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; +import org.hibernate.validator.internal.metadata.raw.BeanConfiguration; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; +import org.hibernate.validator.internal.util.CollectionHelper; +import org.hibernate.validator.internal.util.ConcurrentReferenceHashMap; +import org.hibernate.validator.internal.util.Contracts; +import org.hibernate.validator.internal.util.ExecutableHelper; +import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; +import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.util.classhierarchy.ClassHierarchyHelper; +import org.hibernate.validator.internal.util.stereotypes.Immutable; + +/** + * This manager is in charge of providing all constraint related meta data + * required by the validation engine. + *

+ * Actual retrieval of meta data is delegated to {@link MetaDataProvider} + * implementations which load meta-data based e.g. based on annotations or XML. + *

+ *

+ * For performance reasons a cache is used which stores all meta data once + * loaded for repeated retrieval. Upon initialization this cache is populated + * with meta data provided by the given eager providers. If the cache + * doesn't contain the meta data for a requested type it will be retrieved on + * demand using the annotation based provider. + *

+ * + * @author Gunnar Morling + * @author Chris Beckey <cbeckey@paypal.com> + * @author Guillaume Smet +*/ +public class ConstraintMetaDataManager { + /** + * The default initial capacity for this cache. + */ + private static final int DEFAULT_INITIAL_CAPACITY = 16; + + /** + * The default load factor for this cache. + */ + private static final float DEFAULT_LOAD_FACTOR = 0.75f; + + /** + * The default concurrency level for this cache. + */ + private static final int DEFAULT_CONCURRENCY_LEVEL = 16; + + /** + * Additional metadata providers used for meta data retrieval if + * the XML and/or programmatic configuration is used. + */ + @Immutable + private final List metaDataProviders; + + /** + * Helper for builtin constraints and their validator implementations + */ + private final ConstraintHelper constraintHelper; + + /** + * Used for resolving generic type information. + */ + private final TypeResolutionHelper typeResolutionHelper; + + /** + * The {@link ValueExtractor} manager. + */ + private final ValueExtractorManager valueExtractorManager; + + private final ExecutableParameterNameProvider parameterNameProvider; + + /** + * Used to cache the constraint meta data for validated entities + */ + private final ConcurrentReferenceHashMap, BeanMetaData> beanMetaDataCache; + + /** + * Used for resolving type parameters. Thread-safe. + */ + private final ExecutableHelper executableHelper; + + private final ValidationOrderGenerator validationOrderGenerator; + + /** + * the three properties in this field affect the invocation of rules associated to section 4.5.5 + * of the specification. By default they are all false, if true they allow + * for relaxation of the Liskov Substitution Principal. + */ + private final MethodValidationConfiguration methodValidationConfiguration; + + public ConstraintMetaDataManager(ConstraintHelper constraintHelper, + ExecutableHelper executableHelper, + TypeResolutionHelper typeResolutionHelper, + ExecutableParameterNameProvider parameterNameProvider, + ValueExtractorManager valueExtractorManager, + JavaBeanHelper javaBeanHelper, + ValidationOrderGenerator validationOrderGenerator, + List optionalMetaDataProviders, + MethodValidationConfiguration methodValidationConfiguration) { + this.constraintHelper = constraintHelper; + this.executableHelper = executableHelper; + this.typeResolutionHelper = typeResolutionHelper; + this.valueExtractorManager = valueExtractorManager; + this.parameterNameProvider = parameterNameProvider; + this.validationOrderGenerator = validationOrderGenerator; + + this.methodValidationConfiguration = methodValidationConfiguration; + + this.beanMetaDataCache = new ConcurrentReferenceHashMap<>( + DEFAULT_INITIAL_CAPACITY, + DEFAULT_LOAD_FACTOR, + DEFAULT_CONCURRENCY_LEVEL, + SOFT, + SOFT, + EnumSet.of( IDENTITY_COMPARISONS ) + ); + + AnnotationProcessingOptions annotationProcessingOptions = getAnnotationProcessingOptionsFromNonDefaultProviders( optionalMetaDataProviders ); + AnnotationMetaDataProvider defaultProvider = new AnnotationMetaDataProvider( + constraintHelper, + typeResolutionHelper, + valueExtractorManager, + javaBeanHelper, + annotationProcessingOptions + ); + List tmpMetaDataProviders = new ArrayList<>( optionalMetaDataProviders.size() + 1 ); + // We add the annotation based metadata provider at the first position so that the entire metadata model is assembled + // first. + // The other optional metadata providers will then contribute their additional metadata to the preexisting model. + // This helps to mitigate issues like HV-1450. + tmpMetaDataProviders.add( defaultProvider ); + tmpMetaDataProviders.addAll( optionalMetaDataProviders ); + + this.metaDataProviders = CollectionHelper.toImmutableList( tmpMetaDataProviders ); + } + + @SuppressWarnings("unchecked") + public BeanMetaData getBeanMetaData(Class beanClass) { + Contracts.assertNotNull( beanClass, MESSAGES.beanTypeCannotBeNull() ); + + BeanMetaData beanMetaData = (BeanMetaData) beanMetaDataCache.computeIfAbsent( beanClass, + bc -> createBeanMetaData( bc ) ); + + return beanMetaData; + } + + public void clear() { + beanMetaDataCache.clear(); + } + + public int numberOfCachedBeanMetaDataInstances() { + return beanMetaDataCache.size(); + } + + /** + * Creates a {@link org.hibernate.validator.internal.metadata.aggregated.BeanMetaData} containing the meta data from all meta + * data providers for the given type and its hierarchy. + * + * @param The type of interest. + * @param clazz The type's class. + * + * @return A bean meta data object for the given type. + */ + private BeanMetaDataImpl createBeanMetaData(Class clazz) { + BeanMetaDataBuilder builder = BeanMetaDataBuilder.getInstance( + constraintHelper, executableHelper, typeResolutionHelper, valueExtractorManager, parameterNameProvider, validationOrderGenerator, clazz, methodValidationConfiguration ); + + for ( MetaDataProvider provider : metaDataProviders ) { + for ( BeanConfiguration beanConfiguration : getBeanConfigurationForHierarchy( provider, clazz ) ) { + builder.add( beanConfiguration ); + } + } + + return builder.build(); + } + + /** + * @return returns the annotation ignores from the non annotation based meta data providers + */ + private AnnotationProcessingOptions getAnnotationProcessingOptionsFromNonDefaultProviders(List optionalMetaDataProviders) { + AnnotationProcessingOptions options = new AnnotationProcessingOptionsImpl(); + for ( MetaDataProvider metaDataProvider : optionalMetaDataProviders ) { + options.merge( metaDataProvider.getAnnotationProcessingOptions() ); + } + + return options; + } + + /** + * Returns a list with the configurations for all types contained in the given type's hierarchy (including + * implemented interfaces) starting at the specified type. + * + * @param beanClass The type of interest. + * @param The type of the class to get the configurations for. + * @return A set with the configurations for the complete hierarchy of the given type. May be empty, but never + * {@code null}. + */ + private List> getBeanConfigurationForHierarchy(MetaDataProvider provider, Class beanClass) { + List> configurations = newArrayList(); + + for ( Class clazz : ClassHierarchyHelper.getHierarchy( beanClass ) ) { + BeanConfiguration configuration = provider.getBeanConfiguration( clazz ); + if ( configuration != null ) { + configurations.add( configuration ); + } + } + + return configurations; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/package-info.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/package-info.java new file mode 100644 index 0000000000..cdb0a7ca23 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/package-info.java @@ -0,0 +1,10 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +/** + * Various metadata manager related interfaces and their implementations. + */ +package org.hibernate.validator.internal.metadata.manager; diff --git a/engine/src/test/java/org/hibernate/validator/test/cfg/ConfigurationFilePropertiesTest.java b/engine/src/test/java/org/hibernate/validator/test/cfg/ConfigurationFilePropertiesTest.java index 8cdc4f3d01..fb4ee6d4ec 100644 --- a/engine/src/test/java/org/hibernate/validator/test/cfg/ConfigurationFilePropertiesTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/cfg/ConfigurationFilePropertiesTest.java @@ -18,7 +18,7 @@ import org.hibernate.validator.internal.IgnoreForbiddenApisErrors; import org.hibernate.validator.internal.engine.MethodValidationConfiguration; import org.hibernate.validator.internal.engine.ValidatorImpl; -import org.hibernate.validator.internal.metadata.BeanMetaDataManager; +import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.testutil.ValidationXmlTestHelper; import org.testng.Assert; import org.testng.annotations.Test; @@ -60,7 +60,7 @@ public void run() { Validator validator = factory.getValidator(); ValidatorImpl hibernateValidatorImpl = (ValidatorImpl) validator; - BeanMetaDataManager bmdm = findPropertyOfType( hibernateValidatorImpl, BeanMetaDataManager.class ); + ConstraintMetaDataManager bmdm = findPropertyOfType( hibernateValidatorImpl, ConstraintMetaDataManager.class ); MethodValidationConfiguration methodConfig = findPropertyOfType( bmdm, MethodValidationConfiguration.class ); Assert.assertTrue( methodConfig.isAllowMultipleCascadedValidationOnReturnValues() ); @@ -89,7 +89,7 @@ public void run() { Validator validator = factory.getValidator(); ValidatorImpl hibernateValidatorImpl = (ValidatorImpl) validator; - BeanMetaDataManager bmdm = findPropertyOfType( hibernateValidatorImpl, BeanMetaDataManager.class ); + ConstraintMetaDataManager bmdm = findPropertyOfType( hibernateValidatorImpl, ConstraintMetaDataManager.class ); MethodValidationConfiguration methodConfig = findPropertyOfType( bmdm, MethodValidationConfiguration.class ); Assert.assertTrue( methodConfig.isAllowOverridingMethodAlterParameterConstraint() ); @@ -118,7 +118,7 @@ public void run() { Validator validator = factory.getValidator(); ValidatorImpl hibernateValidatorImpl = (ValidatorImpl) validator; - BeanMetaDataManager bmdm = findPropertyOfType( hibernateValidatorImpl, BeanMetaDataManager.class ); + ConstraintMetaDataManager bmdm = findPropertyOfType( hibernateValidatorImpl, ConstraintMetaDataManager.class ); MethodValidationConfiguration methodConfig = findPropertyOfType( bmdm, MethodValidationConfiguration.class ); Assert.assertTrue( methodConfig.isAllowParallelMethodsDefineParameterConstraints() ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/path/PathImplTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/path/PathImplTest.java index 3f982953bc..d90136eace 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/path/PathImplTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/path/PathImplTest.java @@ -32,7 +32,7 @@ import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; import org.hibernate.validator.internal.engine.path.PathImpl; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.BeanMetaDataManager; +import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; @@ -193,7 +193,7 @@ public void testNonStringMapKey() { public void testCreationOfExecutablePath() throws Exception { Method executable = Container.class.getMethod( "addItem", Key.class, Item.class ); - BeanMetaDataManager beanMetaDataManager = new BeanMetaDataManager( + ConstraintMetaDataManager constraintMetaDataManager = new ConstraintMetaDataManager( new ConstraintHelper(), new ExecutableHelper( new TypeResolutionHelper() ), new TypeResolutionHelper(), @@ -205,7 +205,7 @@ public void testCreationOfExecutablePath() throws Exception { new MethodValidationConfiguration.Builder().build() ); - ExecutableMetaData executableMetaData = beanMetaDataManager.getBeanMetaData( Container.class ) + ExecutableMetaData executableMetaData = constraintMetaDataManager.getBeanMetaData( Container.class ) .getMetaDataFor( executable ).get(); PathImpl methodParameterPath = PathImpl.createPathForExecutable( executableMetaData ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/BeanMetaDataManagerTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/ConstraintMetaDataManagerTest.java similarity index 96% rename from engine/src/test/java/org/hibernate/validator/test/internal/metadata/BeanMetaDataManagerTest.java rename to engine/src/test/java/org/hibernate/validator/test/internal/metadata/ConstraintMetaDataManagerTest.java index e66c711745..a2e63a155c 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/BeanMetaDataManagerTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/ConstraintMetaDataManagerTest.java @@ -24,7 +24,7 @@ import org.hibernate.validator.internal.engine.MethodValidationConfiguration; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.BeanMetaDataManager; +import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; @@ -42,17 +42,17 @@ /** * @author Hardy Ferentschik */ -public class BeanMetaDataManagerTest { +public class ConstraintMetaDataManagerTest { private static final Log log = LoggerFactory.make( MethodHandles.lookup() ); private static final int LOOP_COUNT = 100000; private static final int ARRAY_ALLOCATION_SIZE = 100000; - private BeanMetaDataManager metaDataManager; + private ConstraintMetaDataManager metaDataManager; @BeforeMethod public void setUpBeanMetaDataManager() { - metaDataManager = new BeanMetaDataManager( + metaDataManager = new ConstraintMetaDataManager( new ConstraintHelper(), new ExecutableHelper( new TypeResolutionHelper() ), new TypeResolutionHelper(), diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java index 67d2e47713..232b9f2fd4 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java @@ -26,7 +26,7 @@ import org.hibernate.validator.internal.engine.MethodValidationConfiguration; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.BeanMetaDataManager; +import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; @@ -52,12 +52,12 @@ */ public class ExecutableMetaDataTest { - private BeanMetaDataManager beanMetaDataManager; + private ConstraintMetaDataManager constraintMetaDataManager; private BeanMetaData beanMetaData; @BeforeMethod public void setupBeanMetaData() { - beanMetaDataManager = new BeanMetaDataManager( + constraintMetaDataManager = new ConstraintMetaDataManager( new ConstraintHelper(), new ExecutableHelper( new TypeResolutionHelper() ), new TypeResolutionHelper(), @@ -69,7 +69,7 @@ public void setupBeanMetaData() { new MethodValidationConfiguration.Builder().build() ); - beanMetaData = beanMetaDataManager.getBeanMetaData( CustomerRepositoryExt.class ); + beanMetaData = constraintMetaDataManager.getBeanMetaData( CustomerRepositoryExt.class ); } @Test @@ -175,7 +175,7 @@ public void getIdentifierForConstructor() throws Exception { @TestForIssue(jiraKey = "HV-1011") public void getIdentifierForOverridingGenericMethod() throws Exception { Method method = JobRepositoryImpl.class.getMethod( "createJob", UUID.class ); - BeanMetaData beanMetaData = beanMetaDataManager.getBeanMetaData( JobRepositoryImpl.class ); + BeanMetaData beanMetaData = constraintMetaDataManager.getBeanMetaData( JobRepositoryImpl.class ); ExecutableMetaData methodMetaData = beanMetaData.getMetaDataFor( method ).get(); assertThat( methodMetaData.getSignatures() ) @@ -183,7 +183,7 @@ public void getIdentifierForOverridingGenericMethod() throws Exception { .containsOnly( "createJob(java.lang.Object)", "createJob(java.util.UUID)" ); method = JobRepository.class.getMethod( "createJob", Object.class ); - beanMetaData = beanMetaDataManager.getBeanMetaData( JobRepository.class ); + beanMetaData = constraintMetaDataManager.getBeanMetaData( JobRepository.class ); methodMetaData = beanMetaData.getMetaDataFor( method ).get(); assertThat( methodMetaData.getSignatures() ) @@ -191,7 +191,7 @@ public void getIdentifierForOverridingGenericMethod() throws Exception { .containsOnly( "createJob(java.lang.Object)" ); method = SpecialJobRepositoryImpl.class.getMethod( "createJob", Object.class ); - beanMetaData = beanMetaDataManager.getBeanMetaData( SpecialJobRepositoryImpl.class ); + beanMetaData = constraintMetaDataManager.getBeanMetaData( SpecialJobRepositoryImpl.class ); methodMetaData = beanMetaData.getMetaDataFor( method ).get(); assertThat( methodMetaData.getSignatures() ) @@ -203,7 +203,7 @@ public void getIdentifierForOverridingGenericMethod() throws Exception { @TestForIssue(jiraKey = "HV-1011") public void getIdentifierForOverloadedMethod() throws Exception { Method method = SpecialJobRepositoryImpl.class.getMethod( "createJob", String.class ); - BeanMetaData beanMetaData = beanMetaDataManager.getBeanMetaData( SpecialJobRepositoryImpl.class ); + BeanMetaData beanMetaData = constraintMetaDataManager.getBeanMetaData( SpecialJobRepositoryImpl.class ); ExecutableMetaData methodMetaData = beanMetaData.getMetaDataFor( method ).get(); assertThat( methodMetaData.getSignatures() ) diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ParameterMetaDataTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ParameterMetaDataTest.java index adf433af8c..f2621432dd 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ParameterMetaDataTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ParameterMetaDataTest.java @@ -27,7 +27,7 @@ import org.hibernate.validator.internal.engine.MethodValidationConfiguration; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.BeanMetaDataManager; +import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData; import org.hibernate.validator.internal.metadata.aggregated.ParameterMetaData; @@ -57,7 +57,7 @@ public class ParameterMetaDataTest { @BeforeMethod public void setupBeanMetaData() { - BeanMetaDataManager beanMetaDataManager = new BeanMetaDataManager( + ConstraintMetaDataManager constraintMetaDataManager = new ConstraintMetaDataManager( new ConstraintHelper(), new ExecutableHelper( new TypeResolutionHelper() ), new TypeResolutionHelper(), @@ -69,7 +69,7 @@ public void setupBeanMetaData() { new MethodValidationConfiguration.Builder().build() ); - beanMetaData = beanMetaDataManager.getBeanMetaData( CustomerRepository.class ); + beanMetaData = constraintMetaDataManager.getBeanMetaData( CustomerRepository.class ); } @Test @@ -128,7 +128,7 @@ public void parameterNameInInheritanceHierarchy() throws Exception { // // The failure rate on my current VM before fixing the bug is 50%. // Running it in a loop does not improve the odds of failure: all tests will pass or fail for all loop run. - BeanMetaDataManager beanMetaDataManager = new BeanMetaDataManager( + ConstraintMetaDataManager constraintMetaDataManager = new ConstraintMetaDataManager( new ConstraintHelper(), new ExecutableHelper( new TypeResolutionHelper() ), new TypeResolutionHelper(), @@ -139,7 +139,7 @@ public void parameterNameInInheritanceHierarchy() throws Exception { Collections.emptyList(), new MethodValidationConfiguration.Builder().build() ); - BeanMetaData localBeanMetaData = beanMetaDataManager.getBeanMetaData( ServiceImpl.class ); + BeanMetaData localBeanMetaData = constraintMetaDataManager.getBeanMetaData( ServiceImpl.class ); Method method = Service.class.getMethod( "sayHello", String.class ); ExecutableMetaData methodMetaData = localBeanMetaData.getMetaDataFor( method ).get(); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/PropertyMetaDataTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/PropertyMetaDataTest.java index 4f3c2616aa..7fb7a2241d 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/PropertyMetaDataTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/PropertyMetaDataTest.java @@ -20,7 +20,7 @@ import org.hibernate.validator.internal.engine.MethodValidationConfiguration; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.BeanMetaDataManager; +import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.metadata.aggregated.PropertyMetaData; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; @@ -37,11 +37,11 @@ */ public class PropertyMetaDataTest { - private BeanMetaDataManager beanMetaDataManager; + private ConstraintMetaDataManager constraintMetaDataManager; @BeforeMethod public void setupBeanMetaDataManager() { - beanMetaDataManager = new BeanMetaDataManager( + constraintMetaDataManager = new ConstraintMetaDataManager( new ConstraintHelper(), new ExecutableHelper( new TypeResolutionHelper() ), new TypeResolutionHelper(), @@ -56,21 +56,21 @@ public void setupBeanMetaDataManager() { @Test public void locallyDefinedGroupConversion() { - PropertyMetaData property = beanMetaDataManager.getBeanMetaData( User1.class ).getMetaDataFor( "addresses" ); + PropertyMetaData property = constraintMetaDataManager.getBeanMetaData( User1.class ).getMetaDataFor( "addresses" ); assertThat( property.getCascadables().iterator().next().getCascadingMetaData().convertGroup( Default.class ) ).isEqualTo( BasicPostal.class ); } @Test public void groupConversionDefinedInHierarchy() { - PropertyMetaData property = beanMetaDataManager.getBeanMetaData( User2.class ).getMetaDataFor( "addresses" ); + PropertyMetaData property = constraintMetaDataManager.getBeanMetaData( User2.class ).getMetaDataFor( "addresses" ); assertThat( property.getCascadables().iterator().next().getCascadingMetaData().convertGroup( Default.class ) ).isEqualTo( BasicPostal.class ); } @Test(expectedExceptions = ConstraintDeclarationException.class, expectedExceptionsMessageRegExp = "HV000124.*") public void groupConversionInHierarchyWithSameFrom() { - beanMetaDataManager.getBeanMetaData( User3.class ).getMetaDataFor( "addresses" ); + constraintMetaDataManager.getBeanMetaData( User3.class ).getMetaDataFor( "addresses" ); } public interface Complete extends Default { From 4a523a8f33abf02c5178e4c3ddb3aacf5512eae4 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Mon, 9 Jul 2018 20:38:35 +0200 Subject: [PATCH 05/14] [property-holder-preparation] Extract bean metadata provider code to its own class --- .../metadata/aggregated/BeanMetaDataImpl.java | 4 + .../BeanMetaDataProvider.java} | 49 ++--- .../manager/ConstraintMetaDataManager.java | 188 ++---------------- .../metadata/manager/MetaDataCache.java | 71 +++++++ .../PropertyHolderBeanMetaDataProvider.java | 96 +++++++++ .../PropertyHolderMetaDataProvider.java | 19 ++ .../PropertyHolderConfiguration.java | 94 +++++++++ .../PropertyHolderConfigurationSource.java | 62 ++++++ .../cfg/ConfigurationFilePropertiesTest.java | 19 +- 9 files changed, 382 insertions(+), 220 deletions(-) rename engine/src/main/java/org/hibernate/validator/internal/metadata/{BeanMetaDataManager.java => manager/BeanMetaDataProvider.java} (87%) create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/manager/MetaDataCache.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/manager/PropertyHolderBeanMetaDataProvider.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/provider/PropertyHolderMetaDataProvider.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/PropertyHolderConfiguration.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/PropertyHolderConfigurationSource.java diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java index 3926d11e81..6a11912d31 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java @@ -571,6 +571,10 @@ public String toString() { + ", defaultGroupSequence=" + getDefaultGroupSequence( null ) + '}'; } + public static class PropertyHolderBeanMetaDataBuilder { + + } + public static class BeanMetaDataBuilder { private final ConstraintHelper constraintHelper; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/BeanMetaDataManager.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/BeanMetaDataProvider.java similarity index 87% rename from engine/src/main/java/org/hibernate/validator/internal/metadata/BeanMetaDataManager.java rename to engine/src/main/java/org/hibernate/validator/internal/metadata/manager/BeanMetaDataProvider.java index d1908005e3..0ecba60abe 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/BeanMetaDataManager.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/BeanMetaDataProvider.java @@ -4,15 +4,12 @@ * License: Apache License, Version 2.0 * See the license.txt file in the root directory or . */ -package org.hibernate.validator.internal.metadata; +package org.hibernate.validator.internal.metadata.manager; import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; -import static org.hibernate.validator.internal.util.ConcurrentReferenceHashMap.Option.IDENTITY_COMPARISONS; -import static org.hibernate.validator.internal.util.ConcurrentReferenceHashMap.ReferenceType.SOFT; import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES; import java.util.ArrayList; -import java.util.EnumSet; import java.util.List; import javax.validation.valueextraction.ValueExtractor; @@ -31,7 +28,6 @@ import org.hibernate.validator.internal.metadata.raw.BeanConfiguration; import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; import org.hibernate.validator.internal.util.CollectionHelper; -import org.hibernate.validator.internal.util.ConcurrentReferenceHashMap; import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; @@ -57,23 +53,9 @@ * @author Gunnar Morling * @author Chris Beckey <cbeckey@paypal.com> * @author Guillaume Smet -*/ -public class BeanMetaDataManager { - /** - * The default initial capacity for this cache. - */ - private static final int DEFAULT_INITIAL_CAPACITY = 16; - - /** - * The default load factor for this cache. - */ - private static final float DEFAULT_LOAD_FACTOR = 0.75f; - - /** - * The default concurrency level for this cache. - */ - private static final int DEFAULT_CONCURRENCY_LEVEL = 16; - + * @author Marko Bekhta + */ +public class BeanMetaDataProvider { /** * Additional metadata providers used for meta data retrieval if * the XML and/or programmatic configuration is used. @@ -98,11 +80,6 @@ public class BeanMetaDataManager { private final ExecutableParameterNameProvider parameterNameProvider; - /** - * Used to cache the constraint meta data for validated entities - */ - private final ConcurrentReferenceHashMap, BeanMetaData> beanMetaDataCache; - /** * Used for resolving type parameters. Thread-safe. */ @@ -117,7 +94,9 @@ public class BeanMetaDataManager { */ private final MethodValidationConfiguration methodValidationConfiguration; - public BeanMetaDataManager(ConstraintHelper constraintHelper, + private final MetaDataCache> beanMetaDataCache; + + public BeanMetaDataProvider(ConstraintHelper constraintHelper, ExecutableHelper executableHelper, TypeResolutionHelper typeResolutionHelper, ExecutableParameterNameProvider parameterNameProvider, @@ -135,14 +114,8 @@ public BeanMetaDataManager(ConstraintHelper constraintHelper, this.methodValidationConfiguration = methodValidationConfiguration; - this.beanMetaDataCache = new ConcurrentReferenceHashMap<>( - DEFAULT_INITIAL_CAPACITY, - DEFAULT_LOAD_FACTOR, - DEFAULT_CONCURRENCY_LEVEL, - SOFT, - SOFT, - EnumSet.of( IDENTITY_COMPARISONS ) - ); + this.beanMetaDataCache = new MetaDataCache<>(); + AnnotationProcessingOptions annotationProcessingOptions = getAnnotationProcessingOptionsFromNonDefaultProviders( optionalMetaDataProviders ); AnnotationMetaDataProvider defaultProvider = new AnnotationMetaDataProvider( @@ -167,7 +140,8 @@ public BeanMetaDataManager(ConstraintHelper constraintHelper, public BeanMetaData getBeanMetaData(Class beanClass) { Contracts.assertNotNull( beanClass, MESSAGES.beanTypeCannotBeNull() ); - BeanMetaData beanMetaData = (BeanMetaData) beanMetaDataCache.computeIfAbsent( beanClass, + BeanMetaData beanMetaData = (BeanMetaData) beanMetaDataCache.computeIfAbsent( + beanClass, bc -> createBeanMetaData( bc ) ); return beanMetaData; @@ -221,6 +195,7 @@ private AnnotationProcessingOptions getAnnotationProcessingOptionsFromNonDefault * * @param beanClass The type of interest. * @param The type of the class to get the configurations for. + * * @return A set with the configurations for the complete hierarchy of the given type. May be empty, but never * {@code null}. */ diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/ConstraintMetaDataManager.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/ConstraintMetaDataManager.java index a24b74ae70..b3dfbd3f73 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/ConstraintMetaDataManager.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/ConstraintMetaDataManager.java @@ -6,116 +6,42 @@ */ package org.hibernate.validator.internal.metadata.manager; -import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; -import static org.hibernate.validator.internal.util.ConcurrentReferenceHashMap.Option.IDENTITY_COMPARISONS; -import static org.hibernate.validator.internal.util.ConcurrentReferenceHashMap.ReferenceType.SOFT; -import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES; - -import java.util.ArrayList; -import java.util.EnumSet; import java.util.List; -import javax.validation.valueextraction.ValueExtractor; - import org.hibernate.validator.internal.engine.MethodValidationConfiguration; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; -import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl; -import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl.BeanMetaDataBuilder; -import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions; -import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; -import org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider; import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; -import org.hibernate.validator.internal.metadata.raw.BeanConfiguration; import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; -import org.hibernate.validator.internal.util.CollectionHelper; -import org.hibernate.validator.internal.util.ConcurrentReferenceHashMap; -import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.TypeResolutionHelper; -import org.hibernate.validator.internal.util.classhierarchy.ClassHierarchyHelper; -import org.hibernate.validator.internal.util.stereotypes.Immutable; /** + * TODO: provide correct description. + *

* This manager is in charge of providing all constraint related meta data * required by the validation engine. *

* Actual retrieval of meta data is delegated to {@link MetaDataProvider} * implementations which load meta-data based e.g. based on annotations or XML. - *

*

* For performance reasons a cache is used which stores all meta data once * loaded for repeated retrieval. Upon initialization this cache is populated * with meta data provided by the given eager providers. If the cache * doesn't contain the meta data for a requested type it will be retrieved on * demand using the annotation based provider. - *

* * @author Gunnar Morling * @author Chris Beckey <cbeckey@paypal.com> * @author Guillaume Smet -*/ + * @author Marko Bekhta + */ public class ConstraintMetaDataManager { - /** - * The default initial capacity for this cache. - */ - private static final int DEFAULT_INITIAL_CAPACITY = 16; - - /** - * The default load factor for this cache. - */ - private static final float DEFAULT_LOAD_FACTOR = 0.75f; - - /** - * The default concurrency level for this cache. - */ - private static final int DEFAULT_CONCURRENCY_LEVEL = 16; - - /** - * Additional metadata providers used for meta data retrieval if - * the XML and/or programmatic configuration is used. - */ - @Immutable - private final List metaDataProviders; - - /** - * Helper for builtin constraints and their validator implementations - */ - private final ConstraintHelper constraintHelper; - /** - * Used for resolving generic type information. - */ - private final TypeResolutionHelper typeResolutionHelper; - - /** - * The {@link ValueExtractor} manager. - */ - private final ValueExtractorManager valueExtractorManager; - - private final ExecutableParameterNameProvider parameterNameProvider; - - /** - * Used to cache the constraint meta data for validated entities - */ - private final ConcurrentReferenceHashMap, BeanMetaData> beanMetaDataCache; - - /** - * Used for resolving type parameters. Thread-safe. - */ - private final ExecutableHelper executableHelper; - - private final ValidationOrderGenerator validationOrderGenerator; - - /** - * the three properties in this field affect the invocation of rules associated to section 4.5.5 - * of the specification. By default they are all false, if true they allow - * for relaxation of the Liskov Substitution Principal. - */ - private final MethodValidationConfiguration methodValidationConfiguration; + private final BeanMetaDataProvider beanMetaDataProvider; public ConstraintMetaDataManager(ConstraintHelper constraintHelper, ExecutableHelper executableHelper, @@ -126,114 +52,28 @@ public ConstraintMetaDataManager(ConstraintHelper constraintHelper, ValidationOrderGenerator validationOrderGenerator, List optionalMetaDataProviders, MethodValidationConfiguration methodValidationConfiguration) { - this.constraintHelper = constraintHelper; - this.executableHelper = executableHelper; - this.typeResolutionHelper = typeResolutionHelper; - this.valueExtractorManager = valueExtractorManager; - this.parameterNameProvider = parameterNameProvider; - this.validationOrderGenerator = validationOrderGenerator; - - this.methodValidationConfiguration = methodValidationConfiguration; - - this.beanMetaDataCache = new ConcurrentReferenceHashMap<>( - DEFAULT_INITIAL_CAPACITY, - DEFAULT_LOAD_FACTOR, - DEFAULT_CONCURRENCY_LEVEL, - SOFT, - SOFT, - EnumSet.of( IDENTITY_COMPARISONS ) - ); - - AnnotationProcessingOptions annotationProcessingOptions = getAnnotationProcessingOptionsFromNonDefaultProviders( optionalMetaDataProviders ); - AnnotationMetaDataProvider defaultProvider = new AnnotationMetaDataProvider( + this.beanMetaDataProvider = new BeanMetaDataProvider( constraintHelper, + executableHelper, typeResolutionHelper, + parameterNameProvider, valueExtractorManager, javaBeanHelper, - annotationProcessingOptions + validationOrderGenerator, + optionalMetaDataProviders, + methodValidationConfiguration ); - List tmpMetaDataProviders = new ArrayList<>( optionalMetaDataProviders.size() + 1 ); - // We add the annotation based metadata provider at the first position so that the entire metadata model is assembled - // first. - // The other optional metadata providers will then contribute their additional metadata to the preexisting model. - // This helps to mitigate issues like HV-1450. - tmpMetaDataProviders.add( defaultProvider ); - tmpMetaDataProviders.addAll( optionalMetaDataProviders ); - - this.metaDataProviders = CollectionHelper.toImmutableList( tmpMetaDataProviders ); } - @SuppressWarnings("unchecked") public BeanMetaData getBeanMetaData(Class beanClass) { - Contracts.assertNotNull( beanClass, MESSAGES.beanTypeCannotBeNull() ); - - BeanMetaData beanMetaData = (BeanMetaData) beanMetaDataCache.computeIfAbsent( beanClass, - bc -> createBeanMetaData( bc ) ); - - return beanMetaData; + return beanMetaDataProvider.getBeanMetaData( beanClass ); } public void clear() { - beanMetaDataCache.clear(); + beanMetaDataProvider.clear(); } public int numberOfCachedBeanMetaDataInstances() { - return beanMetaDataCache.size(); - } - - /** - * Creates a {@link org.hibernate.validator.internal.metadata.aggregated.BeanMetaData} containing the meta data from all meta - * data providers for the given type and its hierarchy. - * - * @param The type of interest. - * @param clazz The type's class. - * - * @return A bean meta data object for the given type. - */ - private BeanMetaDataImpl createBeanMetaData(Class clazz) { - BeanMetaDataBuilder builder = BeanMetaDataBuilder.getInstance( - constraintHelper, executableHelper, typeResolutionHelper, valueExtractorManager, parameterNameProvider, validationOrderGenerator, clazz, methodValidationConfiguration ); - - for ( MetaDataProvider provider : metaDataProviders ) { - for ( BeanConfiguration beanConfiguration : getBeanConfigurationForHierarchy( provider, clazz ) ) { - builder.add( beanConfiguration ); - } - } - - return builder.build(); - } - - /** - * @return returns the annotation ignores from the non annotation based meta data providers - */ - private AnnotationProcessingOptions getAnnotationProcessingOptionsFromNonDefaultProviders(List optionalMetaDataProviders) { - AnnotationProcessingOptions options = new AnnotationProcessingOptionsImpl(); - for ( MetaDataProvider metaDataProvider : optionalMetaDataProviders ) { - options.merge( metaDataProvider.getAnnotationProcessingOptions() ); - } - - return options; - } - - /** - * Returns a list with the configurations for all types contained in the given type's hierarchy (including - * implemented interfaces) starting at the specified type. - * - * @param beanClass The type of interest. - * @param The type of the class to get the configurations for. - * @return A set with the configurations for the complete hierarchy of the given type. May be empty, but never - * {@code null}. - */ - private List> getBeanConfigurationForHierarchy(MetaDataProvider provider, Class beanClass) { - List> configurations = newArrayList(); - - for ( Class clazz : ClassHierarchyHelper.getHierarchy( beanClass ) ) { - BeanConfiguration configuration = provider.getBeanConfiguration( clazz ); - if ( configuration != null ) { - configurations.add( configuration ); - } - } - - return configurations; + return beanMetaDataProvider.numberOfCachedBeanMetaDataInstances(); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/MetaDataCache.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/MetaDataCache.java new file mode 100644 index 0000000000..12df0af801 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/MetaDataCache.java @@ -0,0 +1,71 @@ +/* + * 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.manager; + +import static org.hibernate.validator.internal.util.ConcurrentReferenceHashMap.Option.IDENTITY_COMPARISONS; +import static org.hibernate.validator.internal.util.ConcurrentReferenceHashMap.ReferenceType.SOFT; + +import java.util.EnumSet; +import java.util.function.Function; + +import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; +import org.hibernate.validator.internal.util.ConcurrentReferenceHashMap; + +/** + * This is a preconfigured cache for storing bean metadata + * + * @author Marko Bekhta + */ +public final class MetaDataCache { + /** + * The default initial capacity for this cache. + */ + private static final int DEFAULT_INITIAL_CAPACITY = 16; + + /** + * The default load factor for this cache. + */ + private static final float DEFAULT_LOAD_FACTOR = 0.75f; + + /** + * The default concurrency level for this cache. + */ + private static final int DEFAULT_CONCURRENCY_LEVEL = 16; + + /** + * Used to cache the constraint meta data for validated entities + */ + private final ConcurrentReferenceHashMap> beanMetaDataCache; + + public MetaDataCache() { + this.beanMetaDataCache = new ConcurrentReferenceHashMap<>( + DEFAULT_INITIAL_CAPACITY, + DEFAULT_LOAD_FACTOR, + DEFAULT_CONCURRENCY_LEVEL, + SOFT, + SOFT, + EnumSet.of( IDENTITY_COMPARISONS ) + ); + } + + + public void clear() { + beanMetaDataCache.clear(); + } + + public int numberOfCachedBeanMetaDataInstances() { + return beanMetaDataCache.size(); + } + + public int size() { + return beanMetaDataCache.size(); + } + + public BeanMetaData computeIfAbsent(K key, Function> mappingFunction) { + return beanMetaDataCache.computeIfAbsent( key, mappingFunction ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/PropertyHolderBeanMetaDataProvider.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/PropertyHolderBeanMetaDataProvider.java new file mode 100644 index 0000000000..5475d2e551 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/PropertyHolderBeanMetaDataProvider.java @@ -0,0 +1,96 @@ +/* + * 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.manager; + +import java.util.List; +import java.util.Optional; + +import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; +import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl; +import org.hibernate.validator.internal.metadata.provider.PropertyHolderMetaDataProvider; +import org.hibernate.validator.internal.metadata.raw.propertyholder.PropertyHolderConfiguration; +import org.hibernate.validator.internal.util.stereotypes.Immutable; + +/** + * @author Marko Bekhta + */ +public class PropertyHolderBeanMetaDataProvider { + + @Immutable + private final List propertyHolderMetaDataProviderList; + + private final MetaDataCache metaDataCache; + + public PropertyHolderBeanMetaDataProvider(List propertyHolderMetaDataProviderList) { + this.propertyHolderMetaDataProviderList = propertyHolderMetaDataProviderList; + + this.metaDataCache = new MetaDataCache<>(); + } + + @SuppressWarnings("unchecked") + public BeanMetaData getBeanMetaData(Class beanClass, String mapping) { + return (BeanMetaData) metaDataCache.computeIfAbsent( + new PropertyHolderMetadataKey( beanClass, mapping ), + key -> createBeanMetaData( key ) + ); + } + + private BeanMetaDataImpl createBeanMetaData(PropertyHolderMetadataKey metadataKey) { + for ( PropertyHolderMetaDataProvider metaDataProvider : propertyHolderMetaDataProviderList ) { + Optional beanConfiguration = metaDataProvider.getBeanConfiguration( metadataKey.mapping ); + if ( beanConfiguration.isPresent() ) { + + } + } + return null; + } + + private static class PropertyHolderMetadataKey { + private String mapping; + private Class beanClass; + private int hashCode; + + public PropertyHolderMetadataKey(Class beanClass, String mapping) { + this.mapping = mapping; + this.beanClass = beanClass; + + this.hashCode = buildHashCode(); + } + + private int buildHashCode() { + int result = mapping.hashCode(); + result = 31 * result + beanClass.hashCode(); + return result; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + + PropertyHolderMetadataKey that = (PropertyHolderMetadataKey) o; + + if ( !mapping.equals( that.mapping ) ) { + return false; + } + if ( !beanClass.equals( that.beanClass ) ) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return hashCode; + } + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/PropertyHolderMetaDataProvider.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/PropertyHolderMetaDataProvider.java new file mode 100644 index 0000000000..d38ac6fcd9 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/PropertyHolderMetaDataProvider.java @@ -0,0 +1,19 @@ +/* + * 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.provider; + +import java.util.Optional; + +import org.hibernate.validator.internal.metadata.raw.propertyholder.PropertyHolderConfiguration; + +/** + * @author Marko Bekhta + */ +public interface PropertyHolderMetaDataProvider { + + Optional getBeanConfiguration(String mappingName); +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/PropertyHolderConfiguration.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/PropertyHolderConfiguration.java new file mode 100644 index 0000000000..11f8e861da --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/PropertyHolderConfiguration.java @@ -0,0 +1,94 @@ +/* + * 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.raw.propertyholder; + +import java.util.List; +import java.util.Set; + +import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; +import org.hibernate.validator.internal.util.CollectionHelper; + +/** + * @author Marko Bekhta + */ +public class PropertyHolderConfiguration { + + private final PropertyHolderConfigurationSource source; + + private final String mappingName; + + private final Set constrainedElements; + + private final List> defaultGroupSequence; + + public PropertyHolderConfiguration( + PropertyHolderConfigurationSource source, + String mappingName, + Set constrainedElements, + List> defaultGroupSequence) { + + this.source = source; + this.mappingName = mappingName; + this.constrainedElements = CollectionHelper.newHashSet( constrainedElements ); + this.defaultGroupSequence = defaultGroupSequence; + } + + public PropertyHolderConfigurationSource getSource() { + return source; + } + + public String getMappingName() { + return mappingName; + } + + public Set getConstrainedElements() { + return constrainedElements; + } + + public List> getDefaultGroupSequence() { + return defaultGroupSequence; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder( "PropertyHolderConfiguration{" ); + sb.append( "source=" ).append( source ); + sb.append( ", mappingName='" ).append( mappingName ).append( '\'' ); + sb.append( ", constrainedElements=" ).append( constrainedElements ); + sb.append( ", defaultGroupSequence=" ).append( defaultGroupSequence ); + sb.append( '}' ); + return sb.toString(); + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + + PropertyHolderConfiguration that = (PropertyHolderConfiguration) o; + + if ( source != that.source ) { + return false; + } + if ( !mappingName.equals( that.mappingName ) ) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int result = source.hashCode(); + result = 31 * result + mappingName.hashCode(); + return result; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/PropertyHolderConfigurationSource.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/PropertyHolderConfigurationSource.java new file mode 100644 index 0000000000..acf29b4c79 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/PropertyHolderConfigurationSource.java @@ -0,0 +1,62 @@ +/* + * 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.raw.propertyholder; + +/** + * The source of constraint meta data. + * + * @author Gunnar Morling + * @author Marko Bekhta + */ +public enum PropertyHolderConfigurationSource { + + /** + * The source of configuration is JSON configuration + */ + JSON( 0 ), + /** + * The source of configuration is XML configuration + */ + XML( 1 ), + /** + * The source of configuration is the programmatic API + */ + API( 2 ); + + private int priority; + + private PropertyHolderConfigurationSource(int priority) { + this.priority = priority; + } + + /** + * Returns this sources priority. Can be used to determine which + * configuration shall apply in case of conflicting configurations by + * several providers. + * + * @return This source's priority. + */ + public int getPriority() { + return priority; + } + + /** + * Returns that configuration source from the given two sources, which has + * the higher priority. + * + * @param a + * A configuration source. + * @param b + * Another configuration source. + * + * @return The source with the higher priority. Will be source {@code a} if + * both have the same priority. + */ + public static PropertyHolderConfigurationSource max(PropertyHolderConfigurationSource a, PropertyHolderConfigurationSource b) { + return a.getPriority() >= b.getPriority() ? a : b; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/cfg/ConfigurationFilePropertiesTest.java b/engine/src/test/java/org/hibernate/validator/test/cfg/ConfigurationFilePropertiesTest.java index fb4ee6d4ec..10f5f834af 100644 --- a/engine/src/test/java/org/hibernate/validator/test/cfg/ConfigurationFilePropertiesTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/cfg/ConfigurationFilePropertiesTest.java @@ -18,6 +18,7 @@ import org.hibernate.validator.internal.IgnoreForbiddenApisErrors; import org.hibernate.validator.internal.engine.MethodValidationConfiguration; import org.hibernate.validator.internal.engine.ValidatorImpl; +import org.hibernate.validator.internal.metadata.manager.BeanMetaDataProvider; import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.testutil.ValidationXmlTestHelper; import org.testng.Assert; @@ -59,9 +60,7 @@ public void run() { ValidatorFactory factory = hibernateConfig.buildValidatorFactory(); Validator validator = factory.getValidator(); - ValidatorImpl hibernateValidatorImpl = (ValidatorImpl) validator; - ConstraintMetaDataManager bmdm = findPropertyOfType( hibernateValidatorImpl, ConstraintMetaDataManager.class ); - MethodValidationConfiguration methodConfig = findPropertyOfType( bmdm, MethodValidationConfiguration.class ); + MethodValidationConfiguration methodConfig = getMethodValidationConfiguration( (ValidatorImpl) validator ); Assert.assertTrue( methodConfig.isAllowMultipleCascadedValidationOnReturnValues() ); } @@ -88,9 +87,7 @@ public void run() { ValidatorFactory factory = hibernateConfig.buildValidatorFactory(); Validator validator = factory.getValidator(); - ValidatorImpl hibernateValidatorImpl = (ValidatorImpl) validator; - ConstraintMetaDataManager bmdm = findPropertyOfType( hibernateValidatorImpl, ConstraintMetaDataManager.class ); - MethodValidationConfiguration methodConfig = findPropertyOfType( bmdm, MethodValidationConfiguration.class ); + MethodValidationConfiguration methodConfig = getMethodValidationConfiguration( (ValidatorImpl) validator ); Assert.assertTrue( methodConfig.isAllowOverridingMethodAlterParameterConstraint() ); } @@ -117,9 +114,7 @@ public void run() { ValidatorFactory factory = hibernateConfig.buildValidatorFactory(); Validator validator = factory.getValidator(); - ValidatorImpl hibernateValidatorImpl = (ValidatorImpl) validator; - ConstraintMetaDataManager bmdm = findPropertyOfType( hibernateValidatorImpl, ConstraintMetaDataManager.class ); - MethodValidationConfiguration methodConfig = findPropertyOfType( bmdm, MethodValidationConfiguration.class ); + MethodValidationConfiguration methodConfig = getMethodValidationConfiguration( (ValidatorImpl) validator ); Assert.assertTrue( methodConfig.isAllowParallelMethodsDefineParameterConstraints() ); } @@ -158,6 +153,12 @@ private T findPropertyOfType(Object subject, Class clazz) return null; } + private MethodValidationConfiguration getMethodValidationConfiguration(ValidatorImpl hibernateValidatorImpl) { + ConstraintMetaDataManager cmdm = findPropertyOfType( hibernateValidatorImpl, ConstraintMetaDataManager.class ); + BeanMetaDataProvider bmdp = findPropertyOfType( cmdm, BeanMetaDataProvider.class ); + return findPropertyOfType( bmdp, MethodValidationConfiguration.class ); + } + private void runWithCustomValidationXml(String validationXmlName, Runnable runnable) { new ValidationXmlTestHelper( ConfigurationFilePropertiesTest.class ). runWithCustomValidationXml( validationXmlName, runnable ); From 37d454590aee518cb237bfbaa91c6d02f8ad65e2 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Thu, 12 Jul 2018 20:53:14 +0200 Subject: [PATCH 06/14] [property-holder-preparation] Extract BeanMetaDataBuilder to upper level class and create corresponding analog for property holders --- .../aggregated/BeanMetaDataBuilder.java | 330 ++++++++++++++++++ .../metadata/aggregated/BeanMetaDataImpl.java | 326 +---------------- .../PropertyHolderBeanMetaDataBuilder.java | 218 ++++++++++++ .../manager/BeanMetaDataProvider.java | 2 +- .../PropertyHolderBeanMetaDataProvider.java | 52 ++- 5 files changed, 599 insertions(+), 329 deletions(-) create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataBuilder.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/PropertyHolderBeanMetaDataBuilder.java diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataBuilder.java new file mode 100644 index 0000000000..765fdfe681 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataBuilder.java @@ -0,0 +1,330 @@ +/* + * 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.aggregated; + +import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; + +import java.util.List; +import java.util.Set; + +import org.hibernate.validator.internal.engine.MethodValidationConfiguration; +import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.metadata.raw.BeanConfiguration; +import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; +import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; +import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable; +import org.hibernate.validator.internal.metadata.raw.ConstrainedField; +import org.hibernate.validator.internal.metadata.raw.ConstrainedType; +import org.hibernate.validator.internal.properties.Callable; +import org.hibernate.validator.internal.util.ExecutableHelper; +import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; +import org.hibernate.validator.internal.util.StringHelper; +import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider; + +/** + * @author Hardy Ferentschik + * @author Gunnar Morling + * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI + * @author Chris Beckey <cbeckey@paypal.com> + * @author Guillaume Smet + * @author Marko Bekhta + */ +public class BeanMetaDataBuilder { + + private final ConstraintHelper constraintHelper; + private final ValidationOrderGenerator validationOrderGenerator; + private final Class beanClass; + private final Set builders = newHashSet(); + private final ExecutableHelper executableHelper; + private final TypeResolutionHelper typeResolutionHelper; + private final ValueExtractorManager valueExtractorManager; + private final ExecutableParameterNameProvider parameterNameProvider; + private final MethodValidationConfiguration methodValidationConfiguration; + + private ConfigurationSource sequenceSource; + private ConfigurationSource providerSource; + private List> defaultGroupSequence; + private DefaultGroupSequenceProvider defaultGroupSequenceProvider; + + + private BeanMetaDataBuilder( + ConstraintHelper constraintHelper, + ExecutableHelper executableHelper, + TypeResolutionHelper typeResolutionHelper, + ValueExtractorManager valueExtractorManager, + ExecutableParameterNameProvider parameterNameProvider, + ValidationOrderGenerator validationOrderGenerator, + Class beanClass, + MethodValidationConfiguration methodValidationConfiguration) { + this.beanClass = beanClass; + this.constraintHelper = constraintHelper; + this.validationOrderGenerator = validationOrderGenerator; + this.executableHelper = executableHelper; + this.typeResolutionHelper = typeResolutionHelper; + this.valueExtractorManager = valueExtractorManager; + this.parameterNameProvider = parameterNameProvider; + this.methodValidationConfiguration = methodValidationConfiguration; + } + + public static BeanMetaDataBuilder getInstance( + ConstraintHelper constraintHelper, + ExecutableHelper executableHelper, + TypeResolutionHelper typeResolutionHelper, + ValueExtractorManager valueExtractorManager, + ExecutableParameterNameProvider parameterNameProvider, + ValidationOrderGenerator validationOrderGenerator, + Class beanClass, + MethodValidationConfiguration methodValidationConfiguration) { + return new BeanMetaDataBuilder<>( + constraintHelper, + executableHelper, + typeResolutionHelper, + valueExtractorManager, + parameterNameProvider, + validationOrderGenerator, + beanClass, + methodValidationConfiguration ); + } + + public void add(BeanConfiguration configuration) { + if ( configuration.getBeanClass().equals( beanClass ) ) { + if ( configuration.getDefaultGroupSequence() != null + && ( sequenceSource == null || configuration.getSource() + .getPriority() >= sequenceSource.getPriority() ) ) { + + sequenceSource = configuration.getSource(); + defaultGroupSequence = configuration.getDefaultGroupSequence(); + } + + if ( configuration.getDefaultGroupSequenceProvider() != null + && ( providerSource == null || configuration.getSource() + .getPriority() >= providerSource.getPriority() ) ) { + + providerSource = configuration.getSource(); + defaultGroupSequenceProvider = configuration.getDefaultGroupSequenceProvider(); + } + } + + for ( ConstrainedElement constrainedElement : configuration.getConstrainedElements() ) { + addMetaDataToBuilder( constrainedElement, builders ); + } + } + + private void addMetaDataToBuilder(ConstrainedElement constrainableElement, Set builders) { + for ( BuilderDelegate builder : builders ) { + boolean foundBuilder = builder.add( constrainableElement ); + + if ( foundBuilder ) { + return; + } + } + + builders.add( + new BuilderDelegate( + beanClass, + constrainableElement, + constraintHelper, + executableHelper, + typeResolutionHelper, + valueExtractorManager, + parameterNameProvider, + methodValidationConfiguration + ) + ); + } + + public BeanMetaDataImpl build() { + Set aggregatedElements = newHashSet(); + + for ( BuilderDelegate builder : builders ) { + aggregatedElements.addAll( builder.build() ); + } + + return new BeanMetaDataImpl<>( + beanClass, + defaultGroupSequence, + defaultGroupSequenceProvider, + aggregatedElements, + validationOrderGenerator + ); + } + + private static class BuilderDelegate { + private final Class beanClass; + private final ConstrainedElement constrainedElement; + private final ConstraintHelper constraintHelper; + private final ExecutableHelper executableHelper; + private final TypeResolutionHelper typeResolutionHelper; + private final ValueExtractorManager valueExtractorManager; + private final ExecutableParameterNameProvider parameterNameProvider; + private MetaDataBuilder metaDataBuilder; + private ExecutableMetaData.Builder methodBuilder; + private final MethodValidationConfiguration methodValidationConfiguration; + private final int hashCode; + + public BuilderDelegate( + Class beanClass, + ConstrainedElement constrainedElement, + ConstraintHelper constraintHelper, + ExecutableHelper executableHelper, + TypeResolutionHelper typeResolutionHelper, + ValueExtractorManager valueExtractorManager, + ExecutableParameterNameProvider parameterNameProvider, + MethodValidationConfiguration methodValidationConfiguration + ) { + this.beanClass = beanClass; + this.constrainedElement = constrainedElement; + this.constraintHelper = constraintHelper; + this.executableHelper = executableHelper; + this.typeResolutionHelper = typeResolutionHelper; + this.valueExtractorManager = valueExtractorManager; + this.parameterNameProvider = parameterNameProvider; + this.methodValidationConfiguration = methodValidationConfiguration; + + switch ( constrainedElement.getKind() ) { + case FIELD: + ConstrainedField constrainedField = (ConstrainedField) constrainedElement; + metaDataBuilder = new PropertyMetaData.Builder( + beanClass, + constrainedField, + constraintHelper, + typeResolutionHelper, + valueExtractorManager + ); + break; + case CONSTRUCTOR: + case METHOD: + case GETTER: + ConstrainedExecutable constrainedExecutable = (ConstrainedExecutable) constrainedElement; + Callable callable = constrainedExecutable.getCallable(); + + // HV-890 Not adding meta-data for private super-type methods to the method meta-data of this bean; + // It is not needed and it may conflict with sub-type methods of the same signature + if ( !callable.isPrivate() || beanClass == callable.getDeclaringClass() ) { + methodBuilder = new ExecutableMetaData.Builder( + beanClass, + constrainedExecutable, + constraintHelper, + executableHelper, + typeResolutionHelper, + valueExtractorManager, + parameterNameProvider, + methodValidationConfiguration + ); + } + + if ( constrainedElement.getKind() == ConstrainedElement.ConstrainedElementKind.GETTER ) { + metaDataBuilder = new PropertyMetaData.Builder( + beanClass, + constrainedExecutable, + constraintHelper, + typeResolutionHelper, + valueExtractorManager + ); + } + break; + case TYPE: + ConstrainedType constrainedType = (ConstrainedType) constrainedElement; + metaDataBuilder = new ClassMetaData.Builder( + beanClass, + constrainedType, + constraintHelper, + typeResolutionHelper, + valueExtractorManager + ); + break; + default: + throw new IllegalStateException( + StringHelper.format( "Constrained element kind '%1$s' not supported here.", constrainedElement.getKind() ) ); + } + + this.hashCode = buildHashCode(); + } + + public boolean add(ConstrainedElement constrainedElement) { + boolean added = false; + + if ( methodBuilder != null && methodBuilder.accepts( constrainedElement ) ) { + methodBuilder.add( constrainedElement ); + added = true; + } + + if ( metaDataBuilder != null && metaDataBuilder.accepts( constrainedElement ) ) { + metaDataBuilder.add( constrainedElement ); + + if ( !added && constrainedElement.getKind().isMethod() && methodBuilder == null ) { + ConstrainedExecutable constrainedMethod = (ConstrainedExecutable) constrainedElement; + methodBuilder = new ExecutableMetaData.Builder( + beanClass, + constrainedMethod, + constraintHelper, + executableHelper, + typeResolutionHelper, + valueExtractorManager, + parameterNameProvider, + methodValidationConfiguration + ); + } + + added = true; + } + + return added; + } + + public Set build() { + Set metaDataSet = newHashSet(); + + if ( metaDataBuilder != null ) { + metaDataSet.add( metaDataBuilder.build() ); + } + + if ( methodBuilder != null ) { + metaDataSet.add( methodBuilder.build() ); + } + + return metaDataSet; + } + + @Override + public int hashCode() { + return hashCode; + } + + private int buildHashCode() { + final int prime = 31; + int result = 1; + result = prime * result + beanClass.hashCode(); + result = prime * result + constrainedElement.hashCode(); + return result; + } + + @Override + public boolean equals(Object obj) { + if ( this == obj ) { + return true; + } + if ( !super.equals( obj ) ) { + return false; + } + if ( getClass() != obj.getClass() ) { + return false; + } + BuilderDelegate other = (BuilderDelegate) obj; + if ( !beanClass.equals( other.beanClass ) ) { + return false; + } + if ( !constrainedElement.equals( other.constrainedElement ) ) { + return false; + } + return true; + } + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java index 6a11912d31..4fba8e79b7 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java @@ -27,31 +27,17 @@ import javax.validation.metadata.ConstructorDescriptor; import javax.validation.metadata.PropertyDescriptor; -import org.hibernate.validator.internal.engine.MethodValidationConfiguration; import org.hibernate.validator.internal.engine.groups.Sequence; import org.hibernate.validator.internal.engine.groups.ValidationOrder; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; -import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.descriptor.BeanDescriptorImpl; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; import org.hibernate.validator.internal.metadata.descriptor.ExecutableDescriptorImpl; import org.hibernate.validator.internal.metadata.facets.Cascadable; import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; -import org.hibernate.validator.internal.metadata.raw.BeanConfiguration; -import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; -import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; -import org.hibernate.validator.internal.metadata.raw.ConstrainedElement.ConstrainedElementKind; -import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable; -import org.hibernate.validator.internal.metadata.raw.ConstrainedField; -import org.hibernate.validator.internal.metadata.raw.ConstrainedType; -import org.hibernate.validator.internal.properties.Callable; import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.internal.util.ExecutableHelper; -import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; -import org.hibernate.validator.internal.util.StringHelper; -import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.classhierarchy.ClassHierarchyHelper; import org.hibernate.validator.internal.util.classhierarchy.Filters; import org.hibernate.validator.internal.util.logging.Log; @@ -68,6 +54,7 @@ * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI * @author Chris Beckey <cbeckey@paypal.com> * @author Guillaume Smet + * @author Marko Bekhta */ public final class BeanMetaDataImpl implements BeanMetaData { @@ -267,7 +254,7 @@ public BeanDescriptor getBeanDescriptor() { BeanDescriptor beanDescriptor = this.beanDescriptor; if ( beanDescriptor == null ) { - synchronized ( this ) { + synchronized (this) { beanDescriptor = this.beanDescriptor; if ( beanDescriptor == null ) { @@ -349,9 +336,9 @@ public Iterator getDefaultValidationSequence(T beanState) { if ( hasDefaultGroupSequenceProvider() ) { List> providerDefaultGroupSequence = defaultGroupSequenceProvider.getValidationGroups( beanState ); return validationOrderGenerator.getDefaultValidationOrder( - beanClass, - getValidDefaultGroupSequence( beanClass, providerDefaultGroupSequence ) - ) + beanClass, + getValidDefaultGroupSequence( beanClass, providerDefaultGroupSequence ) + ) .getSequenceIterator(); } else { @@ -435,9 +422,9 @@ private static Map getConstrainedMethodsAsDesc if ( executableMetaData.getKind() == ElementKind.METHOD && executableMetaData.isConstrained() ) { ExecutableDescriptorImpl descriptor = executableMetaData.asDescriptor( - defaultGroupSequenceIsRedefined, - resolvedDefaultGroupSequence - ); + defaultGroupSequenceIsRedefined, + resolvedDefaultGroupSequence + ); for ( String signature : executableMetaData.getSignatures() ) { constrainedMethodDescriptors.put( signature, descriptor ); @@ -571,303 +558,6 @@ public String toString() { + ", defaultGroupSequence=" + getDefaultGroupSequence( null ) + '}'; } - public static class PropertyHolderBeanMetaDataBuilder { - - } - - public static class BeanMetaDataBuilder { - - private final ConstraintHelper constraintHelper; - private final ValidationOrderGenerator validationOrderGenerator; - private final Class beanClass; - private final Set builders = newHashSet(); - private final ExecutableHelper executableHelper; - private final TypeResolutionHelper typeResolutionHelper; - private final ValueExtractorManager valueExtractorManager; - private final ExecutableParameterNameProvider parameterNameProvider; - private final MethodValidationConfiguration methodValidationConfiguration; - - private ConfigurationSource sequenceSource; - private ConfigurationSource providerSource; - private List> defaultGroupSequence; - private DefaultGroupSequenceProvider defaultGroupSequenceProvider; - - - private BeanMetaDataBuilder( - ConstraintHelper constraintHelper, - ExecutableHelper executableHelper, - TypeResolutionHelper typeResolutionHelper, - ValueExtractorManager valueExtractorManager, - ExecutableParameterNameProvider parameterNameProvider, - ValidationOrderGenerator validationOrderGenerator, - Class beanClass, - MethodValidationConfiguration methodValidationConfiguration) { - this.beanClass = beanClass; - this.constraintHelper = constraintHelper; - this.validationOrderGenerator = validationOrderGenerator; - this.executableHelper = executableHelper; - this.typeResolutionHelper = typeResolutionHelper; - this.valueExtractorManager = valueExtractorManager; - this.parameterNameProvider = parameterNameProvider; - this.methodValidationConfiguration = methodValidationConfiguration; - } - - public static BeanMetaDataBuilder getInstance( - ConstraintHelper constraintHelper, - ExecutableHelper executableHelper, - TypeResolutionHelper typeResolutionHelper, - ValueExtractorManager valueExtractorManager, - ExecutableParameterNameProvider parameterNameProvider, - ValidationOrderGenerator validationOrderGenerator, - Class beanClass, - MethodValidationConfiguration methodValidationConfiguration) { - return new BeanMetaDataBuilder<>( - constraintHelper, - executableHelper, - typeResolutionHelper, - valueExtractorManager, - parameterNameProvider, - validationOrderGenerator, - beanClass, - methodValidationConfiguration ); - } - - public void add(BeanConfiguration configuration) { - if ( configuration.getBeanClass().equals( beanClass ) ) { - if ( configuration.getDefaultGroupSequence() != null - && ( sequenceSource == null || configuration.getSource() - .getPriority() >= sequenceSource.getPriority() ) ) { - - sequenceSource = configuration.getSource(); - defaultGroupSequence = configuration.getDefaultGroupSequence(); - } - - if ( configuration.getDefaultGroupSequenceProvider() != null - && ( providerSource == null || configuration.getSource() - .getPriority() >= providerSource.getPriority() ) ) { - - providerSource = configuration.getSource(); - defaultGroupSequenceProvider = configuration.getDefaultGroupSequenceProvider(); - } - } - - for ( ConstrainedElement constrainedElement : configuration.getConstrainedElements() ) { - addMetaDataToBuilder( constrainedElement, builders ); - } - } - - private void addMetaDataToBuilder(ConstrainedElement constrainableElement, Set builders) { - for ( BuilderDelegate builder : builders ) { - boolean foundBuilder = builder.add( constrainableElement ); - - if ( foundBuilder ) { - return; - } - } - - builders.add( - new BuilderDelegate( - beanClass, - constrainableElement, - constraintHelper, - executableHelper, - typeResolutionHelper, - valueExtractorManager, - parameterNameProvider, - methodValidationConfiguration - ) - ); - } - - public BeanMetaDataImpl build() { - Set aggregatedElements = newHashSet(); - - for ( BuilderDelegate builder : builders ) { - aggregatedElements.addAll( builder.build() ); - } - - return new BeanMetaDataImpl<>( - beanClass, - defaultGroupSequence, - defaultGroupSequenceProvider, - aggregatedElements, - validationOrderGenerator - ); - } - } - - private static class BuilderDelegate { - private final Class beanClass; - private final ConstrainedElement constrainedElement; - private final ConstraintHelper constraintHelper; - private final ExecutableHelper executableHelper; - private final TypeResolutionHelper typeResolutionHelper; - private final ValueExtractorManager valueExtractorManager; - private final ExecutableParameterNameProvider parameterNameProvider; - private MetaDataBuilder metaDataBuilder; - private ExecutableMetaData.Builder methodBuilder; - private final MethodValidationConfiguration methodValidationConfiguration; - private final int hashCode; - - public BuilderDelegate( - Class beanClass, - ConstrainedElement constrainedElement, - ConstraintHelper constraintHelper, - ExecutableHelper executableHelper, - TypeResolutionHelper typeResolutionHelper, - ValueExtractorManager valueExtractorManager, - ExecutableParameterNameProvider parameterNameProvider, - MethodValidationConfiguration methodValidationConfiguration - ) { - this.beanClass = beanClass; - this.constrainedElement = constrainedElement; - this.constraintHelper = constraintHelper; - this.executableHelper = executableHelper; - this.typeResolutionHelper = typeResolutionHelper; - this.valueExtractorManager = valueExtractorManager; - this.parameterNameProvider = parameterNameProvider; - this.methodValidationConfiguration = methodValidationConfiguration; - - switch ( constrainedElement.getKind() ) { - case FIELD: - ConstrainedField constrainedField = (ConstrainedField) constrainedElement; - metaDataBuilder = new PropertyMetaData.Builder( - beanClass, - constrainedField, - constraintHelper, - typeResolutionHelper, - valueExtractorManager - ); - break; - case CONSTRUCTOR: - case METHOD: - case GETTER: - ConstrainedExecutable constrainedExecutable = (ConstrainedExecutable) constrainedElement; - Callable callable = constrainedExecutable.getCallable(); - - // HV-890 Not adding meta-data for private super-type methods to the method meta-data of this bean; - // It is not needed and it may conflict with sub-type methods of the same signature - if ( !callable.isPrivate() || beanClass == callable.getDeclaringClass() ) { - methodBuilder = new ExecutableMetaData.Builder( - beanClass, - constrainedExecutable, - constraintHelper, - executableHelper, - typeResolutionHelper, - valueExtractorManager, - parameterNameProvider, - methodValidationConfiguration - ); - } - - if ( constrainedElement.getKind() == ConstrainedElementKind.GETTER ) { - metaDataBuilder = new PropertyMetaData.Builder( - beanClass, - constrainedExecutable, - constraintHelper, - typeResolutionHelper, - valueExtractorManager - ); - } - break; - case TYPE: - ConstrainedType constrainedType = (ConstrainedType) constrainedElement; - metaDataBuilder = new ClassMetaData.Builder( - beanClass, - constrainedType, - constraintHelper, - typeResolutionHelper, - valueExtractorManager - ); - break; - default: - throw new IllegalStateException( - StringHelper.format( "Constrained element kind '%1$s' not supported here.", constrainedElement.getKind() ) ); - } - - this.hashCode = buildHashCode(); - } - - public boolean add(ConstrainedElement constrainedElement) { - boolean added = false; - - if ( methodBuilder != null && methodBuilder.accepts( constrainedElement ) ) { - methodBuilder.add( constrainedElement ); - added = true; - } - - if ( metaDataBuilder != null && metaDataBuilder.accepts( constrainedElement ) ) { - metaDataBuilder.add( constrainedElement ); - - if ( !added && constrainedElement.getKind().isMethod() && methodBuilder == null ) { - ConstrainedExecutable constrainedMethod = (ConstrainedExecutable) constrainedElement; - methodBuilder = new ExecutableMetaData.Builder( - beanClass, - constrainedMethod, - constraintHelper, - executableHelper, - typeResolutionHelper, - valueExtractorManager, - parameterNameProvider, - methodValidationConfiguration - ); - } - - added = true; - } - - return added; - } - - public Set build() { - Set metaDataSet = newHashSet(); - - if ( metaDataBuilder != null ) { - metaDataSet.add( metaDataBuilder.build() ); - } - - if ( methodBuilder != null ) { - metaDataSet.add( methodBuilder.build() ); - } - - return metaDataSet; - } - - @Override - public int hashCode() { - return hashCode; - } - - private int buildHashCode() { - final int prime = 31; - int result = 1; - result = prime * result + beanClass.hashCode(); - result = prime * result + constrainedElement.hashCode(); - return result; - } - - @Override - public boolean equals(Object obj) { - if ( this == obj ) { - return true; - } - if ( !super.equals( obj ) ) { - return false; - } - if ( getClass() != obj.getClass() ) { - return false; - } - BuilderDelegate other = (BuilderDelegate) obj; - if ( !beanClass.equals( other.beanClass ) ) { - return false; - } - if ( !constrainedElement.equals( other.constrainedElement ) ) { - return false; - } - return true; - } - } - /** * Tuple for returning default group sequence, provider and validation order at once. */ diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/PropertyHolderBeanMetaDataBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/PropertyHolderBeanMetaDataBuilder.java new file mode 100644 index 0000000000..6abe88a323 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/PropertyHolderBeanMetaDataBuilder.java @@ -0,0 +1,218 @@ +/* + * 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.aggregated; + +import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; + +import java.util.List; +import java.util.Set; + +import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; +import org.hibernate.validator.internal.metadata.raw.ConstrainedField; +import org.hibernate.validator.internal.metadata.raw.propertyholder.PropertyHolderConfiguration; +import org.hibernate.validator.internal.metadata.raw.propertyholder.PropertyHolderConfigurationSource; +import org.hibernate.validator.internal.util.StringHelper; +import org.hibernate.validator.internal.util.TypeResolutionHelper; + +/** + * @author Hardy Ferentschik + * @author Gunnar Morling + * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI + * @author Chris Beckey <cbeckey@paypal.com> + * @author Guillaume Smet + * @author Marko Bekhta + */ +public class PropertyHolderBeanMetaDataBuilder { + + private final ConstraintHelper constraintHelper; + private final ValidationOrderGenerator validationOrderGenerator; + private final Class propertyHolderClass; + private final Set builders = newHashSet(); + private final TypeResolutionHelper typeResolutionHelper; + private final ValueExtractorManager valueExtractorManager; + + private PropertyHolderConfigurationSource sequenceSource; + private PropertyHolderConfigurationSource providerSource; + private List> defaultGroupSequence; + + + private PropertyHolderBeanMetaDataBuilder( + ConstraintHelper constraintHelper, + TypeResolutionHelper typeResolutionHelper, + ValueExtractorManager valueExtractorManager, + ValidationOrderGenerator validationOrderGenerator, + Class propertyHolderClass) { + this.propertyHolderClass = propertyHolderClass; + this.constraintHelper = constraintHelper; + this.validationOrderGenerator = validationOrderGenerator; + this.typeResolutionHelper = typeResolutionHelper; + this.valueExtractorManager = valueExtractorManager; + } + + public static PropertyHolderBeanMetaDataBuilder getInstance( + ConstraintHelper constraintHelper, + TypeResolutionHelper typeResolutionHelper, + ValueExtractorManager valueExtractorManager, + ValidationOrderGenerator validationOrderGenerator, + Class propertyHolderClass) { + return new PropertyHolderBeanMetaDataBuilder<>( + constraintHelper, + typeResolutionHelper, + valueExtractorManager, + validationOrderGenerator, + propertyHolderClass ); + } + + public void add(PropertyHolderConfiguration configuration) { + if ( configuration.getDefaultGroupSequence() != null + && ( sequenceSource == null || configuration.getSource() + .getPriority() >= sequenceSource.getPriority() ) ) { + + sequenceSource = configuration.getSource(); + defaultGroupSequence = configuration.getDefaultGroupSequence(); + } + + for ( ConstrainedElement constrainedElement : configuration.getConstrainedElements() ) { + addMetaDataToBuilder( constrainedElement, builders ); + } + } + + private void addMetaDataToBuilder(ConstrainedElement constrainableElement, Set builders) { + for ( BuilderDelegate builder : builders ) { + boolean foundBuilder = builder.add( constrainableElement ); + + if ( foundBuilder ) { + return; + } + } + + builders.add( + new BuilderDelegate( + propertyHolderClass, + constrainableElement, + constraintHelper, + typeResolutionHelper, + valueExtractorManager + ) + ); + } + + public BeanMetaDataImpl build() { + Set aggregatedElements = newHashSet(); + + for ( BuilderDelegate builder : builders ) { + aggregatedElements.addAll( builder.build() ); + } + + return new BeanMetaDataImpl<>( + propertyHolderClass, + defaultGroupSequence, + null, + aggregatedElements, + validationOrderGenerator + ); + } + + private static class BuilderDelegate { + private final Class propertyHolderClass; + private final ConstrainedElement constrainedElement; + private final ConstraintHelper constraintHelper; + private final TypeResolutionHelper typeResolutionHelper; + private final ValueExtractorManager valueExtractorManager; + private MetaDataBuilder metaDataBuilder; + private final int hashCode; + + public BuilderDelegate( + Class propertyHolderClass, + ConstrainedElement constrainedElement, + ConstraintHelper constraintHelper, + TypeResolutionHelper typeResolutionHelper, + ValueExtractorManager valueExtractorManager + ) { + this.propertyHolderClass = propertyHolderClass; + this.constrainedElement = constrainedElement; + this.constraintHelper = constraintHelper; + this.typeResolutionHelper = typeResolutionHelper; + this.valueExtractorManager = valueExtractorManager; + + switch ( constrainedElement.getKind() ) { + case FIELD: + ConstrainedField constrainedField = (ConstrainedField) constrainedElement; + metaDataBuilder = new PropertyMetaData.Builder( + propertyHolderClass, + constrainedField, + constraintHelper, + typeResolutionHelper, + valueExtractorManager + ); + break; + default: + throw new IllegalStateException( + StringHelper.format( "Constrained element kind '%1$s' not supported here.", constrainedElement.getKind() ) ); + } + + this.hashCode = buildHashCode(); + } + + public boolean add(ConstrainedElement constrainedElement) { + if ( metaDataBuilder != null && metaDataBuilder.accepts( constrainedElement ) ) { + metaDataBuilder.add( constrainedElement ); + + return true; + } + + return false; + } + + public Set build() { + Set metaDataSet = newHashSet(); + + if ( metaDataBuilder != null ) { + metaDataSet.add( metaDataBuilder.build() ); + } + + return metaDataSet; + } + + @Override + public int hashCode() { + return hashCode; + } + + private int buildHashCode() { + final int prime = 31; + int result = 1; + result = prime * result + propertyHolderClass.hashCode(); + result = prime * result + constrainedElement.hashCode(); + return result; + } + + @Override + public boolean equals(Object obj) { + if ( this == obj ) { + return true; + } + if ( !super.equals( obj ) ) { + return false; + } + if ( getClass() != obj.getClass() ) { + return false; + } + BuilderDelegate other = (BuilderDelegate) obj; + if ( !propertyHolderClass.equals( other.propertyHolderClass ) ) { + return false; + } + if ( !constrainedElement.equals( other.constrainedElement ) ) { + return false; + } + return true; + } + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/BeanMetaDataProvider.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/BeanMetaDataProvider.java index 0ecba60abe..cb24506efe 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/BeanMetaDataProvider.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/BeanMetaDataProvider.java @@ -19,7 +19,7 @@ import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl; -import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl.BeanMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/PropertyHolderBeanMetaDataProvider.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/PropertyHolderBeanMetaDataProvider.java index 5475d2e551..dc80656838 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/PropertyHolderBeanMetaDataProvider.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/PropertyHolderBeanMetaDataProvider.java @@ -9,10 +9,17 @@ import java.util.List; import java.util.Optional; +import javax.validation.valueextraction.ValueExtractor; + +import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl; +import org.hibernate.validator.internal.metadata.aggregated.PropertyHolderBeanMetaDataBuilder; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.provider.PropertyHolderMetaDataProvider; import org.hibernate.validator.internal.metadata.raw.propertyholder.PropertyHolderConfiguration; +import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.stereotypes.Immutable; /** @@ -23,47 +30,72 @@ public class PropertyHolderBeanMetaDataProvider { @Immutable private final List propertyHolderMetaDataProviderList; + /** + * Helper for builtin constraints and their validator implementations + */ + private final ConstraintHelper constraintHelper; + + /** + * Used for resolving generic type information. + */ + private final TypeResolutionHelper typeResolutionHelper; + + /** + * The {@link ValueExtractor} manager. + */ + private final ValueExtractorManager valueExtractorManager; + + private final ValidationOrderGenerator validationOrderGenerator; + private final MetaDataCache metaDataCache; - public PropertyHolderBeanMetaDataProvider(List propertyHolderMetaDataProviderList) { + public PropertyHolderBeanMetaDataProvider(List propertyHolderMetaDataProviderList, ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, ValidationOrderGenerator validationOrderGenerator) { this.propertyHolderMetaDataProviderList = propertyHolderMetaDataProviderList; + this.constraintHelper = constraintHelper; + this.typeResolutionHelper = typeResolutionHelper; + this.valueExtractorManager = valueExtractorManager; + this.validationOrderGenerator = validationOrderGenerator; this.metaDataCache = new MetaDataCache<>(); } @SuppressWarnings("unchecked") - public BeanMetaData getBeanMetaData(Class beanClass, String mapping) { + public BeanMetaData getBeanMetaData(Class propertyHolderClass, String mapping) { return (BeanMetaData) metaDataCache.computeIfAbsent( - new PropertyHolderMetadataKey( beanClass, mapping ), + new PropertyHolderMetadataKey( propertyHolderClass, mapping ), key -> createBeanMetaData( key ) ); } private BeanMetaDataImpl createBeanMetaData(PropertyHolderMetadataKey metadataKey) { + PropertyHolderBeanMetaDataBuilder builder = PropertyHolderBeanMetaDataBuilder.getInstance( + constraintHelper, typeResolutionHelper, valueExtractorManager, validationOrderGenerator, metadataKey.propertyHolderClass + ); + for ( PropertyHolderMetaDataProvider metaDataProvider : propertyHolderMetaDataProviderList ) { Optional beanConfiguration = metaDataProvider.getBeanConfiguration( metadataKey.mapping ); if ( beanConfiguration.isPresent() ) { - + builder.add( beanConfiguration.get() ); } } - return null; + return builder.build(); } private static class PropertyHolderMetadataKey { private String mapping; - private Class beanClass; + private Class propertyHolderClass; private int hashCode; - public PropertyHolderMetadataKey(Class beanClass, String mapping) { + public PropertyHolderMetadataKey(Class propertyHolderClass, String mapping) { this.mapping = mapping; - this.beanClass = beanClass; + this.propertyHolderClass = propertyHolderClass; this.hashCode = buildHashCode(); } private int buildHashCode() { int result = mapping.hashCode(); - result = 31 * result + beanClass.hashCode(); + result = 31 * result + propertyHolderClass.hashCode(); return result; } @@ -81,7 +113,7 @@ public boolean equals(Object o) { if ( !mapping.equals( that.mapping ) ) { return false; } - if ( !beanClass.equals( that.beanClass ) ) { + if ( !propertyHolderClass.equals( that.propertyHolderClass ) ) { return false; } From 00a7af708095297d9583c4c13a38d9872712692f Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Thu, 12 Jul 2018 21:09:07 +0200 Subject: [PATCH 07/14] [property-holder-preparation] Make ConstraintMetaDataManager return metadata for property holders --- .../internal/engine/ValidatorFactoryImpl.java | 15 ++++++++++---- .../manager/ConstraintMetaDataManager.java | 20 +++++++++++++++++-- .../PropertyHolderBeanMetaDataProvider.java | 8 ++++++++ .../internal/engine/path/PathImplTest.java | 8 ++++---- .../ConstraintMetaDataManagerTest.java | 9 +++++---- .../aggregated/ExecutableMetaDataTest.java | 9 +++++---- .../aggregated/ParameterMetaDataTest.java | 14 +++++++------ .../aggregated/PropertyMetaDataTest.java | 9 +++++---- 8 files changed, 64 insertions(+), 28 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java index fa909c020c..e8f9fd9278 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java @@ -42,10 +42,11 @@ import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; import org.hibernate.validator.internal.engine.scripting.DefaultScriptEvaluatorFactory; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; import org.hibernate.validator.internal.metadata.provider.ProgrammaticMetaDataProvider; +import org.hibernate.validator.internal.metadata.provider.PropertyHolderMetaDataProvider; import org.hibernate.validator.internal.metadata.provider.XmlMetaDataProvider; import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; @@ -362,8 +363,10 @@ Validator createValidator(ConstraintValidatorFactory constraintValidatorFactory, valueExtractorManager, javaBeanHelper, validationOrderGenerator, - buildDataProviders(), - methodValidationConfiguration + methodValidationConfiguration, + buildMetaDataProviders(), + buildPropertyHolderMetaDataProvider() + ) ); @@ -377,7 +380,7 @@ Validator createValidator(ConstraintValidatorFactory constraintValidatorFactory, ); } - private List buildDataProviders() { + private List buildMetaDataProviders() { List metaDataProviders = newArrayList(); if ( xmlMetaDataProvider != null ) { metaDataProviders.add( xmlMetaDataProvider ); @@ -396,6 +399,10 @@ private List buildDataProviders() { return metaDataProviders; } + private List buildPropertyHolderMetaDataProvider() { + return Collections.emptyList(); + } + private static boolean checkPropertiesForBoolean(Map properties, String propertyKey, boolean programmaticValue) { boolean value = programmaticValue; String propertyStringValue = properties.get( propertyKey ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/ConstraintMetaDataManager.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/ConstraintMetaDataManager.java index b3dfbd3f73..34d692acfd 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/ConstraintMetaDataManager.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/ConstraintMetaDataManager.java @@ -14,6 +14,7 @@ import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; +import org.hibernate.validator.internal.metadata.provider.PropertyHolderMetaDataProvider; import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; @@ -43,6 +44,8 @@ public class ConstraintMetaDataManager { private final BeanMetaDataProvider beanMetaDataProvider; + private final PropertyHolderBeanMetaDataProvider propertyHolderBeanMetaDataProvider; + public ConstraintMetaDataManager(ConstraintHelper constraintHelper, ExecutableHelper executableHelper, TypeResolutionHelper typeResolutionHelper, @@ -50,8 +53,9 @@ public ConstraintMetaDataManager(ConstraintHelper constraintHelper, ValueExtractorManager valueExtractorManager, JavaBeanHelper javaBeanHelper, ValidationOrderGenerator validationOrderGenerator, + MethodValidationConfiguration methodValidationConfiguration, List optionalMetaDataProviders, - MethodValidationConfiguration methodValidationConfiguration) { + List propertyHolderMetaDataProviders) { this.beanMetaDataProvider = new BeanMetaDataProvider( constraintHelper, executableHelper, @@ -63,17 +67,29 @@ public ConstraintMetaDataManager(ConstraintHelper constraintHelper, optionalMetaDataProviders, methodValidationConfiguration ); + this.propertyHolderBeanMetaDataProvider = new PropertyHolderBeanMetaDataProvider( + propertyHolderMetaDataProviders, + constraintHelper, + typeResolutionHelper, + valueExtractorManager, + validationOrderGenerator + ); } public BeanMetaData getBeanMetaData(Class beanClass) { return beanMetaDataProvider.getBeanMetaData( beanClass ); } + public BeanMetaData getPropertyHolderBeanMetaData(Class propertyHolderClass, String mapping) { + return propertyHolderBeanMetaDataProvider.getBeanMetaData( propertyHolderClass, mapping ); + } + public void clear() { beanMetaDataProvider.clear(); + propertyHolderBeanMetaDataProvider.clear(); } public int numberOfCachedBeanMetaDataInstances() { - return beanMetaDataProvider.numberOfCachedBeanMetaDataInstances(); + return beanMetaDataProvider.numberOfCachedBeanMetaDataInstances() + propertyHolderBeanMetaDataProvider.numberOfCachedBeanMetaDataInstances(); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/PropertyHolderBeanMetaDataProvider.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/PropertyHolderBeanMetaDataProvider.java index dc80656838..10c22d8b4e 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/PropertyHolderBeanMetaDataProvider.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/PropertyHolderBeanMetaDataProvider.java @@ -81,6 +81,14 @@ private BeanMetaDataImpl createBeanMetaData(PropertyHolderMetadataKey met return builder.build(); } + public void clear() { + metaDataCache.clear(); + } + + public int numberOfCachedBeanMetaDataInstances() { + return metaDataCache.size(); + } + private static class PropertyHolderMetadataKey { private String mapping; private Class propertyHolderClass; diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/path/PathImplTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/path/PathImplTest.java index d90136eace..8e81964003 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/path/PathImplTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/path/PathImplTest.java @@ -32,10 +32,9 @@ import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; import org.hibernate.validator.internal.engine.path.PathImpl; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; -import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; +import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; import org.hibernate.validator.internal.util.ExecutableHelper; @@ -201,8 +200,9 @@ public void testCreationOfExecutablePath() throws Exception { new ValueExtractorManager( Collections.emptySet() ), new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy() ), new ValidationOrderGenerator(), - Collections.emptyList(), - new MethodValidationConfiguration.Builder().build() + new MethodValidationConfiguration.Builder().build(), + Collections.emptyList(), + Collections.emptyList() ); ExecutableMetaData executableMetaData = constraintMetaDataManager.getBeanMetaData( Container.class ) diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/ConstraintMetaDataManagerTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/ConstraintMetaDataManagerTest.java index a2e63a155c..5371ddcf8a 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/ConstraintMetaDataManagerTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/ConstraintMetaDataManagerTest.java @@ -24,11 +24,10 @@ import org.hibernate.validator.internal.engine.MethodValidationConfiguration; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; -import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; +import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; import org.hibernate.validator.internal.util.ExecutableHelper; @@ -36,6 +35,7 @@ import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; + import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -60,8 +60,9 @@ public void setUpBeanMetaDataManager() { new ValueExtractorManager( Collections.emptySet() ), new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy() ), new ValidationOrderGenerator(), - Collections.emptyList(), - new MethodValidationConfiguration.Builder().build() + new MethodValidationConfiguration.Builder().build(), + Collections.emptyList(), + Collections.emptyList() ); } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java index 232b9f2fd4..c0c1608a6e 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java @@ -26,12 +26,11 @@ import org.hibernate.validator.internal.engine.MethodValidationConfiguration; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; -import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; +import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; import org.hibernate.validator.internal.util.ExecutableHelper; @@ -42,6 +41,7 @@ import org.hibernate.validator.test.internal.metadata.CustomerRepository.ValidationGroup; import org.hibernate.validator.test.internal.metadata.CustomerRepositoryExt; import org.hibernate.validator.testutil.TestForIssue; + import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -65,8 +65,9 @@ public void setupBeanMetaData() { new ValueExtractorManager( Collections.emptySet() ), new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy() ), new ValidationOrderGenerator(), - Collections.emptyList(), - new MethodValidationConfiguration.Builder().build() + new MethodValidationConfiguration.Builder().build(), + Collections.emptyList(), + Collections.emptyList() ); beanMetaData = constraintMetaDataManager.getBeanMetaData( CustomerRepositoryExt.class ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ParameterMetaDataTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ParameterMetaDataTest.java index f2621432dd..3ac47ca8f9 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ParameterMetaDataTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ParameterMetaDataTest.java @@ -27,12 +27,11 @@ import org.hibernate.validator.internal.engine.MethodValidationConfiguration; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData; import org.hibernate.validator.internal.metadata.aggregated.ParameterMetaData; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; -import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; +import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; import org.hibernate.validator.internal.util.ExecutableHelper; @@ -42,6 +41,7 @@ import org.hibernate.validator.test.internal.metadata.CustomerRepository; import org.hibernate.validator.test.internal.metadata.CustomerRepository.ValidationGroup; import org.hibernate.validator.testutil.TestForIssue; + import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -65,8 +65,9 @@ public void setupBeanMetaData() { new ValueExtractorManager( Collections.emptySet() ), new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy() ), new ValidationOrderGenerator(), - Collections.emptyList(), - new MethodValidationConfiguration.Builder().build() + new MethodValidationConfiguration.Builder().build(), + Collections.emptyList(), + Collections.emptyList() ); beanMetaData = constraintMetaDataManager.getBeanMetaData( CustomerRepository.class ); @@ -136,8 +137,9 @@ public void parameterNameInInheritanceHierarchy() throws Exception { new ValueExtractorManager( Collections.emptySet() ), new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy() ), new ValidationOrderGenerator(), - Collections.emptyList(), - new MethodValidationConfiguration.Builder().build() + new MethodValidationConfiguration.Builder().build(), + Collections.emptyList(), + Collections.emptyList() ); BeanMetaData localBeanMetaData = constraintMetaDataManager.getBeanMetaData( ServiceImpl.class ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/PropertyMetaDataTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/PropertyMetaDataTest.java index 7fb7a2241d..67012f2acd 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/PropertyMetaDataTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/PropertyMetaDataTest.java @@ -20,15 +20,15 @@ import org.hibernate.validator.internal.engine.MethodValidationConfiguration; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.metadata.aggregated.PropertyMetaData; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; -import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; +import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.TypeResolutionHelper; + import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -49,8 +49,9 @@ public void setupBeanMetaDataManager() { new ValueExtractorManager( Collections.emptySet() ), new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy() ), new ValidationOrderGenerator(), - Collections.emptyList(), - new MethodValidationConfiguration.Builder().build() + new MethodValidationConfiguration.Builder().build(), + Collections.emptyList(), + Collections.emptyList() ); } From f75e407b6f9df8eba575552b0e4b1c9e3ee8724c Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Sun, 15 Jul 2018 15:19:52 +0200 Subject: [PATCH 08/14] [property-holder] Create implementations of cascading metadata for property holders --- .../internal/engine/ValidatorImpl.java | 4 +- .../ValueExtractorResolver.java | 4 +- .../AbstractConstraintMetaData.java | 1 + .../aggregated/CascadingMetaData.java | 2 + .../aggregated/CascadingMetaDataBuilder.java | 11 +++ .../ContainerCascadingMetaData.java | 15 ++-- ...tainerPropertyHolderCascadingMetaData.java | 76 +++++++++++++++++++ .../GroupConversionHelper.java | 2 +- .../NonContainerCascadingMetaData.java | 13 +++- ...tainerPropertyHolderCascadingMetaData.java | 53 +++++++++++++ ...PotentiallyContainerCascadingMetaData.java | 27 ++++--- ...tainerPropertyHolderCascadingMetaData.java | 59 ++++++++++++++ 12 files changed, 244 insertions(+), 23 deletions(-) rename engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/{ => cascading}/ContainerCascadingMetaData.java (91%) create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/ContainerPropertyHolderCascadingMetaData.java rename engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/{ => cascading}/GroupConversionHelper.java (97%) rename engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/{ => cascading}/NonContainerCascadingMetaData.java (84%) create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/NonContainerPropertyHolderCascadingMetaData.java rename engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/{ => cascading}/PotentiallyContainerCascadingMetaData.java (76%) create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/PotentiallyContainerPropertyHolderCascadingMetaData.java diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java index e4b197615a..a0eb4b0298 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java @@ -54,7 +54,7 @@ import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaData; -import org.hibernate.validator.internal.metadata.aggregated.ContainerCascadingMetaData; +import org.hibernate.validator.internal.metadata.aggregated.cascading.ContainerCascadingMetaData; import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData; import org.hibernate.validator.internal.metadata.aggregated.ParameterMetaData; import org.hibernate.validator.internal.metadata.aggregated.PropertyMetaData; @@ -603,7 +603,7 @@ private void validateCascadedAnnotatedObjectForCurrentGroup(Object value, BaseBe } private void validateCascadedContainerElementsForCurrentGroup(Object value, BaseBeanValidationContext validationContext, ValueContext valueContext, - List containerElementTypesCascadingMetaData) { + List containerElementTypesCascadingMetaData) { for ( ContainerCascadingMetaData cascadingMetaData : containerElementTypesCascadingMetaData ) { if ( !cascadingMetaData.isMarkedForCascadingOnAnnotatedObjectOrContainerElements() ) { continue; diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ValueExtractorResolver.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ValueExtractorResolver.java index afaa2e247a..f294c27280 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ValueExtractorResolver.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ValueExtractorResolver.java @@ -23,8 +23,8 @@ import javax.validation.valueextraction.ValueExtractor; import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; -import org.hibernate.validator.internal.metadata.aggregated.ContainerCascadingMetaData; -import org.hibernate.validator.internal.metadata.aggregated.PotentiallyContainerCascadingMetaData; +import org.hibernate.validator.internal.metadata.aggregated.cascading.ContainerCascadingMetaData; +import org.hibernate.validator.internal.metadata.aggregated.cascading.PotentiallyContainerCascadingMetaData; import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.ReflectionHelper; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/AbstractConstraintMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/AbstractConstraintMetaData.java index d0760f45ea..e78f42c0f5 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/AbstractConstraintMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/AbstractConstraintMetaData.java @@ -25,6 +25,7 @@ import javax.validation.metadata.ContainerElementTypeDescriptor; import javax.validation.metadata.GroupConversionDescriptor; +import org.hibernate.validator.internal.metadata.aggregated.cascading.ContainerCascadingMetaData; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; import org.hibernate.validator.internal.metadata.descriptor.ContainerElementTypeDescriptorImpl; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaData.java index 3b64e9d1c2..a23e0df354 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaData.java @@ -15,6 +15,8 @@ import org.hibernate.validator.internal.engine.valueextraction.AnnotatedObject; import org.hibernate.validator.internal.engine.valueextraction.ArrayElement; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.aggregated.cascading.ContainerCascadingMetaData; +import org.hibernate.validator.internal.metadata.aggregated.cascading.NonContainerCascadingMetaData; import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; /** diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaDataBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaDataBuilder.java index a44d16b6ab..304e088d41 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaDataBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaDataBuilder.java @@ -25,6 +25,9 @@ import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorHelper; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.aggregated.cascading.ContainerCascadingMetaData; +import org.hibernate.validator.internal.metadata.aggregated.cascading.NonContainerCascadingMetaData; +import org.hibernate.validator.internal.metadata.aggregated.cascading.PotentiallyContainerCascadingMetaData; import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.internal.util.ReflectionHelper; import org.hibernate.validator.internal.util.StringHelper; @@ -47,6 +50,8 @@ public class CascadingMetaDataBuilder { private static final CascadingMetaDataBuilder NON_CASCADING = new CascadingMetaDataBuilder( null, null, null, null, false, Collections.emptyMap(), Collections.emptyMap() ); + private final String mapping; + /** * The enclosing type that defines this type parameter. */ @@ -122,6 +127,8 @@ private CascadingMetaDataBuilder(Type enclosingType, TypeVariable typeParamet } hasContainerElementsMarkedForCascading = tmpHasContainerElementsMarkedForCascading; hasGroupConversionsOnAnnotatedObjectOrContainerElements = tmpHasGroupConversionsOnAnnotatedObjectOrContainerElements; + + mapping = null; } public static CascadingMetaDataBuilder nonCascading() { @@ -140,6 +147,10 @@ public Type getEnclosingType() { return enclosingType; } + public String getMappingName() { + return mapping; + } + public Class getDeclaredContainerClass() { return declaredContainerClass; } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ContainerCascadingMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/ContainerCascadingMetaData.java similarity index 91% rename from engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ContainerCascadingMetaData.java rename to engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/ContainerCascadingMetaData.java index 3ac00782e1..287d5ef6bf 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ContainerCascadingMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/ContainerCascadingMetaData.java @@ -4,7 +4,7 @@ * License: Apache License, Version 2.0 * See the license.txt file in the root directory or . */ -package org.hibernate.validator.internal.metadata.aggregated; +package org.hibernate.validator.internal.metadata.aggregated.cascading; import java.lang.invoke.MethodHandles; import java.lang.reflect.Type; @@ -19,6 +19,8 @@ import org.hibernate.validator.internal.engine.valueextraction.AnnotatedObject; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaData; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.internal.util.StringHelper; import org.hibernate.validator.internal.util.TypeVariables; @@ -66,7 +68,7 @@ public class ContainerCascadingMetaData implements CascadingMetaData { * Possibly the cascading type parameters corresponding to this type parameter if it is a parameterized type. */ @Immutable - private final List containerElementTypesCascadingMetaData; + private final List containerElementTypesCascadingMetaData; /** * If this type parameter is marked for cascading. @@ -92,10 +94,13 @@ public class ContainerCascadingMetaData implements CascadingMetaData { public static ContainerCascadingMetaData of(ValueExtractorManager valueExtractorManager, CascadingMetaDataBuilder cascadingMetaDataBuilder, Object context) { + if ( cascadingMetaDataBuilder.getMappingName() != null ) { + return ContainerPropertyHolderCascadingMetaData.of( valueExtractorManager, cascadingMetaDataBuilder, context ); + } return new ContainerCascadingMetaData( valueExtractorManager, cascadingMetaDataBuilder ); } - private ContainerCascadingMetaData(ValueExtractorManager valueExtractorManager, CascadingMetaDataBuilder cascadingMetaDataBuilder) { + protected ContainerCascadingMetaData(ValueExtractorManager valueExtractorManager, CascadingMetaDataBuilder cascadingMetaDataBuilder) { this( valueExtractorManager, cascadingMetaDataBuilder.getEnclosingType(), @@ -138,7 +143,7 @@ private ContainerCascadingMetaData(ValueExtractorManager valueExtractorManager, } } - ContainerCascadingMetaData(Type enclosingType, List containerElementTypesCascadingMetaData, + ContainerCascadingMetaData(Type enclosingType, List containerElementTypesCascadingMetaData, GroupConversionHelper groupConversionHelper, Set valueExtractorCandidates) { this.enclosingType = enclosingType; this.typeParameter = AnnotatedObject.INSTANCE; @@ -206,7 +211,7 @@ public boolean isMarkedForCascadingOnAnnotatedObjectOrContainerElements() { return cascading || hasContainerElementsMarkedForCascading; } - public List getContainerElementTypesCascadingMetaData() { + public List getContainerElementTypesCascadingMetaData() { return containerElementTypesCascadingMetaData; } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/ContainerPropertyHolderCascadingMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/ContainerPropertyHolderCascadingMetaData.java new file mode 100644 index 0000000000..9665d40f9e --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/ContainerPropertyHolderCascadingMetaData.java @@ -0,0 +1,76 @@ +/* + * 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.aggregated.cascading; + +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.List; +import java.util.Set; + +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor; +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; +import org.hibernate.validator.internal.util.Contracts; +import org.hibernate.validator.internal.util.StringHelper; + +/** + * Extended view of container cascading metadta for property holders that in addition stores mapping name. + * + * @author Marko Bekhta + */ +public class ContainerPropertyHolderCascadingMetaData extends ContainerCascadingMetaData { + + /** + * Name of the constraint mappings to be applied. + */ + private final String mapping; + + public static ContainerPropertyHolderCascadingMetaData of(ValueExtractorManager valueExtractorManager, CascadingMetaDataBuilder cascadingMetaDataBuilder, + Object context) { + Contracts.assertNotEmpty( cascadingMetaDataBuilder.getMappingName(), "Property holder mapping cannot be an empty string." ); + + return new ContainerPropertyHolderCascadingMetaData( valueExtractorManager, cascadingMetaDataBuilder ); + } + + private ContainerPropertyHolderCascadingMetaData(ValueExtractorManager valueExtractorManager, CascadingMetaDataBuilder cascadingMetaDataBuilder) { + super( valueExtractorManager, cascadingMetaDataBuilder ); + this.mapping = cascadingMetaDataBuilder.getMappingName(); + } + + ContainerPropertyHolderCascadingMetaData(String mapping, Type enclosingType, List containerElementTypesCascadingMetaData, + GroupConversionHelper groupConversionHelper, Set valueExtractorCandidates) { + super( enclosingType, containerElementTypesCascadingMetaData, groupConversionHelper, valueExtractorCandidates ); + this.mapping = mapping; + } + + ContainerPropertyHolderCascadingMetaData(String mapping, Type enclosingType, TypeVariable typeParameter, Class declaredContainerClass, TypeVariable declaredTypeParameter, + GroupConversionHelper groupConversionHelper) { + super( enclosingType, typeParameter, declaredContainerClass, declaredTypeParameter, groupConversionHelper ); + this.mapping = mapping; + } + + @Override + public BeanMetaData getBeanMetaDataForCascadable(ConstraintMetaDataManager constraintMetaDataManager, Object value) { + return constraintMetaDataManager.getPropertyHolderBeanMetaData( value.getClass(), mapping ); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append( getClass().getSimpleName() ); + sb.append( " [" ); + sb.append( "mapping=" ).append( mapping ).append( ", " ); + sb.append( "enclosingType=" ).append( StringHelper.toShortString( getEnclosingType() ) ).append( ", " ); + sb.append( "typeParameter=" ).append( getTypeParameter() ).append( ", " ); + sb.append( "cascading=" ).append( isCascading() ).append( ", " ); + sb.append( "containerElementTypesCascadingMetaData=" ).append( getContainerElementTypesCascadingMetaData() ); + sb.append( "]" ); + return sb.toString(); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/GroupConversionHelper.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/GroupConversionHelper.java similarity index 97% rename from engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/GroupConversionHelper.java rename to engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/GroupConversionHelper.java index 29f9d826fb..6f53e0c66c 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/GroupConversionHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/GroupConversionHelper.java @@ -4,7 +4,7 @@ * License: Apache License, Version 2.0 * See the license.txt file in the root directory or . */ -package org.hibernate.validator.internal.metadata.aggregated; +package org.hibernate.validator.internal.metadata.aggregated.cascading; import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/NonContainerCascadingMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/NonContainerCascadingMetaData.java similarity index 84% rename from engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/NonContainerCascadingMetaData.java rename to engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/NonContainerCascadingMetaData.java index 7ae7eb15a6..81efcc9b47 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/NonContainerCascadingMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/NonContainerCascadingMetaData.java @@ -4,7 +4,7 @@ * License: Apache License, Version 2.0 * See the license.txt file in the root directory or . */ -package org.hibernate.validator.internal.metadata.aggregated; +package org.hibernate.validator.internal.metadata.aggregated.cascading; import java.lang.invoke.MethodHandles; import java.lang.reflect.TypeVariable; @@ -14,6 +14,8 @@ import org.hibernate.validator.internal.engine.valueextraction.AnnotatedObject; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaData; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; @@ -47,7 +49,10 @@ public class NonContainerCascadingMetaData implements CascadingMetaData { private GroupConversionHelper groupConversionHelper; public static NonContainerCascadingMetaData of(CascadingMetaDataBuilder cascadingMetaDataBuilder, Object context) { - if ( !cascadingMetaDataBuilder.isCascading() ) { + if ( cascadingMetaDataBuilder.getMappingName() != null ) { + return NonContainerPropertyHolderCascadingMetaData.of( cascadingMetaDataBuilder, context ); + } + else if ( !cascadingMetaDataBuilder.isCascading() ) { return NON_CASCADING; } else if ( cascadingMetaDataBuilder.getGroupConversions().isEmpty() ) { @@ -58,14 +63,14 @@ else if ( cascadingMetaDataBuilder.getGroupConversions().isEmpty() ) { } } - private NonContainerCascadingMetaData(CascadingMetaDataBuilder cascadingMetaDataBuilder) { + protected NonContainerCascadingMetaData(CascadingMetaDataBuilder cascadingMetaDataBuilder) { this( cascadingMetaDataBuilder.isCascading(), GroupConversionHelper.of( cascadingMetaDataBuilder.getGroupConversions() ) ); } - private NonContainerCascadingMetaData(boolean cascading, GroupConversionHelper groupConversionHelper) { + protected NonContainerCascadingMetaData(boolean cascading, GroupConversionHelper groupConversionHelper) { this.cascading = cascading; this.groupConversionHelper = groupConversionHelper; } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/NonContainerPropertyHolderCascadingMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/NonContainerPropertyHolderCascadingMetaData.java new file mode 100644 index 0000000000..5a70d657f0 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/NonContainerPropertyHolderCascadingMetaData.java @@ -0,0 +1,53 @@ +/* + * 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.aggregated.cascading; + +import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; +import org.hibernate.validator.internal.util.Contracts; + +/** + * Extended view of non container cascading metadta for property holders that in addition stores mapping name. + * + * @author Marko Bekhta + */ +public class NonContainerPropertyHolderCascadingMetaData extends NonContainerCascadingMetaData { + + /** + * Name of the constraint mappings to be applied. + */ + private final String mapping; + + public static NonContainerPropertyHolderCascadingMetaData of(CascadingMetaDataBuilder cascadingMetaDataBuilder, Object context) { + Contracts.assertTrue( cascadingMetaDataBuilder.isCascading(), "Property holder cascading metadata should always be cascading." ); + Contracts.assertNotEmpty( cascadingMetaDataBuilder.getMappingName(), "Property holder mapping cannot be an empty string." ); + + return new NonContainerPropertyHolderCascadingMetaData( cascadingMetaDataBuilder ); + } + + private NonContainerPropertyHolderCascadingMetaData(CascadingMetaDataBuilder cascadingMetaDataBuilder) { + super( cascadingMetaDataBuilder ); + this.mapping = cascadingMetaDataBuilder.getMappingName(); + } + + @Override + public BeanMetaData getBeanMetaDataForCascadable(ConstraintMetaDataManager constraintMetaDataManager, Object value) { + return constraintMetaDataManager.getPropertyHolderBeanMetaData( value.getClass(), mapping ); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append( getClass().getSimpleName() ); + sb.append( " [" ); + sb.append( "mapping=" ).append( mapping ).append( ", " ); + sb.append( "cascading=" ).append( isCascading() ).append( ", " ); + sb.append( "]" ); + return sb.toString(); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/PotentiallyContainerCascadingMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/PotentiallyContainerCascadingMetaData.java similarity index 76% rename from engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/PotentiallyContainerCascadingMetaData.java rename to engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/PotentiallyContainerCascadingMetaData.java index d5efd88d08..0437b734a9 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/PotentiallyContainerCascadingMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/PotentiallyContainerCascadingMetaData.java @@ -4,7 +4,7 @@ * License: Apache License, Version 2.0 * See the license.txt file in the root directory or . */ -package org.hibernate.validator.internal.metadata.aggregated; +package org.hibernate.validator.internal.metadata.aggregated.cascading; import java.lang.invoke.MethodHandles; import java.lang.reflect.TypeVariable; @@ -16,6 +16,8 @@ import org.hibernate.validator.internal.engine.valueextraction.AnnotatedObject; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaData; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; @@ -38,10 +40,13 @@ public class PotentiallyContainerCascadingMetaData implements CascadingMetaData private final Set potentialValueExtractorDescriptors; public static PotentiallyContainerCascadingMetaData of(CascadingMetaDataBuilder cascadingMetaDataBuilder, Set potentialValueExtractorDescriptors, Object context) { + if ( cascadingMetaDataBuilder.getMappingName() != null ) { + return PotentiallyContainerPropertyHolderCascadingMetaData.of( cascadingMetaDataBuilder, potentialValueExtractorDescriptors, context ); + } return new PotentiallyContainerCascadingMetaData( cascadingMetaDataBuilder, potentialValueExtractorDescriptors ); } - private PotentiallyContainerCascadingMetaData(CascadingMetaDataBuilder cascadingMetaDataBuilder, Set potentialValueExtractorDescriptors) { + protected PotentiallyContainerCascadingMetaData(CascadingMetaDataBuilder cascadingMetaDataBuilder, Set potentialValueExtractorDescriptors) { this( potentialValueExtractorDescriptors, GroupConversionHelper.of( cascadingMetaDataBuilder.getGroupConversions() ) ); } @@ -91,19 +96,23 @@ public CascadingMetaData addRuntimeContainerSupport(ValueExtractorManager valueE return new ContainerCascadingMetaData( valueClass, Collections.singletonList( - new ContainerCascadingMetaData( - compliantValueExtractor.getContainerType(), - compliantValueExtractor.getExtractedTypeParameter(), - compliantValueExtractor.getContainerType(), - compliantValueExtractor.getExtractedTypeParameter(), - groupConversionHelper.isEmpty() ? GroupConversionHelper.EMPTY : groupConversionHelper - ) + createInnerMetadata( compliantValueExtractor, groupConversionHelper ) ), groupConversionHelper, Collections.singleton( compliantValueExtractor ) ); } + protected ContainerCascadingMetaData createInnerMetadata(ValueExtractorDescriptor compliantValueExtractor, GroupConversionHelper groupConversionHelper) { + return new ContainerCascadingMetaData( + compliantValueExtractor.getContainerType(), + compliantValueExtractor.getExtractedTypeParameter(), + compliantValueExtractor.getContainerType(), + compliantValueExtractor.getExtractedTypeParameter(), + groupConversionHelper.isEmpty() ? GroupConversionHelper.EMPTY : groupConversionHelper + ); + } + @Override @SuppressWarnings("unchecked") public T as(Class clazz) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/PotentiallyContainerPropertyHolderCascadingMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/PotentiallyContainerPropertyHolderCascadingMetaData.java new file mode 100644 index 0000000000..196d0720e9 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/PotentiallyContainerPropertyHolderCascadingMetaData.java @@ -0,0 +1,59 @@ +/* + * 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.aggregated.cascading; + +import java.util.Set; + +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.util.Contracts; + +/** + * Extended view of potentially container cascading metadta for property holders that in addition stores mapping name. + * + * @author Marko Bekhta + */ +public class PotentiallyContainerPropertyHolderCascadingMetaData extends PotentiallyContainerCascadingMetaData { + + /** + * Name of the constraint mappings to be applied. + */ + private final String mapping; + + public static PotentiallyContainerPropertyHolderCascadingMetaData of(CascadingMetaDataBuilder cascadingMetaDataBuilder, Set potentialValueExtractorDescriptors, Object context) { + Contracts.assertNotEmpty( cascadingMetaDataBuilder.getMappingName(), "Property holder mapping cannot be an empty string." ); + + return new PotentiallyContainerPropertyHolderCascadingMetaData( cascadingMetaDataBuilder, potentialValueExtractorDescriptors ); + } + + private PotentiallyContainerPropertyHolderCascadingMetaData(CascadingMetaDataBuilder cascadingMetaDataBuilder, Set potentialValueExtractorDescriptors) { + super( cascadingMetaDataBuilder, potentialValueExtractorDescriptors ); + this.mapping = cascadingMetaDataBuilder.getMappingName(); + } + + @Override + protected ContainerCascadingMetaData createInnerMetadata(ValueExtractorDescriptor compliantValueExtractor, GroupConversionHelper groupConversionHelper) { + return new ContainerPropertyHolderCascadingMetaData( + mapping, + compliantValueExtractor.getContainerType(), + compliantValueExtractor.getExtractedTypeParameter(), + compliantValueExtractor.getContainerType(), + compliantValueExtractor.getExtractedTypeParameter(), + groupConversionHelper.isEmpty() ? GroupConversionHelper.EMPTY : groupConversionHelper + ); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append( getClass().getSimpleName() ); + sb.append( " [" ); + sb.append( "mapping=" ).append( mapping ).append( ", " ); + sb.append( "]" ); + return sb.toString(); + } +} From ac7835f906b422cbe0ffcb1747162c74c214b3a5 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Wed, 18 Jul 2018 21:24:07 +0200 Subject: [PATCH 09/14] [property-holder] Make property holder configuration use location builder --- .../internal/engine/ValidatorFactoryImpl.java | 5 + .../PropertyHolderBeanMetaDataBuilder.java | 27 +++- .../metadata/core/MetaConstraintBuilder.java | 33 +++++ .../metadata/location/ConstraintLocation.java | 8 ++ ...lderPropertyConstraintLocationBuilder.java | 25 ++++ .../manager/ConstraintMetaDataManager.java | 3 + .../PropertyHolderBeanMetaDataProvider.java | 8 +- .../ConstrainedPropertyHolderElement.java | 119 ++++++++++++++++ ...nstrainedPropertyHolderElementBuilder.java | 132 ++++++++++++++++++ .../PropertyHolderConfiguration.java | 17 ++- .../PropertyHolderConfigurationSource.java | 62 -------- .../PropertyHolderProperty.java | 9 +- .../internal/util/CollectionHelper.java | 9 ++ .../internal/engine/path/PathImplTest.java | 2 + .../ConstraintMetaDataManagerTest.java | 2 + .../aggregated/ExecutableMetaDataTest.java | 2 + .../aggregated/ParameterMetaDataTest.java | 3 + .../aggregated/PropertyMetaDataTest.java | 2 + 18 files changed, 383 insertions(+), 85 deletions(-) create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraintBuilder.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/location/PropertyHolderPropertyConstraintLocationBuilder.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/ConstrainedPropertyHolderElement.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/ConstrainedPropertyHolderElementBuilder.java delete mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/PropertyHolderConfigurationSource.java diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java index e8f9fd9278..53987444ec 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java @@ -50,6 +50,7 @@ import org.hibernate.validator.internal.metadata.provider.XmlMetaDataProvider; import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; +import org.hibernate.validator.internal.properties.propertyholder.PropertyAccessorCreatorProvider; import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; @@ -136,6 +137,8 @@ public class ValidatorFactoryImpl implements HibernateValidatorFactory { private final ValueExtractorManager valueExtractorManager; + private final PropertyAccessorCreatorProvider propertyAccessorCreatorProvider; + private final JavaBeanHelper javaBeanHelper; private final ValidationOrderGenerator validationOrderGenerator; @@ -144,6 +147,7 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) { ClassLoader externalClassLoader = getExternalClassLoader( configurationState ); this.valueExtractorManager = new ValueExtractorManager( configurationState.getValueExtractors() ); + this.propertyAccessorCreatorProvider = new PropertyAccessorCreatorProvider(); this.beanMetaDataManagers = new ConcurrentHashMap<>(); this.constraintHelper = new ConstraintHelper(); this.typeResolutionHelper = new TypeResolutionHelper(); @@ -361,6 +365,7 @@ Validator createValidator(ConstraintValidatorFactory constraintValidatorFactory, typeResolutionHelper, validatorFactoryScopedContext.getParameterNameProvider(), valueExtractorManager, + propertyAccessorCreatorProvider, javaBeanHelper, validationOrderGenerator, methodValidationConfiguration, diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/PropertyHolderBeanMetaDataBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/PropertyHolderBeanMetaDataBuilder.java index 6abe88a323..08cac4d42d 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/PropertyHolderBeanMetaDataBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/PropertyHolderBeanMetaDataBuilder.java @@ -14,10 +14,12 @@ import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; import org.hibernate.validator.internal.metadata.raw.ConstrainedField; +import org.hibernate.validator.internal.metadata.raw.propertyholder.ConstrainedPropertyHolderElementBuilder; import org.hibernate.validator.internal.metadata.raw.propertyholder.PropertyHolderConfiguration; -import org.hibernate.validator.internal.metadata.raw.propertyholder.PropertyHolderConfigurationSource; +import org.hibernate.validator.internal.properties.propertyholder.PropertyAccessorCreatorProvider; import org.hibernate.validator.internal.util.StringHelper; import org.hibernate.validator.internal.util.TypeResolutionHelper; @@ -37,9 +39,10 @@ public class PropertyHolderBeanMetaDataBuilder { private final Set builders = newHashSet(); private final TypeResolutionHelper typeResolutionHelper; private final ValueExtractorManager valueExtractorManager; + private final PropertyAccessorCreatorProvider propertyAccessorCreatorProvider; - private PropertyHolderConfigurationSource sequenceSource; - private PropertyHolderConfigurationSource providerSource; + private ConfigurationSource sequenceSource; + private ConfigurationSource providerSource; private List> defaultGroupSequence; @@ -47,6 +50,7 @@ private PropertyHolderBeanMetaDataBuilder( ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, + PropertyAccessorCreatorProvider propertyAccessorCreatorProvider, ValidationOrderGenerator validationOrderGenerator, Class propertyHolderClass) { this.propertyHolderClass = propertyHolderClass; @@ -54,18 +58,21 @@ private PropertyHolderBeanMetaDataBuilder( this.validationOrderGenerator = validationOrderGenerator; this.typeResolutionHelper = typeResolutionHelper; this.valueExtractorManager = valueExtractorManager; + this.propertyAccessorCreatorProvider = propertyAccessorCreatorProvider; } public static PropertyHolderBeanMetaDataBuilder getInstance( ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, + PropertyAccessorCreatorProvider propertyAccessorCreatorProvider, ValidationOrderGenerator validationOrderGenerator, Class propertyHolderClass) { return new PropertyHolderBeanMetaDataBuilder<>( constraintHelper, typeResolutionHelper, valueExtractorManager, + propertyAccessorCreatorProvider, validationOrderGenerator, propertyHolderClass ); } @@ -79,8 +86,18 @@ public void add(PropertyHolderConfiguration configuration) { defaultGroupSequence = configuration.getDefaultGroupSequence(); } - for ( ConstrainedElement constrainedElement : configuration.getConstrainedElements() ) { - addMetaDataToBuilder( constrainedElement, builders ); + for ( ConstrainedPropertyHolderElementBuilder constrainedElement : configuration.getConstrainedElements() ) { + if ( constrainedElement.isConstrained() ) { + addMetaDataToBuilder( + constrainedElement.build( + typeResolutionHelper, + valueExtractorManager, + propertyAccessorCreatorProvider, + propertyHolderClass + ), + builders + ); + } } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraintBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraintBuilder.java new file mode 100644 index 0000000000..6984100991 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraintBuilder.java @@ -0,0 +1,33 @@ +/* + * 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.core; + +import java.lang.annotation.Annotation; + +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.properties.Constrainable; +import org.hibernate.validator.internal.util.TypeResolutionHelper; + +/** + * @author Marko Bekhta + */ +public class MetaConstraintBuilder { + + private final ConstraintDescriptorImpl constraintDescriptor; + private final ConstraintLocation.Builder locationBuilder; + + public MetaConstraintBuilder(ConstraintDescriptorImpl constraintDescriptor, ConstraintLocation.Builder locationBuilder) { + this.constraintDescriptor = constraintDescriptor; + this.locationBuilder = locationBuilder; + } + + public MetaConstraint build(TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, Constrainable constrainable) { + return MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintDescriptor, locationBuilder.build( constrainable ) ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ConstraintLocation.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ConstraintLocation.java index 20a53feee4..9d0b8ba182 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ConstraintLocation.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ConstraintLocation.java @@ -104,6 +104,14 @@ static ConstraintLocation forParameter(Callable callable, int index) { */ ConstraintLocationKind getKind(); + interface Builder { + ConstraintLocation build(Constrainable constrainable); + + static Builder forPropertyHolderProperty() { + return new PropertyHolderPropertyConstraintLocationBuilder(); + } + } + enum ConstraintLocationKind { TYPE( ElementType.TYPE ), CONSTRUCTOR( ElementType.CONSTRUCTOR ), diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/PropertyHolderPropertyConstraintLocationBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/PropertyHolderPropertyConstraintLocationBuilder.java new file mode 100644 index 0000000000..17168c8e7c --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/PropertyHolderPropertyConstraintLocationBuilder.java @@ -0,0 +1,25 @@ +/* + * 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.location; + +import org.hibernate.validator.internal.metadata.location.ConstraintLocation.Builder; +import org.hibernate.validator.internal.properties.Constrainable; +import org.hibernate.validator.internal.properties.Field; +import org.hibernate.validator.internal.util.Contracts; + +/** + * @author Marko Bekhta + */ +public class PropertyHolderPropertyConstraintLocationBuilder implements Builder { + + @Override + public ConstraintLocation build(Constrainable constrainable) { + Contracts.assertTrue( constrainable instanceof Field, "Only Field instances are accepted." ); + + return ConstraintLocation.forField( constrainable.as( Field.class ) ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/ConstraintMetaDataManager.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/ConstraintMetaDataManager.java index 34d692acfd..ae1ce9bbf6 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/ConstraintMetaDataManager.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/ConstraintMetaDataManager.java @@ -16,6 +16,7 @@ import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; import org.hibernate.validator.internal.metadata.provider.PropertyHolderMetaDataProvider; import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; +import org.hibernate.validator.internal.properties.propertyholder.PropertyAccessorCreatorProvider; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.TypeResolutionHelper; @@ -51,6 +52,7 @@ public ConstraintMetaDataManager(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ExecutableParameterNameProvider parameterNameProvider, ValueExtractorManager valueExtractorManager, + PropertyAccessorCreatorProvider propertyAccessorCreatorProvider, JavaBeanHelper javaBeanHelper, ValidationOrderGenerator validationOrderGenerator, MethodValidationConfiguration methodValidationConfiguration, @@ -72,6 +74,7 @@ public ConstraintMetaDataManager(ConstraintHelper constraintHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, + propertyAccessorCreatorProvider, validationOrderGenerator ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/PropertyHolderBeanMetaDataProvider.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/PropertyHolderBeanMetaDataProvider.java index 10c22d8b4e..9af4971ec0 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/PropertyHolderBeanMetaDataProvider.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/PropertyHolderBeanMetaDataProvider.java @@ -19,6 +19,7 @@ import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.provider.PropertyHolderMetaDataProvider; import org.hibernate.validator.internal.metadata.raw.propertyholder.PropertyHolderConfiguration; +import org.hibernate.validator.internal.properties.propertyholder.PropertyAccessorCreatorProvider; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.stereotypes.Immutable; @@ -45,15 +46,18 @@ public class PropertyHolderBeanMetaDataProvider { */ private final ValueExtractorManager valueExtractorManager; + private final PropertyAccessorCreatorProvider propertyAccessorCreatorProvider; + private final ValidationOrderGenerator validationOrderGenerator; private final MetaDataCache metaDataCache; - public PropertyHolderBeanMetaDataProvider(List propertyHolderMetaDataProviderList, ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, ValidationOrderGenerator validationOrderGenerator) { + public PropertyHolderBeanMetaDataProvider(List propertyHolderMetaDataProviderList, ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, PropertyAccessorCreatorProvider propertyAccessorCreatorProvider, ValidationOrderGenerator validationOrderGenerator) { this.propertyHolderMetaDataProviderList = propertyHolderMetaDataProviderList; this.constraintHelper = constraintHelper; this.typeResolutionHelper = typeResolutionHelper; this.valueExtractorManager = valueExtractorManager; + this.propertyAccessorCreatorProvider = propertyAccessorCreatorProvider; this.validationOrderGenerator = validationOrderGenerator; this.metaDataCache = new MetaDataCache<>(); @@ -69,7 +73,7 @@ public BeanMetaData getBeanMetaData(Class propertyHolderClass, String private BeanMetaDataImpl createBeanMetaData(PropertyHolderMetadataKey metadataKey) { PropertyHolderBeanMetaDataBuilder builder = PropertyHolderBeanMetaDataBuilder.getInstance( - constraintHelper, typeResolutionHelper, valueExtractorManager, validationOrderGenerator, metadataKey.propertyHolderClass + constraintHelper, typeResolutionHelper, valueExtractorManager, propertyAccessorCreatorProvider, validationOrderGenerator, metadataKey.propertyHolderClass ); for ( PropertyHolderMetaDataProvider metaDataProvider : propertyHolderMetaDataProviderList ) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/ConstrainedPropertyHolderElement.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/ConstrainedPropertyHolderElement.java new file mode 100644 index 0000000000..526d210e00 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/ConstrainedPropertyHolderElement.java @@ -0,0 +1,119 @@ +/* + * 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.raw.propertyholder; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Set; + +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; +import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; +import org.hibernate.validator.internal.util.CollectionHelper; +import org.hibernate.validator.internal.util.stereotypes.Immutable; + +/** + * Base implementation of with functionality common to all {@link ConstrainedElement} implementations. + * + * @author Gunnar Morling + * @author Hardy Ferentschik + * @author Marko Bekhta + */ +public abstract class ConstrainedPropertyHolderElement implements ConstrainedElement { + private final ConstrainedElementKind kind; + protected final ConfigurationSource source; + @Immutable + protected final Set> constraints; + protected final CascadingMetaDataBuilder cascadingMetaDataBuilder; + @Immutable + protected final Set> typeArgumentConstraints; + + public ConstrainedPropertyHolderElement(ConfigurationSource source, + ConstrainedElementKind kind, + Set> constraints, + Set> typeArgumentConstraints, + CascadingMetaDataBuilder cascadingMetaDataBuilder) { + this.kind = kind; + this.source = source; + this.constraints = constraints != null ? CollectionHelper.toImmutableSet( constraints ) : Collections.>emptySet(); + this.typeArgumentConstraints = typeArgumentConstraints != null ? CollectionHelper.toImmutableSet( typeArgumentConstraints ) : Collections.>emptySet(); + this.cascadingMetaDataBuilder = cascadingMetaDataBuilder; + } + + @Override + public ConstrainedElementKind getKind() { + return kind; + } + + @Override + public Iterator> iterator() { + return constraints.iterator(); + } + + @Override + public Set> getConstraints() { + return constraints; + } + + @Override + public Set> getTypeArgumentConstraints() { + return typeArgumentConstraints; + } + + @Override + public CascadingMetaDataBuilder getCascadingMetaDataBuilder() { + return cascadingMetaDataBuilder; + } + + @Override + public boolean isConstrained() { + return cascadingMetaDataBuilder.isMarkedForCascadingOnAnnotatedObjectOrContainerElements() + || cascadingMetaDataBuilder.hasGroupConversionsOnAnnotatedObjectOrContainerElements() + || !constraints.isEmpty() + || !typeArgumentConstraints.isEmpty(); + } + + @Override + public ConfigurationSource getSource() { + return source; + } + + @Override + public String toString() { + return "AbstractConstrainedElement [kind=" + kind + ", source=" + + source + ", constraints=" + + constraints + ", cascadingMetaDataBuilder=" + + cascadingMetaDataBuilder + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ( ( source == null ) ? 0 : source.hashCode() ); + return result; + } + + @Override + public boolean equals(Object obj) { + if ( this == obj ) { + return true; + } + if ( obj == null ) { + return false; + } + if ( getClass() != obj.getClass() ) { + return false; + } + ConstrainedPropertyHolderElement other = (ConstrainedPropertyHolderElement) obj; + if ( source != other.source ) { + return false; + } + return true; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/ConstrainedPropertyHolderElementBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/ConstrainedPropertyHolderElementBuilder.java new file mode 100644 index 0000000000..d92a720a71 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/ConstrainedPropertyHolderElementBuilder.java @@ -0,0 +1,132 @@ +/* + * 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.raw.propertyholder; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.core.MetaConstraintBuilder; +import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; +import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; +import org.hibernate.validator.internal.metadata.raw.ConstrainedField; +import org.hibernate.validator.internal.properties.PropertyAccessor; +import org.hibernate.validator.internal.properties.propertyholder.PropertyAccessorCreatorProvider; +import org.hibernate.validator.internal.properties.propertyholder.PropertyHolderProperty; +import org.hibernate.validator.internal.util.CollectionHelper; +import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.util.stereotypes.Immutable; +import org.hibernate.validator.spi.propertyholder.PropertyAccessorCreator; + +/** + * @author Marko Bekhta + */ +public abstract class ConstrainedPropertyHolderElementBuilder { + + private final ConfigurationSource source; + + private final String name; + private final Class type; + + @Immutable + private final Set> constraints; + private final CascadingMetaDataBuilder cascadingMetaDataBuilder; + @Immutable + private final Set> typeArgumentConstraints; + + public ConstrainedPropertyHolderElementBuilder(ConfigurationSource source, + String name, Class type, Set> constraints, + Set> typeArgumentConstraints, + CascadingMetaDataBuilder cascadingMetaDataBuilder) { + this.source = source; + this.name = name; + this.type = type; + this.constraints = CollectionHelper.toImmutableSetOfNullable( constraints ); + this.typeArgumentConstraints = CollectionHelper.toImmutableSetOfNullable( typeArgumentConstraints ); + this.cascadingMetaDataBuilder = cascadingMetaDataBuilder; + } + + public boolean isConstrained() { + return cascadingMetaDataBuilder.isMarkedForCascadingOnAnnotatedObjectOrContainerElements() + || cascadingMetaDataBuilder.hasGroupConversionsOnAnnotatedObjectOrContainerElements() + || !constraints.isEmpty() + || !typeArgumentConstraints.isEmpty(); + } + + public ConstrainedElement build(TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, PropertyAccessorCreatorProvider propertyAccessorCreatorProvider, Class propertyHolderType) { + PropertyAccessorCreator propertyAccessorCreator = propertyAccessorCreatorProvider.getPropertyAccessorCreatorFor( propertyHolderType ); + PropertyAccessor propertyAccessor = propertyAccessorCreator.create( name, type ); + + PropertyHolderProperty property = new PropertyHolderProperty( propertyHolderType, propertyAccessor, name, type ); + + return new ConstrainedField( + source, + property, + toMetaConstraints( typeResolutionHelper, valueExtractorManager, property, constraints ), + toMetaConstraints( typeResolutionHelper, valueExtractorManager, property, typeArgumentConstraints ), + cascadingMetaDataBuilder + ); + } + + private Set> toMetaConstraints(TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, PropertyHolderProperty property, Collection> collection) { + Set> builtConstraints = new HashSet<>( constraints.size() ); + for ( MetaConstraintBuilder builder : constraints ) { + builtConstraints.add( builder.build( typeResolutionHelper, valueExtractorManager, property ) ); + } + return builtConstraints; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder( getClass().getSimpleName() ); + sb.append( "{" ); + sb.append( "source=" ).append( source ); + sb.append( ", name='" ).append( name ).append( '\'' ); + sb.append( ", type=" ).append( type ); + sb.append( ", constraints=" ).append( constraints ); + sb.append( ", cascadingMetaDataBuilder=" ).append( cascadingMetaDataBuilder ); + sb.append( ", typeArgumentConstraints=" ).append( typeArgumentConstraints ); + sb.append( '}' ); + return sb.toString(); + } + + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + + ConstrainedPropertyHolderElementBuilder that = (ConstrainedPropertyHolderElementBuilder) o; + + if ( source != that.source ) { + return false; + } + if ( !name.equals( that.name ) ) { + return false; + } + if ( !type.equals( that.type ) ) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int result = source.hashCode(); + result = 31 * result + name.hashCode(); + result = 31 * result + type.hashCode(); + return result; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/PropertyHolderConfiguration.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/PropertyHolderConfiguration.java index 11f8e861da..f0f76cd690 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/PropertyHolderConfiguration.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/PropertyHolderConfiguration.java @@ -9,35 +9,34 @@ import java.util.List; import java.util.Set; -import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; -import org.hibernate.validator.internal.util.CollectionHelper; +import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; /** * @author Marko Bekhta */ public class PropertyHolderConfiguration { - private final PropertyHolderConfigurationSource source; + private final ConfigurationSource source; private final String mappingName; - private final Set constrainedElements; + private final Set constrainedElements; private final List> defaultGroupSequence; public PropertyHolderConfiguration( - PropertyHolderConfigurationSource source, + ConfigurationSource source, String mappingName, - Set constrainedElements, + Set constrainedElements, List> defaultGroupSequence) { this.source = source; this.mappingName = mappingName; - this.constrainedElements = CollectionHelper.newHashSet( constrainedElements ); + this.constrainedElements = constrainedElements; this.defaultGroupSequence = defaultGroupSequence; } - public PropertyHolderConfigurationSource getSource() { + public ConfigurationSource getSource() { return source; } @@ -45,7 +44,7 @@ public String getMappingName() { return mappingName; } - public Set getConstrainedElements() { + public Set getConstrainedElements() { return constrainedElements; } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/PropertyHolderConfigurationSource.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/PropertyHolderConfigurationSource.java deleted file mode 100644 index acf29b4c79..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/PropertyHolderConfigurationSource.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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.raw.propertyholder; - -/** - * The source of constraint meta data. - * - * @author Gunnar Morling - * @author Marko Bekhta - */ -public enum PropertyHolderConfigurationSource { - - /** - * The source of configuration is JSON configuration - */ - JSON( 0 ), - /** - * The source of configuration is XML configuration - */ - XML( 1 ), - /** - * The source of configuration is the programmatic API - */ - API( 2 ); - - private int priority; - - private PropertyHolderConfigurationSource(int priority) { - this.priority = priority; - } - - /** - * Returns this sources priority. Can be used to determine which - * configuration shall apply in case of conflicting configurations by - * several providers. - * - * @return This source's priority. - */ - public int getPriority() { - return priority; - } - - /** - * Returns that configuration source from the given two sources, which has - * the higher priority. - * - * @param a - * A configuration source. - * @param b - * Another configuration source. - * - * @return The source with the higher priority. Will be source {@code a} if - * both have the same priority. - */ - public static PropertyHolderConfigurationSource max(PropertyHolderConfigurationSource a, PropertyHolderConfigurationSource b) { - return a.getPriority() >= b.getPriority() ? a : b; - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/propertyholder/PropertyHolderProperty.java b/engine/src/main/java/org/hibernate/validator/internal/properties/propertyholder/PropertyHolderProperty.java index ab83f1ffe8..4ba2087d33 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/properties/propertyholder/PropertyHolderProperty.java +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/propertyholder/PropertyHolderProperty.java @@ -8,14 +8,14 @@ import java.lang.reflect.Type; -import org.hibernate.validator.internal.metadata.raw.ConstrainedElement.ConstrainedElementKind; +import org.hibernate.validator.internal.properties.Field; import org.hibernate.validator.internal.properties.Property; import org.hibernate.validator.internal.properties.PropertyAccessor; /** * @author Marko Bekhta */ -public class PropertyHolderProperty implements Property { +public class PropertyHolderProperty implements Property, Field { private final Class propertyHolderType; private final PropertyAccessor propertyAccessor; @@ -60,11 +60,6 @@ public Type getType() { return type; } - @Override - public ConstrainedElementKind getConstrainedElementKind() { - return ConstrainedElementKind.FIELD; - } - @Override public boolean equals(Object o) { if ( this == o ) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/CollectionHelper.java b/engine/src/main/java/org/hibernate/validator/internal/util/CollectionHelper.java index cb678c0c88..406adb7a9a 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/CollectionHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/CollectionHelper.java @@ -126,6 +126,15 @@ public static Map toImmutableMap(Map map) { } } + public static Set toImmutableSetOfNullable(Set set) { + if ( set == null ) { + return Collections.emptySet(); + } + else { + return toImmutableSet( set ); + } + } + /** * As the default loadFactor is of 0.75, we need to calculate the initial capacity from the expected size to avoid * resizing the collection when we populate the collection with all the initial elements. We use a calculation diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/path/PathImplTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/path/PathImplTest.java index 8e81964003..0daaf25ef4 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/path/PathImplTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/path/PathImplTest.java @@ -37,6 +37,7 @@ import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; +import org.hibernate.validator.internal.properties.propertyholder.PropertyAccessorCreatorProvider; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.TypeResolutionHelper; @@ -198,6 +199,7 @@ public void testCreationOfExecutablePath() throws Exception { new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), new ValueExtractorManager( Collections.emptySet() ), + new PropertyAccessorCreatorProvider(), new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy() ), new ValidationOrderGenerator(), new MethodValidationConfiguration.Builder().build(), diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/ConstraintMetaDataManagerTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/ConstraintMetaDataManagerTest.java index 5371ddcf8a..d84d62ccdd 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/ConstraintMetaDataManagerTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/ConstraintMetaDataManagerTest.java @@ -30,6 +30,7 @@ import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; +import org.hibernate.validator.internal.properties.propertyholder.PropertyAccessorCreatorProvider; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.TypeResolutionHelper; @@ -58,6 +59,7 @@ public void setUpBeanMetaDataManager() { new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), new ValueExtractorManager( Collections.emptySet() ), + new PropertyAccessorCreatorProvider(), new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy() ), new ValidationOrderGenerator(), new MethodValidationConfiguration.Builder().build(), diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java index c0c1608a6e..02ff0f859e 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java @@ -33,6 +33,7 @@ import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; +import org.hibernate.validator.internal.properties.propertyholder.PropertyAccessorCreatorProvider; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.TypeResolutionHelper; @@ -63,6 +64,7 @@ public void setupBeanMetaData() { new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), new ValueExtractorManager( Collections.emptySet() ), + new PropertyAccessorCreatorProvider(), new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy() ), new ValidationOrderGenerator(), new MethodValidationConfiguration.Builder().build(), diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ParameterMetaDataTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ParameterMetaDataTest.java index 3ac47ca8f9..10e7f4cc6b 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ParameterMetaDataTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ParameterMetaDataTest.java @@ -34,6 +34,7 @@ import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; +import org.hibernate.validator.internal.properties.propertyholder.PropertyAccessorCreatorProvider; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.TypeResolutionHelper; @@ -63,6 +64,7 @@ public void setupBeanMetaData() { new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), new ValueExtractorManager( Collections.emptySet() ), + new PropertyAccessorCreatorProvider(), new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy() ), new ValidationOrderGenerator(), new MethodValidationConfiguration.Builder().build(), @@ -135,6 +137,7 @@ public void parameterNameInInheritanceHierarchy() throws Exception { new TypeResolutionHelper(), new ExecutableParameterNameProvider( new SkewedParameterNameProvider() ), new ValueExtractorManager( Collections.emptySet() ), + new PropertyAccessorCreatorProvider(), new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy() ), new ValidationOrderGenerator(), new MethodValidationConfiguration.Builder().build(), diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/PropertyMetaDataTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/PropertyMetaDataTest.java index 67012f2acd..aa26b82cf3 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/PropertyMetaDataTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/PropertyMetaDataTest.java @@ -25,6 +25,7 @@ import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; +import org.hibernate.validator.internal.properties.propertyholder.PropertyAccessorCreatorProvider; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.TypeResolutionHelper; @@ -47,6 +48,7 @@ public void setupBeanMetaDataManager() { new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), new ValueExtractorManager( Collections.emptySet() ), + new PropertyAccessorCreatorProvider(), new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy() ), new ValidationOrderGenerator(), new MethodValidationConfiguration.Builder().build(), From 92b0696bfbcd3820f17d3cbfc24a13d49b608853 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Thu, 19 Jul 2018 09:39:20 +0200 Subject: [PATCH 10/14] [property-holder] Introduce dummy metadata provider for property holders --- .../internal/engine/ValidatorFactoryImpl.java | 3 +- .../aggregated/CascadingMetaDataBuilder.java | 22 +- .../PropertyHolderBeanMetaDataBuilder.java | 1 + .../metadata/core/MetaConstraintBuilder.java | 21 +- .../DummyPropertyHolderMetaDataProvider.java | 192 ++++++++++++++++++ ...nstrainedPropertyHolderElementBuilder.java | 13 +- 6 files changed, 234 insertions(+), 18 deletions(-) create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/provider/proeprtyholder/DummyPropertyHolderMetaDataProvider.java diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java index 53987444ec..40096aa37c 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java @@ -48,6 +48,7 @@ import org.hibernate.validator.internal.metadata.provider.ProgrammaticMetaDataProvider; import org.hibernate.validator.internal.metadata.provider.PropertyHolderMetaDataProvider; import org.hibernate.validator.internal.metadata.provider.XmlMetaDataProvider; +import org.hibernate.validator.internal.metadata.provider.proeprtyholder.DummyPropertyHolderMetaDataProvider; import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; import org.hibernate.validator.internal.properties.propertyholder.PropertyAccessorCreatorProvider; @@ -405,7 +406,7 @@ private List buildMetaDataProviders() { } private List buildPropertyHolderMetaDataProvider() { - return Collections.emptyList(); + return Collections.singletonList( new DummyPropertyHolderMetaDataProvider() ); } private static boolean checkPropertiesForBoolean(Map properties, String propertyKey, boolean programmaticValue) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaDataBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaDataBuilder.java index 304e088d41..ef7df1794e 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaDataBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaDataBuilder.java @@ -48,7 +48,7 @@ public class CascadingMetaDataBuilder { private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); private static final CascadingMetaDataBuilder NON_CASCADING = - new CascadingMetaDataBuilder( null, null, null, null, false, Collections.emptyMap(), Collections.emptyMap() ); + new CascadingMetaDataBuilder( null, null, null, null, null, false, Collections.emptyMap(), Collections.emptyMap() ); private final String mapping; @@ -101,15 +101,23 @@ public class CascadingMetaDataBuilder { public CascadingMetaDataBuilder(Type enclosingType, TypeVariable typeParameter, boolean cascading, Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, Map, Class> groupConversions) { - this( enclosingType, typeParameter, + this( enclosingType, null, typeParameter, TypeVariables.getContainerClass( typeParameter ), TypeVariables.getActualTypeParameter( typeParameter ), cascading, containerElementTypesCascadingMetaData, groupConversions ); } - private CascadingMetaDataBuilder(Type enclosingType, TypeVariable typeParameter, Class declaredContainerClass, TypeVariable declaredTypeParameter, + private CascadingMetaDataBuilder(String mapping, TypeVariable typeParameter, boolean cascading, + Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, Map, Class> groupConversions) { + this( null, mapping, typeParameter, + TypeVariables.getContainerClass( typeParameter ), TypeVariables.getActualTypeParameter( typeParameter ), + cascading, containerElementTypesCascadingMetaData, groupConversions ); + } + + private CascadingMetaDataBuilder(Type enclosingType, String mapping, TypeVariable typeParameter, Class declaredContainerClass, TypeVariable declaredTypeParameter, boolean cascading, Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, Map, Class> groupConversions) { this.enclosingType = enclosingType; + this.mapping = mapping; this.typeParameter = typeParameter; this.declaredContainerClass = declaredContainerClass; this.declaredTypeParameter = declaredTypeParameter; @@ -127,8 +135,6 @@ private CascadingMetaDataBuilder(Type enclosingType, TypeVariable typeParamet } hasContainerElementsMarkedForCascading = tmpHasContainerElementsMarkedForCascading; hasGroupConversionsOnAnnotatedObjectOrContainerElements = tmpHasGroupConversionsOnAnnotatedObjectOrContainerElements; - - mapping = null; } public static CascadingMetaDataBuilder nonCascading() { @@ -139,6 +145,10 @@ public static CascadingMetaDataBuilder annotatedObject(Type cascadableType, bool return new CascadingMetaDataBuilder( cascadableType, AnnotatedObject.INSTANCE, cascading, containerElementTypesCascadingMetaData, groupConversions ); } + public static CascadingMetaDataBuilder propertyHolder(String mappingName, boolean cascading, Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, Map, Class> groupConversions) { + return new CascadingMetaDataBuilder( mappingName, AnnotatedObject.INSTANCE, cascading, containerElementTypesCascadingMetaData, groupConversions ); + } + public TypeVariable getTypeParameter() { return typeParameter; } @@ -429,7 +439,7 @@ private static Map, CascadingMetaDataBuilder> addCascadingMetaDa } else { amendedCascadingMetadata.put( cascadableTypeParameter, - new CascadingMetaDataBuilder( cascadableClass, cascadableTypeParameter, enclosingType, correspondingTypeParameter, true, + new CascadingMetaDataBuilder( cascadableClass, null, cascadableTypeParameter, enclosingType, correspondingTypeParameter, true, Collections.emptyMap(), groupConversions ) ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/PropertyHolderBeanMetaDataBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/PropertyHolderBeanMetaDataBuilder.java index 08cac4d42d..8345d27ad5 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/PropertyHolderBeanMetaDataBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/PropertyHolderBeanMetaDataBuilder.java @@ -91,6 +91,7 @@ public void add(PropertyHolderConfiguration configuration) { addMetaDataToBuilder( constrainedElement.build( typeResolutionHelper, + constraintHelper, valueExtractorManager, propertyAccessorCreatorProvider, propertyHolderClass diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraintBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraintBuilder.java index 6984100991..9a6e970cef 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraintBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraintBuilder.java @@ -13,21 +13,32 @@ import org.hibernate.validator.internal.metadata.location.ConstraintLocation; import org.hibernate.validator.internal.properties.Constrainable; import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; /** * @author Marko Bekhta */ public class MetaConstraintBuilder { - private final ConstraintDescriptorImpl constraintDescriptor; + private final ConstraintAnnotationDescriptor annotationDescriptor; private final ConstraintLocation.Builder locationBuilder; - public MetaConstraintBuilder(ConstraintDescriptorImpl constraintDescriptor, ConstraintLocation.Builder locationBuilder) { - this.constraintDescriptor = constraintDescriptor; + public MetaConstraintBuilder(ConstraintAnnotationDescriptor annotationDescriptor, ConstraintLocation.Builder locationBuilder) { + this.annotationDescriptor = annotationDescriptor; this.locationBuilder = locationBuilder; } - public MetaConstraint build(TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, Constrainable constrainable) { - return MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintDescriptor, locationBuilder.build( constrainable ) ); + public MetaConstraint build(TypeResolutionHelper typeResolutionHelper, ConstraintHelper constraintHelper, ValueExtractorManager valueExtractorManager, Constrainable constrainable) { + ConstraintLocation constraintLocation = locationBuilder.build( constrainable ); + return MetaConstraints.create( + typeResolutionHelper, + valueExtractorManager, + new ConstraintDescriptorImpl<>( + constraintHelper, + constrainable, + annotationDescriptor, + constraintLocation.getKind() + ), + constraintLocation ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/proeprtyholder/DummyPropertyHolderMetaDataProvider.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/proeprtyholder/DummyPropertyHolderMetaDataProvider.java new file mode 100644 index 0000000000..429a06b368 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/proeprtyholder/DummyPropertyHolderMetaDataProvider.java @@ -0,0 +1,192 @@ +/* + * 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.provider.proeprtyholder; + +import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; + +import javax.validation.ValidationException; + +import org.hibernate.validator.cfg.AnnotationDef; +import org.hibernate.validator.cfg.ConstraintDef; +import org.hibernate.validator.cfg.defs.EmailDef; +import org.hibernate.validator.cfg.defs.MinDef; +import org.hibernate.validator.cfg.defs.NotNullDef; +import org.hibernate.validator.cfg.defs.SizeDef; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.core.MetaConstraintBuilder; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.metadata.provider.PropertyHolderMetaDataProvider; +import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; +import org.hibernate.validator.internal.metadata.raw.propertyholder.ConstrainedPropertyHolderElementBuilder; +import org.hibernate.validator.internal.metadata.raw.propertyholder.PropertyHolderConfiguration; +import org.hibernate.validator.internal.util.CollectionHelper; +import org.hibernate.validator.internal.util.annotation.AnnotationDescriptor; +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.GetDeclaredMethodHandle; + +/** + * A dummy metadata provider just for testing purposes. To be removed compeletely later. + * + * @author Marko Bekhta + */ +public class DummyPropertyHolderMetaDataProvider implements PropertyHolderMetaDataProvider { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final MethodHandle CREATE_ANNOTATION_DESCRIPTOR_METHOD_HANDLE = + run( GetDeclaredMethodHandle.andMakeAccessible( MethodHandles.lookup(), AnnotationDef.class, "createAnnotationDescriptor" ) ); + + public static final String USER_MAPPING_NAME = "user"; + public static final String ADDRESS_MAPPING_NAME = "address"; + + @Override + public Optional getBeanConfiguration(String mappingName) { + switch ( mappingName ) { + case USER_MAPPING_NAME: + return Optional.of( user() ); + case ADDRESS_MAPPING_NAME: + return Optional.of( address() ); + default: + return Optional.empty(); + } + } + + private static PropertyHolderConfiguration user() { + return new PropertyHolderConfiguration( + ConfigurationSource.API, + USER_MAPPING_NAME, + CollectionHelper.asSet( + new ConstrainedPropertyHolderElementBuilder( + ConfigurationSource.API, + "name", + String.class, + CollectionHelper.asSet( + new MetaConstraintBuilder( + createAnnotationDescriptor( new NotNullDef() ), + ConstraintLocation.Builder.forPropertyHolderProperty() + ), + new MetaConstraintBuilder( + createAnnotationDescriptor( new SizeDef().min( 5 ).max( 10 ) ), + ConstraintLocation.Builder.forPropertyHolderProperty() + ) + ), + Collections.emptySet(), + CascadingMetaDataBuilder.nonCascading() + ), + new ConstrainedPropertyHolderElementBuilder( + ConfigurationSource.API, + "email", + String.class, + CollectionHelper.asSet( + new MetaConstraintBuilder( + createAnnotationDescriptor( new NotNullDef() ), + ConstraintLocation.Builder.forPropertyHolderProperty() + ), + new MetaConstraintBuilder( + createAnnotationDescriptor( new EmailDef() ), + ConstraintLocation.Builder.forPropertyHolderProperty() + ) + ), + Collections.emptySet(), + CascadingMetaDataBuilder.nonCascading() + ), + new ConstrainedPropertyHolderElementBuilder( + ConfigurationSource.API, + "address", + Map.class, // TODO: note this won't work need to handle cascading differently + Collections.emptySet(), + Collections.emptySet(), + // TODO: might require a builder over it to collect info and then when we know the property hodler class we + // could build the CascadingMetaDataBuilder from it. + CascadingMetaDataBuilder.propertyHolder( + USER_MAPPING_NAME, + true, + Collections.emptyMap(), + Collections.emptyMap() + ) + ) + ), + Collections.emptyList() + ); + } + + private static PropertyHolderConfiguration address() { + return new PropertyHolderConfiguration( + ConfigurationSource.API, + USER_MAPPING_NAME, + CollectionHelper.asSet( + new ConstrainedPropertyHolderElementBuilder( + ConfigurationSource.API, + "street", + String.class, + CollectionHelper.asSet( + new MetaConstraintBuilder( + createAnnotationDescriptor( new NotNullDef() ), + ConstraintLocation.Builder.forPropertyHolderProperty() + ), + new MetaConstraintBuilder( + createAnnotationDescriptor( new SizeDef().min( 5 ).max( 10 ) ), + ConstraintLocation.Builder.forPropertyHolderProperty() + ) + ), + Collections.emptySet(), + CascadingMetaDataBuilder.nonCascading() + ), + new ConstrainedPropertyHolderElementBuilder( + ConfigurationSource.API, + "buildingNumber", + Long.class, + CollectionHelper.asSet( + new MetaConstraintBuilder( + createAnnotationDescriptor( new NotNullDef() ), + ConstraintLocation.Builder.forPropertyHolderProperty() + ), + new MetaConstraintBuilder( + createAnnotationDescriptor( new MinDef().value( 0 ) ), + ConstraintLocation.Builder.forPropertyHolderProperty() + ) + ), + Collections.emptySet(), + CascadingMetaDataBuilder.nonCascading() + ) + ), + Collections.emptyList() + ); + } + + private static ConstraintAnnotationDescriptor createAnnotationDescriptor(ConstraintDef constraint) { + try { + @SuppressWarnings("unchecked") + AnnotationDescriptor annotationDescriptor = (AnnotationDescriptor) CREATE_ANNOTATION_DESCRIPTOR_METHOD_HANDLE.invoke( constraint ); + return new ConstraintAnnotationDescriptor<>( annotationDescriptor ); + } + catch (Throwable e) { + if ( e instanceof ValidationException ) { + throw (ValidationException) e; + } + throw LOG.getUnableToCreateAnnotationDescriptor( constraint.getClass(), e ); + } + } + + /** + * 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 V run(PrivilegedAction action) { + return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/ConstrainedPropertyHolderElementBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/ConstrainedPropertyHolderElementBuilder.java index d92a720a71..6738264c22 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/ConstrainedPropertyHolderElementBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/ConstrainedPropertyHolderElementBuilder.java @@ -12,6 +12,7 @@ import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.core.MetaConstraintBuilder; import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; @@ -28,7 +29,7 @@ /** * @author Marko Bekhta */ -public abstract class ConstrainedPropertyHolderElementBuilder { +public class ConstrainedPropertyHolderElementBuilder { private final ConfigurationSource source; @@ -60,7 +61,7 @@ public boolean isConstrained() { || !typeArgumentConstraints.isEmpty(); } - public ConstrainedElement build(TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, PropertyAccessorCreatorProvider propertyAccessorCreatorProvider, Class propertyHolderType) { + public ConstrainedElement build(TypeResolutionHelper typeResolutionHelper, ConstraintHelper constraintHelper, ValueExtractorManager valueExtractorManager, PropertyAccessorCreatorProvider propertyAccessorCreatorProvider, Class propertyHolderType) { PropertyAccessorCreator propertyAccessorCreator = propertyAccessorCreatorProvider.getPropertyAccessorCreatorFor( propertyHolderType ); PropertyAccessor propertyAccessor = propertyAccessorCreator.create( name, type ); @@ -69,16 +70,16 @@ public ConstrainedElement build(TypeResolutionHelper typeResolutionHelper, Value return new ConstrainedField( source, property, - toMetaConstraints( typeResolutionHelper, valueExtractorManager, property, constraints ), - toMetaConstraints( typeResolutionHelper, valueExtractorManager, property, typeArgumentConstraints ), + toMetaConstraints( typeResolutionHelper, constraintHelper, valueExtractorManager, property, constraints ), + toMetaConstraints( typeResolutionHelper, constraintHelper, valueExtractorManager, property, typeArgumentConstraints ), cascadingMetaDataBuilder ); } - private Set> toMetaConstraints(TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, PropertyHolderProperty property, Collection> collection) { + private Set> toMetaConstraints(TypeResolutionHelper typeResolutionHelper, ConstraintHelper constraintHelper, ValueExtractorManager valueExtractorManager, PropertyHolderProperty property, Collection> collection) { Set> builtConstraints = new HashSet<>( constraints.size() ); for ( MetaConstraintBuilder builder : constraints ) { - builtConstraints.add( builder.build( typeResolutionHelper, valueExtractorManager, property ) ); + builtConstraints.add( builder.build( typeResolutionHelper, constraintHelper, valueExtractorManager, property ) ); } return builtConstraints; } From 4e021befe26e2773377051c7209e69d76d53194a Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Thu, 19 Jul 2018 22:00:44 +0200 Subject: [PATCH 11/14] [property-holder] Introduce method for validating property holders and add a simple test --- .../internal/engine/ValidatorImpl.java | 21 ++++++++ .../ValidationContextBuilder.java | 15 ++++++ .../propertyholder/ValidatorTest.java | 48 +++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/propertyholder/ValidatorTest.java diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java index a0eb4b0298..ce1ec80468 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java @@ -246,6 +246,27 @@ public Set> validateReturnValue(T object, Method meth return validateReturnValue( object, (Executable) method, returnValue, groups ); } + public Set> validatePropertyHolder(T object, String mapping, Class... groups) { + Contracts.assertNotNull( object, MESSAGES.validatedObjectMustNotBeNull() ); + sanityCheckGroups( groups ); + + BaseBeanValidationContext validationContext = getValidationContextBuilder().forPropertyHolder( object, mapping ); + + if ( !validationContext.getRootBeanMetaData().hasConstraints() ) { + return Collections.emptySet(); + } + + ValidationOrder validationOrder = determineGroupValidationOrder( groups ); + ValueContext valueContext = ValueContext.getLocalExecutionContext( + validatorScopedContext.getParameterNameProvider(), + object, + validationContext.getRootBeanMetaData(), + PathImpl.createRootPath() + ); + + return validateInContext( validationContext, valueContext, validationOrder ); + } + private Set> validateParameters(T object, Executable executable, Object[] parameterValues, Class... groups) { sanityCheckGroups( groups ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ValidationContextBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ValidationContextBuilder.java index 045aeeff3f..425b13d884 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ValidationContextBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ValidationContextBuilder.java @@ -136,4 +136,19 @@ public ExecutableValidationContext forValidateReturnValue( executableReturnValue ); } + + public BaseBeanValidationContext forPropertyHolder(T propertyHolder, String mapping) { + @SuppressWarnings("unchecked") + Class propertyHolderClass = (Class) propertyHolder.getClass(); + return new BeanValidationContext<>( + constraintValidatorManager, + constraintValidatorFactory, + validatorScopedContext, + traversableResolver, + constraintValidatorInitializationContext, + propertyHolder, + propertyHolderClass, + constraintMetaDataManager.getPropertyHolderBeanMetaData( propertyHolderClass, mapping ) + ); + } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/propertyholder/ValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/propertyholder/ValidatorTest.java new file mode 100644 index 0000000000..d6f1615e89 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/propertyholder/ValidatorTest.java @@ -0,0 +1,48 @@ +/* + * 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.test.internal.propertyholder; + +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; +import static org.hibernate.validator.testutils.ValidatorUtil.getValidator; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.constraints.Email; +import javax.validation.constraints.Size; + +import org.hibernate.validator.internal.engine.ValidatorImpl; + +import org.testng.annotations.Test; + +/** + * @author Marko Bekhta + */ +public class ValidatorTest { + @Test + public void testSimplePropertyHolder() { + ValidatorImpl validator = (ValidatorImpl) getValidator(); + + Map address = new HashMap<>(); + address.put( "street", "str" ); + address.put( "buildingNumber", -1L ); + + Map user = new HashMap<>(); + user.put( "name", "jhon" ); + user.put( "email", "not a mail" ); + user.put( "address", address ); + + Set> constraintViolations = validator.validatePropertyHolder( user, "user" ); + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( Size.class ).withProperty( "name" ), + violationOf( Email.class ).withProperty( "email" ) + ); + } +} From 9f1b1da1f903ee7f342d63ff8f9de2b8cf46632f Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Sun, 22 Jul 2018 12:21:16 +0200 Subject: [PATCH 12/14] [property-holder] Rework how constraints are validated for default group --- .../internal/engine/ValidatorImpl.java | 82 +++++++++++-------- 1 file changed, 50 insertions(+), 32 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java index ce1ec80468..283777414a 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java @@ -453,42 +453,60 @@ private void validateConstraintsForDefaultGroup(BaseBeanValidationContext final BeanMetaData beanMetaData = valueContext.getCurrentBeanMetaData(); final Map, Class> validatedInterfaces = new HashMap<>(); - // evaluating the constraints of a bean per class in hierarchy, this is necessary to detect potential default group re-definitions - for ( Class clazz : beanMetaData.getClassHierarchy() ) { - BeanMetaData hostingBeanMetaData = constraintMetaDataManager.getBeanMetaData( clazz ); - boolean defaultGroupSequenceIsRedefined = hostingBeanMetaData.isDefaultGroupSequenceRedefined(); - - // if the current class redefined the default group sequence, this sequence has to be applied to all the class hierarchy. - if ( defaultGroupSequenceIsRedefined ) { - Iterator defaultGroupSequence = hostingBeanMetaData.getDefaultValidationSequence( valueContext.getCurrentBean() ); - Set> metaConstraints = hostingBeanMetaData.getMetaConstraints(); - - while ( defaultGroupSequence.hasNext() ) { - for ( GroupWithInheritance groupOfGroups : defaultGroupSequence.next() ) { - boolean validationSuccessful = true; - - for ( Group defaultSequenceMember : groupOfGroups ) { - validationSuccessful = validateConstraintsForSingleDefaultGroupElement( validationContext, valueContext, validatedInterfaces, clazz, - metaConstraints, defaultSequenceMember ); - } - if ( !validationSuccessful ) { - break; - } - } + boolean defaultGroupSequenceIsRedefined = beanMetaData.isDefaultGroupSequenceRedefined(); + if ( defaultGroupSequenceIsRedefined ) { + validateConstraintsForRedefinedGroupSequence( validationContext, valueContext, validatedInterfaces, beanMetaData.getBeanClass(), beanMetaData ); + validationContext.markCurrentBeanAsProcessed( valueContext ); + } + else { + validateConstraintsForSingleDefaultGroupElement( validationContext, valueContext, validatedInterfaces, beanMetaData.getBeanClass(), beanMetaData.getDirectMetaConstraints(), Group.DEFAULT_GROUP ); + List> classHierarchy = beanMetaData.getClassHierarchy(); + + validationContext.markCurrentBeanAsProcessed( valueContext ); + // we start from the second element as first one (the most specific class) was already validated ^ + + // evaluating the constraints of a bean per class in hierarchy, this is necessary to detect potential default group re-definitions + for ( int i = 1; i < classHierarchy.size(); i++ ) { + Class clazz = classHierarchy.get( i ); + + BeanMetaData hostingBeanMetaData = constraintMetaDataManager.getBeanMetaData( clazz ); + defaultGroupSequenceIsRedefined = hostingBeanMetaData.isDefaultGroupSequenceRedefined(); + + // if the current class redefined the default group sequence, this sequence has to be applied to all the class hierarchy. + if ( defaultGroupSequenceIsRedefined ) { + validateConstraintsForRedefinedGroupSequence( validationContext, valueContext, validatedInterfaces, clazz, hostingBeanMetaData ); + } + // fast path in case the default group sequence hasn't been redefined + else { + Set> metaConstraints = hostingBeanMetaData.getDirectMetaConstraints(); + validateConstraintsForSingleDefaultGroupElement( validationContext, valueContext, validatedInterfaces, clazz, metaConstraints, Group.DEFAULT_GROUP ); + } + + validationContext.markCurrentBeanAsProcessed( valueContext ); + + // all constraints in the hierarchy has been validated, stop validation. + if ( defaultGroupSequenceIsRedefined ) { + break; } } - // fast path in case the default group sequence hasn't been redefined - else { - Set> metaConstraints = hostingBeanMetaData.getDirectMetaConstraints(); - validateConstraintsForSingleDefaultGroupElement( validationContext, valueContext, validatedInterfaces, clazz, metaConstraints, - Group.DEFAULT_GROUP ); - } + } + } - validationContext.markCurrentBeanAsProcessed( valueContext ); + private void validateConstraintsForRedefinedGroupSequence(BaseBeanValidationContext validationContext, ValueContext valueContext, Map, Class> validatedInterfaces, Class clazz, BeanMetaData hostingBeanMetaData) { + Iterator defaultGroupSequence = hostingBeanMetaData.getDefaultValidationSequence( valueContext.getCurrentBean() ); + Set> metaConstraints = hostingBeanMetaData.getMetaConstraints(); - // all constraints in the hierarchy has been validated, stop validation. - if ( defaultGroupSequenceIsRedefined ) { - break; + while ( defaultGroupSequence.hasNext() ) { + for ( GroupWithInheritance groupOfGroups : defaultGroupSequence.next() ) { + boolean validationSuccessful = true; + + for ( Group defaultSequenceMember : groupOfGroups ) { + validationSuccessful = validateConstraintsForSingleDefaultGroupElement( validationContext, valueContext, validatedInterfaces, clazz, + metaConstraints, defaultSequenceMember ); + } + if ( !validationSuccessful ) { + break; + } } } } From 1807098503f245896d36193c240d0f3a23f3e986 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Sun, 22 Jul 2018 22:53:03 +0200 Subject: [PATCH 13/14] [property-holder] Rewrite how cascading constraints are created --- ...dableConstraintMappingContextImplBase.java | 26 +- ...erElementConstraintMappingContextImpl.java | 21 +- ...xecutableConstraintMappingContextImpl.java | 2 +- .../ValueExtractorResolver.java | 2 +- .../AbstractPropertyCascadable.java | 1 + .../aggregated/CascadingMetaData.java | 1 - .../aggregated/ExecutableMetaData.java | 1 + .../metadata/aggregated/FieldCascadable.java | 1 + .../metadata/aggregated/GetterCascadable.java | 1 + .../aggregated/ParameterMetaData.java | 1 + .../cascading/CascadingMetaDataBuilder.java | 56 ++++ .../cascading/ContainerCascadingMetaData.java | 28 +- ...tainerPropertyHolderCascadingMetaData.java | 35 +-- .../NonCascadingMetaDataBuilder.java | 63 +++++ .../NonContainerCascadingMetaData.java | 30 +-- ...tainerPropertyHolderCascadingMetaData.java | 16 +- ...PotentiallyContainerCascadingMetaData.java | 13 +- ...tainerPropertyHolderCascadingMetaData.java | 59 ----- ...ropertyHolderCascadingMetaDataBuilder.java | 148 +++++++++++ .../SimpleBeanCascadingMetaDataBuilder.java} | 249 ++++++++++-------- .../internal/metadata/facets/Cascadable.java | 2 +- .../provider/AnnotationMetaDataProvider.java | 5 +- .../DummyPropertyHolderMetaDataProvider.java | 69 +++-- .../raw/AbstractConstrainedElement.java | 2 +- .../metadata/raw/ConstrainedElement.java | 2 +- .../metadata/raw/ConstrainedExecutable.java | 2 +- .../metadata/raw/ConstrainedField.java | 2 +- .../metadata/raw/ConstrainedParameter.java | 2 +- .../metadata/raw/ConstrainedType.java | 2 +- ...nstrainedPropertyHolderElementBuilder.java | 38 +++ .../ConstrainedPropertyHolderElement.java | 2 +- ...nstrainedPropertyHolderElementBuilder.java | 34 +-- ...nstrainedPropertyHolderElementBuilder.java | 71 +++++ ...AbstractConstrainedElementStaxBuilder.java | 2 +- .../ConstrainedConstructorStaxBuilder.java | 2 +- .../mapping/ConstrainedMethodStaxBuilder.java | 2 +- ...tainerElementTypeConfigurationBuilder.java | 2 +- .../ContainerElementTypeStaxBuilder.java | 5 +- .../xml/mapping/ReturnValueStaxBuilder.java | 2 +- .../propertyholder/ValidatorTest.java | 56 +++- 40 files changed, 714 insertions(+), 344 deletions(-) create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/CascadingMetaDataBuilder.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/NonCascadingMetaDataBuilder.java delete mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/PotentiallyContainerPropertyHolderCascadingMetaData.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/PropertyHolderCascadingMetaDataBuilder.java rename engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/{CascadingMetaDataBuilder.java => cascading/SimpleBeanCascadingMetaDataBuilder.java} (67%) create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/CascadingConstrainedPropertyHolderElementBuilder.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/SimpleConstrainedPropertyHolderElementBuilder.java diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/CascadableConstraintMappingContextImplBase.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/CascadableConstraintMappingContextImplBase.java index a8f40b8944..9838aed7b6 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/CascadableConstraintMappingContextImplBase.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/CascadableConstraintMappingContextImplBase.java @@ -11,11 +11,11 @@ import java.lang.invoke.MethodHandles; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -24,7 +24,7 @@ import org.hibernate.validator.cfg.context.ContainerElementTarget; import org.hibernate.validator.cfg.context.GroupConversionTargetContext; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.location.ConstraintLocation; @@ -160,19 +160,15 @@ protected Set> getTypeArgumentConstraints(ConstraintHelper con } protected CascadingMetaDataBuilder getCascadingMetaDataBuilder() { - Map, CascadingMetaDataBuilder> typeParametersCascadingMetaData = containerElementContexts.values().stream() - .filter( c -> c.getContainerElementCascadingMetaDataBuilder() != null ) - .collect( Collectors.toMap( c -> c.getContainerElementCascadingMetaDataBuilder().getTypeParameter(), - c -> c.getContainerElementCascadingMetaDataBuilder() ) ); - - for ( ContainerElementConstraintMappingContextImpl typeArgumentContext : containerElementContexts.values() ) { - CascadingMetaDataBuilder cascadingMetaDataBuilder = typeArgumentContext.getContainerElementCascadingMetaDataBuilder(); - if ( cascadingMetaDataBuilder != null ) { - typeParametersCascadingMetaData.put( cascadingMetaDataBuilder.getTypeParameter(), cascadingMetaDataBuilder ); - } - } - - return CascadingMetaDataBuilder.annotatedObject( configuredType, isCascading, typeParametersCascadingMetaData, groupConversions ); + return CascadingMetaDataBuilder.annotatedObject( + configuredType, + isCascading, + containerElementContexts.values().stream() + .map( ContainerElementConstraintMappingContextImpl::getContainerElementCascadingMetaDataBuilder ) + .filter( Objects::nonNull ) + .collect( Collectors.toList() ), + groupConversions + ); } private static class ContainerElementPathKey { diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ContainerElementConstraintMappingContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ContainerElementConstraintMappingContextImpl.java index 7a00c66cd4..4bb4654157 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ContainerElementConstraintMappingContextImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ContainerElementConstraintMappingContextImpl.java @@ -17,7 +17,6 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -33,7 +32,7 @@ import org.hibernate.validator.cfg.context.ReturnValueTarget; import org.hibernate.validator.internal.engine.valueextraction.ArrayElement; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.core.MetaConstraints; @@ -219,15 +218,15 @@ protected ConstraintType getConstraintType() { } CascadingMetaDataBuilder getContainerElementCascadingMetaDataBuilder() { - return new CascadingMetaDataBuilder( - parentLocation.getTypeForValidatorResolution(), - typeParameter, - isCascading, - nestedContainerElementContexts.values() - .stream() - .map( ContainerElementConstraintMappingContextImpl::getContainerElementCascadingMetaDataBuilder ) - .collect( Collectors.toMap( CascadingMetaDataBuilder::getTypeParameter, Function.identity() ) ), - groupConversions + return CascadingMetaDataBuilder.typeArgument( + parentLocation.getTypeForValidatorResolution(), + typeParameter, + isCascading, + nestedContainerElementContexts.values() + .stream() + .map( ContainerElementConstraintMappingContextImpl::getContainerElementCascadingMetaDataBuilder ) + .collect( Collectors.toList() ), + groupConversions ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ExecutableConstraintMappingContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ExecutableConstraintMappingContextImpl.java index 503c284637..d451cae961 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ExecutableConstraintMappingContextImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ExecutableConstraintMappingContextImpl.java @@ -16,7 +16,7 @@ import org.hibernate.validator.cfg.context.ParameterConstraintMappingContext; import org.hibernate.validator.cfg.context.ReturnValueConstraintMappingContext; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ValueExtractorResolver.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ValueExtractorResolver.java index f294c27280..327506680d 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ValueExtractorResolver.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ValueExtractorResolver.java @@ -22,7 +22,7 @@ import javax.validation.ConstraintDeclarationException; import javax.validation.valueextraction.ValueExtractor; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.aggregated.cascading.ContainerCascadingMetaData; import org.hibernate.validator.internal.metadata.aggregated.cascading.PotentiallyContainerCascadingMetaData; import org.hibernate.validator.internal.util.CollectionHelper; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/AbstractPropertyCascadable.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/AbstractPropertyCascadable.java index 1cb08aea79..7dee6640be 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/AbstractPropertyCascadable.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/AbstractPropertyCascadable.java @@ -10,6 +10,7 @@ import org.hibernate.validator.internal.engine.path.PathImpl; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.facets.Cascadable; import org.hibernate.validator.internal.properties.Field; import org.hibernate.validator.internal.properties.Getter; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaData.java index a23e0df354..c40a99dc08 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaData.java @@ -16,7 +16,6 @@ import org.hibernate.validator.internal.engine.valueextraction.ArrayElement; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.aggregated.cascading.ContainerCascadingMetaData; -import org.hibernate.validator.internal.metadata.aggregated.cascading.NonContainerCascadingMetaData; import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; /** diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java index 6c8ab7705f..a2d414d76d 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java @@ -23,6 +23,7 @@ import org.hibernate.validator.internal.engine.MethodValidationConfiguration; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.aggregated.rule.MethodConfigurationRule; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.MetaConstraint; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/FieldCascadable.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/FieldCascadable.java index c8ebe21304..6dd1f2a012 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/FieldCascadable.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/FieldCascadable.java @@ -7,6 +7,7 @@ package org.hibernate.validator.internal.metadata.aggregated; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.facets.Cascadable; import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; import org.hibernate.validator.internal.properties.Field; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/GetterCascadable.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/GetterCascadable.java index ab5da7d89f..49c64f0a9e 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/GetterCascadable.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/GetterCascadable.java @@ -7,6 +7,7 @@ package org.hibernate.validator.internal.metadata.aggregated; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.facets.Cascadable; import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; import org.hibernate.validator.internal.properties.Getter; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ParameterMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ParameterMetaData.java index 839d12f812..b9335c047b 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ParameterMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ParameterMetaData.java @@ -15,6 +15,7 @@ import org.hibernate.validator.internal.engine.path.PathImpl; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.descriptor.ParameterDescriptorImpl; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/CascadingMetaDataBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/CascadingMetaDataBuilder.java new file mode 100644 index 0000000000..da6e3cb5d7 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/CascadingMetaDataBuilder.java @@ -0,0 +1,56 @@ +/* + * 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.aggregated.cascading; + +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.List; +import java.util.Map; + +import org.hibernate.validator.internal.engine.valueextraction.AnnotatedObject; +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaData; +import org.hibernate.validator.internal.properties.Constrainable; + +/** + * @author Marko Bekhta + */ +public interface CascadingMetaDataBuilder { + + static CascadingMetaDataBuilder nonCascading() { + return NonCascadingMetaDataBuilder.INSTANCE; + } + + static CascadingMetaDataBuilder annotatedObject(Type cascadableType, boolean cascading, List containerElementTypesCascadingMetaData, Map, Class> groupConversions) { + return new SimpleBeanCascadingMetaDataBuilder( cascadableType, AnnotatedObject.INSTANCE, cascading, containerElementTypesCascadingMetaData, groupConversions ); + } + + static CascadingMetaDataBuilder annotatedObject(Type cascadableType, boolean cascading, Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, Map, Class> groupConversions) { + return new SimpleBeanCascadingMetaDataBuilder( cascadableType, AnnotatedObject.INSTANCE, cascading, containerElementTypesCascadingMetaData, groupConversions ); + } + + static CascadingMetaDataBuilder typeArgument(Type cascadableType, TypeVariable typeParameter, boolean cascading, List containerElementTypesCascadingMetaData, Map, Class> groupConversions) { + return new SimpleBeanCascadingMetaDataBuilder( cascadableType, typeParameter, cascading, containerElementTypesCascadingMetaData, groupConversions ); + } + + boolean isCascading(); + + Map, Class> getGroupConversions(); + + boolean hasContainerElementsMarkedForCascading(); + + boolean isMarkedForCascadingOnAnnotatedObjectOrContainerElements(); + + boolean hasGroupConversionsOnAnnotatedObjectOrContainerElements(); + + Map, CascadingMetaDataBuilder> getContainerElementTypesCascadingMetaData(); + + CascadingMetaDataBuilder merge(CascadingMetaDataBuilder otherCascadingTypeParameter); + + CascadingMetaData build(ValueExtractorManager valueExtractorManager, Constrainable context); + +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/ContainerCascadingMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/ContainerCascadingMetaData.java index 287d5ef6bf..5635182e2f 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/ContainerCascadingMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/ContainerCascadingMetaData.java @@ -12,7 +12,6 @@ import java.util.Collections; import java.util.List; import java.util.Set; -import java.util.stream.Collectors; import javax.validation.metadata.GroupConversionDescriptor; @@ -20,7 +19,6 @@ import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaData; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.internal.util.StringHelper; import org.hibernate.validator.internal.util.TypeVariables; @@ -92,31 +90,7 @@ public class ContainerCascadingMetaData implements CascadingMetaData { */ private final Set valueExtractorCandidates; - public static ContainerCascadingMetaData of(ValueExtractorManager valueExtractorManager, CascadingMetaDataBuilder cascadingMetaDataBuilder, - Object context) { - if ( cascadingMetaDataBuilder.getMappingName() != null ) { - return ContainerPropertyHolderCascadingMetaData.of( valueExtractorManager, cascadingMetaDataBuilder, context ); - } - return new ContainerCascadingMetaData( valueExtractorManager, cascadingMetaDataBuilder ); - } - - protected ContainerCascadingMetaData(ValueExtractorManager valueExtractorManager, CascadingMetaDataBuilder cascadingMetaDataBuilder) { - this( - valueExtractorManager, - cascadingMetaDataBuilder.getEnclosingType(), - cascadingMetaDataBuilder.getTypeParameter(), - cascadingMetaDataBuilder.getDeclaredContainerClass(), - cascadingMetaDataBuilder.getDeclaredTypeParameter(), - cascadingMetaDataBuilder.getContainerElementTypesCascadingMetaData().entrySet().stream() - .map( entry -> new ContainerCascadingMetaData( valueExtractorManager, entry.getValue() ) ) - .collect( Collectors.collectingAndThen( Collectors.toList(), CollectionHelper::toImmutableList ) ), - cascadingMetaDataBuilder.isCascading(), - GroupConversionHelper.of( cascadingMetaDataBuilder.getGroupConversions() ), - cascadingMetaDataBuilder.isMarkedForCascadingOnAnnotatedObjectOrContainerElements() - ); - } - - private ContainerCascadingMetaData(ValueExtractorManager valueExtractorManager, Type enclosingType, TypeVariable typeParameter, + ContainerCascadingMetaData(ValueExtractorManager valueExtractorManager, Type enclosingType, TypeVariable typeParameter, Class declaredContainerClass, TypeVariable declaredTypeParameter, List containerElementTypesCascadingMetaData, boolean cascading, GroupConversionHelper groupConversionHelper, boolean markedForCascadingOnContainerElements) { this.enclosingType = enclosingType; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/ContainerPropertyHolderCascadingMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/ContainerPropertyHolderCascadingMetaData.java index 9665d40f9e..fb78e56417 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/ContainerPropertyHolderCascadingMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/ContainerPropertyHolderCascadingMetaData.java @@ -9,14 +9,10 @@ import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.List; -import java.util.Set; -import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; -import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.StringHelper; /** @@ -31,27 +27,16 @@ public class ContainerPropertyHolderCascadingMetaData extends ContainerCascading */ private final String mapping; - public static ContainerPropertyHolderCascadingMetaData of(ValueExtractorManager valueExtractorManager, CascadingMetaDataBuilder cascadingMetaDataBuilder, - Object context) { - Contracts.assertNotEmpty( cascadingMetaDataBuilder.getMappingName(), "Property holder mapping cannot be an empty string." ); - - return new ContainerPropertyHolderCascadingMetaData( valueExtractorManager, cascadingMetaDataBuilder ); - } - - private ContainerPropertyHolderCascadingMetaData(ValueExtractorManager valueExtractorManager, CascadingMetaDataBuilder cascadingMetaDataBuilder) { - super( valueExtractorManager, cascadingMetaDataBuilder ); - this.mapping = cascadingMetaDataBuilder.getMappingName(); - } - - ContainerPropertyHolderCascadingMetaData(String mapping, Type enclosingType, List containerElementTypesCascadingMetaData, - GroupConversionHelper groupConversionHelper, Set valueExtractorCandidates) { - super( enclosingType, containerElementTypesCascadingMetaData, groupConversionHelper, valueExtractorCandidates ); - this.mapping = mapping; - } - - ContainerPropertyHolderCascadingMetaData(String mapping, Type enclosingType, TypeVariable typeParameter, Class declaredContainerClass, TypeVariable declaredTypeParameter, - GroupConversionHelper groupConversionHelper) { - super( enclosingType, typeParameter, declaredContainerClass, declaredTypeParameter, groupConversionHelper ); + protected ContainerPropertyHolderCascadingMetaData(ValueExtractorManager valueExtractorManager, String mapping, Type enclosingType, TypeVariable typeParameter, + Class declaredContainerClass, TypeVariable declaredTypeParameter, List containerElementTypesCascadingMetaData, + boolean cascading, GroupConversionHelper groupConversionHelper, boolean markedForCascadingOnContainerElements) { + super( valueExtractorManager, + enclosingType, + typeParameter, + declaredContainerClass, + declaredTypeParameter, + containerElementTypesCascadingMetaData, + cascading, groupConversionHelper, markedForCascadingOnContainerElements ); this.mapping = mapping; } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/NonCascadingMetaDataBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/NonCascadingMetaDataBuilder.java new file mode 100644 index 0000000000..6251240124 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/NonCascadingMetaDataBuilder.java @@ -0,0 +1,63 @@ +/* + * 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.aggregated.cascading; + +import java.lang.reflect.TypeVariable; +import java.util.Collections; +import java.util.Map; + +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaData; +import org.hibernate.validator.internal.properties.Constrainable; + +/** + * @author Marko Bekhta + */ +public final class NonCascadingMetaDataBuilder implements CascadingMetaDataBuilder { + + public static final CascadingMetaDataBuilder INSTANCE = new NonCascadingMetaDataBuilder(); + + @Override + public boolean isCascading() { + return false; + } + + @Override + public Map, Class> getGroupConversions() { + return Collections.emptyMap(); + } + + @Override + public boolean hasContainerElementsMarkedForCascading() { + return false; + } + + @Override + public boolean isMarkedForCascadingOnAnnotatedObjectOrContainerElements() { + return false; + } + + @Override + public boolean hasGroupConversionsOnAnnotatedObjectOrContainerElements() { + return false; + } + + @Override + public Map, CascadingMetaDataBuilder> getContainerElementTypesCascadingMetaData() { + return Collections.emptyMap(); + } + + @Override + public CascadingMetaDataBuilder merge(CascadingMetaDataBuilder otherCascadingTypeParameter) { + return otherCascadingTypeParameter; + } + + @Override + public CascadingMetaData build(ValueExtractorManager valueExtractorManager, Constrainable context) { + return NonContainerCascadingMetaData.of( false, Collections.emptyMap() ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/NonContainerCascadingMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/NonContainerCascadingMetaData.java index 81efcc9b47..1a2ef03ef6 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/NonContainerCascadingMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/NonContainerCascadingMetaData.java @@ -8,6 +8,7 @@ import java.lang.invoke.MethodHandles; import java.lang.reflect.TypeVariable; +import java.util.Map; import java.util.Set; import javax.validation.metadata.GroupConversionDescriptor; @@ -15,7 +16,6 @@ import org.hibernate.validator.internal.engine.valueextraction.AnnotatedObject; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaData; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; @@ -28,14 +28,16 @@ * @author Guillaume Smet * @author Marko Bekhta */ -public class NonContainerCascadingMetaData implements CascadingMetaData { +class NonContainerCascadingMetaData implements CascadingMetaData { private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - private static final NonContainerCascadingMetaData NON_CASCADING = new NonContainerCascadingMetaData( false, + private static final NonContainerCascadingMetaData NON_CASCADING = new NonContainerCascadingMetaData( + false, GroupConversionHelper.EMPTY ); - private static final NonContainerCascadingMetaData CASCADING_WITHOUT_GROUP_CONVERSIONS = new NonContainerCascadingMetaData( true, + private static final NonContainerCascadingMetaData CASCADING_WITHOUT_GROUP_CONVERSIONS = new NonContainerCascadingMetaData( + true, GroupConversionHelper.EMPTY ); /** @@ -48,28 +50,20 @@ public class NonContainerCascadingMetaData implements CascadingMetaData { */ private GroupConversionHelper groupConversionHelper; - public static NonContainerCascadingMetaData of(CascadingMetaDataBuilder cascadingMetaDataBuilder, Object context) { - if ( cascadingMetaDataBuilder.getMappingName() != null ) { - return NonContainerPropertyHolderCascadingMetaData.of( cascadingMetaDataBuilder, context ); - } - else if ( !cascadingMetaDataBuilder.isCascading() ) { + public static NonContainerCascadingMetaData of(boolean cascading, Map, Class> groupConversions) { + if ( !cascading ) { return NON_CASCADING; } - else if ( cascadingMetaDataBuilder.getGroupConversions().isEmpty() ) { + else if ( groupConversions.isEmpty() ) { return CASCADING_WITHOUT_GROUP_CONVERSIONS; } else { - return new NonContainerCascadingMetaData( cascadingMetaDataBuilder ); + return new NonContainerCascadingMetaData( + cascading, + GroupConversionHelper.of( groupConversions ) ); } } - protected NonContainerCascadingMetaData(CascadingMetaDataBuilder cascadingMetaDataBuilder) { - this( - cascadingMetaDataBuilder.isCascading(), - GroupConversionHelper.of( cascadingMetaDataBuilder.getGroupConversions() ) - ); - } - protected NonContainerCascadingMetaData(boolean cascading, GroupConversionHelper groupConversionHelper) { this.cascading = cascading; this.groupConversionHelper = groupConversionHelper; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/NonContainerPropertyHolderCascadingMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/NonContainerPropertyHolderCascadingMetaData.java index 5a70d657f0..8a3fb044ae 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/NonContainerPropertyHolderCascadingMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/NonContainerPropertyHolderCascadingMetaData.java @@ -6,8 +6,9 @@ */ package org.hibernate.validator.internal.metadata.aggregated.cascading; +import java.util.Map; + import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.util.Contracts; @@ -23,16 +24,15 @@ public class NonContainerPropertyHolderCascadingMetaData extends NonContainerCas */ private final String mapping; - public static NonContainerPropertyHolderCascadingMetaData of(CascadingMetaDataBuilder cascadingMetaDataBuilder, Object context) { - Contracts.assertTrue( cascadingMetaDataBuilder.isCascading(), "Property holder cascading metadata should always be cascading." ); - Contracts.assertNotEmpty( cascadingMetaDataBuilder.getMappingName(), "Property holder mapping cannot be an empty string." ); + public static NonContainerPropertyHolderCascadingMetaData of(String mapping, Map, Class> groupConversions) { + Contracts.assertNotEmpty( mapping, "Property holder mapping cannot be an empty string." ); - return new NonContainerPropertyHolderCascadingMetaData( cascadingMetaDataBuilder ); + return new NonContainerPropertyHolderCascadingMetaData( mapping, groupConversions ); } - private NonContainerPropertyHolderCascadingMetaData(CascadingMetaDataBuilder cascadingMetaDataBuilder) { - super( cascadingMetaDataBuilder ); - this.mapping = cascadingMetaDataBuilder.getMappingName(); + private NonContainerPropertyHolderCascadingMetaData(String mapping, Map, Class> groupConversions) { + super( true, GroupConversionHelper.of( groupConversions ) ); + this.mapping = mapping; } @Override diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/PotentiallyContainerCascadingMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/PotentiallyContainerCascadingMetaData.java index 0437b734a9..1ac1f65e38 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/PotentiallyContainerCascadingMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/PotentiallyContainerCascadingMetaData.java @@ -9,6 +9,7 @@ import java.lang.invoke.MethodHandles; import java.lang.reflect.TypeVariable; import java.util.Collections; +import java.util.Map; import java.util.Set; import javax.validation.metadata.GroupConversionDescriptor; @@ -17,7 +18,6 @@ import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaData; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; @@ -39,15 +39,12 @@ public class PotentiallyContainerCascadingMetaData implements CascadingMetaData private final Set potentialValueExtractorDescriptors; - public static PotentiallyContainerCascadingMetaData of(CascadingMetaDataBuilder cascadingMetaDataBuilder, Set potentialValueExtractorDescriptors, Object context) { - if ( cascadingMetaDataBuilder.getMappingName() != null ) { - return PotentiallyContainerPropertyHolderCascadingMetaData.of( cascadingMetaDataBuilder, potentialValueExtractorDescriptors, context ); - } - return new PotentiallyContainerCascadingMetaData( cascadingMetaDataBuilder, potentialValueExtractorDescriptors ); + public static PotentiallyContainerCascadingMetaData of(Map, Class> groupConversions, Set potentialValueExtractorDescriptors) { + return new PotentiallyContainerCascadingMetaData( groupConversions, potentialValueExtractorDescriptors ); } - protected PotentiallyContainerCascadingMetaData(CascadingMetaDataBuilder cascadingMetaDataBuilder, Set potentialValueExtractorDescriptors) { - this( potentialValueExtractorDescriptors, GroupConversionHelper.of( cascadingMetaDataBuilder.getGroupConversions() ) ); + protected PotentiallyContainerCascadingMetaData(Map, Class> groupConversions, Set potentialValueExtractorDescriptors) { + this( potentialValueExtractorDescriptors, GroupConversionHelper.of( groupConversions ) ); } private PotentiallyContainerCascadingMetaData(Set potentialValueExtractorDescriptors, GroupConversionHelper groupConversionHelper) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/PotentiallyContainerPropertyHolderCascadingMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/PotentiallyContainerPropertyHolderCascadingMetaData.java deleted file mode 100644 index 196d0720e9..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/PotentiallyContainerPropertyHolderCascadingMetaData.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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.aggregated.cascading; - -import java.util.Set; - -import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; -import org.hibernate.validator.internal.util.Contracts; - -/** - * Extended view of potentially container cascading metadta for property holders that in addition stores mapping name. - * - * @author Marko Bekhta - */ -public class PotentiallyContainerPropertyHolderCascadingMetaData extends PotentiallyContainerCascadingMetaData { - - /** - * Name of the constraint mappings to be applied. - */ - private final String mapping; - - public static PotentiallyContainerPropertyHolderCascadingMetaData of(CascadingMetaDataBuilder cascadingMetaDataBuilder, Set potentialValueExtractorDescriptors, Object context) { - Contracts.assertNotEmpty( cascadingMetaDataBuilder.getMappingName(), "Property holder mapping cannot be an empty string." ); - - return new PotentiallyContainerPropertyHolderCascadingMetaData( cascadingMetaDataBuilder, potentialValueExtractorDescriptors ); - } - - private PotentiallyContainerPropertyHolderCascadingMetaData(CascadingMetaDataBuilder cascadingMetaDataBuilder, Set potentialValueExtractorDescriptors) { - super( cascadingMetaDataBuilder, potentialValueExtractorDescriptors ); - this.mapping = cascadingMetaDataBuilder.getMappingName(); - } - - @Override - protected ContainerCascadingMetaData createInnerMetadata(ValueExtractorDescriptor compliantValueExtractor, GroupConversionHelper groupConversionHelper) { - return new ContainerPropertyHolderCascadingMetaData( - mapping, - compliantValueExtractor.getContainerType(), - compliantValueExtractor.getExtractedTypeParameter(), - compliantValueExtractor.getContainerType(), - compliantValueExtractor.getExtractedTypeParameter(), - groupConversionHelper.isEmpty() ? GroupConversionHelper.EMPTY : groupConversionHelper - ); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append( getClass().getSimpleName() ); - sb.append( " [" ); - sb.append( "mapping=" ).append( mapping ).append( ", " ); - sb.append( "]" ); - return sb.toString(); - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/PropertyHolderCascadingMetaDataBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/PropertyHolderCascadingMetaDataBuilder.java new file mode 100644 index 0000000000..cbfe5e2ace --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/PropertyHolderCascadingMetaDataBuilder.java @@ -0,0 +1,148 @@ +/* + * 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.aggregated.cascading; + +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.hibernate.validator.internal.engine.valueextraction.AnnotatedObject; +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor; +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.util.CollectionHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; + +/** + * @author Marko Bekhta + */ +public class PropertyHolderCascadingMetaDataBuilder extends SimpleBeanCascadingMetaDataBuilder { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private final String mapping; + + public PropertyHolderCascadingMetaDataBuilder( + Type enclosingType, + String mapping, + TypeVariable typeParameter, + boolean cascading, + Map, Class> groupConversions) { + super( enclosingType, typeParameter, cascading, Collections.emptyList(), groupConversions ); + this.mapping = mapping; + } + + public PropertyHolderCascadingMetaDataBuilder( + Type enclosingType, + String mapping, + TypeVariable typeParameter, + boolean cascading, + Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, + Map, Class> groupConversions) { + super( enclosingType, typeParameter, cascading, containerElementTypesCascadingMetaData, groupConversions ); + this.mapping = mapping; + } + + public static PropertyHolderCascadingMetaDataBuilder simplePropertyHolder( + String mappingName, + boolean cascading, + Map, Class> groupConversions) { + return new PropertyHolderCascadingMetaDataBuilder( null, mappingName, AnnotatedObject.INSTANCE, cascading, groupConversions ); + } + + public static PropertyHolderCascadingMetaDataBuilder propertyHolderContainer( + boolean cascading, + Class enclosingType, + Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, + Map, Class> groupConversions) { + return new PropertyHolderCascadingMetaDataBuilder( + enclosingType, + null, + AnnotatedObject.INSTANCE, + cascading, + containerElementTypesCascadingMetaData, + groupConversions ); + } + + public static PropertyHolderCascadingMetaDataBuilder propertyHolderContainer( + String mapping, + boolean cascading, + Class declaredContainerClass, + TypeVariable declaredTypeVariable, + Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, + Map, Class> groupConversions) { + return new PropertyHolderCascadingMetaDataBuilder( + declaredContainerClass, + mapping, + declaredTypeVariable, + cascading, + containerElementTypesCascadingMetaData, + groupConversions + ); + } + + @Override + public CascadingMetaDataBuilder merge(CascadingMetaDataBuilder otherCascadingTypeParameter) { + if ( otherCascadingTypeParameter == NonCascadingMetaDataBuilder.INSTANCE ) { + return this; + } + + boolean cascading = this.cascading || otherCascadingTypeParameter.isCascading(); + + Map, Class> groupConversions = mergeGroupConversion( this.groupConversions, otherCascadingTypeParameter.getGroupConversions() ); + + Map, CascadingMetaDataBuilder> nestedCascadingTypeParameterMap = Stream + .concat( + this.containerElementTypesCascadingMetaData.entrySet().stream(), + otherCascadingTypeParameter.getContainerElementTypesCascadingMetaData().entrySet().stream() ) + .collect( + Collectors.toMap( entry -> entry.getKey(), entry -> entry.getValue(), (value1, value2) -> value1.merge( value2 ) ) ); + + return new SimpleBeanCascadingMetaDataBuilder( this.enclosingType, this.typeParameter, cascading, nestedCascadingTypeParameterMap, groupConversions ); + } + + @Override + protected Set findContainerDetectionValueExtractorCandidates(ValueExtractorManager valueExtractorManager) { + return mapping != null ? Collections.emptySet() : super.findContainerDetectionValueExtractorCandidates( valueExtractorManager ); + } + + @Override + protected Set findPotentialValueExtractorCandidates(ValueExtractorManager valueExtractorManager) { + return Collections.emptySet(); + } + + @Override + protected NonContainerCascadingMetaData nonContainerCascadingMetaData() { + return mapping != null ? NonContainerPropertyHolderCascadingMetaData.of( mapping, groupConversions ) : super.nonContainerCascadingMetaData(); + } + + @Override + protected ContainerCascadingMetaData toContainerCascadingMetaData(ValueExtractorManager valueExtractorManager) { + if ( mapping == null ) { + return super.toContainerCascadingMetaData( valueExtractorManager ); + } + return new ContainerPropertyHolderCascadingMetaData( + valueExtractorManager, + mapping, + enclosingType, + typeParameter, + declaredContainerClass, + declaredTypeParameter, + containerElementTypesCascadingMetaData.entrySet().stream() + .map( entry -> toContainerCascadingMetaData( entry.getValue(), valueExtractorManager ) ) + .collect( Collectors.collectingAndThen( Collectors.toList(), CollectionHelper::toImmutableList ) ), + cascading, + GroupConversionHelper.of( groupConversions ), + isMarkedForCascadingOnAnnotatedObjectOrContainerElements() + ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaDataBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/SimpleBeanCascadingMetaDataBuilder.java similarity index 67% rename from engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaDataBuilder.java rename to engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/SimpleBeanCascadingMetaDataBuilder.java index ef7df1794e..e31be8eb8e 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/CascadingMetaDataBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/cascading/SimpleBeanCascadingMetaDataBuilder.java @@ -4,13 +4,14 @@ * License: Apache License, Version 2.0 * See the license.txt file in the root directory or . */ -package org.hibernate.validator.internal.metadata.aggregated; +package org.hibernate.validator.internal.metadata.aggregated.cascading; import java.lang.invoke.MethodHandles; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; @@ -20,15 +21,14 @@ import javax.validation.GroupSequence; -import org.hibernate.validator.internal.engine.valueextraction.AnnotatedObject; import org.hibernate.validator.internal.engine.valueextraction.ArrayElement; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorHelper; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.aggregated.cascading.ContainerCascadingMetaData; -import org.hibernate.validator.internal.metadata.aggregated.cascading.NonContainerCascadingMetaData; -import org.hibernate.validator.internal.metadata.aggregated.cascading.PotentiallyContainerCascadingMetaData; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaData; +import org.hibernate.validator.internal.properties.Constrainable; import org.hibernate.validator.internal.util.CollectionHelper; +import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.ReflectionHelper; import org.hibernate.validator.internal.util.StringHelper; import org.hibernate.validator.internal.util.TypeVariableBindings; @@ -42,52 +42,48 @@ * gets. * * @author Guillaume Smet + * @author Marko Bekhta */ -public class CascadingMetaDataBuilder { +public class SimpleBeanCascadingMetaDataBuilder implements CascadingMetaDataBuilder { private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - private static final CascadingMetaDataBuilder NON_CASCADING = - new CascadingMetaDataBuilder( null, null, null, null, null, false, Collections.emptyMap(), Collections.emptyMap() ); - - private final String mapping; - /** * The enclosing type that defines this type parameter. */ - private final Type enclosingType; + protected final Type enclosingType; /** * The type parameter. */ - private final TypeVariable typeParameter; + protected final TypeVariable typeParameter; /** * The declared container class: it is the one used in the node of the property path. */ - private final Class declaredContainerClass; + protected final Class declaredContainerClass; /** * The declared type parameter: it is the one used in the node of the property path. */ - private final TypeVariable declaredTypeParameter; + protected final TypeVariable declaredTypeParameter; /** * Possibly the cascading type parameters corresponding to this type parameter if it is a parameterized type. */ @Immutable - private final Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData; + protected final Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData; /** * If this type parameter is marked for cascading. */ - private final boolean cascading; + protected final boolean cascading; /** * Group conversions defined for this type parameter. */ @Immutable - private final Map, Class> groupConversions; + protected final Map, Class> groupConversions; /** * Whether any container element (it can be nested) is marked for cascaded validation. @@ -99,25 +95,30 @@ public class CascadingMetaDataBuilder { */ private final boolean hasGroupConversionsOnAnnotatedObjectOrContainerElements; - public CascadingMetaDataBuilder(Type enclosingType, TypeVariable typeParameter, boolean cascading, - Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, Map, Class> groupConversions) { - this( enclosingType, null, typeParameter, - TypeVariables.getContainerClass( typeParameter ), TypeVariables.getActualTypeParameter( typeParameter ), - cascading, containerElementTypesCascadingMetaData, groupConversions ); + public SimpleBeanCascadingMetaDataBuilder(Type enclosingType, TypeVariable typeParameter, boolean cascading, + List containerElementTypesCascadingMetaData, Map, Class> groupConversions) { + this( + enclosingType, + typeParameter, + TypeVariables.getContainerClass( typeParameter ), + TypeVariables.getActualTypeParameter( typeParameter ), + cascading, + convertContainerElementTypesCascadingMetaData( containerElementTypesCascadingMetaData ), + groupConversions + ); } - private CascadingMetaDataBuilder(String mapping, TypeVariable typeParameter, boolean cascading, + public SimpleBeanCascadingMetaDataBuilder(Type enclosingType, TypeVariable typeParameter, boolean cascading, Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, Map, Class> groupConversions) { - this( null, mapping, typeParameter, + this( enclosingType, typeParameter, TypeVariables.getContainerClass( typeParameter ), TypeVariables.getActualTypeParameter( typeParameter ), cascading, containerElementTypesCascadingMetaData, groupConversions ); } - private CascadingMetaDataBuilder(Type enclosingType, String mapping, TypeVariable typeParameter, Class declaredContainerClass, TypeVariable declaredTypeParameter, + private SimpleBeanCascadingMetaDataBuilder(Type enclosingType, TypeVariable typeParameter, Class declaredContainerClass, TypeVariable declaredTypeParameter, boolean cascading, Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, Map, Class> groupConversions) { this.enclosingType = enclosingType; - this.mapping = mapping; this.typeParameter = typeParameter; this.declaredContainerClass = declaredContainerClass; this.declaredTypeParameter = declaredTypeParameter; @@ -125,96 +126,68 @@ private CascadingMetaDataBuilder(Type enclosingType, String mapping, TypeVariabl this.groupConversions = CollectionHelper.toImmutableMap( groupConversions ); this.containerElementTypesCascadingMetaData = CollectionHelper.toImmutableMap( containerElementTypesCascadingMetaData ); - boolean tmpHasContainerElementsMarkedForCascading = false; + boolean tmpHasGroupConversionsOnAnnotatedObjectOrContainerElements = !groupConversions.isEmpty(); for ( CascadingMetaDataBuilder nestedCascadingTypeParameter : containerElementTypesCascadingMetaData.values() ) { - tmpHasContainerElementsMarkedForCascading = tmpHasContainerElementsMarkedForCascading - || nestedCascadingTypeParameter.cascading || nestedCascadingTypeParameter.hasContainerElementsMarkedForCascading; tmpHasGroupConversionsOnAnnotatedObjectOrContainerElements = tmpHasGroupConversionsOnAnnotatedObjectOrContainerElements - || nestedCascadingTypeParameter.hasGroupConversionsOnAnnotatedObjectOrContainerElements; + || nestedCascadingTypeParameter.hasGroupConversionsOnAnnotatedObjectOrContainerElements(); } - hasContainerElementsMarkedForCascading = tmpHasContainerElementsMarkedForCascading; hasGroupConversionsOnAnnotatedObjectOrContainerElements = tmpHasGroupConversionsOnAnnotatedObjectOrContainerElements; + hasContainerElementsMarkedForCascading = hasContainerElementsMarkedForCascading( containerElementTypesCascadingMetaData ); } - public static CascadingMetaDataBuilder nonCascading() { - return NON_CASCADING; - } - - public static CascadingMetaDataBuilder annotatedObject(Type cascadableType, boolean cascading, Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, Map, Class> groupConversions) { - return new CascadingMetaDataBuilder( cascadableType, AnnotatedObject.INSTANCE, cascading, containerElementTypesCascadingMetaData, groupConversions ); - } - - public static CascadingMetaDataBuilder propertyHolder(String mappingName, boolean cascading, Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, Map, Class> groupConversions) { - return new CascadingMetaDataBuilder( mappingName, AnnotatedObject.INSTANCE, cascading, containerElementTypesCascadingMetaData, groupConversions ); - } - - public TypeVariable getTypeParameter() { - return typeParameter; - } - - public Type getEnclosingType() { - return enclosingType; - } - - public String getMappingName() { - return mapping; - } - - public Class getDeclaredContainerClass() { - return declaredContainerClass; - } - - public TypeVariable getDeclaredTypeParameter() { - return declaredTypeParameter; - } - + @Override public boolean isCascading() { return cascading; } + @Override public Map, Class> getGroupConversions() { return groupConversions; } + @Override public boolean hasContainerElementsMarkedForCascading() { return hasContainerElementsMarkedForCascading; } + @Override public boolean isMarkedForCascadingOnAnnotatedObjectOrContainerElements() { return cascading || hasContainerElementsMarkedForCascading; } + @Override public boolean hasGroupConversionsOnAnnotatedObjectOrContainerElements() { return hasGroupConversionsOnAnnotatedObjectOrContainerElements; } + @Override public Map, CascadingMetaDataBuilder> getContainerElementTypesCascadingMetaData() { return containerElementTypesCascadingMetaData; } + @Override public CascadingMetaDataBuilder merge(CascadingMetaDataBuilder otherCascadingTypeParameter) { - if ( this == NON_CASCADING ) { - return otherCascadingTypeParameter; - } - if ( otherCascadingTypeParameter == NON_CASCADING ) { + if ( otherCascadingTypeParameter == NonCascadingMetaDataBuilder.INSTANCE ) { return this; } - boolean cascading = this.cascading || otherCascadingTypeParameter.cascading; + boolean cascading = this.cascading || otherCascadingTypeParameter.isCascading(); - Map, Class> groupConversions = mergeGroupConversion( this.groupConversions, otherCascadingTypeParameter.groupConversions ); + Map, Class> groupConversions = mergeGroupConversion( this.groupConversions, otherCascadingTypeParameter.getGroupConversions() ); Map, CascadingMetaDataBuilder> nestedCascadingTypeParameterMap = Stream - .concat( this.containerElementTypesCascadingMetaData.entrySet().stream(), - otherCascadingTypeParameter.containerElementTypesCascadingMetaData.entrySet().stream() ) + .concat( + this.containerElementTypesCascadingMetaData.entrySet().stream(), + otherCascadingTypeParameter.getContainerElementTypesCascadingMetaData().entrySet().stream() ) .collect( - Collectors.toMap( entry -> entry.getKey(), entry -> entry.getValue(), ( value1, value2 ) -> value1.merge( value2 ) ) ); + Collectors.toMap( entry -> entry.getKey(), entry -> entry.getValue(), (value1, value2) -> value1.merge( value2 ) ) ); - return new CascadingMetaDataBuilder( this.enclosingType, this.typeParameter, cascading, nestedCascadingTypeParameterMap, groupConversions ); + return new SimpleBeanCascadingMetaDataBuilder( this.enclosingType, this.typeParameter, cascading, nestedCascadingTypeParameterMap, groupConversions ); } - public CascadingMetaData build(ValueExtractorManager valueExtractorManager, Object context) { + @Override + public CascadingMetaData build(ValueExtractorManager valueExtractorManager, Constrainable context) { validateGroupConversions( context ); // In the case the whole object is not annotated as cascading, we don't need to enable @@ -222,11 +195,11 @@ public CascadingMetaData build(ValueExtractorManager valueExtractorManager, Obje if ( !cascading ) { // We have cascading enabled for at least one of the container elements if ( !containerElementTypesCascadingMetaData.isEmpty() && hasContainerElementsMarkedForCascading ) { - return ContainerCascadingMetaData.of( valueExtractorManager, this, context ); + return toContainerCascadingMetaData( valueExtractorManager ); } // It is not a container or it doesn't have cascading enabled on any container element else { - return NonContainerCascadingMetaData.of( this, context ); + return nonContainerCascadingMetaData(); } } @@ -244,8 +217,7 @@ public CascadingMetaData build(ValueExtractorManager valueExtractorManager, Obje // // The value extractor returned here is just used to add the proper cascading metadata to the type // argument of the container. Proper value extractor resolution is executed at runtime. - Set containerDetectionValueExtractorCandidates = valueExtractorManager.getResolver() - .getValueExtractorCandidatesForContainerDetectionOfGlobalCascadedValidation( enclosingType ); + Set containerDetectionValueExtractorCandidates = findContainerDetectionValueExtractorCandidates( valueExtractorManager ); if ( !containerDetectionValueExtractorCandidates.isEmpty() ) { if ( containerDetectionValueExtractorCandidates.size() > 1 ) { throw LOG.getUnableToGetMostSpecificValueExtractorDueToSeveralMaximallySpecificValueExtractorsDeclaredException( @@ -254,17 +226,20 @@ public CascadingMetaData build(ValueExtractorManager valueExtractorManager, Obje ); } - return ContainerCascadingMetaData.of( - valueExtractorManager, - new CascadingMetaDataBuilder( + return toContainerCascadingMetaData( + new SimpleBeanCascadingMetaDataBuilder( enclosingType, typeParameter, cascading, - addCascadingMetaDataBasedOnContainerDetection( enclosingType, containerElementTypesCascadingMetaData, groupConversions, - containerDetectionValueExtractorCandidates.iterator().next() ), + addCascadingMetaDataBasedOnContainerDetection( + enclosingType, + containerElementTypesCascadingMetaData, + groupConversions, + containerDetectionValueExtractorCandidates.iterator().next() + ), groupConversions ), - context + valueExtractorManager ); } @@ -275,18 +250,31 @@ public CascadingMetaData build(ValueExtractorManager valueExtractorManager, Obje // extends ContainerWithoutRegisteredVE) // so we are looking for VEs such that ValueExtractorDescriptor#getContainerType() is assignable to the declared // type under inspection. - Set potentialValueExtractorCandidates = valueExtractorManager.getResolver() - .getPotentialValueExtractorCandidatesForCascadedValidation( enclosingType ); + Set potentialValueExtractorCandidates = findPotentialValueExtractorCandidates( valueExtractorManager ); // if such VEs were found we return an instance of PotentiallyContainerCascadingMetaData that will store those potential VEs // and they will be used at runtime to check if any of those could be applied to a runtime type and if PotentiallyContainerCascadingMetaData // should be promoted to ContainerCascadingMetaData or not. if ( !potentialValueExtractorCandidates.isEmpty() ) { - return PotentiallyContainerCascadingMetaData.of( this, potentialValueExtractorCandidates, context ); + return PotentiallyContainerCascadingMetaData.of( groupConversions, potentialValueExtractorCandidates ); } // if cascading == false, or none of the above cases matched we just return a non container metadata - return NonContainerCascadingMetaData.of( this, context ); + return nonContainerCascadingMetaData(); + } + + protected NonContainerCascadingMetaData nonContainerCascadingMetaData() { + return NonContainerCascadingMetaData.of( cascading, groupConversions ); + } + + protected Set findContainerDetectionValueExtractorCandidates(ValueExtractorManager valueExtractorManager) { + return valueExtractorManager.getResolver() + .getValueExtractorCandidatesForContainerDetectionOfGlobalCascadedValidation( enclosingType ); + } + + protected Set findPotentialValueExtractorCandidates(ValueExtractorManager valueExtractorManager) { + return valueExtractorManager.getResolver() + .getPotentialValueExtractorCandidatesForCascadedValidation( enclosingType ); } private void validateGroupConversions(Object context) { @@ -303,7 +291,9 @@ private void validateGroupConversions(Object context) { } for ( CascadingMetaDataBuilder containerElementCascadingTypeParameter : containerElementTypesCascadingMetaData.values() ) { - containerElementCascadingTypeParameter.validateGroupConversions( context ); + if ( containerElementCascadingTypeParameter instanceof SimpleBeanCascadingMetaDataBuilder ) { + ( (SimpleBeanCascadingMetaDataBuilder) containerElementCascadingTypeParameter ).validateGroupConversions( context ); + } } } @@ -347,7 +337,7 @@ public boolean equals(Object obj) { if ( getClass() != obj.getClass() ) { return false; } - CascadingMetaDataBuilder other = (CascadingMetaDataBuilder) obj; + SimpleBeanCascadingMetaDataBuilder other = (SimpleBeanCascadingMetaDataBuilder) obj; if ( !typeParameter.equals( other.typeParameter ) ) { return false; } @@ -363,7 +353,26 @@ public boolean equals(Object obj) { return true; } - private static Map, Class> mergeGroupConversion(Map, Class> groupConversions, Map, Class> otherGroupConversions) { + private static Map, CascadingMetaDataBuilder> convertContainerElementTypesCascadingMetaData(List containerElementTypesCascadingMetaData) { + Map, CascadingMetaDataBuilder> converted = CollectionHelper.newHashMap( containerElementTypesCascadingMetaData.size() ); + for ( CascadingMetaDataBuilder containerElement : containerElementTypesCascadingMetaData ) { + Contracts.assertTrue( containerElement instanceof SimpleBeanCascadingMetaDataBuilder, "Supports only SimpleBeanCascadingMetaDataBuilder." ); + + converted.put( ( (SimpleBeanCascadingMetaDataBuilder) containerElement ).typeParameter, containerElement ); + } + return converted; + } + + private boolean hasContainerElementsMarkedForCascading(Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData) { + boolean tmpHasContainerElementsMarkedForCascading = false; + for ( CascadingMetaDataBuilder nestedCascadingTypeParameter : containerElementTypesCascadingMetaData.values() ) { + tmpHasContainerElementsMarkedForCascading = tmpHasContainerElementsMarkedForCascading + || nestedCascadingTypeParameter.isCascading() || nestedCascadingTypeParameter.hasContainerElementsMarkedForCascading(); + } + return tmpHasContainerElementsMarkedForCascading; + } + + protected static Map, Class> mergeGroupConversion(Map, Class> groupConversions, Map, Class> otherGroupConversions) { if ( groupConversions.isEmpty() && otherGroupConversions.isEmpty() ) { // this is a rather common case so let's optimize it return Collections.emptyMap(); @@ -389,7 +398,7 @@ private static Map, Class> mergeGroupConversion(Map, Class< private static Map, CascadingMetaDataBuilder> addCascadingMetaDataBasedOnContainerDetection(Type cascadableType, Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, Map, Class> groupConversions, - ValueExtractorDescriptor possibleValueExtractor) { + ValueExtractorDescriptor possibleValueExtractor) { Class cascadableClass = ReflectionHelper.getClassFromType( cascadableType ); if ( cascadableClass.isArray() ) { // for arrays, we need to add an ArrayElement cascading metadata: it's the only way arrays support cascading at the moment. @@ -397,13 +406,13 @@ private static Map, CascadingMetaDataBuilder> addCascadingMetaDa } else { Map, CascadingMetaDataBuilder> cascadingMetaData = containerElementTypesCascadingMetaData; - cascadingMetaData = addCascadingMetaData( - cascadableClass, - possibleValueExtractor.getContainerType(), - possibleValueExtractor.getExtractedTypeParameter(), - cascadingMetaData, - groupConversions - ); + cascadingMetaData = addCascadingMetaData( + cascadableClass, + possibleValueExtractor.getContainerType(), + possibleValueExtractor.getExtractedTypeParameter(), + cascadingMetaData, + groupConversions + ); return cascadingMetaData; } } @@ -434,12 +443,14 @@ private static Map, CascadingMetaDataBuilder> addCascadingMetaDa amendedCascadingMetadata.putAll( containerElementTypesCascadingMetaData ); if ( containerElementTypesCascadingMetaData.containsKey( cascadableTypeParameter ) ) { - amendedCascadingMetadata.put( cascadableTypeParameter, - makeCascading( containerElementTypesCascadingMetaData.get( cascadableTypeParameter ), groupConversions ) ); + amendedCascadingMetadata.put( + cascadableTypeParameter, + makeCascading( (SimpleBeanCascadingMetaDataBuilder) containerElementTypesCascadingMetaData.get( cascadableTypeParameter ), groupConversions ) ); } else { - amendedCascadingMetadata.put( cascadableTypeParameter, - new CascadingMetaDataBuilder( cascadableClass, null, cascadableTypeParameter, enclosingType, correspondingTypeParameter, true, + amendedCascadingMetadata.put( + cascadableTypeParameter, + new SimpleBeanCascadingMetaDataBuilder( cascadableClass, cascadableTypeParameter, enclosingType, correspondingTypeParameter, true, Collections.emptyMap(), groupConversions ) ); } @@ -454,15 +465,37 @@ private static Map, CascadingMetaDataBuilder> addArrayElementCas TypeVariable cascadableTypeParameter = new ArrayElement( enclosingType ); - amendedCascadingMetadata.put( cascadableTypeParameter, - new CascadingMetaDataBuilder( enclosingType, cascadableTypeParameter, true, Collections.emptyMap(), groupConversions ) ); + amendedCascadingMetadata.put( + cascadableTypeParameter, + new SimpleBeanCascadingMetaDataBuilder( enclosingType, cascadableTypeParameter, true, Collections.emptyMap(), groupConversions ) ); return amendedCascadingMetadata; } - private static CascadingMetaDataBuilder makeCascading(CascadingMetaDataBuilder cascadingTypeParameter, Map, Class> groupConversions) { - return new CascadingMetaDataBuilder( cascadingTypeParameter.enclosingType, cascadingTypeParameter.typeParameter, true, + private static SimpleBeanCascadingMetaDataBuilder makeCascading(SimpleBeanCascadingMetaDataBuilder cascadingTypeParameter, Map, Class> groupConversions) { + return new SimpleBeanCascadingMetaDataBuilder( cascadingTypeParameter.enclosingType, cascadingTypeParameter.typeParameter, true, cascadingTypeParameter.containerElementTypesCascadingMetaData, cascadingTypeParameter.groupConversions.isEmpty() ? groupConversions : cascadingTypeParameter.groupConversions ); } + + protected ContainerCascadingMetaData toContainerCascadingMetaData(ValueExtractorManager valueExtractorManager) { + return new ContainerCascadingMetaData( + valueExtractorManager, + enclosingType, + typeParameter, + declaredContainerClass, + declaredTypeParameter, + containerElementTypesCascadingMetaData.entrySet().stream() + .map( entry -> toContainerCascadingMetaData( entry.getValue(), valueExtractorManager ) ) + .collect( Collectors.collectingAndThen( Collectors.toList(), CollectionHelper::toImmutableList ) ), + cascading, + GroupConversionHelper.of( groupConversions ), + isMarkedForCascadingOnAnnotatedObjectOrContainerElements() + ); + } + + protected static ContainerCascadingMetaData toContainerCascadingMetaData(CascadingMetaDataBuilder builder, ValueExtractorManager valueExtractorManager) { + Contracts.assertTrue( builder instanceof SimpleBeanCascadingMetaDataBuilder, "Only instances of SimpleBeanCascadingMetaDataBuilder type are supported here." ); + return ( (SimpleBeanCascadingMetaDataBuilder) builder ).toContainerCascadingMetaData( valueExtractorManager ); + } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/facets/Cascadable.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/facets/Cascadable.java index fd5864a3d2..d7f6a82aed 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/facets/Cascadable.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/facets/Cascadable.java @@ -10,7 +10,7 @@ import org.hibernate.validator.internal.engine.path.PathImpl; import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaData; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; /** diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/AnnotationMetaDataProvider.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/AnnotationMetaDataProvider.java index 0f68ebf22c..69903adfb4 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/AnnotationMetaDataProvider.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/AnnotationMetaDataProvider.java @@ -41,7 +41,8 @@ import org.hibernate.validator.group.GroupSequenceProvider; import org.hibernate.validator.internal.engine.valueextraction.ArrayElement; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.cascading.SimpleBeanCascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; @@ -666,7 +667,7 @@ private Map, CascadingMetaDataBuilder> getTypeParametersCascadin Map, CascadingMetaDataBuilder> nestedTypeParametersCascadingMetadata = getTypeParametersCascadingMetaDataForAnnotatedType( annotatedTypeArgument ); - typeParametersCascadingMetadata.put( typeParameters[i], new CascadingMetaDataBuilder( annotatedParameterizedType.getType(), typeParameters[i], + typeParametersCascadingMetadata.put( typeParameters[i], new SimpleBeanCascadingMetaDataBuilder( annotatedParameterizedType.getType(), typeParameters[i], annotatedTypeArgument.isAnnotationPresent( Valid.class ), nestedTypeParametersCascadingMetadata, getGroupConversions( annotatedTypeArgument ) ) ); i++; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/proeprtyholder/DummyPropertyHolderMetaDataProvider.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/proeprtyholder/DummyPropertyHolderMetaDataProvider.java index 429a06b368..262607c7fc 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/proeprtyholder/DummyPropertyHolderMetaDataProvider.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/proeprtyholder/DummyPropertyHolderMetaDataProvider.java @@ -12,7 +12,7 @@ import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Collections; -import java.util.Map; +import java.util.List; import java.util.Optional; import javax.validation.ValidationException; @@ -23,13 +23,14 @@ import org.hibernate.validator.cfg.defs.MinDef; import org.hibernate.validator.cfg.defs.NotNullDef; import org.hibernate.validator.cfg.defs.SizeDef; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.cascading.PropertyHolderCascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.MetaConstraintBuilder; import org.hibernate.validator.internal.metadata.location.ConstraintLocation; import org.hibernate.validator.internal.metadata.provider.PropertyHolderMetaDataProvider; import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; -import org.hibernate.validator.internal.metadata.raw.propertyholder.ConstrainedPropertyHolderElementBuilder; +import org.hibernate.validator.internal.metadata.raw.propertyholder.CascadingConstrainedPropertyHolderElementBuilder; import org.hibernate.validator.internal.metadata.raw.propertyholder.PropertyHolderConfiguration; +import org.hibernate.validator.internal.metadata.raw.propertyholder.SimpleConstrainedPropertyHolderElementBuilder; import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.internal.util.annotation.AnnotationDescriptor; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; @@ -69,7 +70,7 @@ private static PropertyHolderConfiguration user() { ConfigurationSource.API, USER_MAPPING_NAME, CollectionHelper.asSet( - new ConstrainedPropertyHolderElementBuilder( + new SimpleConstrainedPropertyHolderElementBuilder( ConfigurationSource.API, "name", String.class, @@ -83,10 +84,9 @@ private static PropertyHolderConfiguration user() { ConstraintLocation.Builder.forPropertyHolderProperty() ) ), - Collections.emptySet(), - CascadingMetaDataBuilder.nonCascading() + Collections.emptySet() ), - new ConstrainedPropertyHolderElementBuilder( + new SimpleConstrainedPropertyHolderElementBuilder( ConfigurationSource.API, "email", String.class, @@ -100,21 +100,48 @@ private static PropertyHolderConfiguration user() { ConstraintLocation.Builder.forPropertyHolderProperty() ) ), - Collections.emptySet(), - CascadingMetaDataBuilder.nonCascading() + Collections.emptySet() ), - new ConstrainedPropertyHolderElementBuilder( + new CascadingConstrainedPropertyHolderElementBuilder( ConfigurationSource.API, "address", - Map.class, // TODO: note this won't work need to handle cascading differently Collections.emptySet(), Collections.emptySet(), - // TODO: might require a builder over it to collect info and then when we know the property hodler class we - // could build the CascadingMetaDataBuilder from it. - CascadingMetaDataBuilder.propertyHolder( - USER_MAPPING_NAME, + PropertyHolderCascadingMetaDataBuilder.simplePropertyHolder( + ADDRESS_MAPPING_NAME, true, - Collections.emptyMap(), + Collections.emptyMap() + ) + ), + new SimpleConstrainedPropertyHolderElementBuilder( + ConfigurationSource.API, + "secondaryAddresses", + List.class, + CollectionHelper.asSet( + new MetaConstraintBuilder( + createAnnotationDescriptor( new NotNullDef() ), + ConstraintLocation.Builder.forPropertyHolderProperty() + ), + new MetaConstraintBuilder( + createAnnotationDescriptor( new SizeDef().max( 2 ) ), + ConstraintLocation.Builder.forPropertyHolderProperty() + ) + ), + Collections.emptySet(), + PropertyHolderCascadingMetaDataBuilder.propertyHolderContainer( + false, //TODO: need to throw exception if cascading is true here or completely remove the ability to set it. + List.class, + Collections.singletonMap( + List.class.getTypeParameters()[0], + PropertyHolderCascadingMetaDataBuilder.propertyHolderContainer( + ADDRESS_MAPPING_NAME, + true, + List.class, + List.class.getTypeParameters()[0], + Collections.emptyMap(), + Collections.emptyMap() + ) + ), Collections.emptyMap() ) ) @@ -128,7 +155,7 @@ private static PropertyHolderConfiguration address() { ConfigurationSource.API, USER_MAPPING_NAME, CollectionHelper.asSet( - new ConstrainedPropertyHolderElementBuilder( + new SimpleConstrainedPropertyHolderElementBuilder( ConfigurationSource.API, "street", String.class, @@ -142,10 +169,9 @@ private static PropertyHolderConfiguration address() { ConstraintLocation.Builder.forPropertyHolderProperty() ) ), - Collections.emptySet(), - CascadingMetaDataBuilder.nonCascading() + Collections.emptySet() ), - new ConstrainedPropertyHolderElementBuilder( + new SimpleConstrainedPropertyHolderElementBuilder( ConfigurationSource.API, "buildingNumber", Long.class, @@ -159,8 +185,7 @@ private static PropertyHolderConfiguration address() { ConstraintLocation.Builder.forPropertyHolderProperty() ) ), - Collections.emptySet(), - CascadingMetaDataBuilder.nonCascading() + Collections.emptySet() ) ), Collections.emptyList() diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/AbstractConstrainedElement.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/AbstractConstrainedElement.java index 4ac6c1676f..6bbc4abe50 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/AbstractConstrainedElement.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/AbstractConstrainedElement.java @@ -10,7 +10,7 @@ import java.util.Iterator; import java.util.Set; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.internal.util.stereotypes.Immutable; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedElement.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedElement.java index a1b1c456fd..47d6b93bcc 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedElement.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedElement.java @@ -11,7 +11,7 @@ import javax.validation.Valid; import javax.validation.groups.ConvertGroup; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.MetaConstraint; /** diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedExecutable.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedExecutable.java index 2d9a612929..9b5c518ed1 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedExecutable.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedExecutable.java @@ -17,7 +17,7 @@ import javax.validation.metadata.ConstraintDescriptor; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.properties.Callable; import org.hibernate.validator.internal.util.CollectionHelper; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedField.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedField.java index 143ad0fcc0..66f5aa1b02 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedField.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedField.java @@ -8,7 +8,7 @@ import java.util.Set; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.properties.Field; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedParameter.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedParameter.java index dab9ac417c..63f304906b 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedParameter.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedParameter.java @@ -13,7 +13,7 @@ import java.util.HashSet; import java.util.Set; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.properties.Callable; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedType.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedType.java index a5fe71148b..d237afdc28 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedType.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedType.java @@ -9,7 +9,7 @@ import java.util.Collections; import java.util.Set; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.MetaConstraint; /** diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/CascadingConstrainedPropertyHolderElementBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/CascadingConstrainedPropertyHolderElementBuilder.java new file mode 100644 index 0000000000..be2a902783 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/CascadingConstrainedPropertyHolderElementBuilder.java @@ -0,0 +1,38 @@ +/* + * 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.raw.propertyholder; + +import java.util.Set; + +import org.hibernate.validator.internal.metadata.aggregated.cascading.PropertyHolderCascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.core.MetaConstraintBuilder; +import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; +import org.hibernate.validator.internal.properties.PropertyAccessor; +import org.hibernate.validator.internal.properties.propertyholder.PropertyAccessorCreatorProvider; +import org.hibernate.validator.internal.properties.propertyholder.PropertyHolderProperty; +import org.hibernate.validator.spi.propertyholder.PropertyAccessorCreator; + +/** + * @author Marko Bekhta + */ +public class CascadingConstrainedPropertyHolderElementBuilder extends ConstrainedPropertyHolderElementBuilder { + + public CascadingConstrainedPropertyHolderElementBuilder(ConfigurationSource source, + String name, Set> constraints, + Set> typeArgumentConstraints, + PropertyHolderCascadingMetaDataBuilder cascadingMetaDataBuilder) { + super( source, name, constraints, typeArgumentConstraints, cascadingMetaDataBuilder ); + } + + @Override + protected PropertyHolderProperty createPropertyHolderProperty(PropertyAccessorCreatorProvider propertyAccessorCreatorProvider, Class propertyHolderType) { + PropertyAccessorCreator propertyAccessorCreator = propertyAccessorCreatorProvider.getPropertyAccessorCreatorFor( propertyHolderType ); + PropertyAccessor propertyAccessor = propertyAccessorCreator.create( name, propertyHolderType ); + + return new PropertyHolderProperty( propertyHolderType, propertyAccessor, name, propertyHolderType ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/ConstrainedPropertyHolderElement.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/ConstrainedPropertyHolderElement.java index 526d210e00..b8ac53515c 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/ConstrainedPropertyHolderElement.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/ConstrainedPropertyHolderElement.java @@ -10,7 +10,7 @@ import java.util.Iterator; import java.util.Set; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/ConstrainedPropertyHolderElementBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/ConstrainedPropertyHolderElementBuilder.java index 6738264c22..58e193d945 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/ConstrainedPropertyHolderElementBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/ConstrainedPropertyHolderElementBuilder.java @@ -11,44 +11,40 @@ import java.util.Set; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.core.MetaConstraintBuilder; import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; import org.hibernate.validator.internal.metadata.raw.ConstrainedField; -import org.hibernate.validator.internal.properties.PropertyAccessor; import org.hibernate.validator.internal.properties.propertyholder.PropertyAccessorCreatorProvider; import org.hibernate.validator.internal.properties.propertyholder.PropertyHolderProperty; import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.stereotypes.Immutable; -import org.hibernate.validator.spi.propertyholder.PropertyAccessorCreator; /** * @author Marko Bekhta */ -public class ConstrainedPropertyHolderElementBuilder { +public abstract class ConstrainedPropertyHolderElementBuilder { - private final ConfigurationSource source; + protected final ConfigurationSource source; - private final String name; - private final Class type; + protected final String name; @Immutable - private final Set> constraints; - private final CascadingMetaDataBuilder cascadingMetaDataBuilder; + protected final Set> constraints; + protected final CascadingMetaDataBuilder cascadingMetaDataBuilder; @Immutable - private final Set> typeArgumentConstraints; + protected final Set> typeArgumentConstraints; public ConstrainedPropertyHolderElementBuilder(ConfigurationSource source, - String name, Class type, Set> constraints, + String name, Set> constraints, Set> typeArgumentConstraints, CascadingMetaDataBuilder cascadingMetaDataBuilder) { this.source = source; this.name = name; - this.type = type; this.constraints = CollectionHelper.toImmutableSetOfNullable( constraints ); this.typeArgumentConstraints = CollectionHelper.toImmutableSetOfNullable( typeArgumentConstraints ); this.cascadingMetaDataBuilder = cascadingMetaDataBuilder; @@ -62,10 +58,7 @@ public boolean isConstrained() { } public ConstrainedElement build(TypeResolutionHelper typeResolutionHelper, ConstraintHelper constraintHelper, ValueExtractorManager valueExtractorManager, PropertyAccessorCreatorProvider propertyAccessorCreatorProvider, Class propertyHolderType) { - PropertyAccessorCreator propertyAccessorCreator = propertyAccessorCreatorProvider.getPropertyAccessorCreatorFor( propertyHolderType ); - PropertyAccessor propertyAccessor = propertyAccessorCreator.create( name, type ); - - PropertyHolderProperty property = new PropertyHolderProperty( propertyHolderType, propertyAccessor, name, type ); + PropertyHolderProperty property = createPropertyHolderProperty( propertyAccessorCreatorProvider, propertyHolderType ); return new ConstrainedField( source, @@ -76,7 +69,9 @@ public ConstrainedElement build(TypeResolutionHelper typeResolutionHelper, Const ); } - private Set> toMetaConstraints(TypeResolutionHelper typeResolutionHelper, ConstraintHelper constraintHelper, ValueExtractorManager valueExtractorManager, PropertyHolderProperty property, Collection> collection) { + protected abstract PropertyHolderProperty createPropertyHolderProperty(PropertyAccessorCreatorProvider propertyAccessorCreatorProvider, Class propertyHolderType); + + protected Set> toMetaConstraints(TypeResolutionHelper typeResolutionHelper, ConstraintHelper constraintHelper, ValueExtractorManager valueExtractorManager, PropertyHolderProperty property, Collection> collection) { Set> builtConstraints = new HashSet<>( constraints.size() ); for ( MetaConstraintBuilder builder : constraints ) { builtConstraints.add( builder.build( typeResolutionHelper, constraintHelper, valueExtractorManager, property ) ); @@ -90,7 +85,6 @@ public String toString() { sb.append( "{" ); sb.append( "source=" ).append( source ); sb.append( ", name='" ).append( name ).append( '\'' ); - sb.append( ", type=" ).append( type ); sb.append( ", constraints=" ).append( constraints ); sb.append( ", cascadingMetaDataBuilder=" ).append( cascadingMetaDataBuilder ); sb.append( ", typeArgumentConstraints=" ).append( typeArgumentConstraints ); @@ -116,9 +110,6 @@ public boolean equals(Object o) { if ( !name.equals( that.name ) ) { return false; } - if ( !type.equals( that.type ) ) { - return false; - } return true; } @@ -127,7 +118,6 @@ public boolean equals(Object o) { public int hashCode() { int result = source.hashCode(); result = 31 * result + name.hashCode(); - result = 31 * result + type.hashCode(); return result; } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/SimpleConstrainedPropertyHolderElementBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/SimpleConstrainedPropertyHolderElementBuilder.java new file mode 100644 index 0000000000..331f069608 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/SimpleConstrainedPropertyHolderElementBuilder.java @@ -0,0 +1,71 @@ +/* + * 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.raw.propertyholder; + +import java.util.Set; + +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.core.MetaConstraintBuilder; +import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; +import org.hibernate.validator.internal.properties.PropertyAccessor; +import org.hibernate.validator.internal.properties.propertyholder.PropertyAccessorCreatorProvider; +import org.hibernate.validator.internal.properties.propertyholder.PropertyHolderProperty; +import org.hibernate.validator.spi.propertyholder.PropertyAccessorCreator; + +/** + * @author Marko Bekhta + */ +public class SimpleConstrainedPropertyHolderElementBuilder extends ConstrainedPropertyHolderElementBuilder { + + private final Class type; + + public SimpleConstrainedPropertyHolderElementBuilder(ConfigurationSource source, + String name, Class type, Set> constraints, + Set> typeArgumentConstraints, CascadingMetaDataBuilder cascadingMetaDataBuilder) { + super( source, name, constraints, typeArgumentConstraints, cascadingMetaDataBuilder ); + this.type = type; + } + + public SimpleConstrainedPropertyHolderElementBuilder(ConfigurationSource source, + String name, Class type, Set> constraints, + Set> typeArgumentConstraints) { + this( source, name, type, constraints, typeArgumentConstraints, CascadingMetaDataBuilder.nonCascading() ); + } + + @Override + protected PropertyHolderProperty createPropertyHolderProperty(PropertyAccessorCreatorProvider propertyAccessorCreatorProvider, Class propertyHolderType) { + PropertyAccessorCreator propertyAccessorCreator = propertyAccessorCreatorProvider.getPropertyAccessorCreatorFor( propertyHolderType ); + PropertyAccessor propertyAccessor = propertyAccessorCreator.create( name, type ); + + return new PropertyHolderProperty( propertyHolderType, propertyAccessor, name, type ); + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + + SimpleConstrainedPropertyHolderElementBuilder that = (SimpleConstrainedPropertyHolderElementBuilder) o; + + if ( !type.equals( that.type ) ) { + return false; + } + + return super.equals( o ); + } + + @Override + public int hashCode() { + int result = type.hashCode(); + result = 31 * result + super.hashCode(); + return result; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractConstrainedElementStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractConstrainedElementStaxBuilder.java index 4791436852..ba900e74e7 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractConstrainedElementStaxBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractConstrainedElementStaxBuilder.java @@ -19,7 +19,7 @@ import javax.xml.stream.events.XMLEvent; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.location.ConstraintLocation; diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedConstructorStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedConstructorStaxBuilder.java index d7f56ab36f..9fbdaf508f 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedConstructorStaxBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedConstructorStaxBuilder.java @@ -16,7 +16,7 @@ import javax.xml.namespace.QName; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.MetaConstraint; diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedMethodStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedMethodStaxBuilder.java index 658ea6d07b..a0e2c1a261 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedMethodStaxBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedMethodStaxBuilder.java @@ -16,7 +16,7 @@ import javax.xml.namespace.QName; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.MetaConstraint; diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ContainerElementTypeConfigurationBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ContainerElementTypeConfigurationBuilder.java index fd424cd664..511084002b 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ContainerElementTypeConfigurationBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ContainerElementTypeConfigurationBuilder.java @@ -17,7 +17,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.location.ConstraintLocation; diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ContainerElementTypeStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ContainerElementTypeStaxBuilder.java index eeb2d16a6d..d354059d5b 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ContainerElementTypeStaxBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ContainerElementTypeStaxBuilder.java @@ -25,7 +25,8 @@ import org.hibernate.validator.internal.engine.valueextraction.ArrayElement; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.cascading.SimpleBeanCascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.location.ConstraintLocation; import org.hibernate.validator.internal.util.CollectionHelper; @@ -151,7 +152,7 @@ public ContainerElementTypeConfiguration build(Set con boolean isCascaded = validStaxBuilder.build(); - containerElementTypesCascadingMetaDataBuilder.put( typeParameter, new CascadingMetaDataBuilder( enclosingType, typeParameter, isCascaded, + containerElementTypesCascadingMetaDataBuilder.put( typeParameter, new SimpleBeanCascadingMetaDataBuilder( enclosingType, typeParameter, isCascaded, nestedContainerElementTypeConfiguration.getTypeParametersCascadingMetaData(), groupConversionBuilder.build() ) diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ReturnValueStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ReturnValueStaxBuilder.java index 8157a2ba6d..495b2fdaed 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ReturnValueStaxBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ReturnValueStaxBuilder.java @@ -13,7 +13,7 @@ import javax.xml.namespace.QName; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.MetaConstraint; diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/propertyholder/ValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/propertyholder/ValidatorTest.java index d6f1615e89..14bd28771d 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/propertyholder/ValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/propertyholder/ValidatorTest.java @@ -7,15 +7,21 @@ package org.hibernate.validator.test.internal.propertyholder; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.pathWith; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; import static org.hibernate.validator.testutils.ValidatorUtil.getValidator; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; import javax.validation.ConstraintViolation; +import javax.validation.Valid; import javax.validation.constraints.Email; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import org.hibernate.validator.internal.engine.ValidatorImpl; @@ -42,7 +48,55 @@ public void testSimplePropertyHolder() { Set> constraintViolations = validator.validatePropertyHolder( user, "user" ); assertThat( constraintViolations ).containsOnlyViolations( violationOf( Size.class ).withProperty( "name" ), - violationOf( Email.class ).withProperty( "email" ) + violationOf( Email.class ).withProperty( "email" ), + violationOf( Min.class ) + .withPropertyPath( pathWith() + .property( "address" ) + .property( "buildingNumber" ) + ), + violationOf( Size.class ) + .withPropertyPath( pathWith() + .property( "address" ) + .property( "street" ) + ), + violationOf( NotNull.class ).withProperty( "secondaryAddresses" ) ); } + + @Test + public void testPropertyHolderContainerElements() { + ValidatorImpl validator = (ValidatorImpl) getValidator(); + + Map address = new HashMap<>(); + address.put( "street", "street" ); + address.put( "buildingNumber", 10L ); + + Map invaildAddress = new HashMap<>(); + invaildAddress.put( "street", "str" ); + invaildAddress.put( "buildingNumber", -1L ); + + Map user = new HashMap<>(); + user.put( "name", "jhon doe" ); + user.put( "email", "kinda@valid.mail" ); + user.put( "address", address ); + user.put( "secondaryAddresses", Collections.singletonList( invaildAddress ) ); + + Set> constraintViolations = validator.validatePropertyHolder( user, "user" ); + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( Min.class ) + .withPropertyPath( pathWith() + .property( "secondaryAddresses" ) + .property( "buildingNumber", true, null, 0, List.class, 0 ) + ), + violationOf( Size.class ) + .withPropertyPath( pathWith() + .property( "secondaryAddresses" ) + .property( "street", true, null, 0, List.class, 0 ) + ) + ); + } + + private static class Bar { + List<@Valid Bar> bars; + } } From 8076e67e6b2996affea48a7b72dcfec365da157c Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Thu, 16 Aug 2018 10:16:37 +0200 Subject: [PATCH 14/14] [property-holder] start working on programmatic definition --- .../HibernateValidatorConfiguration.java | 9 +- .../cfg/propertyholder/Cascadable.java | 33 +++ ...tainerElementConstraintMappingContext.java | 14 ++ ...dablePropertyConstraintMappingContext.java | 22 ++ .../cfg/propertyholder/Constrainable.java | 27 +++ ...tainerElementConstraintMappingContext.java | 21 ++ .../ContainerElementTarget.java | 27 +++ .../GroupConversionTargetContext.java | 25 +++ .../PropertyConstraintMappingContext.java | 22 ++ .../PropertyHolderConstraintMapping.java | 28 +++ ...ropertyHolderConstraintMappingContext.java | 23 ++ .../propertyholder/PropertyHolderTarget.java | 33 +++ .../cfg/propertyholder/PropertyTarget.java | 34 +++ .../TypeConstraintMappingContext.java | 27 +++ .../cfg/propertyholder/package-info.java | 13 ++ ...olderConstraintMappingContextImplBase.java | 61 ++++++ .../ConstraintContextImplBase.java | 47 +++++ ...erElementConstraintMappingContextImpl.java | 198 ++++++++++++++++++ .../GroupConversionTargetContextHelper.java | 24 +++ .../GroupConversionTargetContextImpl.java | 33 +++ .../PropertyConstraintMappingContextImpl.java | 185 ++++++++++++++++ ...pertyConstraintMappingContextImplBase.java | 26 +++ ...rtyHolderConstraintMappingContextImpl.java | 70 +++++++ .../PropertyHolderConstraintMappingImpl.java | 80 +++++++ ...olderTypeConstraintMappingContextImpl.java | 116 ++++++++++ .../cfg/propertyholder/package-info.java | 12 ++ .../internal/engine/ConfigurationImpl.java | 20 ++ .../internal/engine/ValidatorFactoryImpl.java | 36 +++- .../internal/engine/ValidatorImpl.java | 1 - .../metadata/core/MetaConstraintBuilder.java | 58 ++++- .../metadata/location/ConstraintLocation.java | 8 - ...lderPropertyConstraintLocationBuilder.java | 25 --- .../manager/ConstraintMetaDataManager.java | 2 +- .../PropertyHolderBeanMetaDataProvider.java | 2 +- .../DummyPropertyHolderMetaDataProvider.java | 34 +-- ...ammaticPropertyHolderMetaDataProvider.java | 88 ++++++++ .../PropertyHolderMetaDataProvider.java | 2 +- ...nstrainedPropertyHolderElementBuilder.java | 4 +- .../validator/internal/util/logging/Log.java | 9 + .../internal/util/logging/Messages.java | 4 + .../propertyholder/ValidatorTest.java | 77 ++++++- 41 files changed, 1503 insertions(+), 77 deletions(-) create mode 100644 engine/src/main/java/org/hibernate/validator/cfg/propertyholder/Cascadable.java create mode 100644 engine/src/main/java/org/hibernate/validator/cfg/propertyholder/CascadableContainerElementConstraintMappingContext.java create mode 100644 engine/src/main/java/org/hibernate/validator/cfg/propertyholder/CascadablePropertyConstraintMappingContext.java create mode 100644 engine/src/main/java/org/hibernate/validator/cfg/propertyholder/Constrainable.java create mode 100644 engine/src/main/java/org/hibernate/validator/cfg/propertyholder/ContainerElementConstraintMappingContext.java create mode 100644 engine/src/main/java/org/hibernate/validator/cfg/propertyholder/ContainerElementTarget.java create mode 100644 engine/src/main/java/org/hibernate/validator/cfg/propertyholder/GroupConversionTargetContext.java create mode 100644 engine/src/main/java/org/hibernate/validator/cfg/propertyholder/PropertyConstraintMappingContext.java create mode 100644 engine/src/main/java/org/hibernate/validator/cfg/propertyholder/PropertyHolderConstraintMapping.java create mode 100644 engine/src/main/java/org/hibernate/validator/cfg/propertyholder/PropertyHolderConstraintMappingContext.java create mode 100644 engine/src/main/java/org/hibernate/validator/cfg/propertyholder/PropertyHolderTarget.java create mode 100644 engine/src/main/java/org/hibernate/validator/cfg/propertyholder/PropertyTarget.java create mode 100644 engine/src/main/java/org/hibernate/validator/cfg/propertyholder/TypeConstraintMappingContext.java create mode 100644 engine/src/main/java/org/hibernate/validator/cfg/propertyholder/package-info.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/CascadablePropertyHolderConstraintMappingContextImplBase.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/ConstraintContextImplBase.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/ContainerElementConstraintMappingContextImpl.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/GroupConversionTargetContextHelper.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/GroupConversionTargetContextImpl.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/PropertyConstraintMappingContextImpl.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/PropertyConstraintMappingContextImplBase.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/PropertyHolderConstraintMappingContextImpl.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/PropertyHolderConstraintMappingImpl.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/PropertyHolderTypeConstraintMappingContextImpl.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/package-info.java delete mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/location/PropertyHolderPropertyConstraintLocationBuilder.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/provider/proeprtyholder/ProgrammaticPropertyHolderMetaDataProvider.java rename engine/src/main/java/org/hibernate/validator/internal/metadata/provider/{ => proeprtyholder}/PropertyHolderMetaDataProvider.java (87%) diff --git a/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java b/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java index 37ef2cba01..2d0e01f282 100644 --- a/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java +++ b/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java @@ -18,6 +18,7 @@ import javax.validation.valueextraction.ValueExtractor; import org.hibernate.validator.cfg.ConstraintMapping; +import org.hibernate.validator.cfg.propertyholder.PropertyHolderConstraintMapping; import org.hibernate.validator.constraints.ParameterScriptAssert; import org.hibernate.validator.constraints.ScriptAssert; import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy; @@ -64,8 +65,8 @@ public interface HibernateValidatorConfiguration extends Configuration. + */ +package org.hibernate.validator.cfg.propertyholder; + +/** + * Facet of a constraint mapping creational context which allows to mark the underlying + * element as to be validated in a cascaded way. + * + * @author Gunnar Morling + * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI + */ +public interface Cascadable> { + + /** + * Marks the current element (property, parameter etc.) as cascadable. + * + * @return The current creational context following the method chaining pattern. + */ + C valid(String mapping); + + /** + * Adds a group conversion for this cascadable element. Several conversions may be configured for one element. + * + * @param from the source group of the conversion to be configured + * + * @return a creational context allow to set the target group of the conversion + */ + GroupConversionTargetContext convertGroup(Class from); +} diff --git a/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/CascadableContainerElementConstraintMappingContext.java b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/CascadableContainerElementConstraintMappingContext.java new file mode 100644 index 0000000000..9348ff43a3 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/CascadableContainerElementConstraintMappingContext.java @@ -0,0 +1,14 @@ +/* + * 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.cfg.propertyholder; + +/** + * @author Marko Bekhta + */ +public interface CascadableContainerElementConstraintMappingContext extends ContainerElementConstraintMappingContext, + Cascadable { +} diff --git a/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/CascadablePropertyConstraintMappingContext.java b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/CascadablePropertyConstraintMappingContext.java new file mode 100644 index 0000000000..e40ba67905 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/CascadablePropertyConstraintMappingContext.java @@ -0,0 +1,22 @@ +/* + * 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.cfg.propertyholder; + +/** + * Constraint mapping creational context representing a property of a property holder. Allows + * to place constraints on the property, mark the property as cascadable and to + * navigate to other constraint targets. + * + * @author Gunnar Morling + * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI + * @author Marko Bekhta + */ +public interface CascadablePropertyConstraintMappingContext extends Constrainable, + PropertyHolderTarget, + PropertyTarget, + Cascadable { +} diff --git a/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/Constrainable.java b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/Constrainable.java new file mode 100644 index 0000000000..00b03b7aaa --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/Constrainable.java @@ -0,0 +1,27 @@ +/* + * 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.cfg.propertyholder; + +import org.hibernate.validator.cfg.ConstraintDef; + +/** + * Facet of a property holder constraint mapping creational context which allows to place + * constraints on the underlying element. + * + * @author Gunnar Morling + * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI + */ +public interface Constrainable> { + /** + * Adds a new constraint. + * + * @param definition The constraint to add. + * + * @return The current creational context following the method chaining pattern. + */ + C constraint(ConstraintDef definition); +} diff --git a/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/ContainerElementConstraintMappingContext.java b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/ContainerElementConstraintMappingContext.java new file mode 100644 index 0000000000..763096706a --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/ContainerElementConstraintMappingContext.java @@ -0,0 +1,21 @@ +/* + * 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.cfg.propertyholder; + +/** + * Constraint mapping creational context representing a type argument of a property, parameter or method return value + * with a generic (return) type. Allows to place constraints on that type argument, mark it as cascadable and to + * navigate to other constraint targets. + * + * @author Gunnar Morling + * @since 6.0 + */ +public interface ContainerElementConstraintMappingContext extends Constrainable, + PropertyTarget, + PropertyHolderTarget, + ContainerElementTarget { +} diff --git a/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/ContainerElementTarget.java b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/ContainerElementTarget.java new file mode 100644 index 0000000000..df37095186 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/ContainerElementTarget.java @@ -0,0 +1,27 @@ +/* + * 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.cfg.propertyholder; + +import org.hibernate.validator.Incubating; + +/** + * Facet of a constraint mapping creational context which allows to select a type argument or the component type of the + * (return) type of the current property as target for the next operations. + * + * @author Marko Bekhta + */ +@Incubating +public interface ContainerElementTarget { + + ContainerElementConstraintMappingContext containerElementType(Class type); + + ContainerElementConstraintMappingContext containerElementType(Class type, int index, int... nestedIndexes); + + CascadableContainerElementConstraintMappingContext containerElementType(String mapping); + + CascadableContainerElementConstraintMappingContext containerElementType(String mapping, int index, int... nestedIndexes); +} diff --git a/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/GroupConversionTargetContext.java b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/GroupConversionTargetContext.java new file mode 100644 index 0000000000..0cbffc5523 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/GroupConversionTargetContext.java @@ -0,0 +1,25 @@ +/* + * 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.cfg.propertyholder; + +/** + * Creational context which allows to set the target group of a group conversion configured via + * {@link Cascadable#convertGroup(Class)}. + * + * @author Gunnar Morling + */ +public interface GroupConversionTargetContext { + + /** + * Sets the target group of the conversion to be configured. + * + * @param to the target group of the conversion + * + * @return The current creational context following the method chaining pattern. + */ + C to(Class to); +} diff --git a/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/PropertyConstraintMappingContext.java b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/PropertyConstraintMappingContext.java new file mode 100644 index 0000000000..e0c8898f70 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/PropertyConstraintMappingContext.java @@ -0,0 +1,22 @@ +/* + * 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.cfg.propertyholder; + +/** + * Constraint mapping creational context representing a property of a property holder. Allows + * to place constraints on the property, mark the property as cascadable and to + * navigate to other constraint targets. + * + * @author Gunnar Morling + * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI + * @author Marko Bekhta + */ +public interface PropertyConstraintMappingContext extends Constrainable, + PropertyTarget, + PropertyHolderTarget, + ContainerElementTarget { +} diff --git a/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/PropertyHolderConstraintMapping.java b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/PropertyHolderConstraintMapping.java new file mode 100644 index 0000000000..a0cc6ef25f --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/PropertyHolderConstraintMapping.java @@ -0,0 +1,28 @@ +/* + * 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.cfg.propertyholder; + +/** + * Represents a property holder constraint mapping configured via the programmatic API. + * + * @author Marko Bekhta + */ +public interface PropertyHolderConstraintMapping { + + /** + * Starts defining constraints for the specified unique mapping name. Each mapping name may only be used + * once within all property holder constraint mappings used for configuring one validator factory. + * + * @param propertyHolderMappingName The mapping name for which to define constraints. All constraints + * defined after calling this method are added to the bean of the type {@code beanClass} until the + * next call of {@link this#type(String)}. + * + * @return Instance allowing for defining constraints for the specified property holder mapping name. + */ + TypeConstraintMappingContext type(String propertyHolderMappingName); + +} diff --git a/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/PropertyHolderConstraintMappingContext.java b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/PropertyHolderConstraintMappingContext.java new file mode 100644 index 0000000000..d8576a5b88 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/PropertyHolderConstraintMappingContext.java @@ -0,0 +1,23 @@ +/* + * 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.cfg.propertyholder; + +/** + * Constraint mapping creational context representing a property of a property holder. Allows + * to place constraints on the property, mark the property as cascadable and to + * navigate to other constraint targets. + * + * @author Gunnar Morling + * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI + * @author Marko Bekhta + */ +public interface PropertyHolderConstraintMappingContext extends Constrainable, + PropertyHolderTarget, + PropertyTarget, + Cascadable { + +} diff --git a/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/PropertyHolderTarget.java b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/PropertyHolderTarget.java new file mode 100644 index 0000000000..20e8b5207e --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/PropertyHolderTarget.java @@ -0,0 +1,33 @@ +/* + * 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.cfg.propertyholder; + +/** + * Facet of a property holder constraint mapping creational context which allows to specify the + * property to which the next operations should be apply. + * + * @author Marko Bekhta + */ +public interface PropertyHolderTarget { + + /** + * Defines a property, that is a property holder itself, to which the next operations shall apply. + *

+ * Until this method is called constraints apply on property holder level. After calling this method constraints + * apply on the specified property with the given property type. + *

+ *

+ * A given property may only be configured once. + *

+ * + * @param property The property holder on which to apply the following constraints. + * + * @return A creational context representing the selected property. + */ + PropertyHolderConstraintMappingContext propertyHolder(String property); + +} diff --git a/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/PropertyTarget.java b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/PropertyTarget.java new file mode 100644 index 0000000000..687589785a --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/PropertyTarget.java @@ -0,0 +1,34 @@ +/* + * 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.cfg.propertyholder; + +/** + * Facet of a property holder constraint mapping creational context which allows to specify the + * property to which the next operations should be apply. + * + * @author Marko Bekhta + */ +public interface PropertyTarget { + + /** + * Defines a property to which the next operations shall apply. + *

+ * Until this method is called constraints apply on property holder level. After calling this method constraints + * apply on the specified property with the given property type. + *

+ *

+ * A given property may only be configured once. + *

+ * + * @param property The property on which to apply the following constraints. + * @param propertyType The type of the specified property. + * + * @return A creational context representing the selected property. + */ + PropertyConstraintMappingContext property(String property, Class propertyType); + +} diff --git a/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/TypeConstraintMappingContext.java b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/TypeConstraintMappingContext.java new file mode 100644 index 0000000000..5c8cf91e5a --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/TypeConstraintMappingContext.java @@ -0,0 +1,27 @@ +/* + * 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.cfg.propertyholder; + +/** + * Constraint mapping creational context representing a type. Allows place + * class-level constraints on that type, define its default group sequence (and provider) + * and to navigate to other constraint targets. + * + * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI + * @author Gunnar Morling + */ +public interface TypeConstraintMappingContext extends PropertyTarget, PropertyHolderTarget { + + /** + * Defines the default group sequence for current type. + * + * @param defaultGroupSequence the default group sequence. + * + * @return The current creational context following the method chaining pattern. + */ + TypeConstraintMappingContext defaultGroupSequence(Class... defaultGroupSequence); +} diff --git a/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/package-info.java b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/package-info.java new file mode 100644 index 0000000000..728ce63321 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/cfg/propertyholder/package-info.java @@ -0,0 +1,13 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ + +/** + *

Contains facet and creational context interfaces forming the API for programmatic constraint + * definition for property holders validation.

+ *

This package is part of the public Hibernate Validator API.

+ */ +package org.hibernate.validator.cfg.propertyholder; diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/CascadablePropertyHolderConstraintMappingContextImplBase.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/CascadablePropertyHolderConstraintMappingContextImplBase.java new file mode 100644 index 0000000000..17a55d0ada --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/CascadablePropertyHolderConstraintMappingContextImplBase.java @@ -0,0 +1,61 @@ +/* + * 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.cfg.propertyholder; + +import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; + +import java.util.Map; + +import org.hibernate.validator.cfg.propertyholder.Cascadable; +import org.hibernate.validator.cfg.propertyholder.GroupConversionTargetContext; +import org.hibernate.validator.internal.metadata.aggregated.cascading.PropertyHolderCascadingMetaDataBuilder; + +/** + * Base class for all implementations of cascadable context types. + * + * @author Gunnar Morling + * @author Marko Bekhta + */ +abstract class CascadablePropertyHolderConstraintMappingContextImplBase> + extends PropertyConstraintMappingContextImplBase implements Cascadable, GroupConversionTargetContextHelper { + + protected final Map, Class> groupConversions = newHashMap(); + protected boolean isCascading; + private String mapping; + + CascadablePropertyHolderConstraintMappingContextImplBase(PropertyHolderConstraintMappingImpl mapping, String property) { + super( mapping, property ); + } + + protected abstract C getThis(); + + @Override + public void addGroupConversion(Class from, Class to) { + groupConversions.put( from, to ); + } + + @Override + public C valid(String mapping) { + this.mapping = mapping; + this.isCascading = true; + + return getThis(); + } + + @Override + public GroupConversionTargetContext convertGroup(Class from) { + return new GroupConversionTargetContextImpl<>( from, getThis(), this ); + } + + public boolean isCascading() { + return isCascading; + } + + protected PropertyHolderCascadingMetaDataBuilder getCascadingMetaDataBuilder() { + return PropertyHolderCascadingMetaDataBuilder.simplePropertyHolder( mapping, isCascading, groupConversions ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/ConstraintContextImplBase.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/ConstraintContextImplBase.java new file mode 100644 index 0000000000..925a91c7a5 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/ConstraintContextImplBase.java @@ -0,0 +1,47 @@ +/* + * 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.cfg.propertyholder; + +import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; + +import java.util.Set; + +import org.hibernate.validator.cfg.propertyholder.TypeConstraintMappingContext; +import org.hibernate.validator.internal.metadata.core.MetaConstraintBuilder; + +/** + * Base class for implementations of constraint-related context types. + * + * @author Marko Bekhta + */ +abstract class ConstraintContextImplBase { + + protected final PropertyHolderConstraintMappingImpl mapping; + + private final Set> constraints; + + public ConstraintContextImplBase(PropertyHolderConstraintMappingImpl mapping) { + this.mapping = mapping; + this.constraints = newHashSet(); + } + + public TypeConstraintMappingContext type(String propertyHolderMappingName) { + return mapping.type( propertyHolderMappingName ); + } + + protected PropertyHolderConstraintMappingImpl getConstraintMapping() { + return mapping; + } + + protected void addConstraint(MetaConstraintBuilder constraint) { + constraints.add( constraint ); + } + + protected Set> getConstraints() { + return constraints; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/ContainerElementConstraintMappingContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/ContainerElementConstraintMappingContextImpl.java new file mode 100644 index 0000000000..ce87a8576f --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/ContainerElementConstraintMappingContextImpl.java @@ -0,0 +1,198 @@ +/* + * 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.cfg.propertyholder; + +import java.lang.invoke.MethodHandles; +import java.lang.reflect.TypeVariable; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.hibernate.validator.cfg.ConstraintDef; +import org.hibernate.validator.cfg.propertyholder.CascadableContainerElementConstraintMappingContext; +import org.hibernate.validator.cfg.propertyholder.ContainerElementConstraintMappingContext; +import org.hibernate.validator.cfg.propertyholder.ContainerElementTarget; +import org.hibernate.validator.cfg.propertyholder.PropertyConstraintMappingContext; +import org.hibernate.validator.cfg.propertyholder.PropertyHolderConstraintMappingContext; +import org.hibernate.validator.internal.engine.valueextraction.ArrayElement; +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.aggregated.cascading.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.core.MetaConstraintBuilder; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; + +/** + * Context for simple container elements that are not property holders themselve. Hence no cascading on them should be allowed. + * + * @author Marko Bekhta + */ +public class ContainerElementConstraintMappingContextImpl implements + ContainerElementConstraintMappingContext { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private final PropertyHolderTypeConstraintMappingContextImpl typeContext; + private final ContainerElementTarget parentContainerElementTarget; + + /** + * The type configured through this context. Either a {@code ParameterizedType} or an array type. + */ + private final Class configuredType; + + /** + * The index of the type parameter configured through this context. Always 0 in case of an array type. + */ + private final int index; + + /** + * The type parameter configured through this context. An instance of {@link ArrayElement} in case of an array type. + */ + private final TypeVariable typeParameter; + + private final Class containerElementType; + + /** + * Contexts for configuring nested container elements, if any. Indexed by type parameter. + */ + protected final Map nestedContainerElementContexts; + + private final Set> constraints; + + ContainerElementConstraintMappingContextImpl( + PropertyHolderTypeConstraintMappingContextImpl typeContext, + ContainerElementTarget parentContainerElementTarget, + Class type, + int index, + Class containerElementType) { + this.typeContext = typeContext; + this.parentContainerElementTarget = parentContainerElementTarget; + + // HV-1428 Container element support is disabled for arrays + if ( type.isArray() ) { + throw LOG.getContainerElementConstraintsAndCascadedValidationNotSupportedOnArraysException( type ); + } + + TypeVariable[] typeParameters = type.getTypeParameters(); + + if ( index > typeParameters.length - 1 ) { + throw LOG.getInvalidTypeArgumentIndexException( type, index ); + } + else { + this.typeParameter = typeParameters[index]; + } + + this.configuredType = type; + this.index = index; + this.containerElementType = containerElementType; + this.constraints = new HashSet<>(); + this.nestedContainerElementContexts = new HashMap<>(); + } + + @Override + public PropertyHolderConstraintMappingContext propertyHolder(String property) { + return typeContext.propertyHolder( property ); + } + + @Override + public PropertyConstraintMappingContext property(String property, Class propertyType) { + return typeContext.property( property, propertyType ); + } + + @Override + public ContainerElementConstraintMappingContext containerElementType(Class containerElementType) { + return parentContainerElementTarget.containerElementType( containerElementType ); + } + + @Override + public ContainerElementConstraintMappingContext containerElementType(Class containerElementType, int index, int... nestedIndexes) { + return parentContainerElementTarget.containerElementType( containerElementType, index, nestedIndexes ); + } + + @Override + public CascadableContainerElementConstraintMappingContext containerElementType(String mapping) { + return parentContainerElementTarget.containerElementType( mapping ); + } + + @Override + public CascadableContainerElementConstraintMappingContext containerElementType(String mapping, int index, int... nestedIndexes) { + return parentContainerElementTarget.containerElementType( mapping, index, nestedIndexes ); + } + + ContainerElementConstraintMappingContext nestedContainerElement(int[] nestedIndexes) { + + ContainerElementConstraintMappingContextImpl nestedContext = nestedContainerElementContexts.get( nestedIndexes[0] ); + if ( nestedContext == null ) { + nestedContext = new ContainerElementConstraintMappingContextImpl( + typeContext, + parentContainerElementTarget, + null, + nestedIndexes[0], + null + ); + nestedContainerElementContexts.put( nestedIndexes[0], nestedContext ); + } + + if ( nestedIndexes.length > 1 ) { + return nestedContext.nestedContainerElement( Arrays.copyOfRange( nestedIndexes, 1, nestedIndexes.length ) ); + } + else { + return nestedContext; + } + } + + @Override + public ContainerElementConstraintMappingContext constraint(ConstraintDef definition) { + constraints.add( + new MetaConstraintBuilder( + definition + ) + // ConfiguredConstraint.forTypeArgument( definition, parentLocation, typeParameter, getContainerElementType() ) + ); + return this; + } + + CascadingMetaDataBuilder getContainerElementCascadingMetaDataBuilder(ConstraintLocation parentLocation) { + ConstraintLocation location = getCurrentConstraintLocation( parentLocation ); + return CascadingMetaDataBuilder.typeArgument( + parentLocation.getTypeForValidatorResolution(), + typeParameter, + false, + nestedContainerElementContexts.values() + .stream() + .map( context -> context.getContainerElementCascadingMetaDataBuilder( location ) ) + .collect( Collectors.toList() ), + Collections.emptyMap() + ); + } + + Set> build(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, + ValueExtractorManager valueExtractorManager, ConstraintLocation parentLocation) { + ConstraintLocation location = getCurrentConstraintLocation( parentLocation ); + return Stream.concat( + constraints.stream() + .map( c -> c.build( typeResolutionHelper, constraintHelper, valueExtractorManager, location ) ), + nestedContainerElementContexts.values() + .stream() + .map( c -> c.build( constraintHelper, typeResolutionHelper, valueExtractorManager, location ) ) + .flatMap( Set::stream ) + ) + .collect( Collectors.toSet() ); + } + + private ConstraintLocation getCurrentConstraintLocation(ConstraintLocation parentLocation) { + return ConstraintLocation.forTypeArgument( parentLocation, typeParameter, containerElementType ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/GroupConversionTargetContextHelper.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/GroupConversionTargetContextHelper.java new file mode 100644 index 0000000000..28f0df555a --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/GroupConversionTargetContextHelper.java @@ -0,0 +1,24 @@ +/* + * 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.cfg.propertyholder; + +import org.hibernate.validator.cfg.propertyholder.GroupConversionTargetContext; + +/** + * Adds internal method to {@link GroupConversionTargetContext} that allows adding group conversions. + * + * @author Marko Bekhta + */ +interface GroupConversionTargetContextHelper { + /** + * Adds a group conversion for this element. + * + * @param from the source group of the conversion + * @param to the target group of the conversion + */ + void addGroupConversion(Class from, Class to); +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/GroupConversionTargetContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/GroupConversionTargetContextImpl.java new file mode 100644 index 0000000000..7a57badafe --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/GroupConversionTargetContextImpl.java @@ -0,0 +1,33 @@ +/* + * 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.cfg.propertyholder; + +import org.hibernate.validator.cfg.propertyholder.GroupConversionTargetContext; + +/** + * Context allowing to set the target of a group conversion. + * + * @author Gunnar Morling + */ +class GroupConversionTargetContextImpl implements GroupConversionTargetContext { + + private final C cascadableContext; + private final Class from; + private final GroupConversionTargetContextHelper target; + + GroupConversionTargetContextImpl(Class from, C cascadableContext, GroupConversionTargetContextHelper target) { + this.from = from; + this.cascadableContext = cascadableContext; + this.target = target; + } + + @Override + public C to(Class to) { + target.addGroupConversion( from, to ); + return cascadableContext; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/PropertyConstraintMappingContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/PropertyConstraintMappingContextImpl.java new file mode 100644 index 0000000000..c961932378 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/PropertyConstraintMappingContextImpl.java @@ -0,0 +1,185 @@ +/* + * 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.cfg.propertyholder; + +import java.lang.invoke.MethodHandles; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.hibernate.validator.cfg.ConstraintDef; +import org.hibernate.validator.cfg.propertyholder.CascadableContainerElementConstraintMappingContext; +import org.hibernate.validator.cfg.propertyholder.ContainerElementConstraintMappingContext; +import org.hibernate.validator.cfg.propertyholder.PropertyConstraintMappingContext; +import org.hibernate.validator.cfg.propertyholder.PropertyHolderConstraintMappingContext; +import org.hibernate.validator.internal.metadata.aggregated.cascading.NonCascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.core.MetaConstraintBuilder; +import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; +import org.hibernate.validator.internal.metadata.raw.propertyholder.ConstrainedPropertyHolderElementBuilder; +import org.hibernate.validator.internal.metadata.raw.propertyholder.SimpleConstrainedPropertyHolderElementBuilder; +import org.hibernate.validator.internal.util.Contracts; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; + +/** + * Constraint mapping creational context which allows to configure the constraints for one proeprty holder simple property. + * The type of the property should be a simple type or a collection and no cascading is allowed for it. + * + * @author Marko Bekhta + */ +final class PropertyConstraintMappingContextImpl extends PropertyConstraintMappingContextImplBase + implements PropertyConstraintMappingContext { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private final PropertyHolderTypeConstraintMappingContextImpl typeContext; + + private final Map containerElementContexts = new HashMap<>(); + private final Set configuredPaths = new HashSet<>(); + + private final Class type; + + + PropertyConstraintMappingContextImpl(PropertyHolderTypeConstraintMappingContextImpl typeContext, String property, Class type) { + super( typeContext.getConstraintMapping(), property ); + this.typeContext = typeContext; + this.type = type; + } + + @Override + public PropertyConstraintMappingContext constraint(ConstraintDef definition) { + super.addConstraint( + new MetaConstraintBuilder( + definition + ) + ); + return this; + } + + @Override + public PropertyConstraintMappingContext property(String property, Class propertyType) { + return typeContext.property( property, propertyType ); + } + + @Override + public PropertyHolderConstraintMappingContext propertyHolder(String property) { + return typeContext.propertyHolder( property ); + } + + @Override + public ContainerElementConstraintMappingContext containerElementType(Class containerElementType) { + if ( type.getTypeParameters().length > 1 ) { + throw LOG.getNoTypeArgumentIndexIsGivenForTypeWithMultipleTypeArgumentsException( type ); + } + return containerElementType( containerElementType, 0 ); + } + + @Override + public ContainerElementConstraintMappingContext containerElementType(Class containerElementType, int index, int... nestedIndexes) { + Contracts.assertTrue( index >= 0, "Type argument index must not be negative" ); + + // HV-1428 Container element support is disabled for arrays + if ( type.isArray() ) { + throw LOG.getContainerElementConstraintsAndCascadedValidationNotSupportedOnArraysException( type ); + } + + if ( ( type.getTypeParameters().length == 0 ) ) { + throw LOG.getTypeIsNotAParameterizedNorArrayTypeException( type ); + } + + ContainerElementPathKey key = new ContainerElementPathKey( index, nestedIndexes ); + boolean configuredBefore = !configuredPaths.add( key ); + if ( configuredBefore ) { + throw LOG.getContainerElementTypeHasAlreadyBeenConfiguredViaProgrammaticApiException( + // TODO: add another exception method to log + null + ); + } + + // As we already checked that the specific path was not yet configured we should not worry about returning the same context here, + // as it means that there are some nested indexes which make a difference, And at the end a new context will be returned by call + // to containerElementContext#nestedContainerElement(). + ContainerElementConstraintMappingContextImpl containerElementContext = containerElementContexts.get( index ); + if ( containerElementContext == null ) { + containerElementContext = new ContainerElementConstraintMappingContextImpl( typeContext, this, type, index, containerElementType ); + containerElementContexts.put( index, containerElementContext ); + } + + if ( nestedIndexes.length > 0 ) { + return containerElementContext.nestedContainerElement( nestedIndexes ); + } + else { + return containerElementContext; + } + } + + @Override + public CascadableContainerElementConstraintMappingContext containerElementType(String mapping) { + return containerElementType( mapping, 0 ); + } + + @Override + public CascadableContainerElementConstraintMappingContext containerElementType(String mapping, int index, int... nestedIndexes) { + return null; + } + + @Override + protected ConstrainedPropertyHolderElementBuilder build() { + return new SimpleConstrainedPropertyHolderElementBuilder( + ConfigurationSource.API, + property, + type, + getConstraints(), + Collections.emptySet(), + NonCascadingMetaDataBuilder.INSTANCE + ); + } + + private static class ContainerElementPathKey { + + private final int index; + private final int[] nestedIndexes; + + public ContainerElementPathKey(int index, int[] nestedIndexes) { + this.index = index; + this.nestedIndexes = nestedIndexes; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + index; + result = prime * result + Arrays.hashCode( nestedIndexes ); + return result; + } + + @Override + public boolean equals(Object obj) { + if ( this == obj ) { + return true; + } + if ( obj == null ) { + return false; + } + if ( getClass() != obj.getClass() ) { + return false; + } + ContainerElementPathKey other = (ContainerElementPathKey) obj; + if ( index != other.index ) { + return false; + } + if ( !Arrays.equals( nestedIndexes, other.nestedIndexes ) ) { + return false; + } + return true; + } + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/PropertyConstraintMappingContextImplBase.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/PropertyConstraintMappingContextImplBase.java new file mode 100644 index 0000000000..ef7858b292 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/PropertyConstraintMappingContextImplBase.java @@ -0,0 +1,26 @@ +/* + * 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.cfg.propertyholder; + +import org.hibernate.validator.internal.metadata.raw.propertyholder.ConstrainedPropertyHolderElementBuilder; + +/** + * Base class for implementations of constraint mapping creational context types. + * + * @author Marko Bekhta + */ +abstract class PropertyConstraintMappingContextImplBase extends ConstraintContextImplBase { + + protected final String property; + + PropertyConstraintMappingContextImplBase(PropertyHolderConstraintMappingImpl mapping, String property) { + super( mapping ); + this.property = property; + } + + protected abstract ConstrainedPropertyHolderElementBuilder build(); +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/PropertyHolderConstraintMappingContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/PropertyHolderConstraintMappingContextImpl.java new file mode 100644 index 0000000000..48998844b7 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/PropertyHolderConstraintMappingContextImpl.java @@ -0,0 +1,70 @@ +/* + * 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.cfg.propertyholder; + +import java.util.Collections; + +import org.hibernate.validator.cfg.ConstraintDef; +import org.hibernate.validator.cfg.propertyholder.PropertyConstraintMappingContext; +import org.hibernate.validator.cfg.propertyholder.PropertyHolderConstraintMappingContext; +import org.hibernate.validator.internal.metadata.core.MetaConstraintBuilder; +import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; +import org.hibernate.validator.internal.metadata.raw.propertyholder.CascadingConstrainedPropertyHolderElementBuilder; +import org.hibernate.validator.internal.metadata.raw.propertyholder.ConstrainedPropertyHolderElementBuilder; + +/** + * Constraint mapping creational context which allows to configure the constraints for one property holder + * property that is a property holder itself. Hence cascading for it is allowed. + * + * @author Marko Bekhta + */ +final class PropertyHolderConstraintMappingContextImpl + extends CascadablePropertyHolderConstraintMappingContextImplBase + implements PropertyHolderConstraintMappingContext { + + private final PropertyHolderTypeConstraintMappingContextImpl typeContext; + + PropertyHolderConstraintMappingContextImpl(PropertyHolderTypeConstraintMappingContextImpl typeContext, String property) { + super( typeContext.getConstraintMapping(), property ); + this.typeContext = typeContext; + } + + @Override + protected PropertyHolderConstraintMappingContextImpl getThis() { + return this; + } + + @Override + public PropertyHolderConstraintMappingContext constraint(ConstraintDef definition) { + super.addConstraint( + new MetaConstraintBuilder( definition ) + ); + + return this; + } + + @Override + public PropertyConstraintMappingContext property(String property, Class propertyType) { + return typeContext.property( property, propertyType ); + } + + @Override + public PropertyHolderConstraintMappingContext propertyHolder(String property) { + return typeContext.propertyHolder( property ); + } + + @Override + protected ConstrainedPropertyHolderElementBuilder build() { + return new CascadingConstrainedPropertyHolderElementBuilder( + ConfigurationSource.API, + property, + getConstraints(), + Collections.emptySet(), + getCascadingMetaDataBuilder() + ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/PropertyHolderConstraintMappingImpl.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/PropertyHolderConstraintMappingImpl.java new file mode 100644 index 0000000000..b2e0189afa --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/PropertyHolderConstraintMappingImpl.java @@ -0,0 +1,80 @@ +/* + * 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.cfg.propertyholder; + +import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; +import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES; + +import java.lang.invoke.MethodHandles; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.validation.valueextraction.ValueExtractor; + +import org.hibernate.validator.cfg.propertyholder.PropertyHolderConstraintMapping; +import org.hibernate.validator.cfg.propertyholder.TypeConstraintMappingContext; +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.metadata.raw.propertyholder.PropertyHolderConfiguration; +import org.hibernate.validator.internal.util.Contracts; +import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; + +/** + * Implementation of {@link PropertyHolderConstraintMapping}. + * + * @author Marko Bekhta + */ +public class PropertyHolderConstraintMappingImpl implements PropertyHolderConstraintMapping { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private final Set configuredMappingNames; + private final Set typeContexts; + + public PropertyHolderConstraintMappingImpl() { + this.configuredMappingNames = newHashSet(); + this.typeContexts = newHashSet(); + } + + @Override + public final TypeConstraintMappingContext type(String propertyHolderMappingName) { + Contracts.assertNotNull( propertyHolderMappingName, MESSAGES.mappingNameMustNotBeNull() ); + + if ( configuredMappingNames.contains( propertyHolderMappingName ) ) { + throw LOG.getPropertyHolderMappingHasAlreadyBeenConfiguredViaProgrammaticApiException( propertyHolderMappingName ); + } + + PropertyHolderTypeConstraintMappingContextImpl typeContext = new PropertyHolderTypeConstraintMappingContextImpl( this, propertyHolderMappingName ); + typeContexts.add( typeContext ); + configuredMappingNames.add( propertyHolderMappingName ); + + return typeContext; + } + + public Set getConfiguredMappingNames() { + return configuredMappingNames; + } + + /** + * Returns all property holder configurations configured through this constraint mapping. + * + * @param constraintHelper constraint helper required for building constraint descriptors + * @param typeResolutionHelper type resolution helper + * @param valueExtractorManager the {@link ValueExtractor} manager + * + * @return a set of {@link PropertyHolderConfiguration}s with an element for each type configured through this mapping + */ + public Set getPropertyHolderConfigurations(ConstraintHelper constraintHelper, + TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager) { + return typeContexts.stream() + .map( context -> context.build() ) + .collect( Collectors.toSet() ); + } + +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/PropertyHolderTypeConstraintMappingContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/PropertyHolderTypeConstraintMappingContextImpl.java new file mode 100644 index 0000000000..3810d59694 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/PropertyHolderTypeConstraintMappingContextImpl.java @@ -0,0 +1,116 @@ +/* + * 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.cfg.propertyholder; + +import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; +import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES; + +import java.lang.invoke.MethodHandles; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +import org.hibernate.validator.cfg.propertyholder.PropertyConstraintMappingContext; +import org.hibernate.validator.cfg.propertyholder.PropertyHolderConstraintMappingContext; +import org.hibernate.validator.cfg.propertyholder.TypeConstraintMappingContext; +import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; +import org.hibernate.validator.internal.metadata.raw.propertyholder.ConstrainedPropertyHolderElementBuilder; +import org.hibernate.validator.internal.metadata.raw.propertyholder.PropertyHolderConfiguration; +import org.hibernate.validator.internal.util.Contracts; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; + +/** + * Constraint mapping creational context which allows to configure the class-level constraints for one bean. + * + * @author Hardy Ferentschik + * @author Gunnar Morling + * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI + * @author Marko Bekhta + */ +public final class PropertyHolderTypeConstraintMappingContextImpl extends ConstraintContextImplBase + implements TypeConstraintMappingContext { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private final String propertyHolderMappingName; + + private final Set propertyContexts = newHashSet(); + private final Set configuredMembers = newHashSet(); + + private List> defaultGroupSequence; + + PropertyHolderTypeConstraintMappingContextImpl(PropertyHolderConstraintMappingImpl mapping, String propertyHolderMappingName) { + super( mapping ); + this.propertyHolderMappingName = propertyHolderMappingName; + } + + @Override + public TypeConstraintMappingContext defaultGroupSequence(Class... defaultGroupSequence) { + this.defaultGroupSequence = Arrays.asList( defaultGroupSequence ); + return this; + } + + @Override + public PropertyConstraintMappingContext property(String property, Class propertyType) { + Contracts.assertNotNull( property, "The property name must not be null." ); + Contracts.assertNotEmpty( property, MESSAGES.propertyNameMustNotBeEmpty() ); + + if ( configuredMembers.contains( property ) ) { + throw LOG.getPropertyHolderMappingPropertyHasAlreadyBeenConfiguredViaProgrammaticApiException( propertyHolderMappingName, property ); + } + + PropertyConstraintMappingContextImpl context = new PropertyConstraintMappingContextImpl( + this, + property, + propertyType + ); + + configuredMembers.add( property ); + propertyContexts.add( context ); + return context; + } + + @Override + public PropertyHolderConstraintMappingContext propertyHolder(String property) { + Contracts.assertNotNull( property, "The property name must not be null." ); + Contracts.assertNotEmpty( property, MESSAGES.propertyNameMustNotBeEmpty() ); + + if ( configuredMembers.contains( property ) ) { + throw LOG.getPropertyHolderMappingPropertyHasAlreadyBeenConfiguredViaProgrammaticApiException( propertyHolderMappingName, property ); + } + + PropertyHolderConstraintMappingContextImpl context = new PropertyHolderConstraintMappingContextImpl( + this, + property + ); + + configuredMembers.add( property ); + propertyContexts.add( context ); + return context; + } + + PropertyHolderConfiguration build() { + return new PropertyHolderConfiguration( + ConfigurationSource.API, + propertyHolderMappingName, + buildConstraintElements(), + defaultGroupSequence + ); + } + + private Set buildConstraintElements() { + Set elements = newHashSet(); + + //properties + for ( PropertyConstraintMappingContextImplBase propertyContext : propertyContexts ) { + elements.add( propertyContext.build() ); + } + + return elements; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/package-info.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/package-info.java new file mode 100644 index 0000000000..703f748cba --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/propertyholder/package-info.java @@ -0,0 +1,12 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ + +/** + * Creational context implementations of the API for programmatic + * constraint definition. + */ +package org.hibernate.validator.internal.cfg.propertyholder; diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ConfigurationImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ConfigurationImpl.java index 986bbe5b51..369d9468d9 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ConfigurationImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ConfigurationImpl.java @@ -37,7 +37,9 @@ import org.hibernate.validator.HibernateValidatorConfiguration; import org.hibernate.validator.cfg.ConstraintMapping; +import org.hibernate.validator.cfg.propertyholder.PropertyHolderConstraintMapping; import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping; +import org.hibernate.validator.internal.cfg.propertyholder.PropertyHolderConstraintMappingImpl; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl; import org.hibernate.validator.internal.engine.resolver.TraversableResolvers; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor; @@ -99,6 +101,7 @@ public class ConfigurationImpl implements HibernateValidatorConfiguration, Confi // HV-specific options private final Set programmaticMappings = newHashSet(); + private final Set propertyHolderConstraintMappings = newHashSet(); private boolean failFast; private ClassLoader externalClassLoader; private final MethodValidationConfiguration.Builder methodValidationConfigurationBuilder = new MethodValidationConfiguration.Builder(); @@ -327,6 +330,11 @@ public final DefaultConstraintMapping createConstraintMapping() { return new DefaultConstraintMapping( new JavaBeanHelper( getterPropertySelectionStrategyToUse ) ); } + @Override + public PropertyHolderConstraintMapping createPropertyHolderConstraintMapping() { + return new PropertyHolderConstraintMappingImpl(); + } + @Override public final HibernateValidatorConfiguration addMapping(ConstraintMapping mapping) { Contracts.assertNotNull( mapping, MESSAGES.parameterMustNotBeNull( "mapping" ) ); @@ -335,6 +343,14 @@ public final HibernateValidatorConfiguration addMapping(ConstraintMapping mappin return this; } + @Override + public final HibernateValidatorConfiguration addPropertyHolderMapping(PropertyHolderConstraintMapping mapping) { + Contracts.assertNotNull( mapping, MESSAGES.parameterMustNotBeNull( "mapping" ) ); + + this.propertyHolderConstraintMappings.add( (PropertyHolderConstraintMappingImpl) mapping ); + return this; + } + @Override public final HibernateValidatorConfiguration addProperty(String name, String value) { if ( value != null ) { @@ -535,6 +551,10 @@ public final Set getProgrammaticMappings() { return programmaticMappings; } + public final Set getPropertyHolderConstraintMappings() { + return propertyHolderConstraintMappings; + } + private boolean isSpecificProvider() { return validationBootstrapParameters.getProvider() != null; } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java index 40096aa37c..4881f5101c 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java @@ -36,6 +36,7 @@ import org.hibernate.validator.cfg.ConstraintMapping; import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping; +import org.hibernate.validator.internal.cfg.propertyholder.PropertyHolderConstraintMappingImpl; import org.hibernate.validator.internal.engine.constraintdefinition.ConstraintDefinitionContribution; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; import org.hibernate.validator.internal.engine.constraintvalidation.HibernateConstraintValidatorInitializationContextImpl; @@ -46,9 +47,9 @@ import org.hibernate.validator.internal.metadata.manager.ConstraintMetaDataManager; import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; import org.hibernate.validator.internal.metadata.provider.ProgrammaticMetaDataProvider; -import org.hibernate.validator.internal.metadata.provider.PropertyHolderMetaDataProvider; +import org.hibernate.validator.internal.metadata.provider.proeprtyholder.ProgrammaticPropertyHolderMetaDataProvider; +import org.hibernate.validator.internal.metadata.provider.proeprtyholder.PropertyHolderMetaDataProvider; import org.hibernate.validator.internal.metadata.provider.XmlMetaDataProvider; -import org.hibernate.validator.internal.metadata.provider.proeprtyholder.DummyPropertyHolderMetaDataProvider; import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; import org.hibernate.validator.internal.properties.propertyholder.PropertyAccessorCreatorProvider; @@ -101,6 +102,13 @@ public class ValidatorFactoryImpl implements HibernateValidatorFactory { @Immutable private final Set constraintMappings; + /** + * Programmatic constraints for property holders passed via the Hibernate Validator specific API. + * Empty if there are no programmatic constraints. + */ + @Immutable + private final Set propertyHolderConstraintMappings; + /** * Helper for dealing with built-in validators and determining custom constraint annotations. */ @@ -182,6 +190,10 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) { ) ); + this.propertyHolderConstraintMappings = Collections.unmodifiableSet( + getPropertyHolderConstraintMappings( configurationState ) + ); + registerCustomConstraintValidators( constraintMappings, constraintHelper ); this.methodValidationConfiguration = new MethodValidationConfiguration.Builder() @@ -254,6 +266,17 @@ private static Set getConstraintMappings(TypeResolutio return constraintMappings; } + private static Set getPropertyHolderConstraintMappings(ConfigurationState configurationState) { + Set constraintMappings = newHashSet(); + + if ( configurationState instanceof ConfigurationImpl ) { + ConfigurationImpl hibernateConfiguration = (ConfigurationImpl) configurationState; + constraintMappings.addAll( hibernateConfiguration.getPropertyHolderConstraintMappings() ); + } + + return constraintMappings; + } + @Override public Validator getValidator() { return createValidator( @@ -406,7 +429,14 @@ private List buildMetaDataProviders() { } private List buildPropertyHolderMetaDataProvider() { - return Collections.singletonList( new DummyPropertyHolderMetaDataProvider() ); + if ( !propertyHolderConstraintMappings.isEmpty() ) { + return Collections.singletonList( new ProgrammaticPropertyHolderMetaDataProvider( + constraintHelper, typeResolutionHelper, valueExtractorManager, propertyHolderConstraintMappings + ) ); + } + else { + return Collections.emptyList(); + } } private static boolean checkPropertiesForBoolean(Map properties, String propertyKey, boolean programmaticValue) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java index 283777414a..40899c93e7 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java @@ -337,7 +337,6 @@ private ValidationContextBuilder getValidationContextBuilder() { validatorScopedContext, TraversableResolvers.wrapWithCachingForSingleValidation( traversableResolver, validatorScopedContext.isTraversableResolverResultCacheEnabled() ), constraintValidatorInitializationContext - ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraintBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraintBuilder.java index 9a6e970cef..821cb61a40 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraintBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraintBuilder.java @@ -7,38 +7,82 @@ package org.hibernate.validator.internal.metadata.core; import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.security.AccessController; +import java.security.PrivilegedAction; +import javax.validation.ValidationException; + +import org.hibernate.validator.cfg.AnnotationDef; +import org.hibernate.validator.cfg.ConstraintDef; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; import org.hibernate.validator.internal.metadata.location.ConstraintLocation; -import org.hibernate.validator.internal.properties.Constrainable; import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.util.annotation.AnnotationDescriptor; 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.GetDeclaredMethodHandle; /** * @author Marko Bekhta */ public class MetaConstraintBuilder
{ + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final MethodHandle CREATE_ANNOTATION_DESCRIPTOR_METHOD_HANDLE = + run( GetDeclaredMethodHandle.andMakeAccessible( MethodHandles.lookup(), AnnotationDef.class, "createAnnotationDescriptor" ) ); + private final ConstraintAnnotationDescriptor annotationDescriptor; - private final ConstraintLocation.Builder locationBuilder; - public MetaConstraintBuilder(ConstraintAnnotationDescriptor annotationDescriptor, ConstraintLocation.Builder locationBuilder) { + public MetaConstraintBuilder(ConstraintAnnotationDescriptor annotationDescriptor) { this.annotationDescriptor = annotationDescriptor; - this.locationBuilder = locationBuilder; } - public MetaConstraint build(TypeResolutionHelper typeResolutionHelper, ConstraintHelper constraintHelper, ValueExtractorManager valueExtractorManager, Constrainable constrainable) { - ConstraintLocation constraintLocation = locationBuilder.build( constrainable ); + public MetaConstraintBuilder(ConstraintDef constraintDef) { + this( createAnnotationDescriptor( constraintDef ) ); + } + + public MetaConstraint build( + TypeResolutionHelper typeResolutionHelper, + ConstraintHelper constraintHelper, + ValueExtractorManager valueExtractorManager, + ConstraintLocation constraintLocation) { return MetaConstraints.create( typeResolutionHelper, valueExtractorManager, new ConstraintDescriptorImpl<>( constraintHelper, - constrainable, + constraintLocation.getConstrainable(), annotationDescriptor, constraintLocation.getKind() ), constraintLocation ); } + + private static ConstraintAnnotationDescriptor createAnnotationDescriptor(ConstraintDef constraint) { + try { + @SuppressWarnings("unchecked") + AnnotationDescriptor annotationDescriptor = (AnnotationDescriptor) CREATE_ANNOTATION_DESCRIPTOR_METHOD_HANDLE.invoke( constraint ); + return new ConstraintAnnotationDescriptor<>( annotationDescriptor ); + } + catch (Throwable e) { + if ( e instanceof ValidationException ) { + throw (ValidationException) e; + } + throw LOG.getUnableToCreateAnnotationDescriptor( constraint.getClass(), e ); + } + } + + /** + * 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 V run(PrivilegedAction action) { + return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); + } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ConstraintLocation.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ConstraintLocation.java index 9d0b8ba182..20a53feee4 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ConstraintLocation.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ConstraintLocation.java @@ -104,14 +104,6 @@ static ConstraintLocation forParameter(Callable callable, int index) { */ ConstraintLocationKind getKind(); - interface Builder { - ConstraintLocation build(Constrainable constrainable); - - static Builder forPropertyHolderProperty() { - return new PropertyHolderPropertyConstraintLocationBuilder(); - } - } - enum ConstraintLocationKind { TYPE( ElementType.TYPE ), CONSTRUCTOR( ElementType.CONSTRUCTOR ), diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/PropertyHolderPropertyConstraintLocationBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/PropertyHolderPropertyConstraintLocationBuilder.java deleted file mode 100644 index 17168c8e7c..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/PropertyHolderPropertyConstraintLocationBuilder.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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.location; - -import org.hibernate.validator.internal.metadata.location.ConstraintLocation.Builder; -import org.hibernate.validator.internal.properties.Constrainable; -import org.hibernate.validator.internal.properties.Field; -import org.hibernate.validator.internal.util.Contracts; - -/** - * @author Marko Bekhta - */ -public class PropertyHolderPropertyConstraintLocationBuilder implements Builder { - - @Override - public ConstraintLocation build(Constrainable constrainable) { - Contracts.assertTrue( constrainable instanceof Field, "Only Field instances are accepted." ); - - return ConstraintLocation.forField( constrainable.as( Field.class ) ); - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/ConstraintMetaDataManager.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/ConstraintMetaDataManager.java index ae1ce9bbf6..38f7173ec7 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/ConstraintMetaDataManager.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/ConstraintMetaDataManager.java @@ -14,7 +14,7 @@ import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; -import org.hibernate.validator.internal.metadata.provider.PropertyHolderMetaDataProvider; +import org.hibernate.validator.internal.metadata.provider.proeprtyholder.PropertyHolderMetaDataProvider; import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; import org.hibernate.validator.internal.properties.propertyholder.PropertyAccessorCreatorProvider; import org.hibernate.validator.internal.util.ExecutableHelper; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/PropertyHolderBeanMetaDataProvider.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/PropertyHolderBeanMetaDataProvider.java index 9af4971ec0..8566b50c1c 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/PropertyHolderBeanMetaDataProvider.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/manager/PropertyHolderBeanMetaDataProvider.java @@ -17,7 +17,7 @@ import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl; import org.hibernate.validator.internal.metadata.aggregated.PropertyHolderBeanMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; -import org.hibernate.validator.internal.metadata.provider.PropertyHolderMetaDataProvider; +import org.hibernate.validator.internal.metadata.provider.proeprtyholder.PropertyHolderMetaDataProvider; import org.hibernate.validator.internal.metadata.raw.propertyholder.PropertyHolderConfiguration; import org.hibernate.validator.internal.properties.propertyholder.PropertyAccessorCreatorProvider; import org.hibernate.validator.internal.util.TypeResolutionHelper; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/proeprtyholder/DummyPropertyHolderMetaDataProvider.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/proeprtyholder/DummyPropertyHolderMetaDataProvider.java index 262607c7fc..1b797b8826 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/proeprtyholder/DummyPropertyHolderMetaDataProvider.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/proeprtyholder/DummyPropertyHolderMetaDataProvider.java @@ -25,8 +25,6 @@ import org.hibernate.validator.cfg.defs.SizeDef; import org.hibernate.validator.internal.metadata.aggregated.cascading.PropertyHolderCascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.MetaConstraintBuilder; -import org.hibernate.validator.internal.metadata.location.ConstraintLocation; -import org.hibernate.validator.internal.metadata.provider.PropertyHolderMetaDataProvider; import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; import org.hibernate.validator.internal.metadata.raw.propertyholder.CascadingConstrainedPropertyHolderElementBuilder; import org.hibernate.validator.internal.metadata.raw.propertyholder.PropertyHolderConfiguration; @@ -76,12 +74,10 @@ private static PropertyHolderConfiguration user() { String.class, CollectionHelper.asSet( new MetaConstraintBuilder( - createAnnotationDescriptor( new NotNullDef() ), - ConstraintLocation.Builder.forPropertyHolderProperty() + createAnnotationDescriptor( new NotNullDef() ) ), new MetaConstraintBuilder( - createAnnotationDescriptor( new SizeDef().min( 5 ).max( 10 ) ), - ConstraintLocation.Builder.forPropertyHolderProperty() + createAnnotationDescriptor( new SizeDef().min( 5 ).max( 10 ) ) ) ), Collections.emptySet() @@ -92,12 +88,10 @@ private static PropertyHolderConfiguration user() { String.class, CollectionHelper.asSet( new MetaConstraintBuilder( - createAnnotationDescriptor( new NotNullDef() ), - ConstraintLocation.Builder.forPropertyHolderProperty() + createAnnotationDescriptor( new NotNullDef() ) ), new MetaConstraintBuilder( - createAnnotationDescriptor( new EmailDef() ), - ConstraintLocation.Builder.forPropertyHolderProperty() + createAnnotationDescriptor( new EmailDef() ) ) ), Collections.emptySet() @@ -119,17 +113,15 @@ private static PropertyHolderConfiguration user() { List.class, CollectionHelper.asSet( new MetaConstraintBuilder( - createAnnotationDescriptor( new NotNullDef() ), - ConstraintLocation.Builder.forPropertyHolderProperty() + createAnnotationDescriptor( new NotNullDef() ) ), new MetaConstraintBuilder( - createAnnotationDescriptor( new SizeDef().max( 2 ) ), - ConstraintLocation.Builder.forPropertyHolderProperty() + createAnnotationDescriptor( new SizeDef().max( 2 ) ) ) ), Collections.emptySet(), PropertyHolderCascadingMetaDataBuilder.propertyHolderContainer( - false, //TODO: need to throw exception if cascading is true here or completely remove the ability to set it. + false, //TODO: need to throw exception if cascading is true here or maybe completely remove the ability to set it ? List.class, Collections.singletonMap( List.class.getTypeParameters()[0], @@ -161,12 +153,10 @@ private static PropertyHolderConfiguration address() { String.class, CollectionHelper.asSet( new MetaConstraintBuilder( - createAnnotationDescriptor( new NotNullDef() ), - ConstraintLocation.Builder.forPropertyHolderProperty() + createAnnotationDescriptor( new NotNullDef() ) ), new MetaConstraintBuilder( - createAnnotationDescriptor( new SizeDef().min( 5 ).max( 10 ) ), - ConstraintLocation.Builder.forPropertyHolderProperty() + createAnnotationDescriptor( new SizeDef().min( 5 ).max( 10 ) ) ) ), Collections.emptySet() @@ -177,12 +167,10 @@ private static PropertyHolderConfiguration address() { Long.class, CollectionHelper.asSet( new MetaConstraintBuilder( - createAnnotationDescriptor( new NotNullDef() ), - ConstraintLocation.Builder.forPropertyHolderProperty() + createAnnotationDescriptor( new NotNullDef() ) ), new MetaConstraintBuilder( - createAnnotationDescriptor( new MinDef().value( 0 ) ), - ConstraintLocation.Builder.forPropertyHolderProperty() + createAnnotationDescriptor( new MinDef().value( 0 ) ) ) ), Collections.emptySet() diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/proeprtyholder/ProgrammaticPropertyHolderMetaDataProvider.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/proeprtyholder/ProgrammaticPropertyHolderMetaDataProvider.java new file mode 100644 index 0000000000..38c18a6121 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/proeprtyholder/ProgrammaticPropertyHolderMetaDataProvider.java @@ -0,0 +1,88 @@ +/* + * 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.provider.proeprtyholder; + +import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; + +import java.lang.invoke.MethodHandles; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import org.hibernate.validator.internal.cfg.propertyholder.PropertyHolderConstraintMappingImpl; +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; +import org.hibernate.validator.internal.metadata.raw.propertyholder.PropertyHolderConfiguration; +import org.hibernate.validator.internal.util.CollectionHelper; +import org.hibernate.validator.internal.util.Contracts; +import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.util.stereotypes.Immutable; + +/** + * A property holder {@link MetaDataProvider} based on the programmatic constraint API. + * + * @author Gunnar Morling + * @author Marko Bekhta + */ +public class ProgrammaticPropertyHolderMetaDataProvider implements PropertyHolderMetaDataProvider { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + @Immutable + private final Map configuredBeans; + + public ProgrammaticPropertyHolderMetaDataProvider(ConstraintHelper constraintHelper, + TypeResolutionHelper typeResolutionHelper, + ValueExtractorManager valueExtractorManager, + Set constraintMappings) { + Contracts.assertNotNull( constraintMappings ); + + configuredBeans = CollectionHelper.toImmutableMap( + createBeanConfigurations( constraintMappings, constraintHelper, typeResolutionHelper, valueExtractorManager ) + ); + + assertUniquenessOfConfiguredTypes( constraintMappings ); + } + + private static void assertUniquenessOfConfiguredTypes(Set mappings) { + Set allConfiguredTypes = newHashSet(); + + for ( PropertyHolderConstraintMappingImpl constraintMapping : mappings ) { + for ( String propertyHolderMappingName : constraintMapping.getConfiguredMappingNames() ) { + if ( allConfiguredTypes.contains( propertyHolderMappingName ) ) { + throw LOG.getPropertyHolderMappingHasAlreadyBeenConfiguredViaProgrammaticApiException( propertyHolderMappingName ); + } + } + + allConfiguredTypes.addAll( constraintMapping.getConfiguredMappingNames() ); + } + } + + private static Map createBeanConfigurations(Set mappings, ConstraintHelper constraintHelper, + TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager) { + final Map configuredBeans = new HashMap<>(); + for ( PropertyHolderConstraintMappingImpl mapping : mappings ) { + Set beanConfigurations = mapping.getPropertyHolderConfigurations( constraintHelper, typeResolutionHelper, + valueExtractorManager + ); + + for ( PropertyHolderConfiguration beanConfiguration : beanConfigurations ) { + configuredBeans.put( beanConfiguration.getMappingName(), beanConfiguration ); + } + } + return configuredBeans; + } + + @Override + public Optional getBeanConfiguration(String mappingName) { + return Optional.ofNullable( configuredBeans.get( mappingName ) ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/PropertyHolderMetaDataProvider.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/proeprtyholder/PropertyHolderMetaDataProvider.java similarity index 87% rename from engine/src/main/java/org/hibernate/validator/internal/metadata/provider/PropertyHolderMetaDataProvider.java rename to engine/src/main/java/org/hibernate/validator/internal/metadata/provider/proeprtyholder/PropertyHolderMetaDataProvider.java index d38ac6fcd9..2b3c47f389 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/PropertyHolderMetaDataProvider.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/proeprtyholder/PropertyHolderMetaDataProvider.java @@ -4,7 +4,7 @@ * License: Apache License, Version 2.0 * See the license.txt file in the root directory or . */ -package org.hibernate.validator.internal.metadata.provider; +package org.hibernate.validator.internal.metadata.provider.proeprtyholder; import java.util.Optional; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/ConstrainedPropertyHolderElementBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/ConstrainedPropertyHolderElementBuilder.java index 58e193d945..df9a20a9c9 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/ConstrainedPropertyHolderElementBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/propertyholder/ConstrainedPropertyHolderElementBuilder.java @@ -15,6 +15,7 @@ import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.core.MetaConstraintBuilder; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; import org.hibernate.validator.internal.metadata.raw.ConstrainedField; @@ -73,8 +74,9 @@ public ConstrainedElement build(TypeResolutionHelper typeResolutionHelper, Const protected Set> toMetaConstraints(TypeResolutionHelper typeResolutionHelper, ConstraintHelper constraintHelper, ValueExtractorManager valueExtractorManager, PropertyHolderProperty property, Collection> collection) { Set> builtConstraints = new HashSet<>( constraints.size() ); + ConstraintLocation constraintLocation = ConstraintLocation.forField( property ); for ( MetaConstraintBuilder builder : constraints ) { - builtConstraints.add( builder.build( typeResolutionHelper, constraintHelper, valueExtractorManager, property ) ); + builtConstraints.add( builder.build( typeResolutionHelper, constraintHelper, valueExtractorManager, constraintLocation ) ); } return builtConstraints; } diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java b/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java index c04731319f..bae671c137 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java @@ -887,4 +887,13 @@ ConstraintDefinitionException getConstraintValidatorDefinitionConstraintMismatch @Message(id = 252, value = "Unexpected property type in a given property holder. Expected that '%3$s' will be of %1$s type, but instead it is %2$s.") ValidationException getUnexpextedPropertyTypeInPropertyHolderException(Type expecetedType, @FormatWith(ClassObjectFormatter.class) Class realType, String propertyName); + + @Message(id = 253, value = "%s is configured more than once via the programmatic constraint declaration API.") + ValidationException getPropertyHolderMappingHasAlreadyBeenConfiguredViaProgrammaticApiException(String mappingName); + + @Message(id = 254, value = "Property \"%2$s\" in mapping %1$s is configured more than once via the programmatic constraint declaration API.") + ValidationException getPropertyHolderMappingPropertyHasAlreadyBeenConfiguredViaProgrammaticApiException(String mappingName, String propertyName); + + @Message(id = 255, value = "Object of type \"%1$s\" is not a property holder, and cannot be validated as one.") + ValidationException getCannotConvertToPropertyHolderException(@FormatWith(ClassObjectFormatter.class) Class clazz); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/logging/Messages.java b/engine/src/main/java/org/hibernate/validator/internal/util/logging/Messages.java index f03e6102ee..9bd56a5ae9 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/logging/Messages.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/logging/Messages.java @@ -97,4 +97,8 @@ public interface Messages { @Message(value = "The annotation type must be annotated with @javax.validation.Constraint when creating a constraint definition.", format = Message.Format.NO_FORMAT) String annotationTypeMustBeAnnotatedWithConstraint(); + + @Message(value = "Mapping name must not be null when creating a constraint mapping.", + format = Message.Format.NO_FORMAT) + String mappingNameMustNotBeNull(); } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/propertyholder/ValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/propertyholder/ValidatorTest.java index 14bd28771d..88b959a485 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/propertyholder/ValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/propertyholder/ValidatorTest.java @@ -18,12 +18,19 @@ import java.util.Set; import javax.validation.ConstraintViolation; -import javax.validation.Valid; +import javax.validation.Validation; import javax.validation.constraints.Email; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; +import org.hibernate.validator.HibernateValidator; +import org.hibernate.validator.HibernateValidatorConfiguration; +import org.hibernate.validator.cfg.defs.EmailDef; +import org.hibernate.validator.cfg.defs.MinDef; +import org.hibernate.validator.cfg.defs.NotNullDef; +import org.hibernate.validator.cfg.defs.SizeDef; +import org.hibernate.validator.cfg.propertyholder.PropertyHolderConstraintMapping; import org.hibernate.validator.internal.engine.ValidatorImpl; import org.testng.annotations.Test; @@ -32,7 +39,7 @@ * @author Marko Bekhta */ public class ValidatorTest { - @Test + @Test(enabled = false) public void testSimplePropertyHolder() { ValidatorImpl validator = (ValidatorImpl) getValidator(); @@ -63,7 +70,7 @@ public void testSimplePropertyHolder() { ); } - @Test + @Test(enabled = false) public void testPropertyHolderContainerElements() { ValidatorImpl validator = (ValidatorImpl) getValidator(); @@ -96,7 +103,67 @@ public void testPropertyHolderContainerElements() { ); } - private static class Bar { - List<@Valid Bar> bars; + @Test + public void testProgrammaticMapping() { + HibernateValidatorConfiguration configuration = Validation.byProvider( HibernateValidator.class ) + .configure(); + PropertyHolderConstraintMapping mapping = configuration.createPropertyHolderConstraintMapping(); + + mapping.type( "user" ) + .property( "name", String.class ) + .constraint( new NotNullDef() ) + .constraint( new SizeDef().min( 5 ) ) + + .property( "email", String.class ) + .constraint( new NotNullDef() ) + .constraint( new EmailDef() ) + + .propertyHolder( "address" ) + .valid( "address" ) + + .property( "secondaryAddresses", List.class ) + .constraint( new SizeDef().max( 2 ) ) + .constraint( new NotNullDef() ) +// .containerElementType() +// .valid( "addess" ) + ; + mapping.type( "address" ) + .property( "street", String.class ) + .constraint( new NotNullDef() ) + .constraint( new SizeDef().min( 5 ).max( 10 ) ) + .property( "buildingNumber", Long.class ) + .constraint( new NotNullDef() ) + .constraint( new MinDef().value( 0L ) ); + + configuration.addPropertyHolderMapping( mapping ); + + ValidatorImpl validator = (ValidatorImpl) configuration.buildValidatorFactory().getValidator(); + + Map address = new HashMap<>(); + address.put( "street", "str" ); + address.put( "buildingNumber", -1L ); + + Map user = new HashMap<>(); + user.put( "name", "jhon" ); + user.put( "email", "not a mail" ); + user.put( "address", address ); + + Set> constraintViolations = validator.validatePropertyHolder( user, "user" ); + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( Size.class ).withProperty( "name" ), + violationOf( Email.class ).withProperty( "email" ), + violationOf( Min.class ) + .withPropertyPath( pathWith() + .property( "address" ) + .property( "buildingNumber" ) + ), + violationOf( Size.class ) + .withPropertyPath( pathWith() + .property( "address" ) + .property( "street" ) + ), + violationOf( NotNull.class ).withProperty( "secondaryAddresses" ) + ); + } }