diff --git a/.travis.yml b/.travis.yml index 1c7f86505d..eb421ac9dd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,8 +16,6 @@ addons: cache: directories: - $HOME/.m2 -before_install: - - BUILD_OPTIONS='-s settings-example.xml' install: # The Maven install provided by Travis is outdated, use Maven wrapper to get the latest version - mvn -N io.takari:maven:wrapper diff --git a/README.md b/README.md index ec1fe6f9a1..977b7886d9 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ # Hibernate Validator -*Version: 6.0.9.Final - 27-03-2018* +*Version: 6.1.0.Final - 25-10-2019* ## What is it? -This is the reference implementation of [JSR-380 - Bean Validation 2.0](http://beanvalidation.org/). -Bean Validation defines a metadata model and API for JavaBean as well as method validation. +This is the reference implementation of [Jakarta Bean Validation 2.0](http://beanvalidation.org/). +Jakarta Bean Validation defines a metadata model and API for JavaBean as well as method validation. The default metadata source are annotations, with the ability to override and extend -the meta-data through the use of XML validation descriptors. +the metadata through the use of XML validation descriptors. ## Documentation @@ -20,7 +20,7 @@ The full list of changes for this release can be found in changelog.txt. ## System Requirements -JDK 1.8 or above. +JDK 8 or above. ## Using Hibernate Validator @@ -30,31 +30,31 @@ the JBoss Logging API, an abstraction layer which supports several logging solut provided by the JDK) as implementation. Just add a supported logging library to the classpath (e.g. _log4j-<version>.jar_) and JBoss Logging will delegate any log requests to that provider. -* Add the following to your Maven or Ivy dependency list +* Add the following artifact to your Maven/Ivy/Gradle dependency list: org.hibernate.validator hibernate-validator - 6.0.9.Final + 6.1.0.Final You also need an API and implementation of the Unified Expression Language. These dependencies must be explicitly added in an SE environment. - In an EE environment they are often already provided. + In a Jakarta EE environment, they are often already provided. org.glassfish - javax.el - 3.0.1-b09 + jakarta.el + 3.0.3 -* Bean Validation defines integration points with [CDI](http://jcp.org/en/jsr/detail?id=346). If your application runs +* Jakarta Bean Validation defines integration points with [CDI](http://jcp.org/en/jsr/detail?id=346). If your application runs in an environment which does not provide this integration out of the box, you may use the Hibernate Validator CDI portable extension by adding the following dependency: org.hibernate.validator hibernate-validator-cdi - 6.0.9.Final + 6.1.0.Final * _hibernate-validator-annotation-processor-<version>.jar_ is an optional jar which can be integrated with your build @@ -63,34 +63,18 @@ documentation](https://docs.jboss.org/hibernate/stable/validator/reference/en-US ## Licensing -Hibernate Validator itself as well as the Bean Validation API and TCK are all provided and distributed under +Hibernate Validator itself as well as the Jakarta Bean Validation API and TCK are all provided and distributed under the Apache Software License 2.0. Refer to license.txt for more information. ## Build from Source -You can build Hibernate Validator from source by cloning the git repository git://github.com/hibernate/hibernate-validator.git. -You will also need a JDK 8 and Maven 3 (>= 3.3.1). With these prerequisites in place you can compile the source via: +You can build Hibernate Validator from source by cloning the git repository `git://github.com/hibernate/hibernate-validator.git`. +You will also need a JDK 8+ and Maven 3 (>= 3.3.1). With these prerequisites in place you can compile the source via: - mvn -s settings-example.xml clean install + mvn clean install There are more build options available as well. For more information refer to [Contributing to Hibernate Validator](http://hibernate.org/validator/contribute/). -### Build on JDK 9 - -To build Hibernate Validator with JDK 9, export the following environment variable: - - export MAVEN_OPTS="--add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-modules=java.xml.bind" - -Then the build can be started like this: - - mvn -s settings-example.xml clean install - -Here are the reasons why we added the various build options: - - * add-opens java.security: required by wildfly-maven-plugin:execute-commands (for the WildFly integration tests and the TCK runner running in container mode) - * add-opens java.lang: required by JRuby for Asciidoc processing - * add-modules java.xml.bind: required by the forbiddenapis Maven plugin - ## Continuous Integration The official Continuous Integration service for the project is hosted on [ci.hibernate.org](http://ci.hibernate.org/view/Validator/). @@ -100,7 +84,7 @@ We provide a `.travis.yml` file so that you can enable CI for your GitHub fork b ## Hibernate Validator URLs * [Home Page](http://hibernate.org/validator/) -* [Bean Validation Home](http://beanvalidation.org/) +* [Jakarta Bean Validation Home](http://beanvalidation.org/) * [Downloads](http://hibernate.org/validator/downloads/) * [Mailing Lists](http://hibernate.org/community/) * [Issue Tracking](https://hibernate.atlassian.net/browse/HV) diff --git a/annotation-processor/pom.xml b/annotation-processor/pom.xml index 3687fd8c44..0268ca502b 100644 --- a/annotation-processor/pom.xml +++ b/annotation-processor/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.1.0.Final ../pom.xml diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/AbstractElementVisitor.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/AbstractElementVisitor.java index b093800b2a..db9cb070a4 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/AbstractElementVisitor.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/AbstractElementVisitor.java @@ -10,7 +10,7 @@ import java.util.Set; import javax.lang.model.element.ElementVisitor; -import javax.lang.model.util.ElementKindVisitor6; +import javax.lang.model.util.ElementKindVisitor8; import org.hibernate.validator.ap.internal.checks.ConstraintCheckIssue; import org.hibernate.validator.ap.internal.util.CollectionHelper; @@ -25,7 +25,7 @@ * * @author Marko Bekhta */ -public class AbstractElementVisitor extends ElementKindVisitor6 { +public class AbstractElementVisitor extends ElementKindVisitor8 { protected final MessagerAdapter messager; diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/checks/AnnotationTypeMemberCheck.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/checks/AnnotationTypeMemberCheck.java index b323fef90c..f4196445be 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/checks/AnnotationTypeMemberCheck.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/checks/AnnotationTypeMemberCheck.java @@ -17,8 +17,8 @@ import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.WildcardType; -import javax.lang.model.util.SimpleAnnotationValueVisitor6; -import javax.lang.model.util.TypeKindVisitor6; +import javax.lang.model.util.SimpleAnnotationValueVisitor8; +import javax.lang.model.util.TypeKindVisitor8; import javax.lang.model.util.Types; import org.hibernate.validator.ap.internal.util.AnnotationApiHelper; @@ -225,13 +225,13 @@ private ExecutableElement getMethod(TypeElement element, String name) { private DeclaredType getComponentTypeOfArrayReturnType(ExecutableElement method) { return method.getReturnType().accept( - new TypeKindVisitor6() { + new TypeKindVisitor8() { @Override public DeclaredType visitArray(ArrayType t, Void p) { return t.getComponentType().accept( - new TypeKindVisitor6() { + new TypeKindVisitor8() { @Override public DeclaredType visitDeclared(DeclaredType t, Void p) { @@ -259,7 +259,7 @@ public DeclaredType visitDeclared(DeclaredType t, Void p) { private boolean validateWildcardBounds(TypeMirror type, final TypeMirror expectedExtendsBound, final TypeMirror expectedSuperBound) { Boolean theValue = type.accept( - new TypeKindVisitor6() { + new TypeKindVisitor8() { @Override public Boolean visitWildcard(WildcardType t, Void p) { @@ -289,7 +289,7 @@ private boolean isEmptyArray(AnnotationValue annotationValue) { return annotationValue != null && Boolean.TRUE.equals( annotationValue.accept( - new SimpleAnnotationValueVisitor6() { + new SimpleAnnotationValueVisitor8() { @Override public Boolean visitArray(List values, Void p) { diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/checks/CrossParameterConstraintCheck.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/checks/CrossParameterConstraintCheck.java index 0bf9e88250..2c24579ef6 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/checks/CrossParameterConstraintCheck.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/checks/CrossParameterConstraintCheck.java @@ -15,9 +15,9 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; -import javax.lang.model.util.ElementKindVisitor6; -import javax.lang.model.util.SimpleAnnotationValueVisitor6; -import javax.lang.model.util.TypeKindVisitor6; +import javax.lang.model.util.ElementKindVisitor8; +import javax.lang.model.util.SimpleAnnotationValueVisitor8; +import javax.lang.model.util.TypeKindVisitor8; import javax.lang.model.util.Types; import org.hibernate.validator.ap.internal.util.AnnotationApiHelper; @@ -61,7 +61,7 @@ public Set checkAnnotationType(TypeElement element, Annota return Collections.emptySet(); } - DeclaredType elementType = element.asType().accept( new TypeKindVisitor6() { + DeclaredType elementType = element.asType().accept( new TypeKindVisitor8() { @Override public DeclaredType visitDeclared(DeclaredType t, Void p) { @@ -135,7 +135,7 @@ private boolean checkValidationAppliesToReturnType(ExecutableElement validationA final DeclaredType constraintTargetType = annotationApiHelper.getDeclaredTypeByName( BeanValidationTypes.CONSTRAINT_TARGET ); // Check the return type - return validationAppliesToMethod.getReturnType().accept( new TypeKindVisitor6() { + return validationAppliesToMethod.getReturnType().accept( new TypeKindVisitor8() { @Override public Boolean visitDeclared(DeclaredType t, Void p) { @@ -153,7 +153,7 @@ private boolean checkValidationAppliesToDefaultValue(ExecutableElement validatio final DeclaredType constraintTargetType = annotationApiHelper.getDeclaredTypeByName( BeanValidationTypes.CONSTRAINT_TARGET ); // Check the return type - return validationAppliesToMethod.getDefaultValue().accept( new SimpleAnnotationValueVisitor6() { + return validationAppliesToMethod.getDefaultValue().accept( new SimpleAnnotationValueVisitor8() { @Override public Boolean visitEnumConstant(VariableElement c, Void p) { @@ -167,7 +167,7 @@ public Boolean visitEnumConstant(VariableElement c, Void p) { private ExecutableElement getValidationAppliesToMethod(Element annotation) { for ( Element e : annotation.getEnclosedElements() ) { - ExecutableElement method = e.accept( new ElementKindVisitor6() { + ExecutableElement method = e.accept( new ElementKindVisitor8() { @Override public ExecutableElement visitExecutableAsMethod(ExecutableElement e, Void p) { diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/checks/GroupSequenceProviderCheck.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/checks/GroupSequenceProviderCheck.java index 3cadaf801b..bfd0b0170f 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/checks/GroupSequenceProviderCheck.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/checks/GroupSequenceProviderCheck.java @@ -17,8 +17,8 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.ElementKindVisitor6; -import javax.lang.model.util.SimpleTypeVisitor6; +import javax.lang.model.util.ElementKindVisitor8; +import javax.lang.model.util.SimpleTypeVisitor8; import javax.lang.model.util.Types; import org.hibernate.validator.ap.internal.util.AnnotationApiHelper; @@ -150,7 +150,7 @@ private Set checkAnnotationValue(TypeElement element, Anno */ private boolean hasPublicDefaultConstructor(TypeElement element) { return element.accept( - new ElementKindVisitor6( Boolean.FALSE ) { + new ElementKindVisitor8( Boolean.FALSE ) { @Override public Boolean visitTypeAsClass(TypeElement typeElement, Void aVoid) { @@ -182,11 +182,11 @@ public Boolean visitExecutableAsConstructor(ExecutableElement constructorElement * * @param typeMirror The {@code TypeMirror} instance. * - * @return The generic type or {@code null} if the given type doesn't implement the {@link org.hibernate.validator.group.DefaultGroupSequenceProvider} interface. + * @return The generic type or {@code null} if the given type doesn't implement the {@link org.hibernate.validator.spi.group.DefaultGroupSequenceProvider} interface. */ private TypeMirror retrieveGenericProviderType(TypeMirror typeMirror) { return typeMirror.accept( - new SimpleTypeVisitor6() { + new SimpleTypeVisitor8() { @Override public TypeMirror visitDeclared(DeclaredType declaredType, Void aVoid) { diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/AnnotationApiHelper.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/AnnotationApiHelper.java index 5040642b8a..b5d8c3ee98 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/AnnotationApiHelper.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/AnnotationApiHelper.java @@ -22,7 +22,7 @@ import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; -import javax.lang.model.util.SimpleAnnotationValueVisitor6; +import javax.lang.model.util.SimpleAnnotationValueVisitor8; import javax.lang.model.util.Types; /** @@ -268,7 +268,7 @@ public List getAnnotationArrayValue(AnnotationMirror } List theValue = annotationValue.accept( - new SimpleAnnotationValueVisitor6, Void>() { + new SimpleAnnotationValueVisitor8, Void>() { @Override public List visitArray(List values, Void p) { diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/CollectionHelper.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/CollectionHelper.java index f9396fb377..b6e4c52e89 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/CollectionHelper.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/CollectionHelper.java @@ -40,13 +40,13 @@ public static ArrayList newArrayList() { return new ArrayList(); } + @SafeVarargs public static Set asSet(T... ts) { - return new HashSet( Arrays.asList( ts ) ); } + @SafeVarargs public static Set asTreeSet(T... ts) { - return new TreeSet( Arrays.asList( ts ) ); } diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/Configuration.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/Configuration.java index f13c3c6ffc..52861f67a7 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/Configuration.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/Configuration.java @@ -6,8 +6,8 @@ */ package org.hibernate.validator.ap.internal.util; -import java.text.MessageFormat; import java.util.Map; + import javax.annotation.processing.Messager; import javax.tools.Diagnostic.Kind; @@ -97,11 +97,11 @@ private Kind getDiagnosticKindOption(Map options, Messager messa } catch (IllegalArgumentException e) { messager.printMessage( - Kind.WARNING, MessageFormat.format( - "The given value {0} is no valid diagnostic kind. {1} will be used.", - diagnosticKindFromOptions, - DEFAULT_DIAGNOSTIC_KIND - ) + Kind.WARNING, StringHelper.format( + "The given value %1$s is no valid diagnostic kind. %2$s will be used.", + diagnosticKindFromOptions, + DEFAULT_DIAGNOSTIC_KIND + ) ); } } @@ -118,10 +118,10 @@ private boolean getVerboseOption(Map options, Messager messager) if ( theValue ) { messager.printMessage( - Kind.NOTE, MessageFormat.format( - "Verbose reporting is activated. Some processing information will be displayed using diagnostic kind {0}.", - Kind.NOTE - ) + Kind.NOTE, StringHelper.format( + "Verbose reporting is activated. Some processing information will be displayed using diagnostic kind %1$s.", + Kind.NOTE + ) ); } diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/ConstraintHelper.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/ConstraintHelper.java index 0cc022c357..9b5090f802 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/ConstraintHelper.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/ConstraintHelper.java @@ -44,9 +44,9 @@ import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.ElementKindVisitor6; -import javax.lang.model.util.SimpleAnnotationValueVisitor6; -import javax.lang.model.util.TypeKindVisitor6; +import javax.lang.model.util.ElementKindVisitor8; +import javax.lang.model.util.SimpleAnnotationValueVisitor8; +import javax.lang.model.util.TypeKindVisitor8; import javax.lang.model.util.Types; import org.hibernate.validator.ap.internal.util.TypeNames.BeanValidationTypes; @@ -254,6 +254,7 @@ public ConstraintHelper(Types typeUtils, AnnotationApiHelper annotationApiHelper registerAllowedTypesForBuiltInConstraint( BeanValidationTypes.DECIMAL_MIN, Number.class, String.class ); registerAllowedTypesForBuiltInConstraint( BeanValidationTypes.DECIMAL_MIN, JavaMoneyTypes.MONETARY_AMOUNT ); registerAllowedTypesForBuiltInConstraint( BeanValidationTypes.DIGITS, Number.class, String.class ); + registerAllowedTypesForBuiltInConstraint( BeanValidationTypes.DIGITS, JavaMoneyTypes.MONETARY_AMOUNT ); registerAllowedTypesForBuiltInConstraint( BeanValidationTypes.EMAIL, CharSequence.class ); registerAllowedTypesForBuiltInConstraint( BeanValidationTypes.FUTURE, Calendar.class, Date.class ); registerAllowedTypesForBuiltInConstraint( BeanValidationTypes.FUTURE, JodaTypes.READABLE_PARTIAL, JodaTypes.READABLE_INSTANT ); @@ -394,7 +395,7 @@ public List getPartsOfMultiValuedConstraint( .getAnnotationArrayValue( annotationMirror, "value" ) ) { oneValuePart.accept( - new SimpleAnnotationValueVisitor6() { + new SimpleAnnotationValueVisitor8() { @Override public Void visitAnnotation(AnnotationMirror a, Void p) { @@ -454,7 +455,7 @@ public boolean isComposedConstraint(TypeElement element) { return Boolean.TRUE.equals( element.asType().accept( - new TypeKindVisitor6() { + new TypeKindVisitor8() { @Override public Boolean visitDeclared(DeclaredType constraintValidatorImplementation, Void p) { @@ -558,7 +559,7 @@ public ConstraintCheckResult checkCrossParameterTypes(DeclaredType constraintAnn final TypeMirror objectMirror = annotationApiHelper.getMirrorForType( Object.class ); TypeMirror type = determineSupportedType( crossParameterValidator ); - Boolean supported = type.accept( new TypeKindVisitor6() { + Boolean supported = type.accept( new TypeKindVisitor8() { @Override public Boolean visitArray(ArrayType t, Void p) { return typeUtils.isSameType( t.getComponentType(), objectMirror ); @@ -647,7 +648,7 @@ private boolean isMultiValuedConstraint(AnnotationMirror annotationMirror) { for ( AnnotationValue oneAnnotationValue : annotationArrayValue ) { Boolean isConstraintAnnotation = oneAnnotationValue.accept( - new SimpleAnnotationValueVisitor6() { + new SimpleAnnotationValueVisitor8() { @Override public Boolean visitAnnotation( @@ -797,7 +798,7 @@ private AnnotationProcessorConstraintTarget getConstraintTarget(AnnotationMirror for ( Element e : annotation.getAnnotationType().asElement().getEnclosedElements() ) { - Boolean isValidationAppliesToMethod = e.accept( new ElementKindVisitor6() { + Boolean isValidationAppliesToMethod = e.accept( new ElementKindVisitor8() { @Override public Boolean visitExecutableAsMethod(ExecutableElement e, Void p) { if ( e.getSimpleName().contentEquals( "validationAppliesTo" ) ) { @@ -818,7 +819,7 @@ public Boolean visitExecutableAsMethod(ExecutableElement e, Void p) { } return validationAppliesTo.accept( - new SimpleAnnotationValueVisitor6() { + new SimpleAnnotationValueVisitor8() { private final TypeMirror constraintTargetMirror = annotationApiHelper.getDeclaredTypeByName( BeanValidationTypes.CONSTRAINT_TARGET ); @@ -870,7 +871,7 @@ private TypeMirror determineSupportedType(AnnotationValue validatorClassReferenc TypeMirror constraintValidatorImplementation = getConstraintValidatorSuperType( validatorClassReference ); return constraintValidatorImplementation.accept( - new TypeKindVisitor6() { + new TypeKindVisitor8() { @Override public TypeMirror visitDeclared(DeclaredType constraintValidatorImplementation, Void p) { @@ -889,7 +890,7 @@ private boolean isValidationTargetSupported(AnnotationValue oneValidatorClassRef private Set getSupportedValidationTargets(AnnotationValue oneValidatorClassReference) { // determine the class that could contain the @SupportedValidationTarget annotation. TypeMirror validatorClass = oneValidatorClassReference.accept( - new SimpleAnnotationValueVisitor6() { + new SimpleAnnotationValueVisitor8() { @Override public TypeMirror visitType(TypeMirror t, Void p) { @@ -898,7 +899,7 @@ public TypeMirror visitType(TypeMirror t, Void p) { }, null ); - DeclaredType validatorType = validatorClass.accept( new TypeKindVisitor6() { + DeclaredType validatorType = validatorClass.accept( new TypeKindVisitor8() { @Override public DeclaredType visitDeclared(DeclaredType t, Void p) { return t; @@ -925,7 +926,7 @@ public DeclaredType visitDeclared(DeclaredType t, Void p) { else { List values = annotationApiHelper.getAnnotationArrayValue( supportedTargetDecl, "value" ); for ( AnnotationValue val : values ) { - AnnotationProcessorValidationTarget target = val.accept( new SimpleAnnotationValueVisitor6() { + AnnotationProcessorValidationTarget target = val.accept( new SimpleAnnotationValueVisitor8() { @Override public AnnotationProcessorValidationTarget visitEnumConstant(VariableElement c, Void p) { return AnnotationProcessorValidationTarget.valueOf( c.getSimpleName().toString() ); @@ -944,7 +945,7 @@ public AnnotationProcessorValidationTarget visitEnumConstant(VariableElement c, private TypeMirror getConstraintValidatorSuperType(AnnotationValue oneValidatorClassReference) { TypeMirror type = oneValidatorClassReference.accept( - new SimpleAnnotationValueVisitor6() { + new SimpleAnnotationValueVisitor8() { @Override public TypeMirror visitType(TypeMirror t, Void p) { @@ -1009,7 +1010,7 @@ private List getValidatorClassesFromConstraintMetaAnn AnnotationValue validatedBy = annotationApiHelper.getAnnotationValue( constraintMetaAnnotation, "validatedBy" ); return validatedBy.accept( - new SimpleAnnotationValueVisitor6, Void>() { + new SimpleAnnotationValueVisitor8, Void>() { @Override public List visitArray(List values, Void p) { diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/MessagerAdapter.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/MessagerAdapter.java index 5c6e26b8f0..23a3849d2b 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/MessagerAdapter.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/MessagerAdapter.java @@ -8,6 +8,7 @@ import java.text.MessageFormat; import java.util.Collection; +import java.util.Locale; import java.util.ResourceBundle; import javax.annotation.processing.Messager; @@ -45,7 +46,7 @@ public MessagerAdapter(Messager messager, Kind diagnosticKind) { this.messager = messager; this.diagnosticKind = diagnosticKind; - errorMessages = ResourceBundle.getBundle( "org.hibernate.validator.ap.ValidationProcessorMessages" ); + errorMessages = ResourceBundle.getBundle( "org.hibernate.validator.ap.ValidationProcessorMessages", Locale.getDefault() ); } /** @@ -114,7 +115,8 @@ private void report(ConstraintCheckIssue issue, Kind kind) { String message = errorMessages.getString( issue.getMessageKey() ); if ( issue.getMessageParameters() != null ) { - message = MessageFormat.format( message, issue.getMessageParameters() ); + MessageFormat messageFormat = new MessageFormat( message, Locale.getDefault() ); + message = messageFormat.format( issue.getMessageParameters() ); } messager.printMessage( diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/StringHelper.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/StringHelper.java index ea30a1486b..eb95e5c10d 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/StringHelper.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/StringHelper.java @@ -102,4 +102,7 @@ private static boolean startsWithSeveralUpperCaseLetters(String string) { Character.isUpperCase( string.charAt( 1 ) ); } + public static String format(String format, Object... args) { + return String.format( Locale.ROOT, format, args ); + } } diff --git a/annotation-processor/src/main/resources/META-INF/services/gradle/incremental.annotation.processors b/annotation-processor/src/main/resources/META-INF/services/gradle/incremental.annotation.processors new file mode 100644 index 0000000000..7148eed480 --- /dev/null +++ b/annotation-processor/src/main/resources/META-INF/services/gradle/incremental.annotation.processors @@ -0,0 +1 @@ +org.hibernate.validator.ap.ConstraintValidationProcessor,isolating diff --git a/annotation-processor/src/main/resources/org/hibernate/validator/ap/ValidationProcessorMessages.properties b/annotation-processor/src/main/resources/org/hibernate/validator/ap/ValidationProcessorMessages.properties index 9988c9eab4..cec98220cd 100644 --- a/annotation-processor/src/main/resources/org/hibernate/validator/ap/ValidationProcessorMessages.properties +++ b/annotation-processor/src/main/resources/org/hibernate/validator/ap/ValidationProcessorMessages.properties @@ -48,6 +48,6 @@ INVALID_GROUP_SEQUENCE_VALUE_NOT_INTERFACES=Invalid @GroupSequence configuration INVALID_GROUP_SEQUENCE_VALUE_CYCLIC_DEFINITION=Invalid @GroupSequence configuration. The defined group sequence should be expandable (no cyclic definition). INVALID_GROUP_SEQUENCE_VALUE_MISSING_HOSTING_BEAN_DECLARATION=Invalid default group sequence redefinition. The value should contain the hosting bean class. INVALID_GROUP_SEQUENCE_VALUE_MULTIPLE_DECLARATIONS_OF_THE_SAME_INTERFACE=Invalid @GroupSequence configuration. {0} was already declared in this group sequence. -INVALID_GROUP_SEQUENCE_EXTEND_INTERFACES=Having group sequences extending other interfaces is discouraged by the Bean Validation specification. +INVALID_GROUP_SEQUENCE_EXTEND_INTERFACES=Having group sequences extending other interfaces is discouraged by the Jakarta Bean Validation specification. MIXED_LIST_AND_DIRECT_ANNOTATION_DECLARATION=Constraint @{0} is declared both directly and as a list. Which is not allowed. INVALID_PAYLOAD_UNWRAPPING_VALUE_ANNOTATION_PARAMETERS=Having both Unwrapping.Unwrap and Unwrapping.Skip in the payload is not allowed. diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/ConstraintValidationProcessorTest.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/ConstraintValidationProcessorTest.java index 289aef25c7..bf6f60e705 100644 --- a/annotation-processor/src/test/java/org/hibernate/validator/ap/ConstraintValidationProcessorTest.java +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/ConstraintValidationProcessorTest.java @@ -116,19 +116,19 @@ public void overridingMethodParameterConstraintsTest() { assertFalse( compilationResult ); assertThatDiagnosticsMatch( diagnostics, - new DiagnosticExpectation( Kind.ERROR, 39 ), - new DiagnosticExpectation( Kind.ERROR, 56 ), - new DiagnosticExpectation( Kind.ERROR, 71 ), - new DiagnosticExpectation( Kind.ERROR, 90 ), - new DiagnosticExpectation( Kind.ERROR, 144 ), - new DiagnosticExpectation( Kind.ERROR, 152 ), - new DiagnosticExpectation( Kind.ERROR, 169 ), - new DiagnosticExpectation( Kind.ERROR, 191 ), - new DiagnosticExpectation( Kind.ERROR, 219 ), - new DiagnosticExpectation( Kind.ERROR, 373 ), - new DiagnosticExpectation( Kind.ERROR, 387 ), - new DiagnosticExpectation( Kind.ERROR, 409 ), - new DiagnosticExpectation( Kind.ERROR, 434 ) + new DiagnosticExpectation( Kind.ERROR, 38 ), + new DiagnosticExpectation( Kind.ERROR, 55 ), + new DiagnosticExpectation( Kind.ERROR, 70 ), + new DiagnosticExpectation( Kind.ERROR, 89 ), + new DiagnosticExpectation( Kind.ERROR, 143 ), + new DiagnosticExpectation( Kind.ERROR, 151 ), + new DiagnosticExpectation( Kind.ERROR, 168 ), + new DiagnosticExpectation( Kind.ERROR, 190 ), + new DiagnosticExpectation( Kind.ERROR, 218 ), + new DiagnosticExpectation( Kind.ERROR, 372 ), + new DiagnosticExpectation( Kind.ERROR, 386 ), + new DiagnosticExpectation( Kind.ERROR, 408 ), + new DiagnosticExpectation( Kind.ERROR, 433 ) ); assertEquals( diagnostics.getDiagnostics().get( 0 ).getMessage( Locale.getDefault() ), diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/ModelWithJavaMoneyTypes.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/ModelWithJavaMoneyTypes.java index 67268ae7f8..2dd5279f51 100644 --- a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/ModelWithJavaMoneyTypes.java +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/ModelWithJavaMoneyTypes.java @@ -9,6 +9,7 @@ import javax.money.MonetaryAmount; import javax.validation.constraints.DecimalMax; import javax.validation.constraints.DecimalMin; +import javax.validation.constraints.Digits; import javax.validation.constraints.Max; import javax.validation.constraints.Min; import javax.validation.constraints.Negative; @@ -29,6 +30,7 @@ public class ModelWithJavaMoneyTypes { @DecimalMin("0.00") @Positive @PositiveOrZero + @Digits(integer = 6, fraction = 2) public MonetaryAmount monetaryAmount; @Max(1000L) diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/overriding/MethodOverridingTests.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/overriding/MethodOverridingTests.java index f1df05454d..5d5a0149aa 100644 --- a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/overriding/MethodOverridingTests.java +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/overriding/MethodOverridingTests.java @@ -17,11 +17,10 @@ import javax.validation.constraints.Max; import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; -import org.hibernate.validator.constraints.NotBlank; - public class MethodOverridingTests { /** diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testutil/CompilerTestHelper.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testutil/CompilerTestHelper.java index c123f0080f..36e785deb8 100644 --- a/annotation-processor/src/test/java/org/hibernate/validator/ap/testutil/CompilerTestHelper.java +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testutil/CompilerTestHelper.java @@ -6,6 +6,9 @@ */ package org.hibernate.validator.ap.testutil; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -28,11 +31,9 @@ import org.hibernate.validator.ap.internal.util.CollectionHelper; import org.hibernate.validator.ap.internal.util.Configuration; +import org.hibernate.validator.ap.internal.util.StringHelper; import org.hibernate.validator.ap.util.DiagnosticExpectation; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; - /** * Infrastructure for unit tests based on the Java Compiler API. * @@ -49,7 +50,7 @@ public enum Library { HIBERNATE_VALIDATOR( "hibernate-validator.jar" ), - VALIDATION_API( "validation-api.jar" ), + VALIDATION_API( "jakarta.validation-api.jar" ), JODA_TIME( "joda-time.jar" ), @@ -180,16 +181,16 @@ public boolean compile(Processor annotationProcessor, List options = new ArrayList(); if ( diagnosticKind != null ) { - options.add( String.format( "-A%s=%s", Configuration.DIAGNOSTIC_KIND_PROCESSOR_OPTION, diagnosticKind ) ); + options.add( StringHelper.format( "-A%s=%s", Configuration.DIAGNOSTIC_KIND_PROCESSOR_OPTION, diagnosticKind ) ); } if ( verbose != null ) { - options.add( String.format( "-A%s=%b", Configuration.VERBOSE_PROCESSOR_OPTION, verbose ) ); + options.add( StringHelper.format( "-A%s=%b", Configuration.VERBOSE_PROCESSOR_OPTION, verbose ) ); } if ( allowMethodConstraints != null ) { options.add( - String.format( + StringHelper.format( "-A%s=%b", Configuration.METHOD_CONSTRAINTS_SUPPORTED_PROCESSOR_OPTION, allowMethodConstraints diff --git a/build-config/pom.xml b/build-config/pom.xml index 6e3d9a3cff..9bd35cdaaa 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.1.0.Final ../pom.xml diff --git a/build-config/src/main/resources/checkstyle.xml b/build-config/src/main/resources/checkstyle.xml index dbe0bfd190..df0f8a3a0f 100644 --- a/build-config/src/main/resources/checkstyle.xml +++ b/build-config/src/main/resources/checkstyle.xml @@ -10,8 +10,9 @@ + + - diff --git a/build-config/src/main/resources/forbidden-common.txt b/build-config/src/main/resources/forbidden-common.txt index 3f02a90e8f..47ae8513d0 100644 --- a/build-config/src/main/resources/forbidden-common.txt +++ b/build-config/src/main/resources/forbidden-common.txt @@ -26,3 +26,7 @@ org.assertj.core.api.Assertions#fail() java.lang.StringBuffer org.jboss.logging.processor.util.Objects + +################################################################################################################ +# JAXB shouldn't be used anymore as it is targeted to be removed from the JDK +javax.xml.bind.** diff --git a/cdi/pom.xml b/cdi/pom.xml index 08d7f43ccb..b6f3816ddc 100644 --- a/cdi/pom.xml +++ b/cdi/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.1.0.Final ../pom.xml @@ -33,18 +33,18 @@ hibernate-validator - javax.annotation - javax.annotation-api + jakarta.annotation + jakarta.annotation-api provided - org.jboss.spec.javax.interceptor - jboss-interceptors-api_1.2_spec + jakarta.interceptor + jakarta.interceptor-api provided - javax.enterprise - cdi-api + jakarta.enterprise + jakarta.enterprise.cdi-api provided @@ -63,7 +63,7 @@ org.glassfish - javax.el + jakarta.el test @@ -90,6 +90,28 @@ org.jboss.weld weld-core-impl test + + + javax.enterprise + cdi-api + + + org.jboss.spec.javax.el + jboss-el-api_3.0_spec + + + org.jboss.spec.javax.interceptor + + jboss-interceptors-api_1.2_spec + + + + org.jboss.spec.javax.annotation + + jboss-annotations-api_1.3_spec + + + org.jboss.shrinkwrap.resolver @@ -152,7 +174,7 @@ ${project.build.outputDirectory}/META-INF/MANIFEST.MF - Bean Validation + Jakarta Bean Validation 2.0 ${hibernate-validator-cdi.module-name} @@ -166,8 +188,8 @@ maven-bundle-plugin - ${hibernate-validator-cdi.bundle-name} - ${hibernate-validator.bundle-name} + ${hibernate-validator-cdi.module-name} + ${hibernate-validator.module-name} javax.validation.*;version="[2.0,3.0)", javax.annotation.*;version="[1.2,2.0)", @@ -214,7 +236,7 @@ - --add-opens java.base/java.lang=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED diff --git a/cdi/src/main/java/org/hibernate/validator/cdi/ValidationExtension.java b/cdi/src/main/java/org/hibernate/validator/cdi/ValidationExtension.java index 45c5ef63f6..4be3dab84e 100644 --- a/cdi/src/main/java/org/hibernate/validator/cdi/ValidationExtension.java +++ b/cdi/src/main/java/org/hibernate/validator/cdi/ValidationExtension.java @@ -17,6 +17,7 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Optional; import java.util.Set; import javax.enterprise.event.Observes; @@ -52,9 +53,9 @@ import org.hibernate.validator.cdi.internal.ValidatorFactoryBean; import org.hibernate.validator.cdi.internal.interceptor.ValidationEnabledAnnotatedType; import org.hibernate.validator.cdi.internal.interceptor.ValidationInterceptor; +import org.hibernate.validator.cdi.internal.util.GetterPropertySelectionStrategyHelper; import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.ExecutableHelper; -import org.hibernate.validator.internal.util.ReflectionHelper; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; @@ -99,6 +100,7 @@ public class ValidationExtension implements Extension { */ private final Validator validator; private final ValidatorFactory validatorFactory; + private final GetterPropertySelectionStrategyHelper getterPropertySelectionStrategyHelper; private final Set globalExecutableTypes; private final boolean isExecutableValidationEnabled; @@ -119,6 +121,7 @@ public ValidationExtension() { isExecutableValidationEnabled = bootstrap.isExecutableValidationEnabled(); validatorFactory = config.buildValidatorFactory(); validator = validatorFactory.getValidator(); + getterPropertySelectionStrategyHelper = GetterPropertySelectionStrategyHelper.forValidationFactory( validatorFactory ); executableHelper = new ExecutableHelper( new TypeResolutionHelper() ); } @@ -259,15 +262,16 @@ private void determineConstrainedMethods(AnnotatedType type, BeanDescript for ( AnnotatedMethod annotatedMethod : type.getMethods() ) { Method method = annotatedMethod.getJavaMember(); - boolean isGetter = ReflectionHelper.isGetterMethod( method ); + Optional correspondingProperty = getterPropertySelectionStrategyHelper.getProperty( method ); // obtain @ValidateOnExecution from the top-most method in the hierarchy Method methodForExecutableTypeRetrieval = replaceWithOverriddenOrInterfaceMethod( method, overriddenAndImplementedMethods ); EnumSet classLevelExecutableTypes = executableTypesDefinedOnType( methodForExecutableTypeRetrieval.getDeclaringClass() ); - EnumSet memberLevelExecutableType = executableTypesDefinedOnMethod( methodForExecutableTypeRetrieval, isGetter ); + EnumSet memberLevelExecutableType = executableTypesDefinedOnMethod( methodForExecutableTypeRetrieval, + correspondingProperty.isPresent() ); - ExecutableType currentExecutableType = isGetter ? ExecutableType.GETTER_METHODS : ExecutableType.NON_GETTER_METHODS; + ExecutableType currentExecutableType = correspondingProperty.isPresent() ? ExecutableType.GETTER_METHODS : ExecutableType.NON_GETTER_METHODS; // validation is enabled per default, so explicit configuration can just veto whether // validation occurs @@ -276,11 +280,11 @@ private void determineConstrainedMethods(AnnotatedType type, BeanDescript } boolean needsValidation; - if ( isGetter ) { - needsValidation = isGetterConstrained( method, beanDescriptor ); + if ( correspondingProperty.isPresent() ) { + needsValidation = isGetterConstrained( beanDescriptor, method, correspondingProperty.get() ); } else { - needsValidation = isNonGetterConstrained( method, beanDescriptor ); + needsValidation = isNonGetterConstrained( beanDescriptor, method ); } if ( needsValidation ) { @@ -307,13 +311,12 @@ private void determineConstrainedConstructors(AnnotatedType type, BeanDes } } - private boolean isNonGetterConstrained(Method method, BeanDescriptor beanDescriptor) { + private boolean isNonGetterConstrained(BeanDescriptor beanDescriptor, Method method) { return beanDescriptor.getConstraintsForMethod( method.getName(), method.getParameterTypes() ) != null; } - private boolean isGetterConstrained(Method method, BeanDescriptor beanDescriptor) { - String propertyName = ReflectionHelper.getPropertyName( method ); - PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty( propertyName ); + private boolean isGetterConstrained(BeanDescriptor beanDescriptor, Method method, String property) { + PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty( property ); return propertyDescriptor != null && propertyDescriptor.findConstraints() .declaredOn( ElementType.METHOD ) .hasConstraints(); diff --git a/cdi/src/main/java/org/hibernate/validator/cdi/internal/util/GetterPropertySelectionStrategyHelper.java b/cdi/src/main/java/org/hibernate/validator/cdi/internal/util/GetterPropertySelectionStrategyHelper.java new file mode 100644 index 0000000000..609e53860e --- /dev/null +++ b/cdi/src/main/java/org/hibernate/validator/cdi/internal/util/GetterPropertySelectionStrategyHelper.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.cdi.internal.util; + +import java.lang.reflect.Method; +import java.util.Optional; + +import javax.validation.ValidatorFactory; + +import org.hibernate.validator.HibernateValidatorFactory; +import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; +import org.hibernate.validator.spi.properties.ConstrainableExecutable; +import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy; + +/** + * A wrapper around {@link GetterPropertySelectionStrategy}. + * + * @author Marko Bekhta + */ +public class GetterPropertySelectionStrategyHelper { + + private final GetterPropertySelectionStrategy getterPropertySelectionStrategy; + + private GetterPropertySelectionStrategyHelper(GetterPropertySelectionStrategy getterPropertySelectionStrategy) { + this.getterPropertySelectionStrategy = getterPropertySelectionStrategy; + } + + public Optional getProperty(Method method) { + return getterPropertySelectionStrategy.getProperty( new ConstrainableMethod( method ) ); + } + + public static GetterPropertySelectionStrategyHelper forValidationFactory(ValidatorFactory factory) { + GetterPropertySelectionStrategy getterPropertySelectionStrategy; + if ( factory instanceof HibernateValidatorFactory ) { + getterPropertySelectionStrategy = factory.unwrap( HibernateValidatorFactory.class ).getGetterPropertySelectionStrategy(); + } + else { + getterPropertySelectionStrategy = new DefaultGetterPropertySelectionStrategy(); + } + return new GetterPropertySelectionStrategyHelper( getterPropertySelectionStrategy ); + } + + private static class ConstrainableMethod implements ConstrainableExecutable { + + private final Method method; + + private ConstrainableMethod(Method method) { + this.method = method; + } + + @Override + public Class getReturnType() { + return method.getReturnType(); + } + + @Override + public String getName() { + return method.getName(); + } + + @Override + public Class[] getParameterTypes() { + return method.getParameterTypes(); + } + } +} diff --git a/cdi/src/test/java/org/hibernate/validator/test/cdi/internal/ValidationExtensionTest.java b/cdi/src/test/java/org/hibernate/validator/test/cdi/internal/ValidationExtensionTest.java index 3c6720495d..e861a915ff 100644 --- a/cdi/src/test/java/org/hibernate/validator/test/cdi/internal/ValidationExtensionTest.java +++ b/cdi/src/test/java/org/hibernate/validator/test/cdi/internal/ValidationExtensionTest.java @@ -138,6 +138,7 @@ public void testRegisterBeanWithCustomQualifier() { @Override public Object answer() throws Throwable { + @SuppressWarnings("unchecked") ProcessBean event = getProcessBeanEvent( (Bean) EasyMock.getCurrentArguments()[0] ); extension.processBean( event ); return null; @@ -151,6 +152,7 @@ public Object answer() throws Throwable { @Override public Object answer() throws Throwable { + @SuppressWarnings("unchecked") ProcessBean event = getProcessBeanEvent( (Bean) EasyMock.getCurrentArguments()[0] ); extension.processBean( event ); return null; diff --git a/changelog.txt b/changelog.txt index 9693c913f7..442ca8bf83 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,247 @@ Hibernate Validator Changelog ============================= +6.1.0.Final (25-10-2019) +------------------------- + +** Bug + * HV-1730 - engine - JavaBeanExecutable fails to initialize for enum type + * HV-1715 - engine - Validation can sometimes proceed to the next group in sequence even after one of the constraints generated a violation + +** Improvement + * HV-1729 - performance - Skip allocation of an action for each need to access the context classloader + +** Task + * HV-1743 - build - Upgrade maven-compiler-plugin to 3.8.1 + * HV-1742 - build - Upgrade to WildFly 18.0.0.Final + * HV-1741 - build - Upgrade ByteBuddy test dependency to 1.10.2 + * HV-1740 - engine - Deprecate @SafeHtml + * HV-1739 - engine - CVE-2019-10219 Security issue with @SafeHtml + * HV-1738 - build - Update Jackson test dependency to 2.9.10 + * HV-1733 - tests - Fix locale settings of PredefinedScopeValidatorFactoryTest + * HV-1732 - build - Change tarLongFileMode to posix for assembly building + * HV-1731 - tck-runner - Move TCK signature check to tck-runner module + * HV-1728 - build - Upgrade to WildFly 17.0.1.Final + * HV-1727 - build - Update Jackson Databind test dependency to 2.9.9.2 + * HV-1725 - build - Switch to using Jakarta EE artifacts + * HV-1724 - build - Update to OpenJFX 11.0.2 + * HV-1680 - engine - Avoid reflection by using instrumentation - build the enhancer + +6.1.0.Alpha6 (19-07-2019) +------------------------- + +** Bug + * HV-1722 - engine - Remove settings-example.xml reference from .travis.yml + * HV-1721 - engine - Take into account Hibernate Validator-specific configuration with PredefinedScopeValidatorFactoryImpl + * HV-1720 - engine - Support bounded wildcard types in container value unwrapping + +** New Feature + * HV-1723 - validators - Provide a DigitsValidatorForMonetaryAmount to support @Digits on MonetaryAmounts + +** Task + * HV-1726 - engine - Make PredefinedScopeHibernateValidatorFactory extend HibernateValidatorFactory + +6.1.0.Alpha5 (13-06-2019) +------------------------- + +** Bug + * HV-1713 - engine - Missing violation when a bean is validated with different groups + * HV-1709 - validators - Polish Identification numbers are not considering length of the value + * HV-1706 - validators - ISBN-13 algorithm does not handle checksum 10 + +** Improvement + * HV-1719 - engine - Accept setting per-validator TraversableResolver with PredefinedScopeValidatorFactoryImpl + * HV-1708 - translations - Add Danish translations of validation messages + * HV-1707 - validators - Add ISBN.Type.ANY + +** New Feature + * HV-823 - engine - Provide contract for customization of property names in constraint violation + +** Task + * HV-1718 - engine - Predefined scope ValidatorFactory: uninitialized bean class shouldn't throw an exception + * HV-1717 - build - Update test dependencies + * HV-1716 - build - Update WildFly secondary version to 17.0.0.Beta1 + * HV-1712 - engine - Add org.hibernate.validator.metadata to OSGi manifest + * HV-1711 - build - Fix aggregated javadoc build with recent JDK 11 + * HV-1710 - build - Remove settings-example.xml + +6.1.0.Alpha4 (22-03-2019) +------------------------- + +** Bug + * HV-1704 - build - Build fails on Windows + * HV-1699 - validators - Rounding error when having a BigDecimal at runtime with @Max/@Min annotation set on a Number field + +** Improvement + * HV-1701 - - Add Max/MinValidatorForInteger + * HV-1700 - tests - Remove reference of absent valueextractor from test resources + * HV-1697 - engine - Reduce memory allocation of ValidationContexts + * HV-1696 - engine - Avoid using computeIfAbsent for the common case when getting bean metadata + * HV-1695 - engine - Avoid creating an empty map for group conversions + * HV-1694 - engine - Reduce memory allocation for unconstrained beans + +** Task + * HV-1705 - build - Upgrade WildFly to 16.0.0.Final + * HV-1702 - build - Upgrade to checkstyle 8.18 and maven-checkstyle-plugin 3.0.0 + * HV-1693 - build - Test compatibility with JDK 12 + +6.1.0.Alpha3 (18-02-2019) +------------------------- + +** Bug + * HV-1692 - engine - Custom group sequence might cause StackOverflowError on objects with cycles + * HV-1684 - validators - StackOverflowError with Hibernate Validator 6.0.13.Final + +** Improvement + * HV-1691 - engine - IndexOutOfBoundsException in PathImpl + * HV-1689 - engine - In GetInstancesFromServiceLoader, do not hide ServiceConfigurationError + * HV-1687 - engine - Reduce bootstrap log verbosity + * HV-1686 - translations - Fix a few typos in the Dutch translation + * HV-1683 - build - Upgrade javadoc plugin + +** New Feature + * HV-1657 - engine - Make the “propertyPath” available via the “HibernateMessageInterpolatorContext” + +** Task + * HV-1685 - integration - Upgrade the WildFly versions we generate patches for to 14.0.1.Final and 15.0.0.Final + +6.1.0.Alpha2 (19-12-2018) +------------------------- + +** Bug + * HV-1681 - engine - PredefinedScopeValidatorFactory and @Valid on unregistered bean throws a NPE + +** Improvement + * HV-1651 - translations - Contribute additional language translations from OpenLiberty + +** New Feature + * HV-1682 - engine - Provide a way to normalize the class before getting the bean metadata + +6.1.0.Alpha1 (11-12-2018) +------------------------- + +** Bug + * HV-1650 - validators - French translations are badly encoded + * HV-1645 - extensions - Revert HV-1609 due to increased CDI startup caused by ValidateableBeanFilter + * HV-1644 - build - Using Hibernate Validator with Java 11 brings JavaFX on the classpath + * HV-1637 - translations - PropertyNotFoundException for @DecimalMax when using the German translation + * HV-1634 - engine - Deal with synthetic and implicit parameters properly when getting the generic type of a parameter + * HV-1510 - validators - @NotNull doesn't work in 50% when annotated method is extended from multiple classes + * HV-1450 - engine - BeanMetaDataImpl.BeanMetaDataBuilder#build() can choose ConstraintMetaData w/o constraints + +** Improvement + * HV-1662 - engine - Extract BeanMetaDataBuilder to its own class file + * HV-1661 - engine - Remove impossible case from ValidatorImpl#buildNewLocalExecutionContext() + * HV-1656 - translations - Add translation of Validation Messages for Japanese language + * HV-1653 - engine - Improve the javadoc of ParameterScriptAssert + * HV-1643 - translations - Fix Russian translation for @Null constraint + * HV-1636 - engine - Avoid instantiating unnecessary objects during constraint metadata creation + * HV-1631 - engine - Avoid doing two lookups in the read methods of AnnotationProcessingOptionsImpl + * HV-1630 - engine - Introduce StringHelper.format() + * HV-1629 - engine - Fix compiler warnings + * HV-1628 - annotation-processor, engine, tests - Configure a stricter forbidden-apis policy and remove calls deprecated in Java 10 + * HV-1626 - build - Remove useless Maven plugins and extensions + * HV-1623 - engine - Build an abstraction over reflection in engine + * HV-1622 - integration - Update the WildFly integration tests to use WildFly 13 + * HV-1617 - build - Make our pom files more consistent with WildFly and JBoss parent pom files + * HV-1599 - engine - Avoid creating later discarded violations when reportAsSingleViolation is true + * HV-1526 - engine - Create separate validation context for different validation kinds + * HV-1484 - build - Unify JPMS module names and OSGi bundle names + * HV-667 - engine - Consider to create descriptor model lazily + +** New Feature + * HV-1671 - engine - Allow to retrieve all the built-in constraint annotations from ConstraintHelper + * HV-1670 - engine - Add the ability to preload a set of locales + * HV-1667 - engine - Create a predefined scope ValidatorFactory which initializes things eagerly + * HV-1363 - engine - Support for non-standard Java beans + +** Remove Feature + * HV-1624 - engine - Remove the StaticFieldELResolver + +** Task + * HV-1677 - engine - Allow for an explicit environment variable to disable the JavaFX extensions + * HV-1674 - engine - Make the name of the default bundle public in AbstractMessageInterpolator + * HV-1673 - engine - Audit new privileged calls in the reflection abstraction + * HV-1669 - engine - Only enable the SafeHtml constraint if jsoup in in the classpath + * HV-1668 - engine - Properly register all built-in constraints + * HV-1659 - integration - Upgrade WildFly to 14.0.1 + * HV-1658 - engine - Remove a couple of stream usages in ConstraintHelper that cause a problem for another project + * HV-1649 - tck-runner - Upgrade to Bean Validation TCK 2.0.4.Final + * HV-1648 - build, integration - Reenable WildFly integration tests for JDK 11 + * HV-1647 - tck-runner - Allow running TCK tests in container mode with JDK 11 + * HV-1646 - build, integration, tck-runner - Upgrade WildFly to 14.0.0.Beta1 + * HV-1641 - build - Use the OSS snapshot repository to download the JavaFX dependencies when building with JDK 11 + * HV-1640 - build - Add compatibility with the latest JDK 11 build 22 + * HV-1635 - documentation - Remove specific instructions for building with JDK 9 from the README + * HV-1627 - build - Upgrade our JPA test dependency to 2.2 + * HV-1610 - integration - Reenable OSGi tests for JDK 10 + * HV-1608 - build - Have the build work with JDK 11 + * HV-1577 - engine - Use Stax instead of JAXB to parse the XML descriptors + +6.0.13.Final (22-08-2018) +------------------------- + +** Bug + * HV-1652 - engine - Fix a few theoretical null pointer dereference issues + * HV-1650 - validators - French translations are badly encoded + +6.0.12.Final (10-08-2018) +------------------------- + +** Bug + * HV-1645 - extensions - Revert HV-1609 due to increased CDI startup caused by ValidateableBeanFilter + * HV-1644 - build - Using Hibernate Validator with Java 11 brings JavaFX on the classpath + +** Improvement + * HV-1643 - translations - Fix Russian translation for @Null constraint + +** Task + * HV-1649 - tck-runner - Upgrade to Bean Validation TCK 2.0.4.Final + * HV-1648 - build, integration - Reenable WildFly integration tests for JDK 11 + * HV-1647 - tck-runner - Allow running TCK tests in container mode with JDK 11 + * HV-1646 - build, integration, tck-runner - Upgrade WildFly to 14.0.0.Beta1 + * HV-1627 - build - Upgrade our JPA test dependency to 2.2 + +6.0.11.Final (18-07-2018) +------------------------- + +** Bug + * HV-1637 - translations - PropertNotFoundException for @DecimalMax when using the German translation + +** Improvement + * HV-1628 - annotation-processor, engine, tests - Configure a stricter forbidden-apis policy and remove calls deprecated in Java 10 + * HV-1615 - translations - Improvements on the dutch translations + +** Remove Feature + * HV-1624 - engine - Remove the StaticFieldELResolver + +** Task + * HV-1641 - build - Use the OSS snapshot repository to download the JavaFX dependencies when building with JDK 11 + * HV-1640 - build - Add compatibility with the latest JDK 11 build 22 + * HV-1610 - integration - Reenable OSGi tests for JDK 10 + * HV-1608 - build - Have the build work with JDK 11 + * HV-1577 - engine - Use Stax instead of JAXB to parse the XML descriptors + +6.0.10.Final (15-05-2018) +------------------------- + +** Bug + * HV-1614 - engine - Unable to specify constraints at more than 1 nested parameter of a typed container + * HV-1609 - integration - CDI extension should not rely on @WithAnnotations filtering + * HV-1604 - engine - Initializing JPATraversableResolver fails with IllegalAccessException + * HV-1598 - engine - Fix the behavior of XML default-validated-executable-types + +** Improvement + * HV-1612 - translations - Add Dutch translation of the validation messages + * HV-1611 - translations - Be consistent in the case of the validation messages + * HV-1592 - engine - Make ConstraintValidator declaration stricter + * HV-1534 - engine - Allow getter constraints to be specified for subclasses in XML configuration + +** Task + * HV-1607 - build - Have the build work with JDK 10 + * HV-1606 - tck-runner - Update TCK to 2.0.3.Final + * HV-1605 - build - Update Surefire to 2.21.0 for JDK 10 support + 6.0.9.Final (27-03-2018) ------------------------- diff --git a/copyright.txt b/copyright.txt index 3c78287f8e..9016a56980 100644 --- a/copyright.txt +++ b/copyright.txt @@ -3,6 +3,7 @@ Ahmed Al Hafoudh Alaa Nassef Andrey Derevyanko Andrey Rodionov +Asutosh Pandya Benson Margulies Brent Douglas Carlos Vara @@ -10,6 +11,8 @@ Carlo de Wolf Chris Beckey Christian Ivan Dag Hovland +Damir Alibegovic +Dario Seidl Davide D'Alto Davide Marchignoli Denis Tiago @@ -29,6 +32,7 @@ Henno Vermeulen Hillmer Chona Jan-Willem Willebrands Jason T. Greene +Jesper Preuss Jiri Bilek Julien Furgerot Julien May @@ -51,14 +55,17 @@ Nicola Ferraro Nicolas François Paolo Perrotta Pete Muir +Rob Dickinson Sanne Grinovero Sebastian Bayerl Shahram Goodarzi Shane Bryzak Shelly McGowan +Sjaak Derksen Steve Ebersole Strong Liu Tadhg Pearson +Takashi Aoe Tomaz Cerar Tommy Johansen Victor Rezende dos Santos diff --git a/distribution/pom.xml b/distribution/pom.xml index a8d1f5afdd..66be577bd4 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -10,7 +10,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.1.0.Final ../pom.xml @@ -45,7 +45,7 @@ org.glassfish - javax.el + jakarta.el @@ -54,8 +54,8 @@ log4j - org.hibernate.javax.persistence - hibernate-jpa-2.1-api + jakarta.persistence + jakarta.persistence-api joda-time @@ -74,16 +74,16 @@ paranamer - javax.enterprise - cdi-api + jakarta.enterprise + jakarta.enterprise.cdi-api - org.jboss.spec.javax.interceptor - jboss-interceptors-api_1.2_spec + jakarta.interceptor + jakarta.interceptor-api - javax.annotation - javax.annotation-api + jakarta.annotation + jakarta.annotation-api org.jboss.logging @@ -93,13 +93,13 @@ ${project.groupId} hibernate-validator-modules - wildfly-${wildfly.version}-patch + wildfly-${version.wildfly}-patch zip ${project.groupId} hibernate-validator-modules - wildfly-${wildfly-secondary.version}-patch + wildfly-${version.wildfly.secondary}-patch zip @@ -166,7 +166,7 @@ ${basedir}/src/main/assembly/dist.xml hibernate-validator-${project.version} - gnu + posix ${project.build.directory}/dist/ @@ -207,8 +207,25 @@ [9,) - -html5 --add-modules=${maven-javadoc-plugin.jigsaw.modules} + -html5 + + jdk11+ + + [11,) + + + -html5 -source 8 + + + + org.openjfx + javafx-base + ${version.org.openjfx} + provided + + + diff --git a/distribution/src/main/assembly/dist.xml b/distribution/src/main/assembly/dist.xml index 686cb34420..08abb1d44f 100644 --- a/distribution/src/main/assembly/dist.xml +++ b/distribution/src/main/assembly/dist.xml @@ -33,10 +33,10 @@ dist/lib/required - javax.validation:validation-api + jakarta.validation:jakarta.validation-api org.jboss.logging:jboss-logging com.fasterxml:classmate - org.glassfish:javax.el + org.glassfish:jakarta.el @@ -44,7 +44,7 @@ log4j:log4j joda-time:joda-time - org.hibernate.javax.persistence:hibernate-jpa-2.1-api + jakarta.persistence:jakarta.persistence-api org.jsoup:jsoup com.thoughtworks.paranamer:paranamer diff --git a/documentation/pom.xml b/documentation/pom.xml index 4cfd462026..9288290f95 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.1.0.Final ../pom.xml @@ -31,7 +31,7 @@ ${basedir}/../osgi/integrationtest/src/test/java Hibernate Validator, Annotation based constraints for your domain model - Reference Documentation - hibernate, validator, hibernate validator, validation, bean validation + hibernate, validator, hibernate validator, validation, jakarta bean validation, bean validation validator UA-45270411-3 GTM-NJWS5L @@ -40,7 +40,7 @@ true - -Duser.language=en + -Duser.language=en forbidden-allow-junit.txt .. @@ -54,7 +54,7 @@ org.glassfish - javax.el + jakarta.el test @@ -63,8 +63,8 @@ test - javax.enterprise - cdi-api + jakarta.enterprise + jakarta.enterprise.cdi-api test @@ -98,6 +98,21 @@ assertj-core test + + jakarta.annotation + jakarta.annotation-api + test + + + com.fasterxml.jackson.core + jackson-databind + test + + + com.fasterxml.jackson.core + jackson-annotations + test + @@ -126,7 +141,7 @@ org.hibernate.infra hibernate-asciidoctor-theme - ${hibernate-asciidoctor-theme.version} + ${version.org.hibernate.infra.hibernate-asciidoctor-theme} zip true ${project.build.directory}/ @@ -250,13 +265,13 @@ ${asciidoctor.osgi-integrationtest-source-dir} ${project.version} - ${bv.api.version} - ${jboss.logging.version} - ${classmate.version} - ${javax.el.version} + ${version.jakarta.validation-api} + ${version.org.jboss.logging.jboss-logging} + ${version.com.fasterxml.classmate} + ${version.org.glassfish.jakarta.el} - ${wildfly.version} - ${wildfly-secondary.version} + ${version.wildfly} + ${version.wildfly.secondary} ${java.api-docs.base-url}/ @@ -334,5 +349,19 @@ + + jdk11+ + + [11,) + + + + org.openjfx + javafx-base + ${version.org.openjfx} + test + + + diff --git a/documentation/src/main/asciidoc/ch01.asciidoc b/documentation/src/main/asciidoc/ch01.asciidoc index 9463a8e47a..8c761fe37f 100644 --- a/documentation/src/main/asciidoc/ch01.asciidoc +++ b/documentation/src/main/asciidoc/ch01.asciidoc @@ -1,7 +1,7 @@ [[validator-gettingstarted]] == Getting started -This chapter will show you how to get started with Hibernate Validator, the reference implementation (RI) of Bean Validation. For the following quick-start you need: +This chapter will show you how to get started with Hibernate Validator, the reference implementation (RI) of Jakarta Bean Validation. For the following quick-start you need: * A JDK 8 * http://maven.apache.org/[Apache Maven] @@ -26,18 +26,18 @@ your __pom.xml__: ---- ==== -This transitively pulls in the dependency to the Bean Validation API -(`javax.validation:validation-api:{bvVersion}`). +This transitively pulls in the dependency to the Jakarta Bean Validation API +(`jakarta.validation:jakarta.validation-api:{bvVersion}`). [[validator-gettingstarted-uel]] ==== Unified EL -Hibernate Validator requires an implementation of the Unified Expression Language -(http://jcp.org/en/jsr/detail?id=341[JSR 341]) for evaluating dynamic expressions in constraint +Hibernate Validator requires an implementation of https://projects.eclipse.org/projects/ee4j.el[Jakarta Expression Language] +for evaluating dynamic expressions in constraint violation messages (see <>). When your application runs in a Java EE container such as JBoss AS, an EL implementation is already provided by the container. In a Java SE environment, however, you have to add an implementation as dependency to your POM file. For instance -you can add the following dependency to use the JSR 341 https://javaee.github.io/uel-ri/[reference +you can add the following dependency to use the Jakarta EL https://github.com/eclipse-ee4j/el-ri[reference implementation]: .Maven dependencies for Unified EL reference implementation @@ -47,8 +47,8 @@ implementation]: ---- org.glassfish - javax.el - {javaxElVersion} + jakarta.el + {jakartaElVersion} ---- ==== @@ -57,14 +57,15 @@ implementation]: ==== For environments where one cannot provide a EL implementation Hibernate Validator is offering a <>. However, the use of this interpolator -is not Bean Validation specification compliant. +is not Jakarta Bean Validation specification compliant. ==== [[section-getting-started-cdi]] ==== CDI -Bean Validation defines integration points with CDI (Contexts and Dependency Injection for Java ^TM^ -EE, http://jcp.org/en/jsr/detail?id=346[JSR 346]). If your application runs in an +Jakarta Bean Validation defines integration points with CDI +(https://projects.eclipse.org/projects/ee4j.cdi[Contexts and Dependency Injection for Jakarta EE]). +If your application runs in an environment which does not provide this integration out of the box, you may use the Hibernate Validator CDI portable extension by adding the following Maven dependency to your POM: @@ -82,14 +83,14 @@ Validator CDI portable extension by adding the following Maven dependency to you ==== Note that adding this dependency is usually not required for applications running on a Java EE -application server. You can learn more about the integration of Bean Validation and CDI in +application server. You can learn more about the integration of Jakarta Bean Validation and CDI in <>. [[section-getting-started-security-manager]] ==== Running with a security manager Hibernate Validator supports running with a {javaTechnotesBaseUrl}/guides/security/index.html[security manager] being enabled. -To do so, you must assign several permissions to the code bases of Hibernate Validator, the Bean Validation API, Classmate and JBoss Logging and also to the code base calling Bean Validation. +To do so, you must assign several permissions to the code bases of Hibernate Validator, the Jakarta Bean Validation API, Classmate and JBoss Logging and also to the code base calling Jakarta Bean Validation. The following shows how to do this via a {javaTechnotesBaseUrl}/guides/security/PolicyFiles.html[policy file] as processed by the Java default policy implementation: .Policy file for using Hibernate Validator with a security manager @@ -108,7 +109,7 @@ grant codeBase "file:path/to/hibernate-validator-{hvVersion}.jar" { permission java.util.PropertyPermission "mapAnyUriToUri", "read"; }; -grant codeBase "file:path/to/validation-api-{bvVersion}.jar" { +grant codeBase "file:path/to/jakarta.validation-api-{bvVersion}.jar" { permission java.io.FilePermission "path/to/hibernate-validator-{hvVersion}.jar", "read"; }; @@ -130,7 +131,7 @@ grant codeBase "file:path/to/validation-caller-x.y.z.jar" { ==== Updating Hibernate Validator in WildFly The http://wildfly.org/[WildFly application server] contains Hibernate Validator out of the box. -In order to update the server modules for Bean Validation API and Hibernate Validator to the latest and greatest, the patch mechanism of WildFly can be used. +In order to update the server modules for Jakarta Bean Validation API and Hibernate Validator to the latest and greatest, the patch mechanism of WildFly can be used. You can download the patch file from http://sourceforge.net/projects/hibernate/files/hibernate-validator[SourceForge] or from Maven Central using the following dependency: @@ -198,7 +199,7 @@ There are no JPMS module descriptors provided yet, but Hibernate Validator is us These are the module names as declared using the `Automatic-Module-Name` header: -* Bean Validation API: `java.validation` +* Jakarta Bean Validation API: `java.validation` * Hibernate Validator core: `org.hibernate.validator` * Hibernate Validator CDI extension: `org.hibernate.validator.cdi` * Hibernate Validator test utilities: `org.hibernate.validator.testutils` @@ -206,20 +207,17 @@ These are the module names as declared using the `Automatic-Module-Name` header: These module names are preliminary and may be changed when providing real module descriptors in a future release. -When using Bean Validation XML descriptors (_META-INF/validation.xml_ and/or constraint mapping files), the `java.xml.bind` module must be enabled. -Do so by appending `--add-modules java.xml.bind` to your _java_ invocation. - [WARNING] ==== When using Hibernate Validator with CDI, be careful to not enable the `java.xml.ws.annotation` module of the JDK. -This module contains a subset of the JSR 250 API ("Commons Annotations"), but some annotations such as `javax.annotation.Priority` are missing. +This module contains a subset of Jakarta Annotations, but some annotations such as `javax.annotation.Priority` are missing. This causes the method validation interceptor of Hibernate Validator to not be registered, i.e. method validation won't work. -Instead, add the full JSR 250 API to the unnamed module (i.e. the classpath), e.g. by pulling in the _javax.annotation:javax.annotation-api_ dependency -(there already is a transitive dependency to the JSR 250 API when depending on _org.hibernate.validator:hibernate-validator-cdi_). +Instead, add the full Jakarta Annotations API to the unnamed module (i.e. the classpath), e.g. by pulling in the _jakarta.annotation:jakarta.annotation-api_ dependency +(there already is a transitive dependency to the Jakarta Annotations API when depending on _org.hibernate.validator:hibernate-validator-cdi_). If you need to enable the `java.xml.ws.annotation` module for some reason, you should patch it with the contents of the full API -by appending `--patch-module java.xml.ws.annotation=/path/to/complete-jsr250-api.jar` to your _java_ invocation. +by appending `--patch-module java.xml.ws.annotation=/path/to/complete-jakarta.annotation-api.jar` to your _java_ invocation. ==== [[validator-gettingstarted-createmodel]] @@ -287,11 +285,11 @@ code. [[validator-gettingstarted-whatsnext]] === Where to go next? -That concludes the 5 minutes tour through the world of Hibernate Validator and Bean Validation. +That concludes the 5 minutes tour through the world of Hibernate Validator and Jakarta Bean Validation. Continue exploring the code examples or look at further examples referenced in <>. To learn more about the validation of beans and properties, just continue reading -<>. If you are interested in using Bean Validation for the validation of +<>. If you are interested in using Jakarta Bean Validation for the validation of method pre- and postcondition refer to <>. In case your application has specific validation requirements have a look at <>. diff --git a/documentation/src/main/asciidoc/ch02.asciidoc b/documentation/src/main/asciidoc/ch02.asciidoc index 8f62d8c3c2..db1f7773f4 100644 --- a/documentation/src/main/asciidoc/ch02.asciidoc +++ b/documentation/src/main/asciidoc/ch02.asciidoc @@ -12,7 +12,7 @@ If you are interested in applying constraints to method parameters and return va [[section-declaring-bean-constraints]] === Declaring bean constraints -Constraints in Bean Validation are expressed via Java annotations. In this section you will learn +Constraints in Jakarta Bean Validation are expressed via Java annotations. In this section you will learn how to enhance an object model with these annotations. There are four types of bean constraints: * field constraints @@ -23,7 +23,7 @@ how to enhance an object model with these annotations. There are four types of b [NOTE] ==== Not all constraints can be placed on all of these levels. In fact, none of the default constraints -defined by Bean Validation can be placed at class level. The `java.lang.annotation.Target` annotation +defined by Jakarta Bean Validation can be placed at class level. The `java.lang.annotation.Target` annotation in the constraint annotation itself determines on which elements a constraint can be placed. See <> for more information. ==== @@ -95,7 +95,7 @@ It is possible to specify constraints directly on the type argument of a parameterized type: these constraints are called container element constraints. This requires that `ElementType.TYPE_USE` is specified via `@Target` -in the constraint definition. As of Bean Validation 2.0, built-in Bean Validation as well as +in the constraint definition. As of Jakarta Bean Validation 2.0, built-in Jakarta Bean Validation as well as Hibernate Validator specific constraints specify `ElementType.TYPE_USE` and can be used directly in this context. @@ -332,7 +332,7 @@ evaluated in addition to the `@NotNull` constraint from the superclass. [[section-object-graph-validation]] ==== Object graphs -The Bean Validation API does not only allow to validate single class instances but also complete +The Jakarta Bean Validation API does not only allow to validate single class instances but also complete object graphs (cascaded validation). To do so, just annotate a field or property representing a reference to another object with `@Valid` as demonstrated in <>. @@ -410,7 +410,7 @@ as it is more expressive. [[section-validating-bean-constraints]] === Validating bean constraints -The `Validator` interface is the most important object in Bean Validation. The next section shows how +The `Validator` interface is the most important object in Jakarta Bean Validation. The next section shows how to obtain a `Validator` instance. Afterwards you'll learn how to use the different methods of the `Validator` interface. @@ -496,7 +496,7 @@ include::{sourcedir}/org/hibernate/validator/referenceguide/chapter02/validation ==== -`Validator#validateProperty()` is for example used in the integration of Bean Validation into JSF 2 +`Validator#validateProperty()` is for example used in the integration of Jakarta Bean Validation into JSF 2 (see <>) to perform a validation of the values entered into a form before they are propagated to the model. @@ -539,28 +539,28 @@ The returned `Path` is composed of ``Node``s describing the path to the element. More information about the structure of the `Path` and the various types of ``Node``s can be found in {bvSpecUrl}#validationapi-constraintviolation[the `ConstraintViolation` section] of the -Bean Validation specification. +Jakarta Bean Validation specification. [[section-builtin-constraints]] === Built-in constraints Hibernate Validator comprises a basic set of commonly used constraints. These are foremost the -constraints defined by the Bean Validation specification (see <>). +constraints defined by the Jakarta Bean Validation specification (see <>). Additionally, Hibernate Validator provides useful custom constraints (see <>). [[validator-defineconstraints-spec]] -==== Bean Validation constraints +==== Jakarta Bean Validation constraints -Below you can find a list of all constraints specified in the Bean Validation API. -All these constraints apply to the field/property level, there are no class-level constraints defined in the Bean Validation specification. +Below you can find a list of all constraints specified in the Jakarta Bean Validation API. +All these constraints apply to the field/property level, there are no class-level constraints defined in the Jakarta Bean Validation specification. If you are using the Hibernate object-relational mapper, some of the constraints are taken into account when creating the DDL for your model (see "Hibernate metadata impact"). [NOTE] ==== Hibernate Validator allows some constraints to be applied to more data types than required by the -Bean Validation specification (e.g. `@Max` can be applied to strings). Relying on this feature can -impact portability of your application between Bean Validation providers. +Jakarta Bean Validation specification (e.g. `@Max` can be applied to strings). Relying on this feature can +impact portability of your application between Jakarta Bean Validation providers. ==== `@AssertFalse`:: Checks that the annotated element is false @@ -580,7 +580,7 @@ impact portability of your application between Bean Validation providers. Hibernate metadata impact::: None `@Digits(integer=, fraction=)`:: Checks whether the annotated value is a number having up to `integer` digits and `fraction` fractional digits - Supported data types::: BigDecimal, `BigInteger`, `CharSequence`, `byte`, `short`, `int`, `long` and the respective wrappers of the primitive types; additionally supported by HV: any sub-type of `Number` + Supported data types::: BigDecimal, `BigInteger`, `CharSequence`, `byte`, `short`, `int`, `long` and the respective wrappers of the primitive types; additionally supported by HV: any sub-type of `Number` and `javax.money.MonetaryAmount` Hibernate metadata impact::: Defines column precision and scale `@Email`:: Checks whether the specified character sequence is a valid email address. The optional parameters `regexp` and `flags` allow to specify an additional regular expression (including regular expression flags) which the email must match. @@ -654,13 +654,13 @@ impact portability of your application between Bean Validation providers. [NOTE] ==== On top of the parameters listed above each constraint has the parameters -message, groups and payload. This is a requirement of the Bean Validation specification. +message, groups and payload. This is a requirement of the Jakarta Bean Validation specification. ==== [[validator-defineconstraints-hv-constraints]] ==== Additional constraints -In addition to the constraints defined by the Bean Validation API, Hibernate Validator provides several useful custom constraints which are listed below. +In addition to the constraints defined by the Jakarta Bean Validation API, Hibernate Validator provides several useful custom constraints which are listed below. With one exception also these constraints apply to the field/property level, only `@ScriptAssert` is a class-level constraint. `@CreditCardNumber(ignoreNonDigitCharacters=)`:: Checks that the annotated character sequence passes the Luhn checksum test. Note, this validation aims to check for user mistakes, not credit card validity! See also http://www.dirigodev.com/blog/ecommerce/anatomy-of-a-credit-card-number/[Anatomy of a credit card number]. `ignoreNonDigitCharacters` allows to ignore non digit characters. The default is `false`. @@ -771,7 +771,7 @@ Hibernate Validator! [TIP] ==== -In some cases neither the Bean Validation constraints nor the custom constraints provided by +In some cases neither the Jakarta Bean Validation constraints nor the custom constraints provided by Hibernate Validator will fulfill your requirements. In this case you can easily write your own constraint. You can find more information in <>. ==== diff --git a/documentation/src/main/asciidoc/ch03.asciidoc b/documentation/src/main/asciidoc/ch03.asciidoc index bf334a4133..7df828b782 100644 --- a/documentation/src/main/asciidoc/ch03.asciidoc +++ b/documentation/src/main/asciidoc/ch03.asciidoc @@ -3,7 +3,7 @@ As of Bean Validation 1.1, constraints can not only be applied to JavaBeans and their properties, but also to the parameters and return values of the methods and constructors of any Java type. That -way Bean Validation constraints can be used to specify +way Jakarta Bean Validation constraints can be used to specify * the preconditions that must be satisfied by the caller before a method or constructor may be invoked (by applying constraints to the parameters of an executable) @@ -232,7 +232,7 @@ fail to satisfy these preconditions as is not aware of them. The rules of behavi also known as the http://en.wikipedia.org/wiki/Liskov_substitution_principle[Liskov substitution principle]. -The Bean Validation specification implements the first rule by disallowing parameter constraints on +The Jakarta Bean Validation specification implements the first rule by disallowing parameter constraints on methods which override or implement a method declared in a supertype (superclass or interface). <> shows a violation of this rule. diff --git a/documentation/src/main/asciidoc/ch04.asciidoc b/documentation/src/main/asciidoc/ch04.asciidoc index 1267a9cf21..7ab337396f 100644 --- a/documentation/src/main/asciidoc/ch04.asciidoc +++ b/documentation/src/main/asciidoc/ch04.asciidoc @@ -1,7 +1,7 @@ [[chapter-message-interpolation]] == Interpolating constraint error messages -Message interpolation is the process of creating error messages for violated Bean Validation +Message interpolation is the process of creating error messages for violated Jakarta Bean Validation constraints. In this chapter you will learn how such messages are defined and resolved and how you can plug in custom message interpolators in case the default algorithm is not sufficient for your requirements. @@ -42,7 +42,7 @@ this bundle, such as _$$ValidationMessages_en_US.properties$$_. By default, the (`Locale#getDefault()`) will be used when looking up messages in the bundle. . Resolve any message parameters by using them as key for a resource bundle containing the standard -error messages for the built-in constraints as defined in Appendix B of the Bean Validation +error messages for the built-in constraints as defined in Appendix B of the Jakarta Bean Validation specification. In the case of Hibernate Validator, this bundle is named `org.hibernate.validator.ValidationMessages`. If this step triggers a replacement, step 1 is executed again, otherwise step 3 is applied. @@ -61,7 +61,7 @@ Unified EL in error messages. ==== You can find the formal definition of the interpolation algorithm in section {bvSpecUrl}#validationapi-message-defaultmessageinterpolation-resolutionalgorithm[6.3.1.1] -of the Bean Validation specification. +of the Jakarta Bean Validation specification. ==== [[section-special-characters]] @@ -79,8 +79,8 @@ escaped if you want to use them literally. The following rules apply: [[section-interpolation-with-message-expressions]] ==== Interpolation with message expressions -As of Hibernate Validator 5 (Bean Validation 1.1) it is possible to use the Unified Expression -Language (as defined by link:http://jcp.org/en/jsr/detail?id=341[JSR 341]) in constraint +As of Hibernate Validator 5 (Bean Validation 1.1) it is possible to use the +https://projects.eclipse.org/projects/ee4j.el[Jakarta Expression Language] in constraint violation messages. This allows to define error messages based on conditional logic and also enables advanced formatting options. The validation engine makes the following objects available in the EL context: @@ -111,7 +111,7 @@ Validating an invalid `Car` instance yields constraint violations with the messa assertions in <>: * the `@NotNull` constraint on the `manufacturer` field causes the error message "must not be null", as -this is the default message defined by the Bean Validation specification and no specific descriptor +this is the default message defined by the Jakarta Bean Validation specification and no specific descriptor is given in the message attribute * the `@Size` constraint on the `licensePlate` field shows the interpolation of message parameters @@ -157,7 +157,7 @@ final implementation to the default interpolator, which can be obtained via `Configuration#getDefaultMessageInterpolator()`. In order to use a custom message interpolator it must be registered either by configuring it in the -Bean Validation XML descriptor _META-INF/validation.xml_ (see +Jakarta Bean Validation XML descriptor _META-INF/validation.xml_ (see <>) or by passing it when bootstrapping a `ValidatorFactory` or `Validator` (see <> and <>, respectively). diff --git a/documentation/src/main/asciidoc/ch06.asciidoc b/documentation/src/main/asciidoc/ch06.asciidoc index f32bb7964a..a3ca32e39a 100644 --- a/documentation/src/main/asciidoc/ch06.asciidoc +++ b/documentation/src/main/asciidoc/ch06.asciidoc @@ -1,7 +1,7 @@ [[validator-customconstraints]] == Creating custom constraints -The Bean Validation API defines a whole set of standard constraint annotations such as `@NotNull`, +The Jakarta Bean Validation API defines a whole set of standard constraint annotations such as `@NotNull`, `@Size` etc. In cases where these built-in constraints are not sufficient, you can easily create custom constraints tailored to your specific validation requirements. @@ -46,7 +46,7 @@ include::{sourcedir}/org/hibernate/validator/referenceguide/chapter06/CheckCase. ==== An annotation type is defined using the `@interface` keyword. All attributes of an annotation type are -declared in a method-like manner. The specification of the Bean Validation API demands, that any +declared in a method-like manner. The specification of the Jakarta Bean Validation API demands, that any constraint annotation defines: * an attribute `message` that returns the default key for creating error messages in case the @@ -55,7 +55,7 @@ constraint is violated * an attribute `groups` that allows the specification of validation groups, to which this constraint belongs (see <>). This must default to an empty array of type Class. -* an attribute `payload` that can be used by clients of the Bean Validation API to assign custom +* an attribute `payload` that can be used by clients of the Jakarta Bean Validation API to assign custom payload objects to a constraint. This attribute is not used by the API itself. An example for a custom payload could be the definition of a severity: + @@ -109,14 +109,14 @@ same place, usually with a different configuration. `List` is the containing ann This containing annotation type named `List` is also shown in the example. It allows to specify several `@CheckCase` annotations on the same element, e.g. with different validation groups and messages. -While another name could be used, the Bean Validation specification recommends to use the name +While another name could be used, the Jakarta Bean Validation specification recommends to use the name `List` and make the annotation an inner annotation of the corresponding constraint type. [[section-constraint-validator]] ==== The constraint validator Having defined the annotation, you need to create a constraint validator, which is able to validate -elements with a `@CheckCase` annotation. To do so, implement the Bean Validation interface `ConstraintValidator` +elements with a `@CheckCase` annotation. To do so, implement the Jakarta Bean Validation interface `ConstraintValidator` as shown below: [[example-constraint-validator]] @@ -140,7 +140,7 @@ validator as shown in the example. The `isValid()` method contains the actual validation logic. For `@CheckCase` this is the check whether a given string is either completely lower case or upper case, depending on the case mode retrieved -in `initialize()`. Note that the Bean Validation specification recommends to consider null values as +in `initialize()`. Note that the Jakarta Bean Validation specification recommends to consider null values as being valid. If `null` is not a valid value for an element, it should be annotated with `@NotNull` explicitly. @@ -173,6 +173,20 @@ It is important to add each configured constraint violation by calling `addConst Only after that the new constraint violation will be created. ==== +[WARNING] +==== +Note that the custom message template is passed directly to the Expression Language engine. + +Thus, you should be very careful when integrating user input in a custom message template as it will be interpreted +by the Expression Language engine, which is usually not the behavior you expect and could allow malicious users to leak +sensitive data. + +If you need to integrate user input, you should: + + * either escape it by using the http://beanvalidation.org/2.0/spec/#validationapi-message-defaultmessageinterpolation[Jakarta Bean Validation message interpolation escaping rules]; + * or, even better, <> by unwrapping the context to `HibernateConstraintValidatorContext`. +==== + Refer to <> to learn how to use the `ConstraintValidatorContext` API to control the property path of constraint violations for class-level constraints. @@ -192,7 +206,7 @@ The `initialize()` method of `HibernateConstraintValidator` takes two parameters information, such as the clock provider or the temporal validation tolerance. This extension is marked as incubating so it might be subject to change. -The plan is to standardize it and to include it in Bean Validation in the future. +The plan is to standardize it and to include it in Jakarta Bean Validation in the future. The example below shows how to base your validators on `HibernateConstraintValidator`: @@ -370,7 +384,7 @@ include::{sourcedir}/org/hibernate/validator/referenceguide/chapter06/custompath [[section-cross-parameter-constraints]] === Cross-parameter constraints -Bean Validation distinguishes between two different kinds of constraints. +Jakarta Bean Validation distinguishes between two different kinds of constraints. Generic constraints (which have been discussed so far) apply to the annotated element, e.g. a type, field, container element, method parameter or return value etc. diff --git a/documentation/src/main/asciidoc/ch07.asciidoc b/documentation/src/main/asciidoc/ch07.asciidoc index 39ccf0bad5..bfdcacc9f2 100644 --- a/documentation/src/main/asciidoc/ch07.asciidoc +++ b/documentation/src/main/asciidoc/ch07.asciidoc @@ -27,7 +27,7 @@ Built-in value extractors are present for all the following container types: The complete list of built-in value extractors with all the details on how they behave can be found in the -{bvSpecUrl}#valueextractordefinition-builtinvalueextractors[Bean Validation specification]. +{bvSpecUrl}#valueextractordefinition-builtinvalueextractors[Jakarta Bean Validation specification]. === Implementing a `ValueExtractor` @@ -178,7 +178,7 @@ annotation allows to provide this information to the validation engine. Then you have to tell the validation engine that the `Min` constraint you want to add to the `OptionalInt` property relates to the wrapped value and not the wrapper. -Bean Validation provides the `Unwrapping.Unwrap` payload for this situation: +Jakarta Bean Validation provides the `Unwrapping.Unwrap` payload for this situation: [[example-valueextraction-optionalint-unwrapping]] .Using `Unwrapping.Unwrap` payload @@ -319,7 +319,7 @@ given at lower priorities. In most cases, you should not have to worry about this but, if you are overriding existing value extractors, you can find a detailed description of the value -extractors resolution algorithms in the Bean Validation specification: +extractors resolution algorithms in the Jakarta Bean Validation specification: * for {bvSpecUrl}#constraintdeclarationvalidationprocess-validationroutine-valueextractorresolution-algorithm-constraints[container element constraints], * for {bvSpecUrl}#constraintdeclarationvalidationprocess-validationroutine-valueextractorresolution-algorithm-cascaded[cascaded validation], diff --git a/documentation/src/main/asciidoc/ch08.asciidoc b/documentation/src/main/asciidoc/ch08.asciidoc index f535060684..e3e190c890 100644 --- a/documentation/src/main/asciidoc/ch08.asciidoc +++ b/documentation/src/main/asciidoc/ch08.asciidoc @@ -1,9 +1,9 @@ [[chapter-xml-configuration]] == Configuring via XML -So far we have used the default configuration source for Bean Validation, namely annotations. +So far we have used the default configuration source for Jakarta Bean Validation, namely annotations. However, there also exist two kinds of XML descriptors allowing configuration via XML. The first -descriptor describes general Bean Validation behaviour and is provided as _META-INF/validation.xml_. +descriptor describes general Jakarta Bean Validation behaviour and is provided as _META-INF/validation.xml_. The second one describes constraint declarations and closely matches the constraint declaration approach via annotations. Let's have a look at these two document types. @@ -14,7 +14,7 @@ http://xmlns.jcp.org/xml/ns/validation/configuration and http://xmlns.jcp.org/xml/ns/validation/mapping. More information about the XML schemas can be found on the -http://beanvalidation.org/xml/ns/validation/[Bean Validation website]. +http://beanvalidation.org/xml/ns/validation/[Jakarta Bean Validation website]. ==== [[section-configuration-validation-xml]] @@ -51,7 +51,7 @@ There must only be one file named _META-INF/validation.xml_ on the classpath. If found an exception is thrown. ==== -The node `default-provider` allows to choose the Bean Validation provider. This is useful if there is +The node `default-provider` allows to choose the Jakarta Bean Validation provider. This is useful if there is more than one provider on the classpath. `message-interpolator`, `traversable-resolver`, `constraint-validator-factory`, `parameter-name-provider` and `clock-provider` allow to customize the used implementations for the interfaces `MessageInterpolator`, `TraversableResolver`, @@ -64,7 +64,7 @@ interfaces. container types or to override the built-in value extractors. See <> for more information about how to implement `javax.validation.valueextraction.ValueExtractor`. -`executable-validation` and its subnodes define defaults for method validation. The Bean Validation +`executable-validation` and its subnodes define defaults for method validation. The Jakarta Bean Validation specification defines constructor and non getter methods as defaults. The enabled attribute acts as global switch to turn method validation on and off (see also <>). @@ -136,7 +136,7 @@ The nodes `class`, `field`, `getter`, `container-element-type`, `constructor` an The `valid` node is used to enable cascaded validation and the `constraint` node to add a constraint on the corresponding level. Each constraint definition must define the class via the `annotation` attribute. -The constraint attributes required by the Bean Validation specification (`message`, `groups` and +The constraint attributes required by the Jakarta Bean Validation specification (`message`, `groups` and `payload`) have dedicated nodes. All other constraint specific attributes are configured using the `element` node. diff --git a/documentation/src/main/asciidoc/ch09.asciidoc b/documentation/src/main/asciidoc/ch09.asciidoc index 71bd211202..2c26d41a63 100644 --- a/documentation/src/main/asciidoc/ch09.asciidoc +++ b/documentation/src/main/asciidoc/ch09.asciidoc @@ -30,7 +30,7 @@ Hibernate Validator uses the factory as context for caching constraint metadata work with one factory instance within an application. ==== -Bean Validation supports working with several providers such as Hibernate Validator within one +Jakarta Bean Validation supports working with several providers such as Hibernate Validator within one application. If more than one provider is present on the classpath, it is not guaranteed which one is chosen when creating a factory via `buildDefaultValidatorFactory()`. @@ -71,7 +71,7 @@ If a `ValidatorFactory` instance is no longer in use, it should be disposed by c [[section-validation-provider-resolver]] ==== `ValidationProviderResolver` -By default, available Bean Validation providers are discovered using the +By default, available Jakarta Bean Validation providers are discovered using the {javaTechnotesBaseUrl}/guides/jar/jar.html#Service_Provider[Java Service Provider] mechanism. @@ -114,7 +114,7 @@ If you want to disable the XML based configuration, you can do so by invoking The different values of the XML configuration can be accessed via `Configuration#getBootstrapConfiguration()`. This can for instance be helpful if you want to integrate -Bean Validation into a managed environment and want to create managed instances of the objects +Jakarta Bean Validation into a managed environment and want to create managed instances of the objects configured via XML. Using the fluent configuration API, you can override one or more of the settings when bootstrapping diff --git a/documentation/src/main/asciidoc/ch10.asciidoc b/documentation/src/main/asciidoc/ch10.asciidoc index 6bd9a6fd24..12c13e31b9 100644 --- a/documentation/src/main/asciidoc/ch10.asciidoc +++ b/documentation/src/main/asciidoc/ch10.asciidoc @@ -1,7 +1,7 @@ [[validator-metadata-api]] == Using constraint metadata -The Bean Validation specification provides not only a validation engine, but also an API for +The Jakarta Bean Validation specification provides not only a validation engine, but also an API for retrieving constraint metadata in a uniform way, no matter whether the constraints are declared using annotations or via XML mappings. Read this chapter to learn more about this API and its possibilities. You can find all the metadata API types in the package `javax.validation.metadata`. diff --git a/documentation/src/main/asciidoc/ch11.asciidoc b/documentation/src/main/asciidoc/ch11.asciidoc index 9bf9cf4ece..0ed5b3a502 100644 --- a/documentation/src/main/asciidoc/ch11.asciidoc +++ b/documentation/src/main/asciidoc/ch11.asciidoc @@ -98,7 +98,7 @@ configuration in _hibernate.cfg.xml_: ==== JPA If you are using JPA 2 and Hibernate Validator is in the classpath, the JPA2 specification requires -that Bean Validation gets enabled. The properties `javax.persistence.validation.group.pre-persist`, +that Jakarta Bean Validation gets enabled. The properties `javax.persistence.validation.group.pre-persist`, `javax.persistence.validation.group.pre-update` and `javax.persistence.validation.group.pre-remove` as described in <> can in this case be configured in _persistence.xml_. _persistence.xml_ also defines a node validation-mode which can be set to `AUTO`, @@ -108,7 +108,7 @@ _persistence.xml_. _persistence.xml_ also defines a node validation-mode which c === JSF & Seam -When working with JSF2 or JBoss Seam and Hibernate Validator (Bean Validation) is present in the +When working with JSF2 or JBoss Seam and Hibernate Validator (Jakarta Bean Validation) is present in the runtime environment, validation is triggered for every field in the application. <> shows an example of the `f:validateBean` tag in a JSF page. The `validationGroups` attribute is optional and can be used to specify a comma separated list of validation groups. The default is @@ -117,7 +117,7 @@ specification. [[example-jsf2]] -.Usage of Bean Validation within JSF2 +.Usage of Jakarta Bean Validation within JSF2 ==== [source, XML] ---- @@ -137,12 +137,12 @@ specification. [TIP] ==== -The integration between JSF 2 and Bean Validation is described in the "Bean Validation Integration" +The integration between JSF 2 and Jakarta Bean Validation is described in the "Jakarta Bean Validation Integration" chapter of http://jcp.org/en/jsr/detail?id=314[JSR-314]. It is interesting to know that JSF 2 implements a custom `MessageInterpolator` to ensure proper localization. To encourage the use -of the Bean Validation message facility, JSF 2 will per default only display the generated Bean +of the Jakarta Bean Validation message facility, JSF 2 will per default only display the generated Bean Validation message. This can, however, be configured via the application resource bundle by -providing the following configuration (`{0}` is replaced with the Bean Validation message and `{1}` is +providing the following configuration (`{0}` is replaced with the Jakarta Bean Validation message and `{1}` is replaced with the JSF component label): ---- @@ -160,8 +160,8 @@ javax.faces.validator.BeanValidator.MESSAGE={0} [[section-integration-with-cdi]] === CDI -As of version 1.1, Bean Validation is integrated with CDI (Contexts and Dependency Injection for -Java^TM^ EE). +As of version 1.1, Bean Validation (and therefore Jakarta Bean Validation) is integrated with CDI +(Contexts and Dependency Injection for Jakarta EE). This integration provides CDI managed beans for `Validator` and `ValidatorFactory` and enables dependency injection in constraint validators as well as custom message interpolators, traversable @@ -192,10 +192,10 @@ include::{sourcedir}/org/hibernate/validator/referenceguide/chapter11/cdi/valida ==== The injected beans are the default validator factory and validator instances. In order to configure -them - e.g. to use a custom message interpolator - you can use the Bean Validation XML descriptors +them - e.g. to use a custom message interpolator - you can use the Jakarta Bean Validation XML descriptors as discussed in <>. -If you are working with several Bean Validation providers, you can make sure that factory and +If you are working with several Jakarta Bean Validation providers, you can make sure that factory and validator from Hibernate Validator are injected by annotating the injection points with the `@HibernateValidator` qualifier which is demonstrated in <>. @@ -217,7 +217,7 @@ used for selecting Hibernate Validator when working with the bootstrapping API ( <>). ==== -Via `@Inject` you also can inject dependencies into constraint validators and other Bean Validation +Via `@Inject` you also can inject dependencies into constraint validators and other Jakarta Bean Validation objects such as `MessageInterpolator` implementations etc. <> @@ -237,7 +237,7 @@ include::{sourcedir}/org/hibernate/validator/referenceguide/chapter11/cdi/inject ==== Method validation -The method interception facilities of CDI allow for a very tight integration with Bean Validation's +The method interception facilities of CDI allow for a very tight integration with Jakarta Bean Validation's method validation functionality. Just put constraint annotations to the parameters and return values of the executables of your CDI beans and they will be validated automatically before (parameter constraints) and after (return value constraints) a method or constructor is invoked. @@ -283,7 +283,7 @@ value is marked with `@Valid`. ===== Validated executable types -Bean Validation allows for a fine-grained control of the executable types which are automatically +Jakarta Bean Validation allows for a fine-grained control of the executable types which are automatically validated. By default, constraints on constructors and non-getter methods are validated. Therefore the `@NotNull` constraint on the method `RentalStation#getAvailableCars()` in <> does not get validated when the method is invoked. diff --git a/documentation/src/main/asciidoc/ch12.asciidoc b/documentation/src/main/asciidoc/ch12.asciidoc index 22ad182f53..6d6e205f68 100644 --- a/documentation/src/main/asciidoc/ch12.asciidoc +++ b/documentation/src/main/asciidoc/ch12.asciidoc @@ -2,7 +2,7 @@ == Hibernate Validator Specifics In this chapter you will learn how to make use of several features provided by Hibernate Validator -in addition to the functionality defined by the Bean Validation specification. This includes the +in addition to the functionality defined by the Jakarta Bean Validation specification. This includes the fail fast mode, the API for programmatic constraint configuration and the boolean composition of constraints. @@ -16,7 +16,7 @@ Validator. [NOTE] ==== Using the features described in the following sections may result in application code which is not -portable between Bean Validation providers. +portable between Jakarta Bean Validation providers. ==== === Public API @@ -25,13 +25,13 @@ Let's start, however, with a look at the public API of Hibernate Validator. Belo Note that when a package is part of the public API this is not necessarily true for its sub-packages. `org.hibernate.validator`:: - Classes used by the Bean Validation bootstrap mechanism (eg. validation provider, configuration class); for more details see <>. + Classes used by the Jakarta Bean Validation bootstrap mechanism (eg. validation provider, configuration class); for more details see <>. `org.hibernate.validator.cfg`, `org.hibernate.validator.cfg.context`, `org.hibernate.validator.cfg.defs`, `org.hibernate.validator.spi.cfg`:: Hibernate Validator's fluent API for constraint declaration; in `org.hibernate.validator.cfg` you will find the `ConstraintMapping` interface, in `org.hibernate.validator.cfg.defs` all constraint definitions and in `org.hibernate.validator.spi.cfg` a callback for using the API for configuring the default validator factory. Refer to <> for the details. `org.hibernate.validator.constraints`, `org.hibernate.validator.constraints.br`, `org.hibernate.validator.constraints.pl`:: - Some useful custom constraints provided by Hibernate Validator in addition to the built-in constraints defined by the Bean Validation specification; the constraints are described in detail in <>. + Some useful custom constraints provided by Hibernate Validator in addition to the built-in constraints defined by the Jakarta Bean Validation specification; the constraints are described in detail in <>. `org.hibernate.validator.constraintvalidation`:: Extended constraint validator context which allows to set custom attributes for message interpolation. <> describes how to make use of that feature. @@ -51,6 +51,9 @@ Note that when a package is part of the public API this is not necessarily true `org.hibernate.validator.spi.constraintdefinition`:: An SPI for registering additional constraint validators programmatically, see <>. +`org.hibernate.validator.spi.nodenameprovider`:: + An SPI that can be used to alter how the names of properties will be resolved when the property path is constructed. See <>. + [NOTE] ==== The public packages of Hibernate Validator fall into two categories: while the actual API parts are @@ -104,13 +107,13 @@ fail fast mode when bootstrapping a validator. [[section-method-validation-prerequisite-relaxation]] === Relaxation of requirements for method validation in class hierarchies -The Bean Validation specification defines a set of preconditions which apply when defining +The Jakarta Bean Validation specification defines a set of preconditions which apply when defining constraints on methods within class hierarchies. These preconditions are defined in {bvSpecUrl}#constraintdeclarationvalidationprocess-methodlevelconstraints-inheritance[section 5.6.5] -of the Bean Validation 2.0 specification. See also <> +of the Jakarta Bean Validation 2.0 specification. See also <> in this guide. -As per specification, a Bean Validation provider is allowed to relax these preconditions. +As per specification, a Jakarta Bean Validation provider is allowed to relax these preconditions. With Hibernate Validator you can do this in one of two ways. First you can use the configuration properties _hibernate.validator.allow_parameter_constraint_override_, @@ -151,7 +154,7 @@ include::{sourcedir}/org/hibernate/validator/referenceguide/chapter12/relaxation ==== By default, all of these properties are false, implementing the default behavior as defined in the -Bean Validation specification. +Jakarta Bean Validation specification. [WARNING] ==== @@ -163,7 +166,7 @@ requires changes to the default behaviour. [[section-programmatic-api]] === Programmatic constraint definition and declaration -As per the Bean Validation specification, you can define and declare constraints using Java annotations and XML +As per the Jakarta Bean Validation specification, you can define and declare constraints using Java annotations and XML based constraint mappings. In addition, Hibernate Validator provides a fluent API which allows for the programmatic @@ -189,7 +192,7 @@ include::{sourcedir}/org/hibernate/validator/referenceguide/chapter12/constraint ==== Constraints can be configured on multiple classes and properties using method chaining. The -constraint definition classes `NotNullDef` and SizeDef are helper classes which allow to configure +constraint definition classes `NotNullDef` and `SizeDef` are helper classes which allow to configure constraint parameters in a type-safe fashion. Definition classes exist for all built-in constraints in the `org.hibernate.validator.cfg.defs` package. By calling `ignoreAnnotations()` any constraints configured via annotations or XML are ignored for the given element. @@ -330,7 +333,7 @@ include::{sourcedir}/org/hibernate/validator/referenceguide/chapter12/purelycomp [[section-boolean-constraint-composition]] ==== Boolean composition of constraints -Bean Validation specifies that the constraints of a composed constraint (see +Jakarta Bean Validation specifies that the constraints of a composed constraint (see <>) are all combined via a logical _AND_. This means all of the composing constraints need to return true to obtain an overall successful validation. @@ -418,7 +421,7 @@ include::{sourcedir}/org/hibernate/validator/referenceguide/chapter12/dynamicpay Hibernate Validator requires per default an implementation of the Unified EL (see <>) to be available. This is needed to allow the interpolation -of constraint error messages using EL expressions as defined by the Bean Validation specification. +of constraint error messages using EL expressions as defined by the Jakarta Bean Validation specification. For environments where you cannot or do not want to provide an EL implementation, Hibernate Validator offers a non EL based message interpolator - `org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator`. @@ -442,7 +445,7 @@ interpolation algorithm as defined by the specification. Refer to === Custom contexts -The Bean Validation specification offers at several points in its API the possibility to unwrap a +The Jakarta Bean Validation specification offers at several points in its API the possibility to unwrap a given interface to an implementor specific subtype. In the case of constraint violation creation in `ConstraintValidator` implementations as well as message interpolation in `MessageInterpolator` instances, there exist `unwrap()` methods for the provided context instances - @@ -456,10 +459,11 @@ custom extensions for both of these interfaces. `HibernateConstraintValidatorContext` is a subtype of `ConstraintValidatorContext` which allows you to: * set arbitrary parameters for interpolation via the Expression Language message interpolation -facility using `HibernateConstraintValidatorContext#addExpressionVariable(String, Object)`. +facility using `HibernateConstraintValidatorContext#addExpressionVariable(String, Object)` +or `HibernateConstraintValidatorContext#addMessageParameter(String, Object)`. + -[[example-custom-message-parameter]] -.Custom `@Future` validator with message parameters +[[example-custom-expression-variable]] +.Custom `@Future` validator injecting an expression variable ==== [source, JAVA, indent=0] ---- @@ -467,12 +471,29 @@ include::{sourcedir}/org/hibernate/validator/referenceguide/chapter12/context/My ---- ==== + +[[example-custom-message-parameter]] +.Custom `@Future` validator injecting a message parameter +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/org/hibernate/validator/referenceguide/chapter12/context/MyFutureValidatorMessageParameter.java[tags=include] +---- +==== ++ +[NOTE] +==== +Apart from the syntax, the main difference between message parameters and expression variables is that message parameters +are simply interpolated whereas expression variables are interpreted using the expression language engine. +In practice, it should not change anything. +==== ++ [NOTE] ==== -Note that the parameters specified via `addExpressionVariable(String, Object)` are global and apply -to all constraint violations created by this `isValid()` invocation. This includes the default -constraint violation, but also all violations created by the `ConstraintViolationBuilder`. You can, -however, update the parameters between invocations of +Note that the parameters specified via `addExpressionVariable(String, Object)` and +`addMessageParameter(String, Object)` are global and apply to all constraint violations created by +this `isValid()` invocation. +This includes the default constraint violation, but also all violations created by the `ConstraintViolationBuilder`. +You can, however, update the parameters between invocations of `ConstraintViolationBuilder#addConstraintViolation()`. ==== * set an arbitrary dynamic payload - see <> @@ -525,7 +546,7 @@ Alternatively you can specify a `Paranamer` implementation of your choice when c [[section-constraint-definition-contribution]] === Providing constraint definitions -Bean Validation allows to (re-)define constraint definitions via XML in its constraint mapping +Jakarta Bean Validation allows to (re-)define constraint definitions via XML in its constraint mapping files. See <> for more information and <> for an example. While this approach is sufficient for many use cases, it has its shortcomings in others. Imagine for example a constraint library wanting to contribute constraint @@ -657,3 +678,180 @@ Call `ValidatorFactory#close()` if a given validator factory instance is not nee Failure to do so may result in a class loader leak in cases where applications/bundles are re-deployed and a non-closed validator factory still is referenced by application code. ==== + +[[section-getter-property-selection-strategy]] +=== Customizing the getter property selection strategy + +When a bean is validated by Hibernate Validator, its properties get validated. A property can either +be a field or a getter. +By default, Hibernate Validator respects the JavaBeans specification and considers a method as a getter as soon +as one of the conditions below is true: + +- the method name starts with `get`, it has a non-void return type and has no parameters; +- the method name starts with `is`, has a return type of `boolean` and has no parameters; +- the method name starts with `has`, has a return type of `boolean` and has no parameters (this rule +is specific to Hibernate Validator and is not mandated by the JavaBeans specification) + +While these rules are usually appropriate when following the classic JavaBeans convention, it might happen, +especially with code generators, that the JavaBeans naming convention is not followed and that the getters' +names are following a different convention. + +In this case, the strategy for detecting getters should be redefined in order to fully validate the object. + +A classic example of this requirement is when the classes follow a fluent naming convention, +as illustrated in <>. + +[[example-using-fluent-api-pattern]] +.A class that uses non-standard getters +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/org/hibernate/validator/referenceguide/chapter12/getterselectionstrategy/User.java[tags=include] +---- +==== + +If such object gets validated, no validation will be performed on the getters as they are not detected +by the standard strategy. + +.Validating a class with non-standard getters using the default getter property selection strategy +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/org/hibernate/validator/referenceguide/chapter12/getterselectionstrategy/GetterPropertySelectionStrategyTest.java[tags=no-strategy] +---- +==== + +To make Hibernate Validator treat such methods as properties, a custom `GetterPropertySelectionStrategy` +should be configured. +In this particular case, a possible implementation of the strategy would be: + +.Custom `GetterPropertySelectionStrategy` implementation +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/org/hibernate/validator/referenceguide/chapter12/getterselectionstrategy/FluentGetterPropertySelectionStrategy.java[tags=include] +---- +==== + +There are multiple ways to configure Hibernate Validator to use this strategy. It can either be done +programmatically (see <>) or by using the +`hibernate.validator.getter_property_selection_strategy` property in the XML configuration +(see <>). + +[[custom-getter-strategy-programmatically]] +.Configuring a custom `GetterPropertySelectionStrategy` programmatically +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/org/hibernate/validator/referenceguide/chapter12/getterselectionstrategy/GetterPropertySelectionStrategyTest.java[tags=custom-strategy] +---- +==== + +[[custom-getter-strategy-xml]] +.Configuring a custom `GetterPropertySelectionStrategy` using an XML property +==== +[source, XML, indent=0] +---- +include::{resourcesdir}/org/hibernate/validator/referenceguide/chapter12/getter-property-selection-strategy-validation.xml[] +---- +==== + +[WARNING] +==== +It is important to mention that in cases where programmatic constraints are added using +`HibernateValidatorConfiguration#addMapping(ConstraintMapping)`, adding mappings should +always be done after the required getter property selection strategy is configured. +Otherwise, the default strategy will be used for the mappings added before defining the strategy. +==== + +[[section-property-node-name-provider]] +=== Customizing the property name resolution for constraint violations + +Imagine that we have a simple data class that has `@NotNull` constraints on some fields: +[[example-person-class]] +.Person data class +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/org/hibernate/validator/referenceguide/chapter12/nodenameprovider/Person.java[tags=include] +---- +==== + +This class can be serialized to JSON by using the https://github.com/FasterXML/jackson[Jackson] library: +[[example-person-object-to-json]] +.Serializing Person object to JSON +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/org/hibernate/validator/referenceguide/chapter12/nodenameprovider/PersonSerializationTest.java[tags=include] +---- +==== + +As we can see, the object is serialized to: +[[example-person-json]] +.Person as json +==== +[source, indent=0] +---- +include::{sourcedir}/org/hibernate/validator/referenceguide/chapter12/nodenameprovider/clarkKent.json[] +---- +==== + +Notice how the names of the properties differ. In the Java object, we have `firstName` and `lastName`, whereas in the JSON output, we have +`first_name` and `last_name`. +We customized this behavior through `@JsonProperty` annotations. + +Now imagine that we use this class in a REST environment, where a user can send <> in the request body. +It would be nice, when indicating on which field the validation failed, to indicate the name they use in their JSON request, `first_name`, +and not the name we use internally in our Java code, `firstName`. + +The `org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider` contract allows us to do this. +By implementing it, we can define how the name of a property will be resolved during validation. +In our case, we want to read the value from the Jackson configuration. + +One example of how to do this is to leverage the Jackson API: +[[example-jackson-property-node-name-provider]] +.JacksonPropertyNodeNameProvider implementation +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/org/hibernate/validator/referenceguide/chapter12/nodenameprovider/JacksonPropertyNodeNameProvider.java[tags=include] +---- +==== + +And when doing the validation: +[[example-jackson-property-node-name-provider-field]] +.JacksonPropertyNodeNameProvider usage +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/org/hibernate/validator/referenceguide/chapter12/nodenameprovider/JacksonPropertyNodeNameProviderTest.java[tags=field] +---- +==== + +We can see that the property path now returns `first_name`. + +Note that this also works when the annotations are on a getter: +[[example-jackson-property-node-name-provider-getter]] +.Annotation on a getter +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/org/hibernate/validator/referenceguide/chapter12/nodenameprovider/JacksonPropertyNodeNameProviderTest.java[tags=getter] +---- +==== + +This is just one use case of why we would like to change how the property names are resolved. + +`org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider` can be implemented to provide a property name in +whatever way you see fit (reading from annotations, for instance). + +There are two more interfaces that are worth mentioning: + +- `org.hibernate.validator.spi.nodenameprovider.Property` is a base interface that holds metadata about a property. It +has a single `String getName()` method that can be used to get the "original" name of a property. This interface +should be used as a default way of resolving the name (see how it is used in <>). + +- `org.hibernate.validator.spi.nodenameprovider.JavaBeanProperty` is an interface that holds metadata about a bean property. It +extends `org.hibernate.validator.spi.nodenameprovider.Property` and provide some additional methods like `Class getDeclaringClass()` +which returns the class that is the owner of the property. diff --git a/documentation/src/main/asciidoc/ch13.asciidoc b/documentation/src/main/asciidoc/ch13.asciidoc index 94b266138b..34901b6eb7 100644 --- a/documentation/src/main/asciidoc/ch13.asciidoc +++ b/documentation/src/main/asciidoc/ch13.asciidoc @@ -57,7 +57,7 @@ The behavior of the Hibernate Validator Annotation Processor can be controlled u `methodConstraintsSupported`:: Controls whether constraints are allowed at methods of any kind. Must be set to `true` when working with method level constraints as supported by Hibernate Validator. Can be set to `false` to allow constraints only at - JavaBeans getter methods as defined by the Bean Validation API. Defaults to `true`. + JavaBeans getter methods as defined by the Jakarta Bean Validation API. Defaults to `true`. `verbose`:: Controls whether detailed processing information shall be displayed or not, useful for debugging purposes. Must be either @@ -116,6 +116,24 @@ For using the Hibernate Validator annotation processor with Maven, set it up via ---- ==== +[[validator-annotationprocessor-gradle]] +===== Gradle + +When using https://gradle.org[Gradle] it is enough to reference the annotation processor as an `annotationProcessor` dependency. + +.Using the annotation processor with Gradle +==== +[source, groovy] +[subs="verbatim,attributes"] +---- +dependencies { + annotationProcessor group: 'org.hibernate.validator', name: 'hibernate-validator-annotation-processor', version: '{hvVersion}' + + // any other dependencies ... +} +---- +==== + [[validator-annotationprocessor-ant]] ===== Apache Ant diff --git a/documentation/src/main/asciidoc/ch14.asciidoc b/documentation/src/main/asciidoc/ch14.asciidoc index 3e3127a821..3d53eb087a 100644 --- a/documentation/src/main/asciidoc/ch14.asciidoc +++ b/documentation/src/main/asciidoc/ch14.asciidoc @@ -3,11 +3,11 @@ Last but not least, a few pointers to further information. -A great source for examples is the Bean Validation TCK which is available for anonymous access on +A great source for examples is the Jakarta Bean Validation TCK which is available for anonymous access on https://github.com/beanvalidation/beanvalidation-tck/[GitHub]. In particular the TCK's https://github.com/beanvalidation/beanvalidation-tck/tree/master/tests[tests] might be -of interest. {bvSpecUrl}[The JSR 380] specification itself -is also a great way to deepen your understanding of Bean Validation and Hibernate Validator. +of interest. {bvSpecUrl}[The Jakarta Bean Validation] specification itself +is also a great way to deepen your understanding of Jakarta Bean Validation and Hibernate Validator. If you have any further questions about Hibernate Validator or want to share some of your use cases, have a look at the http://community.jboss.org/en/hibernate/validator[Hibernate Validator diff --git a/documentation/src/main/asciidoc/index.asciidoc b/documentation/src/main/asciidoc/index.asciidoc index 372e1fc27b..369db481d9 100644 --- a/documentation/src/main/asciidoc/index.asciidoc +++ b/documentation/src/main/asciidoc/index.asciidoc @@ -1,4 +1,4 @@ -= Hibernate Validator {hvVersion} - JSR 380 Reference Implementation: Reference Guide += Hibernate Validator {hvVersion} - Jakarta Bean Validation Reference Implementation: Reference Guide Hardy Ferentschik; Gunnar Morling; Guillaume Smet :doctype: book :revdate: {docdate} diff --git a/documentation/src/main/asciidoc/pr01.asciidoc b/documentation/src/main/asciidoc/pr01.asciidoc index acb4bdfc85..16712c068c 100644 --- a/documentation/src/main/asciidoc/pr01.asciidoc +++ b/documentation/src/main/asciidoc/pr01.asciidoc @@ -11,7 +11,7 @@ code which is really metadata about the class itself. image::application-layers.png[] -JSR 380 - Bean Validation 2.0 - defines a metadata model and API for entity and method validation. +Jakarta Bean Validation 2.0 - defines a metadata model and API for entity and method validation. The default metadata source are annotations, with the ability to override and extend the meta-data through the use of XML. The API is not tied to a specific application tier nor programming model. It is specifically not tied to either web or persistence tier, and is available for both server-side @@ -19,8 +19,8 @@ application programming, as well as rich client Swing application developers. image::application-layers2.png[] -Hibernate Validator is the reference implementation of this JSR 380. The implementation itself as -well as the Bean Validation API and TCK are all provided and distributed under the +Hibernate Validator is the reference implementation of Jakarta Bean Validation. The implementation itself as +well as the Jakarta Bean Validation API and TCK are all provided and distributed under the http://www.apache.org/licenses/LICENSE-2.0[Apache Software License 2.0]. -Hibernate Validator 6 and Bean Validation 2.0 require Java 8 or later. +Hibernate Validator 6 and Jakarta Bean Validation 2.0 require Java 8 or later. diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/constraintapi/ConstraintApiTest.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/constraintapi/ConstraintApiTest.java index 0465a19b31..7ad380e88f 100644 --- a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/constraintapi/ConstraintApiTest.java +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/constraintapi/ConstraintApiTest.java @@ -1,8 +1,5 @@ package org.hibernate.validator.referenceguide.chapter12.constraintapi; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; - import java.util.List; import javax.validation.Validation; @@ -33,14 +30,14 @@ public void constraintMapping() { constraintMapping .type( Car.class ) - .property( "manufacturer", FIELD ) + .field( "manufacturer" ) .constraint( new NotNullDef() ) - .property( "licensePlate", FIELD ) + .field( "licensePlate" ) .ignoreAnnotations( true ) .constraint( new NotNullDef() ) .constraint( new SizeDef().min( 2 ).max( 14 ) ) .type( RentalCar.class ) - .property( "rentalStation", METHOD ) + .getter( "rentalStation" ) .constraint( new NotNullDef() ); Validator validator = configuration.addMapping( constraintMapping ) @@ -60,7 +57,7 @@ public void genericConstraintDef() { constraintMapping .type( Car.class ) - .property( "licensePlate", FIELD ) + .field( "licensePlate" ) .constraint( new GenericConstraintDef<>( CheckCase.class ) .param( "value", CaseMode.UPPER ) ); @@ -78,19 +75,19 @@ public void nestedContainerElementConstraint() { constraintMapping .type( Car.class ) - .property( "manufacturer", FIELD ) + .field( "manufacturer" ) .constraint( new NotNullDef() ) - .property( "licensePlate", FIELD ) + .field( "licensePlate" ) .ignoreAnnotations( true ) .constraint( new NotNullDef() ) .constraint( new SizeDef().min( 2 ).max( 14 ) ) - .property( "partManufacturers", FIELD ) + .field( "partManufacturers" ) .containerElementType( 0 ) .constraint( new NotNullDef() ) .containerElementType( 1, 0 ) .constraint( new NotNullDef() ) .type( RentalCar.class ) - .property( "rentalStation", METHOD ) + .getter( "rentalStation" ) .constraint( new NotNullDef() ); //end::nestedContainerElementConstraint[] } @@ -107,17 +104,17 @@ public void cascaded() { constraintMapping .type( Car.class ) - .property( "driver", FIELD ) + .field( "driver" ) .constraint( new NotNullDef() ) .valid() .convertGroup( Default.class ).to( PersonDefault.class ) - .property( "partManufacturers", FIELD ) + .field( "partManufacturers" ) .containerElementType( 0 ) .valid() .containerElementType( 1, 0 ) .valid() .type( Person.class ) - .property( "name", FIELD ) + .field( "name" ) .constraint( new NotNullDef().groups( PersonDefault.class ) ); //end::cascaded[] } diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/constraintapi/MyConstraintMappingContributor.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/constraintapi/MyConstraintMappingContributor.java index b97e2744eb..a876fda655 100644 --- a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/constraintapi/MyConstraintMappingContributor.java +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/constraintapi/MyConstraintMappingContributor.java @@ -2,14 +2,12 @@ package org.hibernate.validator.referenceguide.chapter12.constraintapi; //end::include[] + import org.hibernate.validator.cfg.defs.AssertTrueDef; import org.hibernate.validator.cfg.defs.MinDef; import org.hibernate.validator.cfg.defs.NotNullDef; import org.hibernate.validator.spi.cfg.ConstraintMappingContributor; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; - //tag::include[] public class MyConstraintMappingContributor implements ConstraintMappingContributor { @@ -17,14 +15,14 @@ public class MyConstraintMappingContributor implements ConstraintMappingContribu public void createConstraintMappings(ConstraintMappingBuilder builder) { builder.addConstraintMapping() .type( Marathon.class ) - .property( "name", METHOD ) + .getter( "name" ) .constraint( new NotNullDef() ) - .property( "numberOfHelpers", FIELD ) + .field( "numberOfHelpers" ) .constraint( new MinDef().value( 1 ) ); builder.addConstraintMapping() .type( Runner.class ) - .property( "paidEntryFee", FIELD ) + .field( "paidEntryFee" ) .constraint( new AssertTrueDef() ); } } diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/context/MyFutureValidator.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/context/MyFutureValidator.java index 9cfad74d1f..1f5736a44e 100644 --- a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/context/MyFutureValidator.java +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/context/MyFutureValidator.java @@ -30,7 +30,8 @@ public boolean isValid(Instant value, ConstraintValidatorContext context) { if ( !value.isAfter( now ) ) { hibernateContext.disableDefaultConstraintViolation(); - hibernateContext.addExpressionVariable( "now", now ) + hibernateContext + .addExpressionVariable( "now", now ) .buildConstraintViolationWithTemplate( "Must be after ${now}" ) .addConstraintViolation(); diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/context/MyFutureValidatorMessageParameter.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/context/MyFutureValidatorMessageParameter.java new file mode 100644 index 0000000000..7a6c4c681e --- /dev/null +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/context/MyFutureValidatorMessageParameter.java @@ -0,0 +1,44 @@ +//tag::include[] +package org.hibernate.validator.referenceguide.chapter12.context; + +import java.time.Instant; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.constraints.Future; + +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext; + +//tag::include[] +public class MyFutureValidatorMessageParameter implements ConstraintValidator { + + @Override + public void initialize(Future constraintAnnotation) { + } + + @Override + public boolean isValid(Instant value, ConstraintValidatorContext context) { + if ( value == null ) { + return true; + } + + HibernateConstraintValidatorContext hibernateContext = context.unwrap( + HibernateConstraintValidatorContext.class + ); + + Instant now = Instant.now( context.getClockProvider().getClock() ); + + if ( !value.isAfter( now ) ) { + hibernateContext.disableDefaultConstraintViolation(); + hibernateContext + .addMessageParameter( "now", now ) + .buildConstraintViolationWithTemplate( "Must be after {now}" ) + .addConstraintViolation(); + + return false; + } + + return true; + } +} +//end::include[] diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/getterselectionstrategy/FluentGetterPropertySelectionStrategy.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/getterselectionstrategy/FluentGetterPropertySelectionStrategy.java new file mode 100644 index 0000000000..6885155778 --- /dev/null +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/getterselectionstrategy/FluentGetterPropertySelectionStrategy.java @@ -0,0 +1,44 @@ +//tag::include[] +package org.hibernate.validator.referenceguide.chapter12.getterselectionstrategy; + +//end::include[] +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import org.hibernate.validator.spi.properties.ConstrainableExecutable; +import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy; + +//tag::include[] +public class FluentGetterPropertySelectionStrategy implements GetterPropertySelectionStrategy { + + private final Set methodNamesToIgnore; + + public FluentGetterPropertySelectionStrategy() { + // we will ignore all the method names coming from Object + this.methodNamesToIgnore = Arrays.stream( Object.class.getDeclaredMethods() ) + .map( Method::getName ) + .collect( Collectors.toSet() ); + } + + @Override + public Optional getProperty(ConstrainableExecutable executable) { + if ( methodNamesToIgnore.contains( executable.getName() ) + || executable.getReturnType() == void.class + || executable.getParameterTypes().length > 0 ) { + return Optional.empty(); + } + + return Optional.of( executable.getName() ); + } + + @Override + public Set getGetterMethodNameCandidates(String propertyName) { + // As method name == property name, there always is just one possible name for a method + return Collections.singleton( propertyName ); + } +} +//end::include[] diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/getterselectionstrategy/GetterPropertySelectionStrategyTest.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/getterselectionstrategy/GetterPropertySelectionStrategyTest.java new file mode 100644 index 0000000000..b9a8a23006 --- /dev/null +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/getterselectionstrategy/GetterPropertySelectionStrategyTest.java @@ -0,0 +1,51 @@ +package org.hibernate.validator.referenceguide.chapter12.getterselectionstrategy; + +import static org.junit.Assert.assertEquals; + +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; + +import org.hibernate.validator.HibernateValidator; + +import org.junit.Test; + +public class GetterPropertySelectionStrategyTest { + + @Test + public void defaultStrategyUsed() { + //tag::no-strategy[] + Validator validator = Validation.byProvider( HibernateValidator.class ) + .configure() + .buildValidatorFactory() + .getValidator(); + + User user = new User( "", "", "not an email" ); + + Set> constraintViolations = validator.validate( user ); + + // as User has non-standard getters no violations are triggered + assertEquals( 0, constraintViolations.size() ); + //end::no-strategy[] + } + + @Test + public void customNoPrefixStrategyUsed() { + //tag::custom-strategy[] + Validator validator = Validation.byProvider( HibernateValidator.class ) + .configure() + // Setting a custom getter property selection strategy + .getterPropertySelectionStrategy( new FluentGetterPropertySelectionStrategy() ) + .buildValidatorFactory() + .getValidator(); + + User user = new User( "", "", "not an email" ); + + Set> constraintViolations = validator.validate( user ); + + assertEquals( 3, constraintViolations.size() ); + //end::custom-strategy[] + } +} diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/getterselectionstrategy/User.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/getterselectionstrategy/User.java new file mode 100644 index 0000000000..787120fe90 --- /dev/null +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/getterselectionstrategy/User.java @@ -0,0 +1,40 @@ +//tag::include[] +package org.hibernate.validator.referenceguide.chapter12.getterselectionstrategy; + +//end::include[] +import javax.validation.constraints.Email; +import javax.validation.constraints.NotEmpty; + +//tag::include[] +public class User { + + private String firstName; + private String lastName; + private String email; + + // [...] + + //end::include[] + public User(String firstName, String lastName, String email) { + this.firstName = firstName; + this.lastName = lastName; + this.email = email; + } + + //tag::include[] + @NotEmpty + public String firstName() { + return firstName; + } + + @NotEmpty + public String lastName() { + return lastName; + } + + @Email + public String email() { + return email; + } +} +//end::include[] diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/nodenameprovider/JacksonPropertyNodeNameProvider.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/nodenameprovider/JacksonPropertyNodeNameProvider.java new file mode 100644 index 0000000000..9b876a227b --- /dev/null +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/nodenameprovider/JacksonPropertyNodeNameProvider.java @@ -0,0 +1,41 @@ +package org.hibernate.validator.referenceguide.chapter12.nodenameprovider; + +//tag::include[] +import org.hibernate.validator.spi.nodenameprovider.JavaBeanProperty; +import org.hibernate.validator.spi.nodenameprovider.Property; +import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; + +public class JacksonPropertyNodeNameProvider implements PropertyNodeNameProvider { + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public String getName(Property property) { + if ( property instanceof JavaBeanProperty ) { + return getJavaBeanPropertyName( (JavaBeanProperty) property ); + } + + return getDefaultName( property ); + } + + private String getJavaBeanPropertyName(JavaBeanProperty property) { + JavaType type = objectMapper.constructType( property.getDeclaringClass() ); + BeanDescription desc = objectMapper.getSerializationConfig().introspect( type ); + + return desc.findProperties() + .stream() + .filter( prop -> prop.getInternalName().equals( property.getName() ) ) + .map( BeanPropertyDefinition::getName ) + .findFirst() + .orElse( property.getName() ); + } + + private String getDefaultName(Property property) { + return property.getName(); + } +} +//end::include[] diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/nodenameprovider/JacksonPropertyNodeNameProviderTest.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/nodenameprovider/JacksonPropertyNodeNameProviderTest.java new file mode 100644 index 0000000000..fe298f8f51 --- /dev/null +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/nodenameprovider/JacksonPropertyNodeNameProviderTest.java @@ -0,0 +1,74 @@ +package org.hibernate.validator.referenceguide.chapter12.nodenameprovider; + +import static org.junit.Assert.assertEquals; + +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.HibernateValidator; + +import org.junit.Test; + +import com.fasterxml.jackson.annotation.JsonProperty; + +//tag::field[] +public class JacksonPropertyNodeNameProviderTest { + @Test + public void nameIsReadFromJacksonAnnotationOnField() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .propertyNodeNameProvider( new JacksonPropertyNodeNameProvider() ) + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + Person clarkKent = new Person( null, "Kent" ); + + Set> violations = validator.validate( clarkKent ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "first_name" ); + } +//end::field[] + +//tag::getter[] + @Test + public void nameIsReadFromJacksonAnnotationOnGetter() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .propertyNodeNameProvider( new JacksonPropertyNodeNameProvider() ) + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + Person clarkKent = new Person( null, "Kent" ); + + Set> violations = validator.validate( clarkKent ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "first_name" ); + } + + public class Person { + private final String firstName; + + @JsonProperty("last_name") + private final String lastName; + + public Person(String firstName, String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } + + @NotNull + @JsonProperty("first_name") + public String getFirstName() { + return firstName; + } + } +//end::getter[] +} diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/nodenameprovider/Person.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/nodenameprovider/Person.java new file mode 100644 index 0000000000..ce2266b79b --- /dev/null +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/nodenameprovider/Person.java @@ -0,0 +1,21 @@ +package org.hibernate.validator.referenceguide.chapter12.nodenameprovider; + +import javax.validation.constraints.NotNull; + +import com.fasterxml.jackson.annotation.JsonProperty; + +//tag::include[] +public class Person { + @NotNull + @JsonProperty("first_name") + private final String firstName; + + @JsonProperty("last_name") + private final String lastName; + + public Person(String firstName, String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } +} +//end::include[] diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/nodenameprovider/PersonSerializationTest.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/nodenameprovider/PersonSerializationTest.java new file mode 100644 index 0000000000..5c64af52b7 --- /dev/null +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/nodenameprovider/PersonSerializationTest.java @@ -0,0 +1,23 @@ +package org.hibernate.validator.referenceguide.chapter12.nodenameprovider; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +//tag::include[] +public class PersonSerializationTest { + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + public void personIsSerialized() throws JsonProcessingException { + Person person = new Person( "Clark", "Kent" ); + + String serializedPerson = objectMapper.writeValueAsString( person ); + + assertEquals( "{\"first_name\":\"Clark\",\"last_name\":\"Kent\"}", serializedPerson ); + } +} +//tag::include[] diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/nodenameprovider/clarkKent.json b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/nodenameprovider/clarkKent.json new file mode 100644 index 0000000000..dd8aa9b129 --- /dev/null +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/nodenameprovider/clarkKent.json @@ -0,0 +1,4 @@ +{ + "first_name": "Clark", + "last_name": "Kent" +} diff --git a/documentation/src/test/resources/org/hibernate/validator/referenceguide/chapter12/getter-property-selection-strategy-validation.xml b/documentation/src/test/resources/org/hibernate/validator/referenceguide/chapter12/getter-property-selection-strategy-validation.xml new file mode 100644 index 0000000000..fe08b7ca72 --- /dev/null +++ b/documentation/src/test/resources/org/hibernate/validator/referenceguide/chapter12/getter-property-selection-strategy-validation.xml @@ -0,0 +1,12 @@ + + + + org.hibernate.validator.referenceguide.chapter12.getterselectionstrategy.NoPrefixGetterPropertySelectionStrategy + + + diff --git a/engine/pom.xml b/engine/pom.xml index e615c850cd..c1bd260b60 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -11,14 +11,14 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.1.0.Final ../pom.xml hibernate-validator Hibernate Validator Engine - Hibernate's Bean Validation (JSR-380) reference implementation. + Hibernate's Jakarta Bean Validation reference implementation. .. @@ -36,8 +36,8 @@ Compile time dependencies --> - javax.validation - validation-api + jakarta.validation + jakarta.validation-api org.jboss.logging @@ -53,7 +53,7 @@ --> org.glassfish - javax.el + jakarta.el provided @@ -79,8 +79,8 @@ Optional dependencies --> - org.hibernate.javax.persistence - hibernate-jpa-2.1-api + jakarta.persistence + jakarta.persistence-api true @@ -146,6 +146,33 @@ org.javamoney moneta test + + + javax.annotation + javax.annotation-api + + + + + + jakarta.annotation + jakarta.annotation-api + test + + + com.fasterxml.jackson.core + jackson-databind + test + + + com.fasterxml.jackson.core + jackson-annotations + test + + + net.bytebuddy + byte-buddy + test @@ -192,7 +219,7 @@ ${project.build.outputDirectory}/META-INF/MANIFEST.MF - Bean Validation + Jakarta Bean Validation 2.0 ${hibernate-validator.module-name} @@ -206,7 +233,7 @@ maven-bundle-plugin - ${hibernate-validator.bundle-name} + ${hibernate-validator.module-name} javax.persistence.*;version="[2.0.0,3.0.0)";resolution:=optional, javax.validation.*;version="[2.0.0,3.0.0)", @@ -230,6 +257,7 @@ org.hibernate.validator.engine.*;version="${project.version}", org.hibernate.validator.group;version="${project.version}", org.hibernate.validator.messageinterpolation;version="${project.version}", + org.hibernate.validator.metadata;version="${project.version}", org.hibernate.validator.parameternameprovider;version="${project.version}", org.hibernate.validator.path;version="${project.version}", org.hibernate.validator.resourceloading;version="${project.version}", @@ -274,168 +302,27 @@ - pre-jdk9 - - 1.8 - - - - - org.codehaus.mojo - jaxb2-maven-plugin - - - - xjc - - - - - org.hibernate.validator.internal.xml.binding - true - - true - - 2.1 - - src/main/xsd/validation-configuration-2.0.xsd - src/main/xsd/validation-mapping-2.0.xsd - - - - - - - - jdk9 + jdk9+ [9,) - --illegal-access=deny + --illegal-access=deny - - - - org.apache.maven.plugins - maven-antrun-plugin - - - generate-sources - - - - - - - - run - - - - - - org.codehaus.mojo - exec-maven-plugin - - - generate schema types - generate-sources - - exec - - - - - xjc - - -enableIntrospection - -p - org.hibernate.validator.internal.xml.binding - -extension - -target - 2.1 - -d - target/generated-sources/jaxb - src/main/xsd/validation-configuration-2.0.xsd - src/main/xsd/validation-mapping-2.0.xsd - -b - src/main/xjb/binding-customization.xjb - - - - - org.codehaus.mojo - build-helper-maven-plugin - - - add-source - generate-sources - - add-source - - - - target/generated-sources/jaxb - - - - - - - - sigtest - - - - org.apache.maven.plugins - maven-dependency-plugin - - - copy-tck-bv-api-signature-file - generate-test-sources - - unpack - - - - - org.hibernate.beanvalidation.tck - beanvalidation-tck-tests - ${tck.version} - jar - true - - - - **/*.sig - ${project.build.directory}/api-signature - - - - - - org.netbeans.tools - sigtest-maven-plugin - - - - check - - - - - javax.validation,javax.validation.bootstrap,javax.validation.constraints, - javax.validation.constraintvalidation,javax.validation.executable,javax.validation.groups, - javax.validation.metadata,javax.validation.spi,javax.validation.valueextraction - - ${project.build.directory}/api-signature/validation-api-java8.sig - - - - + jdk11+ + + [11,) + + + + org.openjfx + javafx-base + ${version.org.openjfx} + provided + + diff --git a/engine/src/main/java/org/hibernate/validator/BaseHibernateValidatorConfiguration.java b/engine/src/main/java/org/hibernate/validator/BaseHibernateValidatorConfiguration.java new file mode 100644 index 0000000000..b3347414aa --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/BaseHibernateValidatorConfiguration.java @@ -0,0 +1,363 @@ +/* + * 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; + +import java.time.Duration; +import java.util.Set; + +import javax.validation.Configuration; +import javax.validation.ConstraintViolation; +import javax.validation.TraversableResolver; +import javax.validation.constraints.Future; +import javax.validation.constraints.FutureOrPresent; +import javax.validation.constraints.Past; +import javax.validation.constraints.PastOrPresent; +import javax.validation.valueextraction.ValueExtractor; + +import org.hibernate.validator.cfg.ConstraintMapping; +import org.hibernate.validator.constraints.ParameterScriptAssert; +import org.hibernate.validator.constraints.ScriptAssert; +import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider; +import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy; +import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; +import org.hibernate.validator.spi.scripting.ScriptEvaluator; +import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; + +/** + * Base interface for Hibernate Validator specific configurations. + *

+ * Should not be used directly, prefer {@link HibernateValidatorConfiguration} or + * {@link PredefinedScopeHibernateValidatorConfiguration}. + * + * @author Emmanuel Bernard + * @author Gunnar Morling + * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI + * @author Hardy Ferentschik + * @author Chris Beckey <cbeckey@paypal.com> + */ +public interface BaseHibernateValidatorConfiguration> extends Configuration { + /** + * Property corresponding to the {@link #failFast} method. + * Accepts {@code true} or {@code false}. Defaults to {@code false}. + */ + String FAIL_FAST = "hibernate.validator.fail_fast"; + + /** + * Property corresponding to the {@link #allowOverridingMethodAlterParameterConstraint} method. + * Accepts {@code true} or {@code false}. + * Defaults to {@code false}. + */ + String ALLOW_PARAMETER_CONSTRAINT_OVERRIDE = "hibernate.validator.allow_parameter_constraint_override"; + + /** + * Property corresponding to the {@link #allowMultipleCascadedValidationOnReturnValues} method. + * Accepts {@code true} or {@code false}. + * Defaults to {@code false}. + */ + String ALLOW_MULTIPLE_CASCADED_VALIDATION_ON_RESULT = "hibernate.validator.allow_multiple_cascaded_validation_on_result"; + + /** + * Property corresponding to the {@link #allowParallelMethodsDefineParameterConstraints} method. + * Accepts {@code true} or {@code false}. + * Defaults to {@code false}. + */ + String ALLOW_PARALLEL_METHODS_DEFINE_PARAMETER_CONSTRAINTS = "hibernate.validator.allow_parallel_method_parameter_constraint"; + + /** + * @deprecated planned for removal. Use hibernate.validator.constraint_mapping_contributors instead. + * @since 5.2 + */ + @Deprecated + String CONSTRAINT_MAPPING_CONTRIBUTOR = "hibernate.validator.constraint_mapping_contributor"; + + /** + * Property for configuring constraint mapping contributors, allowing to set up one or more constraint mappings for + * the default validator factory. Accepts a String with the comma separated fully-qualified class names of one or more + * {@link org.hibernate.validator.spi.cfg.ConstraintMappingContributor} implementations. + * + * @since 5.3 + */ + String CONSTRAINT_MAPPING_CONTRIBUTORS = "hibernate.validator.constraint_mapping_contributors"; + + /** + * Property corresponding to the {@link #enableTraversableResolverResultCache(boolean)}. + * Accepts {@code true} or {@code false}. + * Defaults to {@code true}. + * + * @since 6.0.3 + */ + String ENABLE_TRAVERSABLE_RESOLVER_RESULT_CACHE = "hibernate.validator.enable_traversable_resolver_result_cache"; + + /** + * Property for configuring the script evaluator factory, allowing to set up which factory will be used to create + * {@link ScriptEvaluator}s for evaluation of script expressions in + * {@link ScriptAssert} and {@link ParameterScriptAssert} + * constraints. A fully qualified name of a class implementing {@link ScriptEvaluatorFactory} is expected as a value. + * + * @since 6.0.3 + */ + @Incubating + String SCRIPT_EVALUATOR_FACTORY_CLASSNAME = "hibernate.validator.script_evaluator_factory"; + + /** + * Property for configuring temporal validation tolerance, allowing to set the acceptable margin of error when + * comparing date/time in temporal constraints. In milliseconds. + * + * @since 6.0.5 + */ + @Incubating + String TEMPORAL_VALIDATION_TOLERANCE = "hibernate.validator.temporal_validation_tolerance"; + + /** + * Property for configuring the getter property selection strategy, allowing to set which rules will be applied + * to determine if a method is a valid JavaBean getter. + * + * @since 6.1.0 + */ + @Incubating + String GETTER_PROPERTY_SELECTION_STRATEGY_CLASSNAME = "hibernate.validator.getter_property_selection_strategy"; + + /** + * Property for configuring the property node name provider, allowing to select an implementation of {@link PropertyNodeNameProvider} + * which will be used for property name resolution when creating a property path. + * + * @since 6.1.0 + */ + @Incubating + String PROPERTY_NODE_NAME_PROVIDER_CLASSNAME = "hibernate.validator.property_node_name_provider"; + + /** + *

+ * Returns the {@link ResourceBundleLocator} used by the + * {@link Configuration#getDefaultMessageInterpolator() default message + * interpolator} to load user-provided resource bundles. In conformance with + * the specification this default locator retrieves the bundle + * "ValidationMessages". + *

+ *

+ * This locator can be used as delegate for custom locators when setting a + * customized {@link org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator}: + *

+ *
+	 * {@code
+	 * 	HibernateValidatorConfiguration configure =
+	 *    Validation.byProvider(HibernateValidator.class).configure();
+	 *
+	 *  ResourceBundleLocator defaultResourceBundleLocator =
+	 *    configure.getDefaultBundleLocator();
+	 *  ResourceBundleLocator myResourceBundleLocator =
+	 *    new MyResourceBundleLocator(defaultResourceBundleLocator);
+	 *
+	 *  configure.messageInterpolator(
+	 *    new ResourceBundleMessageInterpolator(myResourceBundleLocator));
+	 * }
+	 * 
+ * + * @return The default {@link ResourceBundleLocator}. Never null. + */ + ResourceBundleLocator getDefaultResourceBundleLocator(); + + /** + * Creates a new constraint mapping which can be used to programmatically configure the constraints for given types. After + * the mapping has been set up, it must be added to this configuration via {@link #addMapping(ConstraintMapping)}. + * + * @return A new constraint mapping. + */ + ConstraintMapping createConstraintMapping(); + + /** + * Returns the default {@link ValueExtractor} implementations as per the + * specification. + * + * @return the default {@code ValueExtractor} implementations compliant + * with the specification + * + * @since 6.0 + */ + @Incubating + Set> getDefaultValueExtractors(); + + /** + * Adds the specified {@link ConstraintMapping} instance to the configuration. Constraints configured in {@code mapping} + * will be added to the constraints configured via annotations and/or xml. + * + * @param mapping {@code ConstraintMapping} instance containing programmatic configured constraints + * + * @return {@code this} following the chaining method pattern + * + * @throws IllegalArgumentException if {@code mapping} is {@code null} + */ + S addMapping(ConstraintMapping mapping); + + /** + * En- or disables the fail fast mode. When fail fast is enabled the validation + * will stop on the first constraint violation detected. + * + * @param failFast {@code true} to enable fail fast, {@code false} otherwise. + * + * @return {@code this} following the chaining method pattern + */ + S failFast(boolean failFast); + + /** + * Sets the class loader to be used for loading user-provided resources: + *
    + *
  • XML descriptors ({@code META-INF/validation.xml} as well as XML constraint mappings)
  • + *
  • classes specified by name in XML descriptors (e.g. custom message interpolators etc.)
  • + *
  • the {@code ValidationMessages} resource bundle
  • + *
+ * If no class loader is given, these resources will be obtained through the thread context class loader and as a + * last fallback through Hibernate Validator's own class loader. + * + * @param externalClassLoader The class loader for loading user-provided resources. + * + * @return {@code this} following the chaining method pattern + * + * @since 5.2 + */ + S externalClassLoader(ClassLoader externalClassLoader); + + /** + * Define whether overriding methods that override constraints should throw a {@code ConstraintDefinitionException}. + * The default value is {@code false}, i.e. do not allow. + *

+ * See Section 4.5.5 of the JSR 380 specification, specifically + *

+	 * "In sub types (be it sub classes/interfaces or interface implementations), no parameter constraints may
+	 * be declared on overridden or implemented methods, nor may parameters be marked for cascaded validation.
+	 * This would pose a strengthening of preconditions to be fulfilled by the caller."
+	 * 
+ * + * @param allow flag determining whether validation will allow overriding to alter parameter constraints. + * + * @return {@code this} following the chaining method pattern + * + * @since 5.3 + */ + S allowOverridingMethodAlterParameterConstraint(boolean allow); + + /** + * Define whether more than one constraint on a return value may be marked for cascading validation are allowed. + * The default value is {@code false}, i.e. do not allow. + *

+ * See Section 4.5.5 of the JSR 380 specification, specifically + *

+	 * "One must not mark a method return value for cascaded validation more than once in a line of a class hierarchy.
+	 * In other words, overriding methods on sub types (be it sub classes/interfaces or interface implementations)
+	 * cannot mark the return value for cascaded validation if the return value has already been marked on the
+	 * overridden method of the super type or interface."
+	 * 
+ * + * @param allow flag determining whether validation will allow multiple cascaded validation on return values. + * + * @return {@code this} following the chaining method pattern + * + * @since 5.3 + */ + S allowMultipleCascadedValidationOnReturnValues(boolean allow); + + /** + * Define whether parallel methods that define constraints should throw a {@code ConstraintDefinitionException}. The + * default value is {@code false}, i.e. do not allow. + *

+ * See Section 4.5.5 of the JSR 380 specification, specifically + *

+	 * "If a sub type overrides/implements a method originally defined in several parallel types of the hierarchy
+	 * (e.g. two interfaces not extending each other, or a class and an interface not implemented by said class),
+	 * no parameter constraints may be declared for that method at all nor parameters be marked for cascaded validation.
+	 * This again is to avoid an unexpected strengthening of preconditions to be fulfilled by the caller."
+	 * 
+ * + * @param allow flag determining whether validation will allow parameter constraints in parallel hierarchies + * + * @return {@code this} following the chaining method pattern + * + * @since 5.3 + */ + S allowParallelMethodsDefineParameterConstraints(boolean allow); + + /** + * Define whether the per validation call caching of {@link TraversableResolver} results is enabled. The default + * value is {@code true}, i.e. the caching is enabled. + *

+ * This behavior was initially introduced to cache the {@code JPATraversableResolver} results but the map lookups it + * introduces can be counterproductive when the {@code TraversableResolver} calls are very fast. + * + * @param enabled flag determining whether per validation call caching is enabled for {@code TraversableResolver} + * results. + * + * @return {@code this} following the chaining method pattern + * + * @since 6.0.3 + */ + S enableTraversableResolverResultCache(boolean enabled); + + /** + * Allows to specify a custom {@link ScriptEvaluatorFactory} responsible for creating {@link ScriptEvaluator}s + * used to evaluate script expressions for {@link ScriptAssert} and {@link ParameterScriptAssert} constraints. + * + * @param scriptEvaluatorFactory the {@link ScriptEvaluatorFactory} to be used + * + * @return {@code this} following the chaining method pattern + * + * @since 6.0.3 + */ + @Incubating + S scriptEvaluatorFactory(ScriptEvaluatorFactory scriptEvaluatorFactory); + + /** + * Allows to set the acceptable margin of error when comparing date/time in temporal constraints such as + * {@link Past}/{@link PastOrPresent} and {@link Future}/{@link FutureOrPresent}. + * + * @param temporalValidationTolerance the acceptable tolerance + * + * @return {@code this} following the chaining method pattern + * + * @since 6.0.5 + */ + @Incubating + S temporalValidationTolerance(Duration temporalValidationTolerance); + + /** + * Allows to set a payload which will be passed to the constraint validators. If the method is called multiple + * times, only the payload passed last will be propagated. + * + * @param constraintValidatorPayload the payload passed to constraint validators + * + * @return {@code this} following the chaining method pattern + * + * @since 6.0.8 + */ + @Incubating + S constraintValidatorPayload(Object constraintValidatorPayload); + + /** + * Allows to set a getter property selection strategy defining the rules determining if a method is a getter + * or not. + * + * @param getterPropertySelectionStrategy the {@link GetterPropertySelectionStrategy} to be used + * + * @return {@code this} following the chaining method pattern + * + * @since 6.1.0 + */ + @Incubating + S getterPropertySelectionStrategy(GetterPropertySelectionStrategy getterPropertySelectionStrategy); + + /** + * Allows to set a property node name provider, defining how the name of a property node will be resolved + * when constructing a property path as the one returned by {@link ConstraintViolation#getPropertyPath()}. + * + * @param propertyNodeNameProvider the {@link PropertyNodeNameProvider} to be used + * + * @return {@code this} following the chaining method pattern + * + * @since 6.1.0 + */ + @Incubating + S propertyNodeNameProvider(PropertyNodeNameProvider propertyNodeNameProvider); +} diff --git a/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java b/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java index 45773d3949..186a7e150e 100644 --- a/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java +++ b/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java @@ -6,309 +6,12 @@ */ package org.hibernate.validator; -import java.time.Duration; -import java.util.Set; - -import javax.validation.Configuration; -import javax.validation.TraversableResolver; -import javax.validation.constraints.Future; -import javax.validation.constraints.FutureOrPresent; -import javax.validation.constraints.Past; -import javax.validation.constraints.PastOrPresent; -import javax.validation.valueextraction.ValueExtractor; - -import org.hibernate.validator.cfg.ConstraintMapping; -import org.hibernate.validator.constraints.ParameterScriptAssert; -import org.hibernate.validator.constraints.ScriptAssert; -import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; -import org.hibernate.validator.spi.scripting.ScriptEvaluator; -import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; - /** * Uniquely identifies Hibernate Validator in the Bean Validation bootstrap * strategy. Also contains Hibernate Validator specific configurations. * - * @author Emmanuel Bernard - * @author Gunnar Morling - * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI - * @author Hardy Ferentschik - * @author Chris Beckey <cbeckey@paypal.com> + * @author Guillaume Smet */ -public interface HibernateValidatorConfiguration extends Configuration { - /** - * Property corresponding to the {@link #failFast} method. - * Accepts {@code true} or {@code false}. Defaults to {@code false}. - */ - String FAIL_FAST = "hibernate.validator.fail_fast"; - - /** - * Property corresponding to the {@link #allowOverridingMethodAlterParameterConstraint} method. - * Accepts {@code true} or {@code false}. - * Defaults to {@code false}. - */ - String ALLOW_PARAMETER_CONSTRAINT_OVERRIDE = "hibernate.validator.allow_parameter_constraint_override"; - - /** - * Property corresponding to the {@link #allowMultipleCascadedValidationOnReturnValues} method. - * Accepts {@code true} or {@code false}. - * Defaults to {@code false}. - */ - String ALLOW_MULTIPLE_CASCADED_VALIDATION_ON_RESULT = "hibernate.validator.allow_multiple_cascaded_validation_on_result"; - - /** - * Property corresponding to the {@link #allowParallelMethodsDefineParameterConstraints} method. - * Accepts {@code true} or {@code false}. - * Defaults to {@code false}. - */ - String ALLOW_PARALLEL_METHODS_DEFINE_PARAMETER_CONSTRAINTS = "hibernate.validator.allow_parallel_method_parameter_constraint"; - - /** - * @deprecated planned for removal. Use hibernate.validator.constraint_mapping_contributors instead. - * @since 5.2 - */ - @Deprecated - String CONSTRAINT_MAPPING_CONTRIBUTOR = "hibernate.validator.constraint_mapping_contributor"; - - /** - * Property for configuring constraint mapping contributors, allowing to set up one or more constraint mappings for - * the default validator factory. Accepts a String with the comma separated fully-qualified class names of one or more - * {@link org.hibernate.validator.spi.cfg.ConstraintMappingContributor} implementations. - * - * @since 5.3 - */ - String CONSTRAINT_MAPPING_CONTRIBUTORS = "hibernate.validator.constraint_mapping_contributors"; - - /** - * Property corresponding to the {@link #enableTraversableResolverResultCache(boolean)}. - * Accepts {@code true} or {@code false}. - * Defaults to {@code true}. - * - * @since 6.0.3 - */ - String ENABLE_TRAVERSABLE_RESOLVER_RESULT_CACHE = "hibernate.validator.enable_traversable_resolver_result_cache"; - - /** - * Property for configuring the script evaluator factory, allowing to set up which factory will be used to create - * {@link ScriptEvaluator}s for evaluation of script expressions in - * {@link ScriptAssert} and {@link ParameterScriptAssert} - * constraints. A fully qualified name of a class implementing {@link ScriptEvaluatorFactory} is expected as a value. - * - * @since 6.0.3 - */ - @Incubating - String SCRIPT_EVALUATOR_FACTORY_CLASSNAME = "hibernate.validator.script_evaluator_factory"; - - /** - * Property for configuring temporal validation tolerance, allowing to set the acceptable margin of error when - * comparing date/time in temporal constraints. In milliseconds. - * - * @since 6.0.5 - */ - @Incubating - String TEMPORAL_VALIDATION_TOLERANCE = "hibernate.validator.temporal_validation_tolerance"; - - /** - *

- * Returns the {@link ResourceBundleLocator} used by the - * {@link Configuration#getDefaultMessageInterpolator() default message - * interpolator} to load user-provided resource bundles. In conformance with - * the specification this default locator retrieves the bundle - * "ValidationMessages". - *

- *

- * This locator can be used as delegate for custom locators when setting a - * customized {@link org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator}: - *

- *
-	 * {@code
-	 * 	HibernateValidatorConfiguration configure =
-	 *    Validation.byProvider(HibernateValidator.class).configure();
-	 *
-	 *  ResourceBundleLocator defaultResourceBundleLocator =
-	 *    configure.getDefaultBundleLocator();
-	 *  ResourceBundleLocator myResourceBundleLocator =
-	 *    new MyResourceBundleLocator(defaultResourceBundleLocator);
-	 *
-	 *  configure.messageInterpolator(
-	 *    new ResourceBundleMessageInterpolator(myResourceBundleLocator));
-	 * }
-	 * 
- * - * @return The default {@link ResourceBundleLocator}. Never null. - */ - ResourceBundleLocator getDefaultResourceBundleLocator(); - - /** - * Creates a new constraint mapping which can be used to programmatically configure the constraints for given types. After - * the mapping has been set up, it must be added to this configuration via {@link #addMapping(ConstraintMapping)}. - * - * @return A new constraint mapping. - */ - ConstraintMapping createConstraintMapping(); - - /** - * Returns the default {@link ValueExtractor} implementations as per the - * specification. - * - * @return the default {@code ValueExtractor} implementations compliant - * with the specification - * - * @since 6.0 - */ - @Incubating - Set> getDefaultValueExtractors(); - - /** - * Adds the specified {@link ConstraintMapping} instance to the configuration. Constraints configured in {@code mapping} - * will be added to the constraints configured via annotations and/or xml. - * - * @param mapping {@code ConstraintMapping} instance containing programmatic configured constraints - * - * @return {@code this} following the chaining method pattern - * - * @throws IllegalArgumentException if {@code mapping} is {@code null} - */ - HibernateValidatorConfiguration addMapping(ConstraintMapping mapping); - - /** - * En- or disables the fail fast mode. When fail fast is enabled the validation - * will stop on the first constraint violation detected. - * - * @param failFast {@code true} to enable fail fast, {@code false} otherwise. - * - * @return {@code this} following the chaining method pattern - */ - HibernateValidatorConfiguration failFast(boolean failFast); - - /** - * Sets the class loader to be used for loading user-provided resources: - *
    - *
  • XML descriptors ({@code META-INF/validation.xml} as well as XML constraint mappings)
  • - *
  • classes specified by name in XML descriptors (e.g. custom message interpolators etc.)
  • - *
  • the {@code ValidationMessages} resource bundle
  • - *
- * If no class loader is given, these resources will be obtained through the thread context class loader and as a - * last fallback through Hibernate Validator's own class loader. - * - * @param externalClassLoader The class loader for loading user-provided resources. - * - * @return {@code this} following the chaining method pattern - * - * @since 5.2 - */ - HibernateValidatorConfiguration externalClassLoader(ClassLoader externalClassLoader); - - /** - * Define whether overriding methods that override constraints should throw a {@code ConstraintDefinitionException}. - * The default value is {@code false}, i.e. do not allow. - *

- * See Section 4.5.5 of the JSR 380 specification, specifically - *

-	 * "In sub types (be it sub classes/interfaces or interface implementations), no parameter constraints may
-	 * be declared on overridden or implemented methods, nor may parameters be marked for cascaded validation.
-	 * This would pose a strengthening of preconditions to be fulfilled by the caller."
-	 * 
- * - * @param allow flag determining whether validation will allow overriding to alter parameter constraints. - * - * @return {@code this} following the chaining method pattern - * - * @since 5.3 - */ - HibernateValidatorConfiguration allowOverridingMethodAlterParameterConstraint(boolean allow); - - /** - * Define whether more than one constraint on a return value may be marked for cascading validation are allowed. - * The default value is {@code false}, i.e. do not allow. - *

- * See Section 4.5.5 of the JSR 380 specification, specifically - *

-	 * "One must not mark a method return value for cascaded validation more than once in a line of a class hierarchy.
-	 * In other words, overriding methods on sub types (be it sub classes/interfaces or interface implementations)
-	 * cannot mark the return value for cascaded validation if the return value has already been marked on the
-	 * overridden method of the super type or interface."
-	 * 
- * - * @param allow flag determining whether validation will allow multiple cascaded validation on return values. - * - * @return {@code this} following the chaining method pattern - * - * @since 5.3 - */ - HibernateValidatorConfiguration allowMultipleCascadedValidationOnReturnValues(boolean allow); - - /** - * Define whether parallel methods that define constraints should throw a {@code ConstraintDefinitionException}. The - * default value is {@code false}, i.e. do not allow. - *

- * See Section 4.5.5 of the JSR 380 specification, specifically - *

-	 * "If a sub type overrides/implements a method originally defined in several parallel types of the hierarchy
-	 * (e.g. two interfaces not extending each other, or a class and an interface not implemented by said class),
-	 * no parameter constraints may be declared for that method at all nor parameters be marked for cascaded validation.
-	 * This again is to avoid an unexpected strengthening of preconditions to be fulfilled by the caller."
-	 * 
- * - * @param allow flag determining whether validation will allow parameter constraints in parallel hierarchies - * - * @return {@code this} following the chaining method pattern - * - * @since 5.3 - */ - HibernateValidatorConfiguration allowParallelMethodsDefineParameterConstraints(boolean allow); - - /** - * Define whether the per validation call caching of {@link TraversableResolver} results is enabled. The default - * value is {@code true}, i.e. the caching is enabled. - *

- * This behavior was initially introduced to cache the {@code JPATraversableResolver} results but the map lookups it - * introduces can be counterproductive when the {@code TraversableResolver} calls are very fast. - * - * @param enabled flag determining whether per validation call caching is enabled for {@code TraversableResolver} - * results. - * - * @return {@code this} following the chaining method pattern - * - * @since 6.0.3 - */ - HibernateValidatorConfiguration enableTraversableResolverResultCache(boolean enabled); - - /** - * Allows to specify a custom {@link ScriptEvaluatorFactory} responsible for creating {@link ScriptEvaluator}s - * used to evaluate script expressions for {@link ScriptAssert} and {@link ParameterScriptAssert} constraints. - * - * @param scriptEvaluatorFactory the {@link ScriptEvaluatorFactory} to be used - * - * @return {@code this} following the chaining method pattern - * - * @since 6.0.3 - */ - @Incubating - HibernateValidatorConfiguration scriptEvaluatorFactory(ScriptEvaluatorFactory scriptEvaluatorFactory); - - /** - * Allows to set the acceptable margin of error when comparing date/time in temporal constraints such as - * {@link Past}/{@link PastOrPresent} and {@link Future}/{@link FutureOrPresent}. - * - * @param temporalValidationTolerance the acceptable tolerance - * - * @return {@code this} following the chaining method pattern - * - * @since 6.0.5 - */ - @Incubating - HibernateValidatorConfiguration temporalValidationTolerance(Duration temporalValidationTolerance); +public interface HibernateValidatorConfiguration extends BaseHibernateValidatorConfiguration { - /** - * Allows to set a payload which will be passed to the constraint validators. If the method is called multiple - * times, only the payload passed last will be propagated. - * - * @param constraintValidatorPayload the payload passed to constraint validators - * - * @return {@code this} following the chaining method pattern - * - * @since 6.0.8 - */ - @Incubating - HibernateValidatorConfiguration constraintValidatorPayload(Object constraintValidatorPayload); } diff --git a/engine/src/main/java/org/hibernate/validator/HibernateValidatorFactory.java b/engine/src/main/java/org/hibernate/validator/HibernateValidatorFactory.java index 67af4b8715..9935100968 100644 --- a/engine/src/main/java/org/hibernate/validator/HibernateValidatorFactory.java +++ b/engine/src/main/java/org/hibernate/validator/HibernateValidatorFactory.java @@ -13,6 +13,7 @@ import org.hibernate.validator.constraints.ParameterScriptAssert; import org.hibernate.validator.constraints.ScriptAssert; +import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy; import org.hibernate.validator.spi.scripting.ScriptEvaluator; import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; @@ -47,6 +48,16 @@ public interface HibernateValidatorFactory extends ValidatorFactory { @Incubating Duration getTemporalValidationTolerance(); + /** + * Returns the getter property selection strategy defining the rules determining if a method is a getter or not. + * + * @return the getter property selection strategy of the current {@link ValidatorFactory} + * + * @since 6.1.0 + */ + @Incubating + GetterPropertySelectionStrategy getGetterPropertySelectionStrategy(); + /** * Returns a context for validator configuration via options from the * Bean Validation API as well as specific ones from Hibernate Validator. diff --git a/engine/src/main/java/org/hibernate/validator/PredefinedScopeHibernateValidator.java b/engine/src/main/java/org/hibernate/validator/PredefinedScopeHibernateValidator.java new file mode 100644 index 0000000000..3d17248da3 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/PredefinedScopeHibernateValidator.java @@ -0,0 +1,44 @@ +/* + * 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; + +import javax.validation.Configuration; +import javax.validation.ValidatorFactory; +import javax.validation.spi.BootstrapState; +import javax.validation.spi.ConfigurationState; +import javax.validation.spi.ValidationProvider; + +import org.hibernate.validator.internal.engine.PredefinedScopeConfigurationImpl; +import org.hibernate.validator.internal.engine.PredefinedScopeValidatorFactoryImpl; + +/** + * Implementation of {@code ValidationProvider} limiting validation to a predefined scope. + *

+ * It allows to collect all the necessary metadata at bootstrap. + * + * @author Guillaume Smet + * + * @since 6.1 + */ +@Incubating +public class PredefinedScopeHibernateValidator implements ValidationProvider { + + @Override + public PredefinedScopeHibernateValidatorConfiguration createSpecializedConfiguration(BootstrapState state) { + return new PredefinedScopeConfigurationImpl( this ); + } + + @Override + public Configuration createGenericConfiguration(BootstrapState state) { + return new PredefinedScopeConfigurationImpl( state ); + } + + @Override + public ValidatorFactory buildValidatorFactory(ConfigurationState configurationState) { + return new PredefinedScopeValidatorFactoryImpl( configurationState ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/PredefinedScopeHibernateValidatorConfiguration.java b/engine/src/main/java/org/hibernate/validator/PredefinedScopeHibernateValidatorConfiguration.java new file mode 100644 index 0000000000..ff8a7ac33d --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/PredefinedScopeHibernateValidatorConfiguration.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; + +import java.util.Locale; +import java.util.Set; + +import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer; + +/** + * Extension of {@link HibernateValidatorConfiguration} with additional methods dedicated to defining the predefined + * scope of bean validation e.g. validated classes, constraint validators... + * + * @author Guillaume Smet + * + * @since 6.1 + */ +@Incubating +public interface PredefinedScopeHibernateValidatorConfiguration extends BaseHibernateValidatorConfiguration { + + @Incubating + PredefinedScopeHibernateValidatorConfiguration initializeBeanMetaData(Set> beanClassesToInitialize); + + @Incubating + PredefinedScopeHibernateValidatorConfiguration initializeLocales(Set locales); + + @Incubating + PredefinedScopeHibernateValidatorConfiguration beanMetaDataClassNormalizer(BeanMetaDataClassNormalizer beanMetaDataClassNormalizer); +} diff --git a/engine/src/main/java/org/hibernate/validator/PredefinedScopeHibernateValidatorFactory.java b/engine/src/main/java/org/hibernate/validator/PredefinedScopeHibernateValidatorFactory.java new file mode 100644 index 0000000000..7b0586f4f5 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/PredefinedScopeHibernateValidatorFactory.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; + +import javax.validation.ValidatorFactory; + +/** + * Provides Hibernate Validator extensions to {@link ValidatorFactory} in the context of a predefined scope. + * + * @since 6.1 + */ +@Incubating +public interface PredefinedScopeHibernateValidatorFactory extends HibernateValidatorFactory { +} diff --git a/engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintDefinitionTarget.java b/engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintDefinitionTarget.java index 9d95376c39..be60cfe004 100644 --- a/engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintDefinitionTarget.java +++ b/engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintDefinitionTarget.java @@ -9,7 +9,7 @@ import java.lang.annotation.Annotation; /** - * Facet of a constraint definition creational context which allows to the select the constraint (annotation type) to + * Facet of a constraint definition creational context which allows to select the constraint (annotation type) to * which the next operations shall apply. * * @author Yoann Rodiere diff --git a/engine/src/main/java/org/hibernate/validator/cfg/context/ConstructorTarget.java b/engine/src/main/java/org/hibernate/validator/cfg/context/ConstructorTarget.java index e764b84f8b..5df88fd96b 100644 --- a/engine/src/main/java/org/hibernate/validator/cfg/context/ConstructorTarget.java +++ b/engine/src/main/java/org/hibernate/validator/cfg/context/ConstructorTarget.java @@ -7,7 +7,7 @@ package org.hibernate.validator.cfg.context; /** - * Facet of a constraint mapping creational context which allows to the select the bean + * Facet of a constraint mapping creational context which allows to select the bean * constructor to which the next operations shall apply. * * @author Gunnar Morling diff --git a/engine/src/main/java/org/hibernate/validator/cfg/context/CrossParameterTarget.java b/engine/src/main/java/org/hibernate/validator/cfg/context/CrossParameterTarget.java index 8caa0449b3..a7e12b75be 100644 --- a/engine/src/main/java/org/hibernate/validator/cfg/context/CrossParameterTarget.java +++ b/engine/src/main/java/org/hibernate/validator/cfg/context/CrossParameterTarget.java @@ -7,7 +7,7 @@ package org.hibernate.validator.cfg.context; /** - * Facet of a constraint mapping creational context which allows to the select the cross-parameter element of a method + * Facet of a constraint mapping creational context which allows to select the cross-parameter element of a method * or constructor as target of the next operations. * * @author Gunnar Morling diff --git a/engine/src/main/java/org/hibernate/validator/cfg/context/MethodTarget.java b/engine/src/main/java/org/hibernate/validator/cfg/context/MethodTarget.java index bb46e208be..280998d393 100644 --- a/engine/src/main/java/org/hibernate/validator/cfg/context/MethodTarget.java +++ b/engine/src/main/java/org/hibernate/validator/cfg/context/MethodTarget.java @@ -7,7 +7,7 @@ package org.hibernate.validator.cfg.context; /** - * Facet of a constraint mapping creational context which allows to the select the bean + * Facet of a constraint mapping creational context which allows to select the bean * method to which the next operations shall apply. * * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI diff --git a/engine/src/main/java/org/hibernate/validator/cfg/context/ParameterTarget.java b/engine/src/main/java/org/hibernate/validator/cfg/context/ParameterTarget.java index 7159075290..f2bac6be31 100644 --- a/engine/src/main/java/org/hibernate/validator/cfg/context/ParameterTarget.java +++ b/engine/src/main/java/org/hibernate/validator/cfg/context/ParameterTarget.java @@ -7,7 +7,7 @@ package org.hibernate.validator.cfg.context; /** - * Facet of a constraint mapping creational context which allows to the select a method or constructor parameter to + * Facet of a constraint mapping creational context which allows to select a method or constructor parameter to * which the next operations shall apply. * * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI diff --git a/engine/src/main/java/org/hibernate/validator/cfg/context/PropertyConstraintMappingContext.java b/engine/src/main/java/org/hibernate/validator/cfg/context/PropertyConstraintMappingContext.java index 9361f9fc3a..56304caea0 100644 --- a/engine/src/main/java/org/hibernate/validator/cfg/context/PropertyConstraintMappingContext.java +++ b/engine/src/main/java/org/hibernate/validator/cfg/context/PropertyConstraintMappingContext.java @@ -14,6 +14,7 @@ * @author Gunnar Morling * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI */ +@SuppressWarnings("deprecation") public interface PropertyConstraintMappingContext extends Constrainable, ConstraintMappingTarget, PropertyTarget, diff --git a/engine/src/main/java/org/hibernate/validator/cfg/context/PropertyTarget.java b/engine/src/main/java/org/hibernate/validator/cfg/context/PropertyTarget.java index bce7f0c448..3546ce6dd0 100644 --- a/engine/src/main/java/org/hibernate/validator/cfg/context/PropertyTarget.java +++ b/engine/src/main/java/org/hibernate/validator/cfg/context/PropertyTarget.java @@ -9,7 +9,7 @@ import java.lang.annotation.ElementType; /** - * Facet of a constraint mapping creational context which allows to the select the bean + * Facet of a constraint mapping creational context which allows to select the bean * property to which the next operations shall apply. * * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI @@ -21,15 +21,46 @@ public interface PropertyTarget { *

* Until this method is called constraints apply on class level. After calling this method constraints * apply on the specified property with the given access type. - *

*

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

* * @param property The property on which to apply the following constraints (Java Bean notation). * @param type The access type (field/property). * * @return A creational context representing the selected property. + * + * @deprecated Since 6.1. Planned for removal. Use either {@link PropertyTarget#field(String)} or + * {@link PropertyTarget#getter(String)} instead. */ + @Deprecated PropertyConstraintMappingContext property(String property, ElementType type); + + /** + * Selects a field to which the next operations shall apply. + *

+ * Until this method is called constraints apply on class level. After calling this method constraints + * apply on the specified field property. + *

+ * A given field may only be configured once. + * + * @param property The field name that represents a property on which to apply the following constraints. + * + * @return A creational context representing the selected field property. + */ + PropertyConstraintMappingContext field(String property); + + /** + * Selects a getter to which the next operations shall apply. + *

+ * Until this method is called constraints apply on class level. After calling this method constraints + * apply on the specified getter property. + *

+ * A given getter may only be configured once. + * + * @param property The getter property name (using the Java Bean notation, e.g. {@code name} to address {@code getName()}) + * that represents a property on which to apply the following constraints. + * + * @return A creational context representing the selected getter property. + */ + PropertyConstraintMappingContext getter(String property); } diff --git a/engine/src/main/java/org/hibernate/validator/cfg/context/ReturnValueTarget.java b/engine/src/main/java/org/hibernate/validator/cfg/context/ReturnValueTarget.java index b632c6dc22..46a5e4b39b 100644 --- a/engine/src/main/java/org/hibernate/validator/cfg/context/ReturnValueTarget.java +++ b/engine/src/main/java/org/hibernate/validator/cfg/context/ReturnValueTarget.java @@ -7,7 +7,7 @@ package org.hibernate.validator.cfg.context; /** - * Facet of a constraint mapping creational context which allows to the select the current method's or constructor's + * Facet of a constraint mapping creational context which allows to select the current method's or constructor's * return value as target for the next operations. * * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI diff --git a/engine/src/main/java/org/hibernate/validator/cfg/context/TypeConstraintMappingContext.java b/engine/src/main/java/org/hibernate/validator/cfg/context/TypeConstraintMappingContext.java index 4655d18855..aa3495daa7 100644 --- a/engine/src/main/java/org/hibernate/validator/cfg/context/TypeConstraintMappingContext.java +++ b/engine/src/main/java/org/hibernate/validator/cfg/context/TypeConstraintMappingContext.java @@ -18,6 +18,7 @@ * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI * @author Gunnar Morling */ +@SuppressWarnings("deprecation") public interface TypeConstraintMappingContext extends Constrainable>, ConstraintMappingTarget, PropertyTarget, diff --git a/engine/src/main/java/org/hibernate/validator/cfg/context/TypeTarget.java b/engine/src/main/java/org/hibernate/validator/cfg/context/TypeTarget.java index 471511efb1..588ae68bad 100644 --- a/engine/src/main/java/org/hibernate/validator/cfg/context/TypeTarget.java +++ b/engine/src/main/java/org/hibernate/validator/cfg/context/TypeTarget.java @@ -7,7 +7,7 @@ package org.hibernate.validator.cfg.context; /** - * Facet of a constraint mapping creational context which allows to the select the bean + * Facet of a constraint mapping creational context which allows to select the bean * type to which the next operations shall apply. * * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI diff --git a/engine/src/main/java/org/hibernate/validator/cfg/defs/SafeHtmlDef.java b/engine/src/main/java/org/hibernate/validator/cfg/defs/SafeHtmlDef.java index a12569741a..1f04b6dbbb 100644 --- a/engine/src/main/java/org/hibernate/validator/cfg/defs/SafeHtmlDef.java +++ b/engine/src/main/java/org/hibernate/validator/cfg/defs/SafeHtmlDef.java @@ -12,7 +12,9 @@ /** * @author Marko Bekta + * @deprecated {@code @SafeHtml} support will be removed in a future version */ +@Deprecated public class SafeHtmlDef extends ConstraintDef { public SafeHtmlDef() { diff --git a/engine/src/main/java/org/hibernate/validator/constraints/Email.java b/engine/src/main/java/org/hibernate/validator/constraints/Email.java index 16997e5c26..59445700f9 100644 --- a/engine/src/main/java/org/hibernate/validator/constraints/Email.java +++ b/engine/src/main/java/org/hibernate/validator/constraints/Email.java @@ -25,8 +25,6 @@ import javax.validation.ReportAsSingleViolation; import javax.validation.constraints.Pattern; -import org.hibernate.validator.constraints.Email.List; - /** * The string has to be a well-formed email address. * @@ -39,7 +37,7 @@ @Constraint(validatedBy = { }) @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) @Retention(RUNTIME) -@Repeatable(List.class) +@Repeatable(Email.List.class) @ReportAsSingleViolation @Pattern(regexp = "") @Deprecated diff --git a/engine/src/main/java/org/hibernate/validator/constraints/ISBN.java b/engine/src/main/java/org/hibernate/validator/constraints/ISBN.java index cbce289f99..fec95f5622 100644 --- a/engine/src/main/java/org/hibernate/validator/constraints/ISBN.java +++ b/engine/src/main/java/org/hibernate/validator/constraints/ISBN.java @@ -67,9 +67,13 @@ /** * Defines the ISBN length. Valid lengths of ISBNs are {@code 10} and {@code 13} * which are represented as {@link Type#ISBN_10} and {@link Type#ISBN_13} respectively. + *

+ * Using {@link Type#ANY} allows to validate values that could either be ISBN10 or ISBN13. + * In such case, ISBN type would be determined by the length of the corresponding value. */ enum Type { ISBN_10, - ISBN_13 + ISBN_13, + ANY } } diff --git a/engine/src/main/java/org/hibernate/validator/constraints/ModCheck.java b/engine/src/main/java/org/hibernate/validator/constraints/ModCheck.java index c055b19c21..6c3cf28697 100644 --- a/engine/src/main/java/org/hibernate/validator/constraints/ModCheck.java +++ b/engine/src/main/java/org/hibernate/validator/constraints/ModCheck.java @@ -22,8 +22,6 @@ import javax.validation.Constraint; import javax.validation.Payload; -import org.hibernate.validator.constraints.ModCheck.List; - /** * Modulo check constraint. *

@@ -42,7 +40,7 @@ @Constraint(validatedBy = { }) @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) @Retention(RUNTIME) -@Repeatable(List.class) +@Repeatable(ModCheck.List.class) public @interface ModCheck { String message() default "{org.hibernate.validator.constraints.ModCheck.message}"; diff --git a/engine/src/main/java/org/hibernate/validator/constraints/NotBlank.java b/engine/src/main/java/org/hibernate/validator/constraints/NotBlank.java index b39bf68afb..52da079478 100644 --- a/engine/src/main/java/org/hibernate/validator/constraints/NotBlank.java +++ b/engine/src/main/java/org/hibernate/validator/constraints/NotBlank.java @@ -24,8 +24,6 @@ import javax.validation.ReportAsSingleViolation; import javax.validation.constraints.NotNull; -import org.hibernate.validator.constraints.NotBlank.List; - /** * Validate that the annotated string is not {@code null} or empty. * The difference to {@code NotEmpty} is that trailing whitespaces are getting ignored. @@ -40,7 +38,7 @@ @Retention(RUNTIME) @ReportAsSingleViolation @NotNull -@Repeatable(List.class) +@Repeatable(NotBlank.List.class) @Deprecated public @interface NotBlank { String message() default "{org.hibernate.validator.constraints.NotBlank.message}"; diff --git a/engine/src/main/java/org/hibernate/validator/constraints/NotEmpty.java b/engine/src/main/java/org/hibernate/validator/constraints/NotEmpty.java index d44e03d4d8..b6c19d1845 100644 --- a/engine/src/main/java/org/hibernate/validator/constraints/NotEmpty.java +++ b/engine/src/main/java/org/hibernate/validator/constraints/NotEmpty.java @@ -27,8 +27,6 @@ import javax.validation.constraintvalidation.SupportedValidationTarget; import javax.validation.constraintvalidation.ValidationTarget; -import org.hibernate.validator.constraints.NotEmpty.List; - /** * Asserts that the annotated string, collection, map or array is not {@code null} or empty. * @@ -42,7 +40,7 @@ @SupportedValidationTarget(ValidationTarget.ANNOTATED_ELEMENT) @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) @Retention(RUNTIME) -@Repeatable(List.class) +@Repeatable(NotEmpty.List.class) @ReportAsSingleViolation @NotNull @Size(min = 1) diff --git a/engine/src/main/java/org/hibernate/validator/constraints/ParameterScriptAssert.java b/engine/src/main/java/org/hibernate/validator/constraints/ParameterScriptAssert.java index 889eecd16a..d15c7d833c 100644 --- a/engine/src/main/java/org/hibernate/validator/constraints/ParameterScriptAssert.java +++ b/engine/src/main/java/org/hibernate/validator/constraints/ParameterScriptAssert.java @@ -33,15 +33,16 @@ * ("Scripting for the JavaTM Platform") compatible engine can be * found on the classpath. To refer to a parameter within the scripting * expression, use its name as obtained by the active - * {@link javax.validation.ParameterNameProvider}. By default, {@code arg0}, {@code arg1} etc. - * will be used as parameter names. + * {@link javax.validation.ParameterNameProvider}. The default provider will + * return the actual parameter names, if the -parameters compiler option + * has been enabled, and {@code arg0}, {@code arg1} etc. otherwise. *

*

* The following listing shows an example using the JavaScript engine which * comes with the JDK: *

*
- * {@code @ParameterScriptAssert(script = "arg0.before(arg1)", lang = "javascript")
+ * {@code @ParameterScriptAssert(script = "start.before(end)", lang = "javascript")
  * public void createEvent(Date start, Date end) { ... }
  * }
  * 
diff --git a/engine/src/main/java/org/hibernate/validator/constraints/SafeHtml.java b/engine/src/main/java/org/hibernate/validator/constraints/SafeHtml.java index fa08e2fa81..86d7848cec 100644 --- a/engine/src/main/java/org/hibernate/validator/constraints/SafeHtml.java +++ b/engine/src/main/java/org/hibernate/validator/constraints/SafeHtml.java @@ -34,12 +34,14 @@ * {@code body} tags to the used whitelist as required. * * @author George Gastaldi + * @deprecated {@code @SafeHtml} support will be removed in a future version */ @Documented @Constraint(validatedBy = { }) @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) @Retention(RUNTIME) @Repeatable(List.class) +@Deprecated public @interface SafeHtml { String message() default "{org.hibernate.validator.constraints.SafeHtml.message}"; diff --git a/engine/src/main/java/org/hibernate/validator/constraintvalidation/HibernateCrossParameterConstraintValidatorContext.java b/engine/src/main/java/org/hibernate/validator/constraintvalidation/HibernateCrossParameterConstraintValidatorContext.java new file mode 100644 index 0000000000..b7ad51c76b --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/constraintvalidation/HibernateCrossParameterConstraintValidatorContext.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.constraintvalidation; + +import java.util.List; + +import javax.validation.ConstraintValidatorContext; + +import org.hibernate.validator.Incubating; + +/** + * A custom {@link ConstraintValidatorContext} which provides additional functionality for cross parameter validation contexts. + * + * @author Marko Bekhta + * @since 6.1.0 + */ +@Incubating +public interface HibernateCrossParameterConstraintValidatorContext extends HibernateConstraintValidatorContext { + + /** + * @return the list of the parameter names of the validated method. + */ + List getMethodParameterNames(); +} diff --git a/engine/src/main/java/org/hibernate/validator/engine/HibernateValidatorEnhancedBean.java b/engine/src/main/java/org/hibernate/validator/engine/HibernateValidatorEnhancedBean.java new file mode 100644 index 0000000000..d2818b6b92 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/engine/HibernateValidatorEnhancedBean.java @@ -0,0 +1,49 @@ +/* + * 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.engine; + +import org.hibernate.validator.Incubating; + +/** + * Hibernate Validator specific marker interface. Beans implementing this interface + * would use corresponding {@link HibernateValidatorEnhancedBean#$$_hibernateValidator_getFieldValue(String)} + * and {@link HibernateValidatorEnhancedBean#$$_hibernateValidator_getGetterValue(String)} methods to retrieve + * bean property values instead of using reflection or any other means. + *

+ * It is important to keep in mind that in case of explicit implementation of this interface + * access to all possible constrained getters and fields should be provided, for a class implementing + * the interface and all its super classes as well. Otherwise unexpected {@link IllegalArgumentException} + * could be thrown by the Hibernate Validator engine. + * + * @author Marko Bekhta + * @since 6.1 + */ +@Incubating +public interface HibernateValidatorEnhancedBean { + + String GET_FIELD_VALUE_METHOD_NAME = "$$_hibernateValidator_getFieldValue"; + + String GET_GETTER_VALUE_METHOD_NAME = "$$_hibernateValidator_getGetterValue"; + + /** + * @param name the name of a field property of interest. + * + * @return the value of the field named {@code name} of the current bean. + * + * @throws IllegalArgumentException in case no field could be found for the given name. + */ + Object $$_hibernateValidator_getFieldValue(String name); + + /** + * @param name the name of a getter of interest. + * + * @return the value returned by the getter named {@code name} of the current bean. + * + * @throws IllegalArgumentException in case when no getter property could be found for the given name. + */ + Object $$_hibernateValidator_getGetterValue(String name); +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/PropertyConstraintMappingContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/AbstractPropertyConstraintMappingContextImpl.java similarity index 52% rename from engine/src/main/java/org/hibernate/validator/internal/cfg/context/PropertyConstraintMappingContextImpl.java rename to engine/src/main/java/org/hibernate/validator/internal/cfg/context/AbstractPropertyConstraintMappingContextImpl.java index dcb6681ce5..056470024c 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/PropertyConstraintMappingContextImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/AbstractPropertyConstraintMappingContextImpl.java @@ -7,26 +7,16 @@ package org.hibernate.validator.internal.cfg.context; import java.lang.annotation.ElementType; -import java.lang.reflect.Executable; -import java.lang.reflect.Field; -import java.lang.reflect.Member; -import java.lang.reflect.Method; -import org.hibernate.validator.cfg.ConstraintDef; import org.hibernate.validator.cfg.context.ConstructorConstraintMappingContext; import org.hibernate.validator.cfg.context.ContainerElementConstraintMappingContext; import org.hibernate.validator.cfg.context.MethodConstraintMappingContext; import org.hibernate.validator.cfg.context.PropertyConstraintMappingContext; -import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.engine.ConstraintCreationContext; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl.ConstraintType; 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.ConstrainedExecutable; -import org.hibernate.validator.internal.metadata.raw.ConstrainedField; -import org.hibernate.validator.internal.util.ReflectionHelper; -import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.properties.Property; /** * Constraint mapping creational context which allows to configure the constraints for one bean property. @@ -34,69 +24,58 @@ * @author Hardy Ferentschik * @author Gunnar Morling * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI + * @author Marko Bekhta */ -final class PropertyConstraintMappingContextImpl +abstract class AbstractPropertyConstraintMappingContextImpl extends CascadableConstraintMappingContextImplBase implements PropertyConstraintMappingContext { private final TypeConstraintMappingContextImpl typeContext; // either Field or Method - private final Member member; + private final T property; private final ConstraintLocation location; - PropertyConstraintMappingContextImpl(TypeConstraintMappingContextImpl typeContext, Member member) { - super( typeContext.getConstraintMapping(), ReflectionHelper.typeOf( member ) ); + protected AbstractPropertyConstraintMappingContextImpl(TypeConstraintMappingContextImpl typeContext, T property, ConstraintLocation location) { + super( typeContext.getConstraintMapping(), property.getType() ); this.typeContext = typeContext; - this.member = member; - if ( member instanceof Field ) { - this.location = ConstraintLocation.forField( (Field) member ); - } - else { - this.location = ConstraintLocation.forGetter( (Method) member ); - } + this.property = property; + this.location = location; } @Override - protected PropertyConstraintMappingContextImpl getThis() { - return this; - } - - @Override - public PropertyConstraintMappingContext constraint(ConstraintDef definition) { - if ( member instanceof Field ) { - super.addConstraint( - ConfiguredConstraint.forProperty( - definition, member - ) - ); - } - else { - super.addConstraint( - ConfiguredConstraint.forExecutable( - definition, (Method) member - ) - ); - } + protected AbstractPropertyConstraintMappingContextImpl getThis() { return this; } @Override + @SuppressWarnings("deprecation") public PropertyConstraintMappingContext ignoreAnnotations() { return ignoreAnnotations( true ); } @Override public PropertyConstraintMappingContext ignoreAnnotations(boolean ignoreAnnotations) { - mapping.getAnnotationProcessingOptions().ignoreConstraintAnnotationsOnMember( member, ignoreAnnotations ); + mapping.getAnnotationProcessingOptions().ignoreConstraintAnnotationsOnMember( property, ignoreAnnotations ); return this; } @Override + @SuppressWarnings("deprecation") public PropertyConstraintMappingContext property(String property, ElementType elementType) { return typeContext.property( property, elementType ); } + @Override + public PropertyConstraintMappingContext field(String property) { + return typeContext.field( property ); + } + + @Override + public PropertyConstraintMappingContext getter(String property) { + return typeContext.getter( property ); + } + @Override public ConstructorConstraintMappingContext constructor(Class... parameterTypes) { return typeContext.constructor( parameterTypes ); @@ -117,29 +96,14 @@ public ContainerElementConstraintMappingContext containerElementType(int index, return super.containerElement( this, typeContext, location, index, nestedIndexes ); } - ConstrainedElement build(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager) { - if ( member instanceof Field ) { - return new ConstrainedField( - ConfigurationSource.API, - (Field) member, - getConstraints( constraintHelper, typeResolutionHelper, valueExtractorManager ), - getTypeArgumentConstraints( constraintHelper, typeResolutionHelper, valueExtractorManager ), - getCascadingMetaDataBuilder() - ); - } - else { - return new ConstrainedExecutable( - ConfigurationSource.API, - (Executable) member, - getConstraints( constraintHelper, typeResolutionHelper, valueExtractorManager ), - getTypeArgumentConstraints( constraintHelper, typeResolutionHelper, valueExtractorManager ), - getCascadingMetaDataBuilder() - ); - } - } + abstract ConstrainedElement build(ConstraintCreationContext constraintCreationContext); @Override protected ConstraintType getConstraintType() { return ConstraintType.GENERIC; } + + protected T getProperty() { + return property; + } } 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 323ee74c7a..b77845420a 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 @@ -23,14 +23,12 @@ import org.hibernate.validator.cfg.context.ContainerElementConstraintMappingContext; 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.engine.ConstraintCreationContext; 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.location.ConstraintLocation; import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.TypeHelper; -import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; @@ -130,11 +128,14 @@ public ContainerElementConstraintMappingContext containerElement(ContainerElemen ); } - ContainerElementConstraintMappingContextImpl containerElementContext = new ContainerElementConstraintMappingContextImpl( - typeContext, parent, location, index - ); - - containerElementContexts.put( index, containerElementContext ); + // 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, parent, location, index ); + containerElementContexts.put( index, containerElementContext ); + } if ( nestedIndexes.length > 0 ) { return containerElementContext.nestedContainerElement( nestedIndexes ); @@ -148,10 +149,10 @@ public boolean isCascading() { return isCascading; } - protected Set> getTypeArgumentConstraints(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager) { + protected Set> getTypeArgumentConstraints(ConstraintCreationContext constraintCreationContext) { return containerElementContexts.values() .stream() - .map( t -> t.build( constraintHelper, typeResolutionHelper, valueExtractorManager ) ) + .map( t -> t.build( constraintCreationContext ) ) .flatMap( Set::stream ) .collect( Collectors.toSet() ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConfiguredConstraint.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConfiguredConstraint.java index 8f338757fe..211525ee86 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConfiguredConstraint.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConfiguredConstraint.java @@ -7,13 +7,8 @@ package org.hibernate.validator.internal.cfg.context; import java.lang.annotation.Annotation; -import java.lang.annotation.ElementType; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; -import java.lang.reflect.Executable; -import java.lang.reflect.Field; -import java.lang.reflect.Member; -import java.lang.reflect.Method; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.security.AccessController; @@ -24,7 +19,9 @@ import org.hibernate.validator.cfg.AnnotationDef; import org.hibernate.validator.cfg.ConstraintDef; import org.hibernate.validator.internal.metadata.location.ConstraintLocation; -import org.hibernate.validator.internal.util.ExecutableHelper; +import org.hibernate.validator.internal.properties.Callable; +import org.hibernate.validator.internal.properties.javabean.JavaBeanField; +import org.hibernate.validator.internal.properties.javabean.JavaBeanGetter; import org.hibernate.validator.internal.util.annotation.AnnotationDescriptor; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; import org.hibernate.validator.internal.util.logging.Log; @@ -36,6 +33,7 @@ * related to its location (bean type etc.). * * @author Gunnar Morling + * @author Guillaume Smet */ class ConfiguredConstraint { @@ -46,58 +44,40 @@ class ConfiguredConstraint { private final ConstraintDef constraint; private final ConstraintLocation location; - private final ElementType elementType; - private ConfiguredConstraint(ConstraintDef constraint, ConstraintLocation location, ElementType elementType) { + private ConfiguredConstraint(ConstraintDef constraint, ConstraintLocation location) { this.constraint = constraint; this.location = location; - this.elementType = elementType; } static ConfiguredConstraint forType(ConstraintDef constraint, Class beanType) { - return new ConfiguredConstraint<>( constraint, ConstraintLocation.forClass( beanType ), ElementType.TYPE ); + return new ConfiguredConstraint<>( constraint, ConstraintLocation.forClass( beanType ) ); } - static ConfiguredConstraint forProperty(ConstraintDef constraint, Member member) { - if ( member instanceof Field ) { - return new ConfiguredConstraint<>( - constraint, - ConstraintLocation.forField( (Field) member ), - ElementType.FIELD - ); - } - else { - return new ConfiguredConstraint<>( - constraint, - ConstraintLocation.forGetter( (Method) member ), - ElementType.METHOD - ); - } + static ConfiguredConstraint forField(ConstraintDef constraint, JavaBeanField javaBeanField) { + return new ConfiguredConstraint<>( constraint, ConstraintLocation.forField( javaBeanField ) ); } - public static ConfiguredConstraint forParameter(ConstraintDef constraint, Executable executable, int parameterIndex) { - return new ConfiguredConstraint<>( - constraint, ConstraintLocation.forParameter( executable, parameterIndex ), ExecutableHelper.getElementType( executable ) - ); + static ConfiguredConstraint forGetter(ConstraintDef constraint, JavaBeanGetter javaBeanGetter) { + return forExecutable( constraint, javaBeanGetter ); } - public static ConfiguredConstraint forExecutable(ConstraintDef constraint, Executable executable) { - return new ConfiguredConstraint<>( - constraint, ConstraintLocation.forReturnValue( executable ), ExecutableHelper.getElementType( executable ) - ); + public static ConfiguredConstraint forParameter(ConstraintDef constraint, Callable callable, int parameterIndex) { + return new ConfiguredConstraint<>( constraint, ConstraintLocation.forParameter( callable, parameterIndex ) ); } - public static ConfiguredConstraint forCrossParameter(ConstraintDef constraint, Executable executable) { - return new ConfiguredConstraint<>( - constraint, ConstraintLocation.forCrossParameter( executable ), ExecutableHelper.getElementType( executable ) - ); + public static ConfiguredConstraint forExecutable(ConstraintDef constraint, Callable callable) { + return new ConfiguredConstraint<>( constraint, ConstraintLocation.forReturnValue( callable ) ); } - public static ConfiguredConstraint forTypeArgument(ConstraintDef constraint,ConstraintLocation delegate, TypeVariable typeArgument, Type typeOfAnnotatedElement) { + public static ConfiguredConstraint forCrossParameter(ConstraintDef constraint, Callable callable) { + return new ConfiguredConstraint<>( constraint, ConstraintLocation.forCrossParameter( callable ) ); + } + + public static ConfiguredConstraint forTypeArgument(ConstraintDef constraint, ConstraintLocation delegate, TypeVariable typeArgument, Type typeOfAnnotatedElement) { return new ConfiguredConstraint<>( constraint, - ConstraintLocation.forTypeArgument( delegate, typeArgument, typeOfAnnotatedElement ), - ElementType.TYPE_USE + ConstraintLocation.forTypeArgument( delegate, typeArgument, typeOfAnnotatedElement ) ); } @@ -111,6 +91,7 @@ public ConstraintLocation getLocation() { public ConstraintAnnotationDescriptor createAnnotationDescriptor() { try { + @SuppressWarnings("unchecked") AnnotationDescriptor annotationDescriptor = (AnnotationDescriptor) CREATE_ANNOTATION_DESCRIPTOR_METHOD_HANDLE.invoke( constraint ); return new ConstraintAnnotationDescriptor<>( annotationDescriptor ); } @@ -127,10 +108,6 @@ public String toString() { return constraint.toString(); } - public ElementType getElementType() { - return elementType; - } - /** * 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 diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintDefinitionContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintDefinitionContextImpl.java index 6478ac3611..b49a0b8213 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintDefinitionContextImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintDefinitionContextImpl.java @@ -47,7 +47,7 @@ public ConstraintDefinitionContext includeExistingValidators(boolean includeE @Override public ConstraintDefinitionContext validatedBy(Class> validator) { - validatorDescriptors.add( ConstraintValidatorDescriptor.forClass( validator ) ); + validatorDescriptors.add( ConstraintValidatorDescriptor.forClass( validator, this.annotationType ) ); return this; } diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintMappingContextImplBase.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintMappingContextImplBase.java index 3eb7a9c92c..8207726486 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintMappingContextImplBase.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintMappingContextImplBase.java @@ -12,13 +12,11 @@ import java.util.Collections; import java.util.Set; -import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.engine.ConstraintCreationContext; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.core.MetaConstraints; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl.ConstraintType; -import org.hibernate.validator.internal.util.TypeResolutionHelper; /** * Base class for implementations of constraint mapping creational context types. @@ -54,8 +52,7 @@ protected void addConstraint(ConfiguredConstraint constraint) { constraints.add( constraint ); } - protected Set> getConstraints(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, - ValueExtractorManager valueExtractorManager) { + protected Set> getConstraints(ConstraintCreationContext constraintCreationContext) { if ( constraints == null ) { return Collections.emptySet(); } @@ -63,22 +60,23 @@ protected Set> getConstraints(ConstraintHelper constraintHelpe Set> metaConstraints = newHashSet(); for ( ConfiguredConstraint configuredConstraint : constraints ) { - metaConstraints.add( asMetaConstraint( configuredConstraint, constraintHelper, typeResolutionHelper, valueExtractorManager ) ); + metaConstraints.add( asMetaConstraint( configuredConstraint, constraintCreationContext ) ); } return metaConstraints; } - private MetaConstraint asMetaConstraint(ConfiguredConstraint config, ConstraintHelper constraintHelper, - TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager) { + private MetaConstraint asMetaConstraint(ConfiguredConstraint config, ConstraintCreationContext constraintCreationContext ) { ConstraintDescriptorImpl constraintDescriptor = new ConstraintDescriptorImpl( - constraintHelper, - config.getLocation().getMember(), + constraintCreationContext.getConstraintHelper(), + config.getLocation().getConstrainable(), config.createAnnotationDescriptor(), - config.getElementType(), + config.getLocation().getKind(), getConstraintType() ); - return MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintDescriptor, config.getLocation() ); + return MetaConstraints.create( constraintCreationContext.getTypeResolutionHelper(), + constraintCreationContext.getValueExtractorManager(), + constraintCreationContext.getConstraintValidatorManager(), constraintDescriptor, config.getLocation() ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstructorConstraintMappingContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstructorConstraintMappingContextImpl.java index 7f9683a57a..f062f38ab0 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstructorConstraintMappingContextImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstructorConstraintMappingContextImpl.java @@ -6,9 +6,8 @@ */ package org.hibernate.validator.internal.cfg.context; -import java.lang.reflect.Constructor; - import org.hibernate.validator.cfg.context.ConstructorConstraintMappingContext; +import org.hibernate.validator.internal.properties.javabean.JavaBeanConstructor; /** * Constraint mapping creational context representing a constructor. @@ -17,7 +16,7 @@ */ class ConstructorConstraintMappingContextImpl extends ExecutableConstraintMappingContextImpl implements ConstructorConstraintMappingContext { - ConstructorConstraintMappingContextImpl(TypeConstraintMappingContextImpl typeContext, Constructor constructor) { + ConstructorConstraintMappingContextImpl(TypeConstraintMappingContextImpl typeContext, JavaBeanConstructor constructor) { super( typeContext, constructor ); } @@ -25,7 +24,7 @@ ConstructorConstraintMappingContextImpl(TypeConstraintMappingContextImpl public ConstructorConstraintMappingContext ignoreAnnotations(boolean ignoreAnnotations) { typeContext.mapping .getAnnotationProcessingOptions() - .ignoreConstraintAnnotationsOnMember( executable, ignoreAnnotations ); + .ignoreConstraintAnnotationsOnMember( callable, ignoreAnnotations ); return this; } 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 ca7d0f7514..9502eeb08b 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 @@ -31,10 +31,9 @@ import org.hibernate.validator.cfg.context.PropertyConstraintMappingContext; import org.hibernate.validator.cfg.context.ReturnValueConstraintMappingContext; import org.hibernate.validator.cfg.context.ReturnValueTarget; +import org.hibernate.validator.internal.engine.ConstraintCreationContext; 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.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.core.MetaConstraints; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; @@ -43,7 +42,6 @@ import org.hibernate.validator.internal.util.ReflectionHelper; import org.hibernate.validator.internal.util.StringHelper; import org.hibernate.validator.internal.util.TypeHelper; -import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; @@ -119,10 +117,21 @@ protected ContainerElementConstraintMappingContext getThis() { } @Override + @Deprecated public PropertyConstraintMappingContext property(String property, ElementType elementType) { return typeContext.property( property, elementType ); } + @Override + public PropertyConstraintMappingContext field(String property) { + return typeContext.field( property ); + } + + @Override + public PropertyConstraintMappingContext getter(String property) { + return typeContext.getter( property ); + } + @Override public ConstructorConstraintMappingContext constructor(Class... parameterTypes) { return typeContext.constructor( parameterTypes ); @@ -168,14 +177,16 @@ ContainerElementConstraintMappingContext nestedContainerElement(int[] nestedInde throw LOG.getTypeIsNotAParameterizedNorArrayTypeException( configuredType ); } - ContainerElementConstraintMappingContextImpl nestedContext = new ContainerElementConstraintMappingContextImpl( - typeContext, - parentContainerElementTarget, - ConstraintLocation.forTypeArgument( parentLocation, typeParameter, getContainerElementType() ), - nestedIndexes[0] - ); - - nestedContainerElementContexts.put( nestedIndexes[0], nestedContext ); + ContainerElementConstraintMappingContextImpl nestedContext = nestedContainerElementContexts.get( nestedIndexes[0] ); + if ( nestedContext == null ) { + nestedContext = new ContainerElementConstraintMappingContextImpl( + typeContext, + parentContainerElementTarget, + ConstraintLocation.forTypeArgument( parentLocation, typeParameter, getContainerElementType() ), + nestedIndexes[0] + ); + nestedContainerElementContexts.put( nestedIndexes[0], nestedContext ); + } if ( nestedIndexes.length > 1 ) { return nestedContext.nestedContainerElement( Arrays.copyOfRange( nestedIndexes, 1, nestedIndexes.length ) ); @@ -218,30 +229,29 @@ CascadingMetaDataBuilder getContainerElementCascadingMetaDataBuilder() { ); } - Set> build(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, - ValueExtractorManager valueExtractorManager) { + Set> build(ConstraintCreationContext constraintCreationContext) { return Stream.concat( constraints.stream() - .map( c -> asMetaConstraint( c, constraintHelper, typeResolutionHelper, valueExtractorManager ) ), + .map( c -> asMetaConstraint( c, constraintCreationContext ) ), nestedContainerElementContexts.values() .stream() - .map( c -> c.build( constraintHelper, typeResolutionHelper, valueExtractorManager ) ) + .map( c -> c.build( constraintCreationContext ) ) .flatMap( Set::stream ) ) .collect( Collectors.toSet() ); } - private MetaConstraint asMetaConstraint(ConfiguredConstraint config, ConstraintHelper constraintHelper, - TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager) { + private MetaConstraint asMetaConstraint(ConfiguredConstraint config, ConstraintCreationContext constraintCreationContext) { ConstraintDescriptorImpl constraintDescriptor = new ConstraintDescriptorImpl<>( - constraintHelper, - config.getLocation().getMember(), + constraintCreationContext.getConstraintHelper(), + config.getLocation().getConstrainable(), config.createAnnotationDescriptor(), - config.getElementType(), + config.getLocation().getKind(), getConstraintType() ); - return MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintDescriptor, config.getLocation() ); + return MetaConstraints.create( constraintCreationContext.getTypeResolutionHelper(), constraintCreationContext.getValueExtractorManager(), + constraintCreationContext.getConstraintValidatorManager(), constraintDescriptor, config.getLocation() ); } @Override diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/CrossParameterConstraintMappingContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/CrossParameterConstraintMappingContextImpl.java index 8cae876274..d1247c09eb 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/CrossParameterConstraintMappingContextImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/CrossParameterConstraintMappingContextImpl.java @@ -32,14 +32,14 @@ final class CrossParameterConstraintMappingContextImpl @Override public CrossParameterConstraintMappingContext constraint(ConstraintDef definition) { - super.addConstraint( ConfiguredConstraint.forCrossParameter( definition, executableContext.getExecutable() ) ); + super.addConstraint( ConfiguredConstraint.forCrossParameter( definition, executableContext.getCallable() ) ); return this; } @Override public CrossParameterConstraintMappingContext ignoreAnnotations(boolean ignoreAnnotations) { mapping.getAnnotationProcessingOptions().ignoreConstraintAnnotationsForCrossParameterConstraint( - executableContext.getExecutable(), ignoreAnnotations + executableContext.getCallable(), ignoreAnnotations ); return this; } diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/DefaultConstraintMapping.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/DefaultConstraintMapping.java index 46cd84af48..9b48ab7138 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/DefaultConstraintMapping.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/DefaultConstraintMapping.java @@ -14,18 +14,16 @@ import java.util.Set; import javax.validation.Constraint; -import javax.validation.valueextraction.ValueExtractor; import org.hibernate.validator.cfg.ConstraintMapping; import org.hibernate.validator.cfg.context.ConstraintDefinitionContext; import org.hibernate.validator.cfg.context.TypeConstraintMappingContext; +import org.hibernate.validator.internal.engine.ConstraintCreationContext; import org.hibernate.validator.internal.engine.constraintdefinition.ConstraintDefinitionContribution; -import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; -import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.raw.BeanConfiguration; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; 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; @@ -40,13 +38,15 @@ public class DefaultConstraintMapping implements ConstraintMapping { private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + private final JavaBeanHelper javaBeanHelper; private final AnnotationProcessingOptionsImpl annotationProcessingOptions; private final Set> configuredTypes; private final Set> typeContexts; private final Set> definedConstraints; private final Set> constraintContexts; - public DefaultConstraintMapping() { + public DefaultConstraintMapping(JavaBeanHelper javaBeanHelper) { + this.javaBeanHelper = javaBeanHelper; this.annotationProcessingOptions = new AnnotationProcessingOptionsImpl(); this.configuredTypes = newHashSet(); this.typeContexts = newHashSet(); @@ -62,7 +62,7 @@ public final TypeConstraintMappingContext type(Class type) { throw LOG.getBeanClassHasAlreadyBeConfiguredViaProgrammaticApiException( type ); } - TypeConstraintMappingContextImpl typeContext = new TypeConstraintMappingContextImpl<>( this, type ); + TypeConstraintMappingContextImpl typeContext = new TypeConstraintMappingContextImpl<>( javaBeanHelper, this, type ); typeContexts.add( typeContext ); configuredTypes.add( type ); @@ -80,18 +80,15 @@ public Set> getConfiguredTypes() { /** * Returns all bean 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 + * @param constraintCreationContext the constraint creation context * * @return a set of {@link BeanConfiguration}s with an element for each type configured through this mapping */ - public Set> getBeanConfigurations(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, - ValueExtractorManager valueExtractorManager) { + public Set> getBeanConfigurations(ConstraintCreationContext constraintCreationContext) { Set> configurations = newHashSet(); for ( TypeConstraintMappingContextImpl typeContext : typeContexts ) { - configurations.add( typeContext.build( constraintHelper, typeResolutionHelper, valueExtractorManager ) ); + configurations.add( typeContext.build( constraintCreationContext ) ); } return configurations; 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 3f2f6d3523..260a741654 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 @@ -9,23 +9,20 @@ import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; import java.lang.invoke.MethodHandles; -import java.lang.reflect.Executable; import java.util.Collections; import java.util.List; import org.hibernate.validator.cfg.context.CrossParameterConstraintMappingContext; 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.engine.ConstraintCreationContext; 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.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.ConstrainedParameter; -import org.hibernate.validator.internal.util.ReflectionHelper; -import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.properties.Callable; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; @@ -41,20 +38,20 @@ abstract class ExecutableConstraintMappingContextImpl { private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); protected final TypeConstraintMappingContextImpl typeContext; - protected final Executable executable; + protected final Callable callable; private final ParameterConstraintMappingContextImpl[] parameterContexts; private ReturnValueConstraintMappingContextImpl returnValueContext; private CrossParameterConstraintMappingContextImpl crossParameterContext; - protected ExecutableConstraintMappingContextImpl(TypeConstraintMappingContextImpl typeContext, Executable executable) { + protected ExecutableConstraintMappingContextImpl(TypeConstraintMappingContextImpl typeContext, Callable callable) { this.typeContext = typeContext; - this.executable = executable; - this.parameterContexts = new ParameterConstraintMappingContextImpl[executable.getParameterTypes().length]; + this.callable = callable; + this.parameterContexts = new ParameterConstraintMappingContextImpl[callable.getParameterTypes().length]; } public ParameterConstraintMappingContext parameter(int index) { - if ( index < 0 || index >= executable.getParameterTypes().length ) { - throw LOG.getInvalidExecutableParameterIndexException( executable, index ); + if ( index < 0 || index >= callable.getParameterTypes().length ) { + throw LOG.getInvalidExecutableParameterIndexException( callable, index ); } ParameterConstraintMappingContextImpl context = parameterContexts[index]; @@ -62,7 +59,7 @@ public ParameterConstraintMappingContext parameter(int index) { if ( context != null ) { throw LOG.getParameterHasAlreadyBeConfiguredViaProgrammaticApiException( typeContext.getBeanClass(), - executable, + callable, index ); } @@ -76,7 +73,7 @@ public CrossParameterConstraintMappingContext crossParameter() { if ( crossParameterContext != null ) { throw LOG.getCrossParameterElementHasAlreadyBeConfiguredViaProgrammaticApiException( typeContext.getBeanClass(), - executable + callable ); } @@ -88,7 +85,7 @@ public ReturnValueConstraintMappingContext returnValue() { if ( returnValueContext != null ) { throw LOG.getReturnValueHasAlreadyBeConfiguredViaProgrammaticApiException( typeContext.getBeanClass(), - executable + callable ); } @@ -96,42 +93,40 @@ public ReturnValueConstraintMappingContext returnValue() { return returnValueContext; } - public Executable getExecutable() { - return executable; + public Callable getCallable() { + return callable; } public TypeConstraintMappingContextImpl getTypeContext() { return typeContext; } - public ConstrainedElement build(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, - ValueExtractorManager valueExtractorManager) { + public ConstrainedElement build(ConstraintCreationContext constraintCreationContext) { return new ConstrainedExecutable( ConfigurationSource.API, - executable, - getParameters( constraintHelper, typeResolutionHelper, valueExtractorManager ), - crossParameterContext != null ? crossParameterContext.getConstraints( constraintHelper, typeResolutionHelper, valueExtractorManager ) : Collections.>emptySet(), - returnValueContext != null ? returnValueContext.getConstraints( constraintHelper, typeResolutionHelper, valueExtractorManager ) : Collections.>emptySet(), - returnValueContext != null ? returnValueContext.getTypeArgumentConstraints( constraintHelper, typeResolutionHelper, valueExtractorManager ) : Collections.>emptySet(), + callable, + getParameters( constraintCreationContext ), + crossParameterContext != null ? crossParameterContext.getConstraints( constraintCreationContext ) : Collections.>emptySet(), + returnValueContext != null ? returnValueContext.getConstraints( constraintCreationContext ) : Collections.>emptySet(), + returnValueContext != null ? returnValueContext.getTypeArgumentConstraints( constraintCreationContext ) : Collections.>emptySet(), returnValueContext != null ? returnValueContext.getCascadingMetaDataBuilder() : CascadingMetaDataBuilder.nonCascading() ); } - private List getParameters(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, - ValueExtractorManager valueExtractorManager) { + private List getParameters(ConstraintCreationContext constraintCreationContext) { List constrainedParameters = newArrayList(); for ( int i = 0; i < parameterContexts.length; i++ ) { ParameterConstraintMappingContextImpl parameter = parameterContexts[i]; if ( parameter != null ) { - constrainedParameters.add( parameter.build( constraintHelper, typeResolutionHelper, valueExtractorManager ) ); + constrainedParameters.add( parameter.build( constraintCreationContext ) ); } else { constrainedParameters.add( new ConstrainedParameter( ConfigurationSource.API, - executable, - ReflectionHelper.typeOf( executable, i ), + callable, + callable.getParameterGenericType( i ), i ) ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/FieldConstraintMappingContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/FieldConstraintMappingContextImpl.java new file mode 100644 index 0000000000..984b920ac8 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/FieldConstraintMappingContextImpl.java @@ -0,0 +1,57 @@ +/* + * 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.context; + +import org.hibernate.validator.cfg.ConstraintDef; +import org.hibernate.validator.cfg.context.PropertyConstraintMappingContext; +import org.hibernate.validator.internal.engine.ConstraintCreationContext; +import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl.ConstraintType; +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; +import org.hibernate.validator.internal.properties.javabean.JavaBeanField; + +/** + * An implementation of {@link AbstractPropertyConstraintMappingContextImpl} for a field property. + * Represents a constraint mapping creational context which allows to configure the constraints + * for one of the bean's field properties. + * + * @author Marko Bekhta + */ +final class FieldConstraintMappingContextImpl extends AbstractPropertyConstraintMappingContextImpl { + + FieldConstraintMappingContextImpl(TypeConstraintMappingContextImpl typeContext, JavaBeanField javaBeanField) { + super( typeContext, javaBeanField, ConstraintLocation.forField( javaBeanField ) ); + } + + @Override + public PropertyConstraintMappingContext constraint(ConstraintDef definition) { + super.addConstraint( + ConfiguredConstraint.forField( + definition, getProperty() + ) + ); + return this; + } + + @Override + ConstrainedElement build(ConstraintCreationContext constraintCreationContext) { + return new ConstrainedField( + ConfigurationSource.API, + getProperty(), + getConstraints( constraintCreationContext ), + getTypeArgumentConstraints( constraintCreationContext ), + getCascadingMetaDataBuilder() + ); + } + + @Override + protected ConstraintType getConstraintType() { + return ConstraintType.GENERIC; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/GetterConstraintMappingContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/GetterConstraintMappingContextImpl.java new file mode 100644 index 0000000000..16b72d9b32 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/GetterConstraintMappingContextImpl.java @@ -0,0 +1,51 @@ +/* + * 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.context; + +import org.hibernate.validator.cfg.ConstraintDef; +import org.hibernate.validator.cfg.context.PropertyConstraintMappingContext; +import org.hibernate.validator.internal.engine.ConstraintCreationContext; +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.ConstrainedExecutable; +import org.hibernate.validator.internal.properties.javabean.JavaBeanGetter; + +/** + * An implementation of {@link AbstractPropertyConstraintMappingContextImpl} for a getter property. + * Represents a constraint mapping creational context which allows to configure the constraints + * for one of the bean's getter properties. + * + * @author Marko Bekhta + */ +final class GetterConstraintMappingContextImpl extends AbstractPropertyConstraintMappingContextImpl { + + GetterConstraintMappingContextImpl(TypeConstraintMappingContextImpl typeContext, JavaBeanGetter javaBeanGetter) { + super( typeContext, javaBeanGetter, ConstraintLocation.forGetter( javaBeanGetter ) ); + } + + @Override + public PropertyConstraintMappingContext constraint(ConstraintDef definition) { + super.addConstraint( + ConfiguredConstraint.forGetter( + definition, getProperty() + ) + ); + return this; + } + + @Override + ConstrainedElement build(ConstraintCreationContext constraintCreationContext) { + return new ConstrainedExecutable( + ConfigurationSource.API, + getProperty(), + getConstraints( constraintCreationContext ), + getTypeArgumentConstraints( constraintCreationContext ), + getCascadingMetaDataBuilder() + ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/MethodConstraintMappingContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/MethodConstraintMappingContextImpl.java index 6ff256f34a..7e8ae93bbe 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/MethodConstraintMappingContextImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/MethodConstraintMappingContextImpl.java @@ -6,9 +6,8 @@ */ package org.hibernate.validator.internal.cfg.context; -import java.lang.reflect.Method; - import org.hibernate.validator.cfg.context.MethodConstraintMappingContext; +import org.hibernate.validator.internal.properties.javabean.JavaBeanMethod; /** * Constraint mapping creational context representing a method. @@ -17,15 +16,15 @@ */ class MethodConstraintMappingContextImpl extends ExecutableConstraintMappingContextImpl implements MethodConstraintMappingContext { - MethodConstraintMappingContextImpl(TypeConstraintMappingContextImpl typeContext, Method method) { - super( typeContext, method ); + MethodConstraintMappingContextImpl(TypeConstraintMappingContextImpl typeContext, JavaBeanMethod callable) { + super( typeContext, callable ); } @Override public MethodConstraintMappingContext ignoreAnnotations(boolean ignoreAnnotations) { typeContext.mapping .getAnnotationProcessingOptions() - .ignoreConstraintAnnotationsOnMember( executable, ignoreAnnotations ); + .ignoreConstraintAnnotationsOnMember( callable, ignoreAnnotations ); return this; } diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ParameterConstraintMappingContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ParameterConstraintMappingContextImpl.java index 5009fc76cd..1fb43860d4 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ParameterConstraintMappingContextImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ParameterConstraintMappingContextImpl.java @@ -15,14 +15,11 @@ import org.hibernate.validator.cfg.context.MethodConstraintMappingContext; 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.core.ConstraintHelper; +import org.hibernate.validator.internal.engine.ConstraintCreationContext; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl.ConstraintType; import org.hibernate.validator.internal.metadata.location.ConstraintLocation; import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter; -import org.hibernate.validator.internal.util.ReflectionHelper; -import org.hibernate.validator.internal.util.TypeResolutionHelper; /** * Constraint mapping creational context which allows to configure the constraints for one method parameter. @@ -41,7 +38,7 @@ final class ParameterConstraintMappingContextImpl ParameterConstraintMappingContextImpl(ExecutableConstraintMappingContextImpl executableContext, int parameterIndex) { super( executableContext.getTypeContext().getConstraintMapping(), - executableContext.executable.getGenericParameterTypes()[parameterIndex] + executableContext.callable.getParameterGenericType( parameterIndex ) ); this.executableContext = executableContext; @@ -58,7 +55,7 @@ public ParameterConstraintMappingContext constraint(ConstraintDef definiti super.addConstraint( ConfiguredConstraint.forParameter( definition, - executableContext.getExecutable(), + executableContext.getCallable(), parameterIndex ) ); @@ -68,7 +65,7 @@ public ParameterConstraintMappingContext constraint(ConstraintDef definiti @Override public ParameterConstraintMappingContext ignoreAnnotations(boolean ignoreAnnotations) { mapping.getAnnotationProcessingOptions().ignoreConstraintAnnotationsOnParameter( - executableContext.getExecutable(), + executableContext.getCallable(), parameterIndex, ignoreAnnotations ); @@ -105,7 +102,7 @@ public ContainerElementConstraintMappingContext containerElementType() { return super.containerElement( this, executableContext.getTypeContext(), - ConstraintLocation.forParameter( executableContext.getExecutable(), parameterIndex ) + ConstraintLocation.forParameter( executableContext.getCallable(), parameterIndex ) ); } @@ -114,23 +111,22 @@ public ContainerElementConstraintMappingContext containerElementType(int index, return super.containerElement( this, executableContext.getTypeContext(), - ConstraintLocation.forParameter( executableContext.getExecutable(), parameterIndex ), + ConstraintLocation.forParameter( executableContext.getCallable(), parameterIndex ), index, nestedIndexes ); } - public ConstrainedParameter build(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, - ValueExtractorManager valueExtractorManager) { - Type parameterType = ReflectionHelper.typeOf( executableContext.getExecutable(), parameterIndex ); + public ConstrainedParameter build(ConstraintCreationContext constraintCreationContext) { + Type parameterType = executableContext.getCallable().getParameterGenericType( parameterIndex ); return new ConstrainedParameter( ConfigurationSource.API, - executableContext.getExecutable(), + executableContext.getCallable(), parameterType, parameterIndex, - getConstraints( constraintHelper, typeResolutionHelper, valueExtractorManager ), - getTypeArgumentConstraints( constraintHelper, typeResolutionHelper, valueExtractorManager ), + getConstraints( constraintCreationContext ), + getTypeArgumentConstraints( constraintCreationContext ), getCascadingMetaDataBuilder() ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ReturnValueConstraintMappingContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ReturnValueConstraintMappingContextImpl.java index df930c5ce6..7b98e80ea1 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ReturnValueConstraintMappingContextImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ReturnValueConstraintMappingContextImpl.java @@ -15,7 +15,6 @@ import org.hibernate.validator.cfg.context.ReturnValueConstraintMappingContext; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl.ConstraintType; import org.hibernate.validator.internal.metadata.location.ConstraintLocation; -import org.hibernate.validator.internal.util.ReflectionHelper; /** * Constraint mapping creational context which allows to configure the constraints for one method return value. @@ -31,10 +30,7 @@ final class ReturnValueConstraintMappingContextImpl private final ExecutableConstraintMappingContextImpl executableContext; ReturnValueConstraintMappingContextImpl(ExecutableConstraintMappingContextImpl executableContext) { - super( - executableContext.getTypeContext().getConstraintMapping(), - ReflectionHelper.typeOf( executableContext.getExecutable() ) - ); + super( executableContext.getTypeContext().getConstraintMapping(), executableContext.getCallable().getType() ); this.executableContext = executableContext; } @@ -45,14 +41,14 @@ protected ReturnValueConstraintMappingContext getThis() { @Override public ReturnValueConstraintMappingContext constraint(ConstraintDef definition) { - super.addConstraint( ConfiguredConstraint.forExecutable( definition, executableContext.getExecutable() ) ); + super.addConstraint( ConfiguredConstraint.forExecutable( definition, executableContext.getCallable() ) ); return this; } @Override public ReturnValueConstraintMappingContext ignoreAnnotations(boolean ignoreAnnotations) { mapping.getAnnotationProcessingOptions().ignoreConstraintAnnotationsForReturnValue( - executableContext.getExecutable(), ignoreAnnotations + executableContext.getCallable(), ignoreAnnotations ); return this; } @@ -80,7 +76,7 @@ public ConstructorConstraintMappingContext constructor(Class... parameterType @Override public ContainerElementConstraintMappingContext containerElementType() { return super.containerElement( - this, executableContext.getTypeContext(), ConstraintLocation.forReturnValue( executableContext.getExecutable() ) + this, executableContext.getTypeContext(), ConstraintLocation.forReturnValue( executableContext.getCallable() ) ); } @@ -89,7 +85,7 @@ public ContainerElementConstraintMappingContext containerElementType(int index, return super.containerElement( this, executableContext.getTypeContext(), - ConstraintLocation.forReturnValue( executableContext.getExecutable() ), + ConstraintLocation.forReturnValue( executableContext.getCallable() ), index, nestedIndexes ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/TypeConstraintMappingContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/TypeConstraintMappingContextImpl.java index 82a5403b26..20c012e4e8 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/TypeConstraintMappingContextImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/TypeConstraintMappingContextImpl.java @@ -11,13 +11,11 @@ import java.lang.annotation.ElementType; import java.lang.invoke.MethodHandles; -import java.lang.reflect.Constructor; -import java.lang.reflect.Member; -import java.lang.reflect.Method; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.Set; import org.hibernate.validator.cfg.ConstraintDef; @@ -25,23 +23,22 @@ import org.hibernate.validator.cfg.context.MethodConstraintMappingContext; import org.hibernate.validator.cfg.context.PropertyConstraintMappingContext; import org.hibernate.validator.cfg.context.TypeConstraintMappingContext; -import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.engine.ConstraintCreationContext; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl.ConstraintType; 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.ConstrainedType; +import org.hibernate.validator.internal.properties.Constrainable; +import org.hibernate.validator.internal.properties.javabean.JavaBeanConstructor; +import org.hibernate.validator.internal.properties.javabean.JavaBeanField; +import org.hibernate.validator.internal.properties.javabean.JavaBeanGetter; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; +import org.hibernate.validator.internal.properties.javabean.JavaBeanMethod; import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.ExecutableHelper; -import org.hibernate.validator.internal.util.ReflectionHelper; -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.privilegedactions.GetDeclaredConstructor; -import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredField; -import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredMethod; -import org.hibernate.validator.internal.util.privilegedactions.GetMethod; import org.hibernate.validator.internal.util.privilegedactions.NewInstance; import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider; @@ -53,23 +50,27 @@ * @author Hardy Ferentschik * @author Gunnar Morling * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI + * @author Marko Bekhta */ public final class TypeConstraintMappingContextImpl extends ConstraintMappingContextImplBase implements TypeConstraintMappingContext { private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + private final JavaBeanHelper javaBeanHelper; + private final Class beanClass; private final Set executableContexts = newHashSet(); - private final Set propertyContexts = newHashSet(); - private final Set configuredMembers = newHashSet(); + private final Set> propertyContexts = newHashSet(); + private final Set configuredMembers = newHashSet(); private List> defaultGroupSequence; private Class> defaultGroupSequenceProviderClass; - TypeConstraintMappingContextImpl(DefaultConstraintMapping mapping, Class beanClass) { + TypeConstraintMappingContextImpl(JavaBeanHelper javaBeanHelper, DefaultConstraintMapping mapping, Class beanClass) { super( mapping ); + this.javaBeanHelper = javaBeanHelper; this.beanClass = beanClass; mapping.getAnnotationProcessingOptions().ignoreAnnotationConstraintForClass( beanClass, Boolean.FALSE ); } @@ -81,6 +82,7 @@ public TypeConstraintMappingContext constraint(ConstraintDef definition } @Override + @Deprecated public TypeConstraintMappingContext ignoreAnnotations() { return ignoreAnnotations( true ); } @@ -110,29 +112,62 @@ public TypeConstraintMappingContext defaultGroupSequenceProviderClass(Class foundField = javaBeanHelper.findDeclaredField( beanClass, property ); - if ( member == null || member.getDeclaringClass() != beanClass ) { - throw LOG.getUnableToFindPropertyWithAccessException( beanClass, property, elementType ); + if ( !foundField.isPresent() ) { + throw LOG.getUnableToFindPropertyWithAccessException( beanClass, property, ElementType.FIELD ); } - if ( configuredMembers.contains( member ) ) { + JavaBeanField javaBeanField = foundField.get(); + + if ( configuredMembers.contains( javaBeanField ) ) { throw LOG.getPropertyHasAlreadyBeConfiguredViaProgrammaticApiException( beanClass, property ); } - PropertyConstraintMappingContextImpl context = new PropertyConstraintMappingContextImpl( - this, - member - ); + FieldConstraintMappingContextImpl context = new FieldConstraintMappingContextImpl( this, javaBeanField ); + configuredMembers.add( javaBeanField ); + propertyContexts.add( context ); + return context; + } + + @Override + public PropertyConstraintMappingContext getter(String property) { + Contracts.assertNotEmpty( property, MESSAGES.propertyNameMustNotBeEmpty() ); + + Optional foundGetter = javaBeanHelper.findDeclaredGetter( beanClass, property ); - configuredMembers.add( member ); + if ( !foundGetter.isPresent() ) { + throw LOG.getUnableToFindPropertyWithAccessException( beanClass, property, ElementType.METHOD ); + } + + JavaBeanGetter javaBeanGetter = foundGetter.get(); + + if ( configuredMembers.contains( javaBeanGetter ) ) { + throw LOG.getPropertyHasAlreadyBeConfiguredViaProgrammaticApiException( beanClass, property ); + } + + GetterConstraintMappingContextImpl context = new GetterConstraintMappingContextImpl( this, javaBeanGetter ); + configuredMembers.add( javaBeanGetter ); propertyContexts.add( context ); return context; } @@ -141,21 +176,23 @@ public PropertyConstraintMappingContext property(String property, ElementType el public MethodConstraintMappingContext method(String name, Class... parameterTypes) { Contracts.assertNotNull( name, MESSAGES.methodNameMustNotBeNull() ); - Method method = run( GetDeclaredMethod.action( beanClass, name, parameterTypes ) ); + Optional foundMethod = javaBeanHelper.findDeclaredMethod( beanClass, name, parameterTypes ); - if ( method == null || method.getDeclaringClass() != beanClass ) { - throw LOG.getBeanDoesNotContainMethodException( beanClass, name, Arrays.asList( parameterTypes ) ); + if ( !foundMethod.isPresent() ) { + throw LOG.getBeanDoesNotContainMethodException( beanClass, name, parameterTypes ); } - if ( configuredMembers.contains( method ) ) { - throw LOG.getMethodHasAlreadyBeConfiguredViaProgrammaticApiException( + JavaBeanMethod javaBeanMethod = foundMethod.get(); + + if ( configuredMembers.contains( javaBeanMethod ) ) { + throw LOG.getMethodHasAlreadyBeenConfiguredViaProgrammaticApiException( beanClass, ExecutableHelper.getExecutableAsString( name, parameterTypes ) ); } - MethodConstraintMappingContextImpl context = new MethodConstraintMappingContextImpl( this, method ); - configuredMembers.add( method ); + MethodConstraintMappingContextImpl context = new MethodConstraintMappingContextImpl( this, javaBeanMethod ); + configuredMembers.add( javaBeanMethod ); executableContexts.add( context ); return context; @@ -163,16 +200,18 @@ public MethodConstraintMappingContext method(String name, Class... parameterT @Override public ConstructorConstraintMappingContext constructor(Class... parameterTypes) { - Constructor constructor = run( GetDeclaredConstructor.action( beanClass, parameterTypes ) ); + Optional foundConstructor = javaBeanHelper.findDeclaredConstructor( beanClass, parameterTypes ); - if ( constructor == null || constructor.getDeclaringClass() != beanClass ) { + if ( !foundConstructor.isPresent() ) { throw LOG.getBeanDoesNotContainConstructorException( beanClass, - Arrays.asList( parameterTypes ) + parameterTypes ); } - if ( configuredMembers.contains( constructor ) ) { + JavaBeanConstructor javaBeanConstructor = foundConstructor.get(); + + if ( configuredMembers.contains( javaBeanConstructor ) ) { throw LOG.getConstructorHasAlreadyBeConfiguredViaProgrammaticApiException( beanClass, ExecutableHelper.getExecutableAsString( beanClass.getSimpleName(), parameterTypes ) @@ -181,27 +220,25 @@ public ConstructorConstraintMappingContext constructor(Class... parameterType ConstructorConstraintMappingContextImpl context = new ConstructorConstraintMappingContextImpl( this, - constructor + javaBeanConstructor ); - configuredMembers.add( constructor ); + configuredMembers.add( javaBeanConstructor ); executableContexts.add( context ); return context; } - BeanConfiguration build(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, - ValueExtractorManager valueExtractorManager) { + BeanConfiguration build(ConstraintCreationContext constraintCreationContext) { return new BeanConfiguration<>( ConfigurationSource.API, beanClass, - buildConstraintElements( constraintHelper, typeResolutionHelper, valueExtractorManager ), + buildConstraintElements( constraintCreationContext ), defaultGroupSequence, getDefaultGroupSequenceProvider() ); } - private Set buildConstraintElements(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, - ValueExtractorManager valueExtractorManager) { + private Set buildConstraintElements(ConstraintCreationContext constraintCreationContext) { Set elements = newHashSet(); //class-level configuration @@ -209,18 +246,18 @@ private Set buildConstraintElements(ConstraintHelper constra new ConstrainedType( ConfigurationSource.API, beanClass, - getConstraints( constraintHelper, typeResolutionHelper, valueExtractorManager ) + getConstraints( constraintCreationContext ) ) ); //constructors/methods for ( ExecutableConstraintMappingContextImpl executableContext : executableContexts ) { - elements.add( executableContext.build( constraintHelper, typeResolutionHelper, valueExtractorManager ) ); + elements.add( executableContext.build( constraintCreationContext ) ); } //properties - for ( PropertyConstraintMappingContextImpl propertyContext : propertyContexts ) { - elements.add( propertyContext.build( constraintHelper, typeResolutionHelper, valueExtractorManager ) ); + for ( AbstractPropertyConstraintMappingContextImpl propertyContext : propertyContexts ) { + elements.add( propertyContext.build( constraintCreationContext ) ); } return elements; @@ -244,42 +281,6 @@ protected ConstraintType getConstraintType() { return ConstraintType.GENERIC; } - /** - * Returns the member with the given name and type. - * - * @param clazz The class from which to retrieve the member. Cannot be {@code null}. - * @param property The property name without "is", "get" or "has". Cannot be {@code null} or empty. - * @param elementType The element type. Either {@code ElementType.FIELD} or {@code ElementType METHOD}. - * - * @return the member which matching the name and type or {@code null} if no such member exists. - */ - private Member getMember(Class clazz, String property, ElementType elementType) { - Contracts.assertNotNull( clazz, MESSAGES.classCannotBeNull() ); - - if ( property == null || property.length() == 0 ) { - throw LOG.getPropertyNameCannotBeNullOrEmptyException(); - } - - if ( !( ElementType.FIELD.equals( elementType ) || ElementType.METHOD.equals( elementType ) ) ) { - throw LOG.getElementTypeHasToBeFieldOrMethodException(); - } - - Member member = null; - if ( ElementType.FIELD.equals( elementType ) ) { - member = run( GetDeclaredField.action( clazz, property ) ); - } - else { - String methodName = property.substring( 0, 1 ).toUpperCase() + property.substring( 1 ); - for ( String prefix : ReflectionHelper.PROPERTY_ACCESSOR_PREFIXES ) { - member = run( GetMethod.action( clazz, prefix + methodName ) ); - if ( member != null ) { - break; - } - } - } - return member; - } - /** * Runs the given privileged action, using a privileged block if required. *

diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/money/DigitsValidatorForMonetaryAmount.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/money/DigitsValidatorForMonetaryAmount.java new file mode 100644 index 0000000000..f46ddd81bc --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/money/DigitsValidatorForMonetaryAmount.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.constraintvalidators.bv.money; + +import java.lang.invoke.MethodHandles; +import java.math.BigDecimal; +import javax.money.MonetaryAmount; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.constraints.Digits; + +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; + +/** + * Validates that the number part of the {@code MonetaryAmount} being validated matches the pattern + * defined in the constraint. + * + * @author Dario Seidl + */ +public class DigitsValidatorForMonetaryAmount implements ConstraintValidator { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private int maxIntegerLength; + private int maxFractionLength; + + @Override + public void initialize(Digits constraintAnnotation) { + this.maxIntegerLength = constraintAnnotation.integer(); + this.maxFractionLength = constraintAnnotation.fraction(); + validateParameters(); + } + + @Override + public boolean isValid(MonetaryAmount monetaryAmount, ConstraintValidatorContext constraintValidatorContext) { + //null values are valid + if ( monetaryAmount == null ) { + return true; + } + + BigDecimal bigNum = monetaryAmount.getNumber().numberValueExact( BigDecimal.class ); + + int integerPartLength = bigNum.precision() - bigNum.scale(); + int fractionPartLength = bigNum.scale() < 0 ? 0 : bigNum.scale(); + + return ( maxIntegerLength >= integerPartLength && maxFractionLength >= fractionPartLength ); + } + + private void validateParameters() { + if ( maxIntegerLength < 0 ) { + throw LOG.getInvalidLengthForIntegerPartException(); + } + if ( maxFractionLength < 0 ) { + throw LOG.getInvalidLengthForFractionPartException(); + } + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MaxValidatorForByte.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MaxValidatorForByte.java new file mode 100644 index 0000000000..e7b0b67fc8 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MaxValidatorForByte.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.internal.constraintvalidators.bv.number.bound; + +/** + * Check that the number being validated is less than or equal to the maximum + * value specified. + * + * @author Marko Bekhta + */ +public class MaxValidatorForByte extends AbstractMaxValidator { + + @Override + protected int compare(Byte number) { + return NumberComparatorHelper.compare( number.longValue(), maxValue ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MaxValidatorForInteger.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MaxValidatorForInteger.java new file mode 100644 index 0000000000..dbd70f977e --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MaxValidatorForInteger.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.internal.constraintvalidators.bv.number.bound; + +/** + * Check that the number being validated is less than or equal to the maximum + * value specified. + * + * @author Marko Bekhta + */ +public class MaxValidatorForInteger extends AbstractMaxValidator { + + @Override + protected int compare(Integer number) { + return NumberComparatorHelper.compare( number.longValue(), maxValue ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MaxValidatorForNumber.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MaxValidatorForNumber.java index 6d37014fae..63faf7e075 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MaxValidatorForNumber.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MaxValidatorForNumber.java @@ -6,6 +6,8 @@ */ package org.hibernate.validator.internal.constraintvalidators.bv.number.bound; +import org.hibernate.validator.internal.constraintvalidators.bv.number.InfinityNumberComparatorHelper; + /** * Check that the number being validated is less than or equal to the maximum * value specified. @@ -16,6 +18,6 @@ public class MaxValidatorForNumber extends AbstractMaxValidator { @Override protected int compare(Number number) { - return NumberComparatorHelper.compare( number, maxValue ); + return NumberComparatorHelper.compare( number, maxValue, InfinityNumberComparatorHelper.GREATER_THAN ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MaxValidatorForShort.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MaxValidatorForShort.java new file mode 100644 index 0000000000..352c24bfea --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MaxValidatorForShort.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.internal.constraintvalidators.bv.number.bound; + +/** + * Check that the number being validated is less than or equal to the maximum + * value specified. + * + * @author Marko Bekhta + */ +public class MaxValidatorForShort extends AbstractMaxValidator { + + @Override + protected int compare(Short number) { + return NumberComparatorHelper.compare( number.longValue(), maxValue ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MinValidatorForByte.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MinValidatorForByte.java new file mode 100644 index 0000000000..58542ffb41 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MinValidatorForByte.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.internal.constraintvalidators.bv.number.bound; + +/** + * Check that the number being validated is greater than or equal to the minimum + * value specified. + * + * @author Marko Bekhta + */ +public class MinValidatorForByte extends AbstractMinValidator { + + @Override + protected int compare(Byte number) { + return NumberComparatorHelper.compare( number.longValue(), minValue ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MinValidatorForInteger.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MinValidatorForInteger.java new file mode 100644 index 0000000000..61e5b8e36f --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MinValidatorForInteger.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.internal.constraintvalidators.bv.number.bound; + +/** + * Check that the number being validated is greater than or equal to the minimum + * value specified. + * + * @author Marko Bekhta + */ +public class MinValidatorForInteger extends AbstractMinValidator { + + @Override + protected int compare(Integer number) { + return NumberComparatorHelper.compare( number.longValue(), minValue ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MinValidatorForNumber.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MinValidatorForNumber.java index 7b2974df76..cd56e942af 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MinValidatorForNumber.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MinValidatorForNumber.java @@ -6,6 +6,8 @@ */ package org.hibernate.validator.internal.constraintvalidators.bv.number.bound; +import org.hibernate.validator.internal.constraintvalidators.bv.number.InfinityNumberComparatorHelper; + /** * Check that the number being validated is greater than or equal to the minimum * value specified. @@ -16,6 +18,6 @@ public class MinValidatorForNumber extends AbstractMinValidator { @Override protected int compare(Number number) { - return NumberComparatorHelper.compare( number, minValue ); + return NumberComparatorHelper.compare( number, minValue, InfinityNumberComparatorHelper.LESS_THAN ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MinValidatorForShort.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MinValidatorForShort.java new file mode 100644 index 0000000000..52bd2415cb --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MinValidatorForShort.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.internal.constraintvalidators.bv.number.bound; + +/** + * Check that the number being validated is greater than or equal to the minimum + * value specified. + * + * @author Marko Bekhta + */ +public class MinValidatorForShort extends AbstractMinValidator { + + @Override + protected int compare(Short number) { + return NumberComparatorHelper.compare( number.longValue(), minValue ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/NumberComparatorHelper.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/NumberComparatorHelper.java index c70256ff95..36a9c19320 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/NumberComparatorHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/NumberComparatorHelper.java @@ -32,8 +32,33 @@ public static int compare(Long number, long value) { return number.compareTo( value ); } - public static int compare(Number number, long value) { - return Long.compare( number.longValue(), value ); + public static int compare(Number number, long value, OptionalInt treatNanAs) { + // In case of comparing numbers we need to check for special cases: + // 1. Floating point numbers should consider nan/infinity as values hence they should + // be directed to corresponding overloaded methods: + if ( number instanceof Double ) { + return compare( (Double) number, value, treatNanAs ); + } + if ( number instanceof Float ) { + return compare( (Float) number, value, treatNanAs ); + } + + // 2. For big numbers we don't want to lose any data so we just cast them and call corresponding methods: + if ( number instanceof BigDecimal ) { + return compare( (BigDecimal) number, value ); + } + if ( number instanceof BigInteger ) { + return compare( (BigInteger) number, value ); + } + + // 3. For any integer types we convert them to long as we would do that anyway + // when comparing with a long value. And use corresponding method for longs: + if ( number instanceof Byte || number instanceof Integer || number instanceof Long || number instanceof Short ) { + return compare( number.longValue(), value ); + } + + // 4. As a fallback we convert the number to double: + return compare( number.doubleValue(), value, treatNanAs ); } public static int compare(Double number, long value, OptionalInt treatNanAs) { @@ -41,7 +66,7 @@ public static int compare(Double number, long value, OptionalInt treatNanAs) { if ( infinity.isPresent() ) { return infinity.getAsInt(); } - return Long.compare( number.longValue(), value ); + return Double.compare( number, value ); } public static int compare(Float number, long value, OptionalInt treatNanAs) { @@ -49,6 +74,6 @@ public static int compare(Float number, long value, OptionalInt treatNanAs) { if ( infinity.isPresent() ) { return infinity.getAsInt(); } - return Long.compare( number.longValue(), value ); + return Float.compare( number, value ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMaxValidatorForByte.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMaxValidatorForByte.java new file mode 100644 index 0000000000..08c98d28e5 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMaxValidatorForByte.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.internal.constraintvalidators.bv.number.bound.decimal; + +/** + * Check that the number being validated is less than or equal to the maximum + * value specified. + * + * @author Marko Bekhta + */ +public class DecimalMaxValidatorForByte extends AbstractDecimalMaxValidator { + + @Override + protected int compare(Byte number) { + return DecimalNumberComparatorHelper.compare( number.longValue(), maxValue ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMaxValidatorForInteger.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMaxValidatorForInteger.java new file mode 100644 index 0000000000..7377be0a93 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMaxValidatorForInteger.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.internal.constraintvalidators.bv.number.bound.decimal; + +/** + * Check that the number being validated is less than or equal to the maximum + * value specified. + * + * @author Marko Bekhta + */ +public class DecimalMaxValidatorForInteger extends AbstractDecimalMaxValidator { + + @Override + protected int compare(Integer number) { + return DecimalNumberComparatorHelper.compare( number.longValue(), maxValue ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMaxValidatorForNumber.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMaxValidatorForNumber.java index 1d26c614e3..5290001508 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMaxValidatorForNumber.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMaxValidatorForNumber.java @@ -6,6 +6,8 @@ */ package org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal; +import org.hibernate.validator.internal.constraintvalidators.bv.number.InfinityNumberComparatorHelper; + /** * Check that the number being validated is less than or equal to the maximum * value specified. @@ -14,7 +16,8 @@ */ public class DecimalMaxValidatorForNumber extends AbstractDecimalMaxValidator { - @Override protected int compare(Number number) { - return DecimalNumberComparatorHelper.compare( number, maxValue ); + @Override + protected int compare(Number number) { + return DecimalNumberComparatorHelper.compare( number, maxValue, InfinityNumberComparatorHelper.GREATER_THAN ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMaxValidatorForShort.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMaxValidatorForShort.java new file mode 100644 index 0000000000..d858041208 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMaxValidatorForShort.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.internal.constraintvalidators.bv.number.bound.decimal; + +/** + * Check that the number being validated is less than or equal to the maximum + * value specified. + * + * @author Marko Bekhta + */ +public class DecimalMaxValidatorForShort extends AbstractDecimalMaxValidator { + + @Override + protected int compare(Short number) { + return DecimalNumberComparatorHelper.compare( number.longValue(), maxValue ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMinValidatorForByte.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMinValidatorForByte.java new file mode 100644 index 0000000000..aab9e90a93 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMinValidatorForByte.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.internal.constraintvalidators.bv.number.bound.decimal; + +/** + * Check that the number being validated is greater than or equal to the minimum + * value specified. + * + * @author Marko Bekhta + */ +public class DecimalMinValidatorForByte extends AbstractDecimalMinValidator { + + @Override + protected int compare(Byte number) { + return DecimalNumberComparatorHelper.compare( number.longValue(), minValue ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMinValidatorForInteger.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMinValidatorForInteger.java new file mode 100644 index 0000000000..ca52640ae1 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMinValidatorForInteger.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.internal.constraintvalidators.bv.number.bound.decimal; + +/** + * Check that the number being validated is greater than or equal to the minimum + * value specified. + * + * @author Marko Bekhta + */ +public class DecimalMinValidatorForInteger extends AbstractDecimalMinValidator { + + @Override + protected int compare(Integer number) { + return DecimalNumberComparatorHelper.compare( number.longValue(), minValue ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMinValidatorForNumber.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMinValidatorForNumber.java index 571a7c8f24..ee40ebfbb0 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMinValidatorForNumber.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMinValidatorForNumber.java @@ -6,6 +6,8 @@ */ package org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal; +import org.hibernate.validator.internal.constraintvalidators.bv.number.InfinityNumberComparatorHelper; + /** * Check that the number being validated is greater than or equal to the minimum * value specified. @@ -14,7 +16,8 @@ */ public class DecimalMinValidatorForNumber extends AbstractDecimalMinValidator { - @Override protected int compare(Number number) { - return DecimalNumberComparatorHelper.compare( number, minValue ); + @Override + protected int compare(Number number) { + return DecimalNumberComparatorHelper.compare( number, minValue, InfinityNumberComparatorHelper.LESS_THAN ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMinValidatorForShort.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMinValidatorForShort.java new file mode 100644 index 0000000000..398928ea53 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalMinValidatorForShort.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.internal.constraintvalidators.bv.number.bound.decimal; + +/** + * Check that the number being validated is greater than or equal to the minimum + * value specified. + * + * @author Marko Bekhta + */ +public class DecimalMinValidatorForShort extends AbstractDecimalMinValidator { + + @Override + protected int compare(Short number) { + return DecimalNumberComparatorHelper.compare( number.longValue(), minValue ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalNumberComparatorHelper.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalNumberComparatorHelper.java index 1b9cb3b150..c5fa1d2b76 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalNumberComparatorHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/decimal/DecimalNumberComparatorHelper.java @@ -32,8 +32,33 @@ public static int compare(Long number, BigDecimal value) { return BigDecimal.valueOf( number ).compareTo( value ); } - public static int compare(Number number, BigDecimal value) { - return BigDecimal.valueOf( number.doubleValue() ).compareTo( value ); + public static int compare(Number number, BigDecimal value, OptionalInt treatNanAs) { + // In case of comparing numbers we need to check for special cases: + // 1. Floating point numbers should consider nan/infinity as values hence they should + // be directed to corresponding overloaded methods: + if ( number instanceof Double ) { + return compare( (Double) number, value, treatNanAs ); + } + if ( number instanceof Float ) { + return compare( (Float) number, value, treatNanAs ); + } + + // 2. For big numbers we don't want to lose any data so we just cast them and call corresponding methods: + if ( number instanceof BigDecimal ) { + return compare( (BigDecimal) number, value ); + } + if ( number instanceof BigInteger ) { + return compare( (BigInteger) number, value ); + } + + // 3. For any integer types we convert them to long as we would do that anyway + // to create a BigDecimal instance. And use corresponding method for longs: + if ( number instanceof Byte || number instanceof Integer || number instanceof Long || number instanceof Short ) { + return compare( number.longValue(), value ); + } + + // 4. As a fallback we convert the number to double: + return compare( number.doubleValue(), value, treatNanAs ); } public static int compare(Double number, BigDecimal value, OptionalInt treatNanAs) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/ISBNValidator.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/ISBNValidator.java index 5d107e388c..db104faadf 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/ISBNValidator.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/ISBNValidator.java @@ -6,13 +6,13 @@ */ package org.hibernate.validator.internal.constraintvalidators.hv; -import java.util.function.Function; import java.util.regex.Pattern; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import org.hibernate.validator.constraints.ISBN; +import org.hibernate.validator.internal.util.Contracts; /** * Checks that a given character sequence (e.g. string) is a valid ISBN. @@ -26,22 +26,11 @@ public class ISBNValidator implements ConstraintValidator { */ private static Pattern NOT_DIGITS_OR_NOT_X = Pattern.compile( "[^\\dX]" ); - private int length; - - private Function checkChecksumFunction; + private ISBNValidationAlgorithm isbnValidationAlgorithm; @Override public void initialize(ISBN constraintAnnotation) { - switch ( constraintAnnotation.type() ) { - case ISBN_10: - length = 10; - checkChecksumFunction = this::checkChecksumISBN10; - break; - case ISBN_13: - length = 13; - checkChecksumFunction = this::checkChecksumISBN13; - break; - } + this.isbnValidationAlgorithm = ISBNValidationAlgorithm.from( constraintAnnotation.type() ); } @Override @@ -54,36 +43,100 @@ public boolean isValid(CharSequence isbn, ConstraintValidatorContext context) { String digits = NOT_DIGITS_OR_NOT_X.matcher( isbn ).replaceAll( "" ); // Check if the length of resulting string matches the expecting one - if ( digits.length() != length ) { + if ( !isbnValidationAlgorithm.isValidLength( digits.length() ) ) { return false; } - return checkChecksumFunction.apply( digits ); + return isbnValidationAlgorithm.isValidChecksum( digits ); } - /** - * Check the digits for ISBN 10 using algorithm from - * Wikipedia. - */ - private boolean checkChecksumISBN10(String isbn) { - int sum = 0; - for ( int i = 0; i < isbn.length() - 1; i++ ) { - sum += ( isbn.charAt( i ) - '0' ) * ( i + 1 ); + private interface ISBNValidationAlgorithm { + boolean isValidLength(int length); + + boolean isValidChecksum(String isbn); + + static ISBNValidationAlgorithmImpl from(ISBN.Type type) { + Contracts.assertNotNull( type ); + switch ( type ) { + case ISBN_10: + return ISBNValidationAlgorithmImpl.ISBN_10; + case ISBN_13: + return ISBNValidationAlgorithmImpl.ISBN_13; + case ANY: + default: + return ISBNValidationAlgorithmImpl.ANY; + } } - char checkSum = isbn.charAt( 9 ); - return sum % 11 == ( checkSum == 'X' ? 10 : checkSum - '0' ); } - /** - * Check the digits for ISBN 13 using algorithm from - * Wikipedia. - */ - private boolean checkChecksumISBN13(String isbn) { - int sum = 0; - for ( int i = 0; i < isbn.length() - 1; i++ ) { - sum += ( isbn.charAt( i ) - '0' ) * ( i % 2 == 0 ? 1 : 3 ); + private enum ISBNValidationAlgorithmImpl implements ISBNValidationAlgorithm { + + ISBN_10 { + @Override + public boolean isValidChecksum(String isbn) { + return checkChecksumISBN10( isbn ); + } + + @Override + public boolean isValidLength(int length) { + return 10 == length; + } + }, + ISBN_13 { + @Override + public boolean isValidChecksum(String isbn) { + return checkChecksumISBN13( isbn ); + } + + @Override + public boolean isValidLength(int length) { + return 13 == length; + } + }, + ANY { + @Override + public boolean isValidLength(int length) { + return 10 == length || 13 == length; + } + + @Override + public boolean isValidChecksum(String isbn) { + int length = isbn.length(); + if ( length == 10 ) { + return checkChecksumISBN10( isbn ); + } + else if ( length == 13 ) { + return checkChecksumISBN13( isbn ); + } + throw new IllegalStateException( "Invalid/unsupported isbn value length" ); + } + }; + + /** + * Check the digits for ISBN 10 using algorithm from + * Wikipedia. + */ + private static boolean checkChecksumISBN10(String isbn) { + int sum = 0; + for ( int i = 0; i < isbn.length() - 1; i++ ) { + sum += ( isbn.charAt( i ) - '0' ) * ( 10 - i ); + } + sum += isbn.charAt( 9 ) == 'X' ? 10 : isbn.charAt( 9 ) - '0'; + + return ( sum % 11 ) == 0; + } + + /** + * Check the digits for ISBN 13 using algorithm from + * Wikipedia. + */ + private static boolean checkChecksumISBN13(String isbn) { + int sum = 0; + for ( int i = 0; i < isbn.length(); i++ ) { + sum += ( isbn.charAt( i ) - '0' ) * ( i % 2 == 0 ? 1 : 3 ); + } + + return ( sum % 10 ) == 0; } - char checkSum = isbn.charAt( 12 ); - return 10 - sum % 10 == ( checkSum - '0' ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/ParameterScriptAssertValidator.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/ParameterScriptAssertValidator.java index 8bcf798283..8cfcf4dab7 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/ParameterScriptAssertValidator.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/ParameterScriptAssertValidator.java @@ -9,6 +9,7 @@ import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -18,9 +19,8 @@ import javax.validation.metadata.ConstraintDescriptor; import org.hibernate.validator.constraints.ParameterScriptAssert; -import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext; import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; -import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorContextImpl; +import org.hibernate.validator.constraintvalidation.HibernateCrossParameterConstraintValidatorContext; import org.hibernate.validator.internal.util.Contracts; /** @@ -42,14 +42,18 @@ public void initialize(ConstraintDescriptor constraintDes @Override public boolean isValid(Object[] arguments, ConstraintValidatorContext constraintValidatorContext) { - if ( constraintValidatorContext instanceof HibernateConstraintValidatorContext ) { - constraintValidatorContext.unwrap( HibernateConstraintValidatorContext.class ).addMessageParameter( "script", escapedScript ); - } + Map bindings; + if ( constraintValidatorContext instanceof HibernateCrossParameterConstraintValidatorContext ) { + HibernateCrossParameterConstraintValidatorContext crossParameterConstraintValidatorContext = constraintValidatorContext + .unwrap( HibernateCrossParameterConstraintValidatorContext.class ); - List parameterNames = ( (ConstraintValidatorContextImpl) constraintValidatorContext ) - .getMethodParameterNames(); + crossParameterConstraintValidatorContext.addMessageParameter( "script", escapedScript ); - Map bindings = getBindings( arguments, parameterNames ); + bindings = getBindings( arguments, crossParameterConstraintValidatorContext.getMethodParameterNames() ); + } + else { + bindings = Collections.emptyMap(); + } return scriptAssertContext.evaluateScriptAssertExpression( bindings ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/SafeHtmlValidator.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/SafeHtmlValidator.java index 89dd07f438..3949aedc51 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/SafeHtmlValidator.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/SafeHtmlValidator.java @@ -6,7 +6,7 @@ */ package org.hibernate.validator.internal.constraintvalidators.hv; -import java.util.Iterator; +import java.util.List; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; @@ -14,7 +14,7 @@ import org.hibernate.validator.constraints.SafeHtml; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; +import org.jsoup.nodes.Node; import org.jsoup.parser.Parser; import org.jsoup.safety.Cleaner; import org.jsoup.safety.Whitelist; @@ -27,7 +27,9 @@ * @author George Gastaldi * @author Hardy Ferentschik * @author Marko Bekhta + * @deprecated {@code @SafeHtml} support will be removed in a future version */ +@Deprecated public class SafeHtmlValidator implements ConstraintValidator { private Whitelist whitelist; @@ -91,9 +93,9 @@ private Document getFragmentAsDocument(CharSequence value) { Document document = Document.createShell( baseURI ); // add the fragment's nodes to the body of resulting document - Iterator nodes = fragment.children().iterator(); - while ( nodes.hasNext() ) { - document.body().appendChild( nodes.next() ); + List childNodes = fragment.childNodes(); + for ( Node node : childNodes ) { + document.body().appendChild( node.clone() ); } return document; diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/pl/PESELValidator.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/pl/PESELValidator.java index 4b33031e2c..326e558e8c 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/pl/PESELValidator.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/pl/PESELValidator.java @@ -30,7 +30,7 @@ public void initialize(PESEL constraintAnnotation) { 0, Integer.MAX_VALUE, -1, - true + false ); } @@ -38,6 +38,11 @@ public void initialize(PESEL constraintAnnotation) { public boolean isCheckDigitValid(List digits, char checkDigit) { Collections.reverse( digits ); + // if the length of the number is incorrect we can return fast + if ( digits.size() != WEIGHTS_PESEL.length ) { + return false; + } + int modResult = ModUtil.calculateModXCheckWithWeights( digits, 10, Integer.MAX_VALUE, WEIGHTS_PESEL ); switch ( modResult ) { case 10: diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/pl/PolishNumberValidator.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/pl/PolishNumberValidator.java index f41cdb780a..1ab9289d7e 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/pl/PolishNumberValidator.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/pl/PolishNumberValidator.java @@ -27,8 +27,15 @@ public abstract class PolishNumberValidator extends ModChe public boolean isCheckDigitValid(List digits, char checkDigit) { Collections.reverse( digits ); + int[] weights = getWeights( digits ); + + // if the length of the number is incorrect we can return fast + if ( weights.length != digits.size() ) { + return false; + } + // as we need sum % 11 rather than 11 - (sum % 11) returned by Mod11 algorithm: - int modResult = 11 - ModUtil.calculateModXCheckWithWeights( digits, 11, Integer.MAX_VALUE, getWeights( digits ) ); + int modResult = 11 - ModUtil.calculateModXCheckWithWeights( digits, 11, Integer.MAX_VALUE, weights ); switch ( modResult ) { case 10: case 11: diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/AbstractConfigurationImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/AbstractConfigurationImpl.java new file mode 100644 index 0000000000..9c1e28bde6 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/AbstractConfigurationImpl.java @@ -0,0 +1,740 @@ +/* + * 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.engine; + +import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; +import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.invoke.MethodHandles; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.time.Duration; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.validation.BootstrapConfiguration; +import javax.validation.ClockProvider; +import javax.validation.ConstraintValidatorFactory; +import javax.validation.MessageInterpolator; +import javax.validation.ParameterNameProvider; +import javax.validation.TraversableResolver; +import javax.validation.ValidationProviderResolver; +import javax.validation.ValidatorFactory; +import javax.validation.spi.BootstrapState; +import javax.validation.spi.ConfigurationState; +import javax.validation.spi.ValidationProvider; +import javax.validation.valueextraction.ValueExtractor; + +import org.hibernate.validator.BaseHibernateValidatorConfiguration; +import org.hibernate.validator.cfg.ConstraintMapping; +import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl; +import org.hibernate.validator.internal.engine.resolver.TraversableResolvers; +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor; +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; +import org.hibernate.validator.internal.util.Contracts; +import org.hibernate.validator.internal.util.Version; +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.internal.util.privilegedactions.SetContextClassLoader; +import org.hibernate.validator.internal.xml.config.ValidationBootstrapParameters; +import org.hibernate.validator.internal.xml.config.ValidationXmlParser; +import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; +import org.hibernate.validator.resourceloading.PlatformResourceBundleLocator; +import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider; +import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy; +import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; +import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; + +/** + * Hibernate specific {@code Configuration} implementation. + * + * @author Emmanuel Bernard + * @author Hardy Ferentschik + * @author Gunnar Morling + * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI + * @author Chris Beckey <cbeckey@paypal.com> + */ +public abstract class AbstractConfigurationImpl> + implements BaseHibernateValidatorConfiguration, ConfigurationState { + + static { + Version.touch(); + } + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + + /** + * Built lazily so RBMI and its dependency on EL is only initialized if actually needed + */ + private ResourceBundleLocator defaultResourceBundleLocator; + private MessageInterpolator defaultMessageInterpolator; + private MessageInterpolator messageInterpolator; + + private final TraversableResolver defaultTraversableResolver; + private final ConstraintValidatorFactory defaultConstraintValidatorFactory; + private final ParameterNameProvider defaultParameterNameProvider; + private final ClockProvider defaultClockProvider; + private final PropertyNodeNameProvider defaultPropertyNodeNameProvider; + + private ValidationProviderResolver providerResolver; + private final ValidationBootstrapParameters validationBootstrapParameters; + private boolean ignoreXmlConfiguration = false; + private final Set configurationStreams = newHashSet(); + private BootstrapConfiguration bootstrapConfiguration; + + private final Map valueExtractorDescriptors = new HashMap<>(); + + // HV-specific options + private final Set programmaticMappings = newHashSet(); + private boolean failFast; + private ClassLoader externalClassLoader; + private final MethodValidationConfiguration.Builder methodValidationConfigurationBuilder = new MethodValidationConfiguration.Builder(); + private boolean traversableResolverResultCacheEnabled = true; + private ScriptEvaluatorFactory scriptEvaluatorFactory; + private Duration temporalValidationTolerance; + private Object constraintValidatorPayload; + private GetterPropertySelectionStrategy getterPropertySelectionStrategy; + + // locales to initialize eagerly + private Set localesToInitialize = Collections.emptySet(); + + protected AbstractConfigurationImpl(BootstrapState state) { + this(); + if ( state.getValidationProviderResolver() == null ) { + this.providerResolver = state.getDefaultValidationProviderResolver(); + } + else { + this.providerResolver = state.getValidationProviderResolver(); + } + } + + protected AbstractConfigurationImpl(ValidationProvider provider) { + this(); + if ( provider == null ) { + throw LOG.getInconsistentConfigurationException(); + } + this.providerResolver = null; + validationBootstrapParameters.setProvider( provider ); + } + + private AbstractConfigurationImpl() { + this.validationBootstrapParameters = new ValidationBootstrapParameters(); + + this.defaultTraversableResolver = TraversableResolvers.getDefault(); + this.defaultConstraintValidatorFactory = new ConstraintValidatorFactoryImpl(); + this.defaultParameterNameProvider = new DefaultParameterNameProvider(); + this.defaultClockProvider = DefaultClockProvider.INSTANCE; + this.defaultPropertyNodeNameProvider = new DefaultPropertyNodeNameProvider(); + } + + @Override + public final T ignoreXmlConfiguration() { + ignoreXmlConfiguration = true; + return thisAsT(); + } + + @Override + public final T messageInterpolator(MessageInterpolator interpolator) { + if ( LOG.isDebugEnabled() ) { + if ( interpolator != null ) { + LOG.debug( "Setting custom MessageInterpolator of type " + interpolator.getClass().getName() ); + } + } + this.validationBootstrapParameters.setMessageInterpolator( interpolator ); + return thisAsT(); + } + + @Override + public final T traversableResolver(TraversableResolver resolver) { + if ( LOG.isDebugEnabled() ) { + if ( resolver != null ) { + LOG.debug( "Setting custom TraversableResolver of type " + resolver.getClass().getName() ); + } + } + this.validationBootstrapParameters.setTraversableResolver( resolver ); + return thisAsT(); + } + + @Override + public final T enableTraversableResolverResultCache(boolean enabled) { + this.traversableResolverResultCacheEnabled = enabled; + return thisAsT(); + } + + public final boolean isTraversableResolverResultCacheEnabled() { + return traversableResolverResultCacheEnabled; + } + + @Override + public final T constraintValidatorFactory(ConstraintValidatorFactory constraintValidatorFactory) { + if ( LOG.isDebugEnabled() ) { + if ( constraintValidatorFactory != null ) { + LOG.debug( + "Setting custom ConstraintValidatorFactory of type " + constraintValidatorFactory.getClass() + .getName() + ); + } + } + this.validationBootstrapParameters.setConstraintValidatorFactory( constraintValidatorFactory ); + return thisAsT(); + } + + @Override + public T parameterNameProvider(ParameterNameProvider parameterNameProvider) { + if ( LOG.isDebugEnabled() ) { + if ( parameterNameProvider != null ) { + LOG.debug( + "Setting custom ParameterNameProvider of type " + parameterNameProvider.getClass() + .getName() + ); + } + } + this.validationBootstrapParameters.setParameterNameProvider( parameterNameProvider ); + return thisAsT(); + } + + @Override + public T clockProvider(ClockProvider clockProvider) { + if ( LOG.isDebugEnabled() ) { + if ( clockProvider != null ) { + LOG.debug( "Setting custom ClockProvider of type " + clockProvider.getClass().getName() ); + } + } + this.validationBootstrapParameters.setClockProvider( clockProvider ); + return thisAsT(); + } + + @Override + public T propertyNodeNameProvider(PropertyNodeNameProvider propertyNodeNameProvider) { + if ( LOG.isDebugEnabled() ) { + if ( propertyNodeNameProvider != null ) { + LOG.debug( "Setting custom PropertyNodeNameProvider of type " + propertyNodeNameProvider.getClass() + .getName() ); + } + } + this.validationBootstrapParameters.setPropertyNodeNameProvider( propertyNodeNameProvider ); + + return thisAsT(); + } + + @Override + public T addValueExtractor(ValueExtractor extractor) { + Contracts.assertNotNull( extractor, MESSAGES.parameterMustNotBeNull( "extractor" ) ); + + ValueExtractorDescriptor descriptor = new ValueExtractorDescriptor( extractor ); + ValueExtractorDescriptor previous = valueExtractorDescriptors.put( descriptor.getKey(), descriptor ); + + if ( previous != null ) { + throw LOG.getValueExtractorForTypeAndTypeUseAlreadyPresentException( extractor, previous.getValueExtractor() ); + } + + if ( LOG.isDebugEnabled() ) { + LOG.debug( "Adding value extractor " + extractor ); + } + + return thisAsT(); + } + + @Override + public final T addMapping(InputStream stream) { + Contracts.assertNotNull( stream, MESSAGES.inputStreamCannotBeNull() ); + + validationBootstrapParameters.addMapping( stream.markSupported() ? stream : new BufferedInputStream( stream ) ); + return thisAsT(); + } + + @Override + public final T failFast(boolean failFast) { + this.failFast = failFast; + return thisAsT(); + } + + @Override + public T allowOverridingMethodAlterParameterConstraint(boolean allow) { + this.methodValidationConfigurationBuilder.allowOverridingMethodAlterParameterConstraint( allow ); + return thisAsT(); + } + + public boolean isAllowOverridingMethodAlterParameterConstraint() { + return this.methodValidationConfigurationBuilder.isAllowOverridingMethodAlterParameterConstraint(); + } + + @Override + public T allowMultipleCascadedValidationOnReturnValues(boolean allow) { + this.methodValidationConfigurationBuilder.allowMultipleCascadedValidationOnReturnValues( allow ); + return thisAsT(); + } + + public boolean isAllowMultipleCascadedValidationOnReturnValues() { + return this.methodValidationConfigurationBuilder.isAllowMultipleCascadedValidationOnReturnValues(); + } + + @Override + public T allowParallelMethodsDefineParameterConstraints(boolean allow) { + this.methodValidationConfigurationBuilder.allowParallelMethodsDefineParameterConstraints( allow ); + return thisAsT(); + } + + @Override + public T scriptEvaluatorFactory(ScriptEvaluatorFactory scriptEvaluatorFactory) { + Contracts.assertNotNull( scriptEvaluatorFactory, MESSAGES.parameterMustNotBeNull( "scriptEvaluatorFactory" ) ); + + this.scriptEvaluatorFactory = scriptEvaluatorFactory; + return thisAsT(); + } + + @Override + public T temporalValidationTolerance(Duration temporalValidationTolerance) { + Contracts.assertNotNull( temporalValidationTolerance, MESSAGES.parameterMustNotBeNull( "temporalValidationTolerance" ) ); + + this.temporalValidationTolerance = temporalValidationTolerance.abs(); + return thisAsT(); + } + + @Override + public T constraintValidatorPayload(Object constraintValidatorPayload) { + Contracts.assertNotNull( constraintValidatorPayload, MESSAGES.parameterMustNotBeNull( "constraintValidatorPayload" ) ); + + this.constraintValidatorPayload = constraintValidatorPayload; + return thisAsT(); + } + + @Override + public T getterPropertySelectionStrategy(GetterPropertySelectionStrategy getterPropertySelectionStrategy) { + Contracts.assertNotNull( getterPropertySelectionStrategy, MESSAGES.parameterMustNotBeNull( "getterPropertySelectionStrategy" ) ); + + this.getterPropertySelectionStrategy = getterPropertySelectionStrategy; + return thisAsT(); + } + + public boolean isAllowParallelMethodsDefineParameterConstraints() { + return this.methodValidationConfigurationBuilder.isAllowParallelMethodsDefineParameterConstraints(); + } + + public MethodValidationConfiguration getMethodValidationConfiguration() { + return this.methodValidationConfigurationBuilder.build(); + } + + @Override + public final DefaultConstraintMapping createConstraintMapping() { + GetterPropertySelectionStrategy getterPropertySelectionStrategyToUse = null; + if ( getterPropertySelectionStrategy == null ) { + getterPropertySelectionStrategyToUse = new DefaultGetterPropertySelectionStrategy(); + } + else { + getterPropertySelectionStrategyToUse = getterPropertySelectionStrategy; + } + + return new DefaultConstraintMapping( new JavaBeanHelper( getterPropertySelectionStrategyToUse, defaultPropertyNodeNameProvider ) ); + } + + @Override + public final T addMapping(ConstraintMapping mapping) { + Contracts.assertNotNull( mapping, MESSAGES.parameterMustNotBeNull( "mapping" ) ); + + this.programmaticMappings.add( (DefaultConstraintMapping) mapping ); + return thisAsT(); + } + + @Override + public final T addProperty(String name, String value) { + if ( value != null ) { + validationBootstrapParameters.addConfigProperty( name, value ); + } + return thisAsT(); + } + + @Override + public T externalClassLoader(ClassLoader externalClassLoader) { + Contracts.assertNotNull( externalClassLoader, MESSAGES.parameterMustNotBeNull( "externalClassLoader" ) ); + this.externalClassLoader = externalClassLoader; + + return thisAsT(); + } + + @Override + public final ValidatorFactory buildValidatorFactory() { + loadValueExtractorsFromServiceLoader(); + parseValidationXml(); + + for ( ValueExtractorDescriptor valueExtractorDescriptor : valueExtractorDescriptors.values() ) { + validationBootstrapParameters.addValueExtractorDescriptor( valueExtractorDescriptor ); + } + + ValidatorFactory factory = null; + try { + if ( isSpecificProvider() ) { + factory = validationBootstrapParameters.getProvider().buildValidatorFactory( this ); + } + else { + final Class> providerClass = validationBootstrapParameters.getProviderClass(); + if ( providerClass != null ) { + for ( ValidationProvider provider : providerResolver.getValidationProviders() ) { + if ( providerClass.isAssignableFrom( provider.getClass() ) ) { + factory = provider.buildValidatorFactory( this ); + break; + } + } + if ( factory == null ) { + throw LOG.getUnableToFindProviderException( providerClass ); + } + } + else { + List> providers = providerResolver.getValidationProviders(); + assert providers.size() != 0; // I run therefore I am + factory = providers.get( 0 ).buildValidatorFactory( this ); + } + } + } + finally { + // close all input streams opened by this configuration + for ( InputStream in : configurationStreams ) { + try { + in.close(); + } + catch (IOException io) { + LOG.unableToCloseInputStream(); + } + } + } + + return factory; + } + + @Override + public final boolean isIgnoreXmlConfiguration() { + return ignoreXmlConfiguration; + } + + @Override + public final MessageInterpolator getMessageInterpolator() { + if ( messageInterpolator == null ) { + // apply explicitly given MI, otherwise use default one + MessageInterpolator interpolator = validationBootstrapParameters.getMessageInterpolator(); + if ( interpolator != null ) { + messageInterpolator = interpolator; + } + else { + messageInterpolator = getDefaultMessageInterpolatorConfiguredWithClassLoader(); + } + } + + return messageInterpolator; + } + + @Override + public final Set getMappingStreams() { + return validationBootstrapParameters.getMappings(); + } + + public final boolean getFailFast() { + return failFast; + } + + @Override + public final ConstraintValidatorFactory getConstraintValidatorFactory() { + return validationBootstrapParameters.getConstraintValidatorFactory(); + } + + @Override + public final TraversableResolver getTraversableResolver() { + return validationBootstrapParameters.getTraversableResolver(); + } + + @Override + public BootstrapConfiguration getBootstrapConfiguration() { + if ( bootstrapConfiguration == null ) { + bootstrapConfiguration = new ValidationXmlParser( externalClassLoader ).parseValidationXml(); + } + return bootstrapConfiguration; + } + + @Override + public ParameterNameProvider getParameterNameProvider() { + return validationBootstrapParameters.getParameterNameProvider(); + } + + @Override + public ClockProvider getClockProvider() { + return validationBootstrapParameters.getClockProvider(); + } + + public PropertyNodeNameProvider getPropertyNodeNameProvider() { + return validationBootstrapParameters.getPropertyNodeNameProvider(); + } + + public ScriptEvaluatorFactory getScriptEvaluatorFactory() { + return scriptEvaluatorFactory; + } + + public Duration getTemporalValidationTolerance() { + return temporalValidationTolerance; + } + + public Object getConstraintValidatorPayload() { + return constraintValidatorPayload; + } + + public GetterPropertySelectionStrategy getGetterPropertySelectionStrategy() { + return getterPropertySelectionStrategy; + } + + @Override + public Set> getValueExtractors() { + return validationBootstrapParameters.getValueExtractorDescriptors() + .values() + .stream() + .map( ValueExtractorDescriptor::getValueExtractor ) + .collect( Collectors.toSet() ); + } + + @Override + public final Map getProperties() { + return validationBootstrapParameters.getConfigProperties(); + } + + public ClassLoader getExternalClassLoader() { + return externalClassLoader; + } + + @Override + public final MessageInterpolator getDefaultMessageInterpolator() { + if ( defaultMessageInterpolator == null ) { + defaultMessageInterpolator = new ResourceBundleMessageInterpolator( getDefaultResourceBundleLocator(), localesToInitialize ); + } + + return defaultMessageInterpolator; + } + + @Override + public final TraversableResolver getDefaultTraversableResolver() { + return defaultTraversableResolver; + } + + @Override + public final ConstraintValidatorFactory getDefaultConstraintValidatorFactory() { + return defaultConstraintValidatorFactory; + } + + @Override + public final ResourceBundleLocator getDefaultResourceBundleLocator() { + if ( defaultResourceBundleLocator == null ) { + defaultResourceBundleLocator = new PlatformResourceBundleLocator( + ResourceBundleMessageInterpolator.USER_VALIDATION_MESSAGES, localesToInitialize ); + } + + return defaultResourceBundleLocator; + } + + @Override + public ParameterNameProvider getDefaultParameterNameProvider() { + return defaultParameterNameProvider; + } + + @Override + public ClockProvider getDefaultClockProvider() { + return defaultClockProvider; + } + + @Override + public Set> getDefaultValueExtractors() { + return ValueExtractorManager.getDefaultValueExtractors(); + } + + public final Set getProgrammaticMappings() { + return programmaticMappings; + } + + private boolean isSpecificProvider() { + return validationBootstrapParameters.getProvider() != null; + } + + /** + * Tries to check whether a validation.xml file exists and parses it + */ + private void parseValidationXml() { + if ( ignoreXmlConfiguration ) { + LOG.ignoringXmlConfiguration(); + + if ( validationBootstrapParameters.getTraversableResolver() == null ) { + validationBootstrapParameters.setTraversableResolver( defaultTraversableResolver ); + } + if ( validationBootstrapParameters.getConstraintValidatorFactory() == null ) { + validationBootstrapParameters.setConstraintValidatorFactory( defaultConstraintValidatorFactory ); + } + if ( validationBootstrapParameters.getParameterNameProvider() == null ) { + validationBootstrapParameters.setParameterNameProvider( defaultParameterNameProvider ); + } + if ( validationBootstrapParameters.getClockProvider() == null ) { + validationBootstrapParameters.setClockProvider( defaultClockProvider ); + } + if ( validationBootstrapParameters.getPropertyNodeNameProvider() == null ) { + validationBootstrapParameters.setPropertyNodeNameProvider( defaultPropertyNodeNameProvider ); + } + } + else { + ValidationBootstrapParameters xmlParameters = new ValidationBootstrapParameters( + getBootstrapConfiguration(), externalClassLoader + ); + applyXmlSettings( xmlParameters ); + } + } + + @SuppressWarnings("rawtypes") + private void loadValueExtractorsFromServiceLoader() { + List valueExtractors = run( GetInstancesFromServiceLoader.action( + externalClassLoader != null ? externalClassLoader : run( GetClassLoader.fromContext() ), + ValueExtractor.class + ) ); + + for ( ValueExtractor valueExtractor : valueExtractors ) { + validationBootstrapParameters.addValueExtractorDescriptor( new ValueExtractorDescriptor( valueExtractor ) ); + } + } + + private void applyXmlSettings(ValidationBootstrapParameters xmlParameters) { + validationBootstrapParameters.setProviderClass( xmlParameters.getProviderClass() ); + + if ( validationBootstrapParameters.getMessageInterpolator() == null ) { + if ( xmlParameters.getMessageInterpolator() != null ) { + validationBootstrapParameters.setMessageInterpolator( xmlParameters.getMessageInterpolator() ); + } + } + + if ( validationBootstrapParameters.getTraversableResolver() == null ) { + if ( xmlParameters.getTraversableResolver() != null ) { + validationBootstrapParameters.setTraversableResolver( xmlParameters.getTraversableResolver() ); + } + else { + validationBootstrapParameters.setTraversableResolver( defaultTraversableResolver ); + } + } + + if ( validationBootstrapParameters.getConstraintValidatorFactory() == null ) { + if ( xmlParameters.getConstraintValidatorFactory() != null ) { + validationBootstrapParameters.setConstraintValidatorFactory( + xmlParameters.getConstraintValidatorFactory() + ); + } + else { + validationBootstrapParameters.setConstraintValidatorFactory( defaultConstraintValidatorFactory ); + } + } + + if ( validationBootstrapParameters.getParameterNameProvider() == null ) { + if ( xmlParameters.getParameterNameProvider() != null ) { + validationBootstrapParameters.setParameterNameProvider( xmlParameters.getParameterNameProvider() ); + } + else { + validationBootstrapParameters.setParameterNameProvider( defaultParameterNameProvider ); + } + } + + if ( validationBootstrapParameters.getClockProvider() == null ) { + if ( xmlParameters.getClockProvider() != null ) { + validationBootstrapParameters.setClockProvider( xmlParameters.getClockProvider() ); + } + else { + validationBootstrapParameters.setClockProvider( defaultClockProvider ); + } + } + + if ( validationBootstrapParameters.getPropertyNodeNameProvider() == null ) { + if ( xmlParameters.getPropertyNodeNameProvider() != null ) { + validationBootstrapParameters.setPropertyNodeNameProvider( + xmlParameters.getPropertyNodeNameProvider() ); + } + else { + validationBootstrapParameters.setPropertyNodeNameProvider( defaultPropertyNodeNameProvider ); + } + } + + for ( ValueExtractorDescriptor valueExtractorDescriptor : xmlParameters.getValueExtractorDescriptors().values() ) { + validationBootstrapParameters.addValueExtractorDescriptor( valueExtractorDescriptor ); + } + + validationBootstrapParameters.addAllMappings( xmlParameters.getMappings() ); + configurationStreams.addAll( xmlParameters.getMappings() ); + + for ( Map.Entry entry : xmlParameters.getConfigProperties().entrySet() ) { + if ( validationBootstrapParameters.getConfigProperties().get( entry.getKey() ) == null ) { + validationBootstrapParameters.addConfigProperty( entry.getKey(), entry.getValue() ); + } + } + } + + /** + * Returns the default message interpolator, configured with the given user class loader, if present. + */ + private MessageInterpolator getDefaultMessageInterpolatorConfiguredWithClassLoader() { + if ( externalClassLoader != null ) { + PlatformResourceBundleLocator userResourceBundleLocator = new PlatformResourceBundleLocator( + ResourceBundleMessageInterpolator.USER_VALIDATION_MESSAGES, + localesToInitialize, + externalClassLoader + ); + PlatformResourceBundleLocator contributorResourceBundleLocator = new PlatformResourceBundleLocator( + ResourceBundleMessageInterpolator.CONTRIBUTOR_VALIDATION_MESSAGES, + localesToInitialize, + externalClassLoader, + true + ); + + // Within RBMI, the expression factory implementation is loaded from the TCCL; thus we set the TCCL to the + // given external class loader for this call + final ClassLoader originalContextClassLoader = run( GetClassLoader.fromContext() ); + + try { + run( SetContextClassLoader.action( externalClassLoader ) ); + return new ResourceBundleMessageInterpolator( + userResourceBundleLocator, + contributorResourceBundleLocator, + localesToInitialize + ); + } + finally { + run( SetContextClassLoader.action( originalContextClassLoader ) ); + } + } + else { + return getDefaultMessageInterpolator(); + } + } + + protected void setLocalesToInitialize(Set localesToInitialize) { + this.localesToInitialize = localesToInitialize; + } + + @SuppressWarnings("unchecked") + protected T thisAsT() { + return (T) this; + } + + /** + * 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 T run(PrivilegedAction action) { + return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); + } +} 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 5fe13d90f0..f6292695e8 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 @@ -6,55 +6,11 @@ */ package org.hibernate.validator.internal.engine; -import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; -import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES; - -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.lang.invoke.MethodHandles; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.time.Duration; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import javax.validation.BootstrapConfiguration; -import javax.validation.ClockProvider; -import javax.validation.ConstraintValidatorFactory; -import javax.validation.MessageInterpolator; -import javax.validation.ParameterNameProvider; -import javax.validation.TraversableResolver; -import javax.validation.ValidationProviderResolver; -import javax.validation.ValidatorFactory; import javax.validation.spi.BootstrapState; import javax.validation.spi.ConfigurationState; import javax.validation.spi.ValidationProvider; -import javax.validation.valueextraction.ValueExtractor; import org.hibernate.validator.HibernateValidatorConfiguration; -import org.hibernate.validator.cfg.ConstraintMapping; -import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping; -import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl; -import org.hibernate.validator.internal.engine.resolver.TraversableResolvers; -import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor; -import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.util.Contracts; -import org.hibernate.validator.internal.util.Version; -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.internal.util.privilegedactions.SetContextClassLoader; -import org.hibernate.validator.internal.xml.ValidationBootstrapParameters; -import org.hibernate.validator.internal.xml.ValidationXmlParser; -import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; -import org.hibernate.validator.resourceloading.PlatformResourceBundleLocator; -import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; -import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; /** * Hibernate specific {@code Configuration} implementation. @@ -65,599 +21,13 @@ * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI * @author Chris Beckey <cbeckey@paypal.com> */ -public class ConfigurationImpl implements HibernateValidatorConfiguration, ConfigurationState { - - static { - Version.touch(); - } - - private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - - private final ResourceBundleLocator defaultResourceBundleLocator; - - /** - * Built lazily so RBMI and its dependency on EL is only initialized if actually needed - */ - private MessageInterpolator defaultMessageInterpolator; - private MessageInterpolator messageInterpolator; - - private final TraversableResolver defaultTraversableResolver; - private final ConstraintValidatorFactory defaultConstraintValidatorFactory; - private final ParameterNameProvider defaultParameterNameProvider; - private final ClockProvider defaultClockProvider; - - private ValidationProviderResolver providerResolver; - private final ValidationBootstrapParameters validationBootstrapParameters; - private boolean ignoreXmlConfiguration = false; - private final Set configurationStreams = newHashSet(); - private BootstrapConfiguration bootstrapConfiguration; - - private final Map valueExtractorDescriptors = new HashMap<>(); - - // HV-specific options - private final Set programmaticMappings = newHashSet(); - private boolean failFast; - private ClassLoader externalClassLoader; - private final MethodValidationConfiguration.Builder methodValidationConfigurationBuilder = new MethodValidationConfiguration.Builder(); - private boolean traversableResolverResultCacheEnabled = true; - private ScriptEvaluatorFactory scriptEvaluatorFactory; - private Duration temporalValidationTolerance; - private Object constraintValidatorPayload; +public class ConfigurationImpl extends AbstractConfigurationImpl implements HibernateValidatorConfiguration, ConfigurationState { public ConfigurationImpl(BootstrapState state) { - this(); - if ( state.getValidationProviderResolver() == null ) { - this.providerResolver = state.getDefaultValidationProviderResolver(); - } - else { - this.providerResolver = state.getValidationProviderResolver(); - } + super( state ); } public ConfigurationImpl(ValidationProvider provider) { - this(); - if ( provider == null ) { - throw LOG.getInconsistentConfigurationException(); - } - this.providerResolver = null; - validationBootstrapParameters.setProvider( provider ); - } - - private ConfigurationImpl() { - this.validationBootstrapParameters = new ValidationBootstrapParameters(); - - this.defaultResourceBundleLocator = new PlatformResourceBundleLocator( - ResourceBundleMessageInterpolator.USER_VALIDATION_MESSAGES - ); - this.defaultTraversableResolver = TraversableResolvers.getDefault(); - this.defaultConstraintValidatorFactory = new ConstraintValidatorFactoryImpl(); - this.defaultParameterNameProvider = new DefaultParameterNameProvider(); - this.defaultClockProvider = DefaultClockProvider.INSTANCE; - } - - @Override - public final HibernateValidatorConfiguration ignoreXmlConfiguration() { - ignoreXmlConfiguration = true; - return this; - } - - @Override - public final ConfigurationImpl messageInterpolator(MessageInterpolator interpolator) { - if ( LOG.isDebugEnabled() ) { - if ( interpolator != null ) { - LOG.debug( "Setting custom MessageInterpolator of type " + interpolator.getClass().getName() ); - } - } - this.validationBootstrapParameters.setMessageInterpolator( interpolator ); - return this; - } - - @Override - public final ConfigurationImpl traversableResolver(TraversableResolver resolver) { - if ( LOG.isDebugEnabled() ) { - if ( resolver != null ) { - LOG.debug( "Setting custom TraversableResolver of type " + resolver.getClass().getName() ); - } - } - this.validationBootstrapParameters.setTraversableResolver( resolver ); - return this; - } - - @Override - public final ConfigurationImpl enableTraversableResolverResultCache(boolean enabled) { - this.traversableResolverResultCacheEnabled = enabled; - return this; - } - - public final boolean isTraversableResolverResultCacheEnabled() { - return traversableResolverResultCacheEnabled; - } - - @Override - public final ConfigurationImpl constraintValidatorFactory(ConstraintValidatorFactory constraintValidatorFactory) { - if ( LOG.isDebugEnabled() ) { - if ( constraintValidatorFactory != null ) { - LOG.debug( - "Setting custom ConstraintValidatorFactory of type " + constraintValidatorFactory.getClass() - .getName() - ); - } - } - this.validationBootstrapParameters.setConstraintValidatorFactory( constraintValidatorFactory ); - return this; - } - - @Override - public HibernateValidatorConfiguration parameterNameProvider(ParameterNameProvider parameterNameProvider) { - if ( LOG.isDebugEnabled() ) { - if ( parameterNameProvider != null ) { - LOG.debug( - "Setting custom ParameterNameProvider of type " + parameterNameProvider.getClass() - .getName() - ); - } - } - this.validationBootstrapParameters.setParameterNameProvider( parameterNameProvider ); - return this; - } - - @Override - public HibernateValidatorConfiguration clockProvider(ClockProvider clockProvider) { - if ( LOG.isDebugEnabled() ) { - if ( clockProvider != null ) { - LOG.debug( "Setting custom ClockProvider of type " + clockProvider.getClass().getName() ); - } - } - this.validationBootstrapParameters.setClockProvider( clockProvider ); - return this; - } - - @Override - public HibernateValidatorConfiguration addValueExtractor(ValueExtractor extractor) { - Contracts.assertNotNull( extractor, MESSAGES.parameterMustNotBeNull( "extractor" ) ); - - ValueExtractorDescriptor descriptor = new ValueExtractorDescriptor( extractor ); - ValueExtractorDescriptor previous = valueExtractorDescriptors.put( descriptor.getKey(), descriptor ); - - if ( previous != null ) { - throw LOG.getValueExtractorForTypeAndTypeUseAlreadyPresentException( extractor, previous.getValueExtractor() ); - } - - if ( LOG.isDebugEnabled() ) { - LOG.debug( "Adding value extractor " + extractor ); - } - - return this; - } - - @Override - public final HibernateValidatorConfiguration addMapping(InputStream stream) { - Contracts.assertNotNull( stream, MESSAGES.inputStreamCannotBeNull() ); - - validationBootstrapParameters.addMapping( stream.markSupported() ? stream : new BufferedInputStream( stream ) ); - return this; - } - - @Override - public final HibernateValidatorConfiguration failFast(boolean failFast) { - this.failFast = failFast; - return this; - } - - @Override - public HibernateValidatorConfiguration allowOverridingMethodAlterParameterConstraint(boolean allow) { - this.methodValidationConfigurationBuilder.allowOverridingMethodAlterParameterConstraint( allow ); - return this; - } - - public boolean isAllowOverridingMethodAlterParameterConstraint() { - return this.methodValidationConfigurationBuilder.isAllowOverridingMethodAlterParameterConstraint(); - } - - @Override - public HibernateValidatorConfiguration allowMultipleCascadedValidationOnReturnValues(boolean allow) { - this.methodValidationConfigurationBuilder.allowMultipleCascadedValidationOnReturnValues( allow ); - return this; - } - - public boolean isAllowMultipleCascadedValidationOnReturnValues() { - return this.methodValidationConfigurationBuilder.isAllowMultipleCascadedValidationOnReturnValues(); - } - - @Override - public HibernateValidatorConfiguration allowParallelMethodsDefineParameterConstraints(boolean allow) { - this.methodValidationConfigurationBuilder.allowParallelMethodsDefineParameterConstraints( allow ); - return this; - } - - @Override - public HibernateValidatorConfiguration scriptEvaluatorFactory(ScriptEvaluatorFactory scriptEvaluatorFactory) { - Contracts.assertNotNull( scriptEvaluatorFactory, MESSAGES.parameterMustNotBeNull( "scriptEvaluatorFactory" ) ); - - this.scriptEvaluatorFactory = scriptEvaluatorFactory; - return this; - } - - @Override - public HibernateValidatorConfiguration temporalValidationTolerance(Duration temporalValidationTolerance) { - Contracts.assertNotNull( temporalValidationTolerance, MESSAGES.parameterMustNotBeNull( "temporalValidationTolerance" ) ); - - this.temporalValidationTolerance = temporalValidationTolerance.abs(); - return this; - } - - @Override - public HibernateValidatorConfiguration constraintValidatorPayload(Object constraintValidatorPayload) { - Contracts.assertNotNull( constraintValidatorPayload, MESSAGES.parameterMustNotBeNull( "constraintValidatorPayload" ) ); - - this.constraintValidatorPayload = constraintValidatorPayload; - return this; - } - - public boolean isAllowParallelMethodsDefineParameterConstraints() { - return this.methodValidationConfigurationBuilder.isAllowParallelMethodsDefineParameterConstraints(); - } - - public MethodValidationConfiguration getMethodValidationConfiguration() { - return this.methodValidationConfigurationBuilder.build(); - } - - @Override - public final DefaultConstraintMapping createConstraintMapping() { - return new DefaultConstraintMapping(); - } - - @Override - public final HibernateValidatorConfiguration addMapping(ConstraintMapping mapping) { - Contracts.assertNotNull( mapping, MESSAGES.parameterMustNotBeNull( "mapping" ) ); - - this.programmaticMappings.add( (DefaultConstraintMapping) mapping ); - return this; - } - - @Override - public final HibernateValidatorConfiguration addProperty(String name, String value) { - if ( value != null ) { - validationBootstrapParameters.addConfigProperty( name, value ); - } - return this; - } - - @Override - public HibernateValidatorConfiguration externalClassLoader(ClassLoader externalClassLoader) { - Contracts.assertNotNull( externalClassLoader, MESSAGES.parameterMustNotBeNull( "externalClassLoader" ) ); - this.externalClassLoader = externalClassLoader; - - return this; - } - - @Override - public final ValidatorFactory buildValidatorFactory() { - loadValueExtractorsFromServiceLoader(); - parseValidationXml(); - - for ( ValueExtractorDescriptor valueExtractorDescriptor : valueExtractorDescriptors.values() ) { - validationBootstrapParameters.addValueExtractorDescriptor( valueExtractorDescriptor ); - } - - ValidatorFactory factory = null; - try { - if ( isSpecificProvider() ) { - factory = validationBootstrapParameters.getProvider().buildValidatorFactory( this ); - } - else { - final Class> providerClass = validationBootstrapParameters.getProviderClass(); - if ( providerClass != null ) { - for ( ValidationProvider provider : providerResolver.getValidationProviders() ) { - if ( providerClass.isAssignableFrom( provider.getClass() ) ) { - factory = provider.buildValidatorFactory( this ); - break; - } - } - if ( factory == null ) { - throw LOG.getUnableToFindProviderException( providerClass ); - } - } - else { - List> providers = providerResolver.getValidationProviders(); - assert providers.size() != 0; // I run therefore I am - factory = providers.get( 0 ).buildValidatorFactory( this ); - } - } - } - finally { - // close all input streams opened by this configuration - for ( InputStream in : configurationStreams ) { - try { - in.close(); - } - catch (IOException io) { - LOG.unableToCloseInputStream(); - } - } - } - - return factory; - } - - @Override - public final boolean isIgnoreXmlConfiguration() { - return ignoreXmlConfiguration; - } - - @Override - public final MessageInterpolator getMessageInterpolator() { - if ( messageInterpolator == null ) { - // apply explicitly given MI, otherwise use default one - MessageInterpolator interpolator = validationBootstrapParameters.getMessageInterpolator(); - if ( interpolator != null ) { - messageInterpolator = interpolator; - } - else { - messageInterpolator = getDefaultMessageInterpolatorConfiguredWithClassLoader(); - } - } - - return messageInterpolator; - } - - @Override - public final Set getMappingStreams() { - return validationBootstrapParameters.getMappings(); - } - - public final boolean getFailFast() { - return failFast; - } - - @Override - public final ConstraintValidatorFactory getConstraintValidatorFactory() { - return validationBootstrapParameters.getConstraintValidatorFactory(); - } - - @Override - public final TraversableResolver getTraversableResolver() { - return validationBootstrapParameters.getTraversableResolver(); - } - - @Override - public BootstrapConfiguration getBootstrapConfiguration() { - if ( bootstrapConfiguration == null ) { - bootstrapConfiguration = new ValidationXmlParser( externalClassLoader ).parseValidationXml(); - } - return bootstrapConfiguration; - } - - @Override - public ParameterNameProvider getParameterNameProvider() { - return validationBootstrapParameters.getParameterNameProvider(); - } - - @Override - public ClockProvider getClockProvider() { - return validationBootstrapParameters.getClockProvider(); - } - - public ScriptEvaluatorFactory getScriptEvaluatorFactory() { - return scriptEvaluatorFactory; - } - - public Duration getTemporalValidationTolerance() { - return temporalValidationTolerance; - } - - public Object getConstraintValidatorPayload() { - return constraintValidatorPayload; - } - - @Override - public Set> getValueExtractors() { - return validationBootstrapParameters.getValueExtractorDescriptors() - .values() - .stream() - .map( ValueExtractorDescriptor::getValueExtractor ) - .collect( Collectors.toSet() ); - } - - @Override - public final Map getProperties() { - return validationBootstrapParameters.getConfigProperties(); - } - - public ClassLoader getExternalClassLoader() { - return externalClassLoader; - } - - @Override - public final MessageInterpolator getDefaultMessageInterpolator() { - if ( defaultMessageInterpolator == null ) { - defaultMessageInterpolator = new ResourceBundleMessageInterpolator( defaultResourceBundleLocator ); - } - - return defaultMessageInterpolator; - } - - @Override - public final TraversableResolver getDefaultTraversableResolver() { - return defaultTraversableResolver; - } - - @Override - public final ConstraintValidatorFactory getDefaultConstraintValidatorFactory() { - return defaultConstraintValidatorFactory; - } - - @Override - public final ResourceBundleLocator getDefaultResourceBundleLocator() { - return defaultResourceBundleLocator; - } - - @Override - public ParameterNameProvider getDefaultParameterNameProvider() { - return defaultParameterNameProvider; - } - - @Override - public ClockProvider getDefaultClockProvider() { - return defaultClockProvider; - } - - @Override - public Set> getDefaultValueExtractors() { - return ValueExtractorManager.getDefaultValueExtractors(); - } - - public final Set getProgrammaticMappings() { - return programmaticMappings; - } - - private boolean isSpecificProvider() { - return validationBootstrapParameters.getProvider() != null; - } - - /** - * Tries to check whether a validation.xml file exists and parses it - */ - private void parseValidationXml() { - if ( ignoreXmlConfiguration ) { - LOG.ignoringXmlConfiguration(); - - if ( validationBootstrapParameters.getTraversableResolver() == null ) { - validationBootstrapParameters.setTraversableResolver( defaultTraversableResolver ); - } - if ( validationBootstrapParameters.getConstraintValidatorFactory() == null ) { - validationBootstrapParameters.setConstraintValidatorFactory( defaultConstraintValidatorFactory ); - } - if ( validationBootstrapParameters.getParameterNameProvider() == null ) { - validationBootstrapParameters.setParameterNameProvider( defaultParameterNameProvider ); - } - if ( validationBootstrapParameters.getClockProvider() == null ) { - validationBootstrapParameters.setClockProvider( defaultClockProvider ); - } - } - else { - ValidationBootstrapParameters xmlParameters = new ValidationBootstrapParameters( - getBootstrapConfiguration(), externalClassLoader - ); - applyXmlSettings( xmlParameters ); - } - } - - @SuppressWarnings("rawtypes") - private void loadValueExtractorsFromServiceLoader() { - List valueExtractors = run( GetInstancesFromServiceLoader.action( - externalClassLoader != null ? externalClassLoader : run( GetClassLoader.fromContext() ), - ValueExtractor.class - ) ); - - for ( ValueExtractor valueExtractor : valueExtractors ) { - validationBootstrapParameters.addValueExtractorDescriptor( new ValueExtractorDescriptor( valueExtractor ) ); - } - } - - private void applyXmlSettings(ValidationBootstrapParameters xmlParameters) { - validationBootstrapParameters.setProviderClass( xmlParameters.getProviderClass() ); - - if ( validationBootstrapParameters.getMessageInterpolator() == null ) { - if ( xmlParameters.getMessageInterpolator() != null ) { - validationBootstrapParameters.setMessageInterpolator( xmlParameters.getMessageInterpolator() ); - } - } - - if ( validationBootstrapParameters.getTraversableResolver() == null ) { - if ( xmlParameters.getTraversableResolver() != null ) { - validationBootstrapParameters.setTraversableResolver( xmlParameters.getTraversableResolver() ); - } - else { - validationBootstrapParameters.setTraversableResolver( defaultTraversableResolver ); - } - } - - if ( validationBootstrapParameters.getConstraintValidatorFactory() == null ) { - if ( xmlParameters.getConstraintValidatorFactory() != null ) { - validationBootstrapParameters.setConstraintValidatorFactory( - xmlParameters.getConstraintValidatorFactory() - ); - } - else { - validationBootstrapParameters.setConstraintValidatorFactory( defaultConstraintValidatorFactory ); - } - } - - if ( validationBootstrapParameters.getParameterNameProvider() == null ) { - if ( xmlParameters.getParameterNameProvider() != null ) { - validationBootstrapParameters.setParameterNameProvider( xmlParameters.getParameterNameProvider() ); - } - else { - validationBootstrapParameters.setParameterNameProvider( defaultParameterNameProvider ); - } - } - - if ( validationBootstrapParameters.getClockProvider() == null ) { - if ( xmlParameters.getClockProvider() != null ) { - validationBootstrapParameters.setClockProvider( xmlParameters.getClockProvider() ); - } - else { - validationBootstrapParameters.setClockProvider( defaultClockProvider ); - } - } - - for ( ValueExtractorDescriptor valueExtractorDescriptor : xmlParameters.getValueExtractorDescriptors().values() ) { - validationBootstrapParameters.addValueExtractorDescriptor( valueExtractorDescriptor ); - } - - validationBootstrapParameters.addAllMappings( xmlParameters.getMappings() ); - configurationStreams.addAll( xmlParameters.getMappings() ); - - for ( Map.Entry entry : xmlParameters.getConfigProperties().entrySet() ) { - if ( validationBootstrapParameters.getConfigProperties().get( entry.getKey() ) == null ) { - validationBootstrapParameters.addConfigProperty( entry.getKey(), entry.getValue() ); - } - } - } - - /** - * Returns the default message interpolator, configured with the given user class loader, if present. - */ - private MessageInterpolator getDefaultMessageInterpolatorConfiguredWithClassLoader() { - if ( externalClassLoader != null ) { - PlatformResourceBundleLocator userResourceBundleLocator = new PlatformResourceBundleLocator( - ResourceBundleMessageInterpolator.USER_VALIDATION_MESSAGES, - externalClassLoader - ); - PlatformResourceBundleLocator contributorResourceBundleLocator = new PlatformResourceBundleLocator( - ResourceBundleMessageInterpolator.CONTRIBUTOR_VALIDATION_MESSAGES, - externalClassLoader, - true - ); - - // Within RBMI, the expression factory implementation is loaded from the TCCL; thus we set the TCCL to the - // given external class loader for this call - final ClassLoader originalContextClassLoader = run( GetClassLoader.fromContext() ); - - try { - run( SetContextClassLoader.action( externalClassLoader ) ); - return new ResourceBundleMessageInterpolator( - userResourceBundleLocator, - contributorResourceBundleLocator - ); - } - finally { - run( SetContextClassLoader.action( originalContextClassLoader ) ); - } - } - else { - return getDefaultMessageInterpolator(); - } - } - - /** - * 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 T run(PrivilegedAction action) { - return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); + super( provider ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ConstraintCreationContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ConstraintCreationContext.java new file mode 100644 index 0000000000..8a8ce2c573 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ConstraintCreationContext.java @@ -0,0 +1,49 @@ +/* + * 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.engine; + +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.util.TypeResolutionHelper; + +public class ConstraintCreationContext { + + private final ConstraintHelper constraintHelper; + + private final ConstraintValidatorManager constraintValidatorManager; + + private final TypeResolutionHelper typeResolutionHelper; + + private final ValueExtractorManager valueExtractorManager; + + public ConstraintCreationContext(ConstraintHelper constraintHelper, + ConstraintValidatorManager constraintValidatorManager, + TypeResolutionHelper typeResolutionHelper, + ValueExtractorManager valueExtractorManager) { + this.constraintHelper = constraintHelper; + this.constraintValidatorManager = constraintValidatorManager; + this.typeResolutionHelper = typeResolutionHelper; + this.valueExtractorManager = valueExtractorManager; + } + + public ConstraintHelper getConstraintHelper() { + return constraintHelper; + } + + public ConstraintValidatorManager getConstraintValidatorManager() { + return constraintValidatorManager; + } + + public TypeResolutionHelper getTypeResolutionHelper() { + return typeResolutionHelper; + } + + public ValueExtractorManager getValueExtractorManager() { + return valueExtractorManager; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ConstraintViolationImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ConstraintViolationImpl.java index eecdd5fbbd..69aa6d1619 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ConstraintViolationImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ConstraintViolationImpl.java @@ -7,7 +7,6 @@ package org.hibernate.validator.internal.engine; import java.io.Serializable; -import java.lang.annotation.ElementType; import java.lang.invoke.MethodHandles; import java.util.Map; @@ -39,24 +38,22 @@ public class ConstraintViolationImpl implements HibernateConstraintViolation< private final Map messageParameters; private final Map expressionVariables; private final Class rootBeanClass; - private final ElementType elementType; private final Object[] executableParameters; private final Object executableReturnValue; private final Object dynamicPayload; private final int hashCode; public static ConstraintViolation forBeanValidation(String messageTemplate, - Map messageParameters, - Map expressionVariables, - String interpolatedMessage, - Class rootBeanClass, - T rootBean, - Object leafBeanInstance, - Object value, - Path propertyPath, - ConstraintDescriptor constraintDescriptor, - ElementType elementType, - Object dynamicPayload) { + Map messageParameters, + Map expressionVariables, + String interpolatedMessage, + Class rootBeanClass, + T rootBean, + Object leafBeanInstance, + Object value, + Path propertyPath, + ConstraintDescriptor constraintDescriptor, + Object dynamicPayload) { return new ConstraintViolationImpl<>( messageTemplate, messageParameters, @@ -68,7 +65,6 @@ public static ConstraintViolation forBeanValidation(String messageTemplat value, propertyPath, constraintDescriptor, - elementType, null, null, dynamicPayload @@ -76,18 +72,17 @@ public static ConstraintViolation forBeanValidation(String messageTemplat } public static ConstraintViolation forParameterValidation(String messageTemplate, - Map messageParameters, - Map expressionVariables, - String interpolatedMessage, - Class rootBeanClass, - T rootBean, - Object leafBeanInstance, - Object value, - Path propertyPath, - ConstraintDescriptor constraintDescriptor, - ElementType elementType, - Object[] executableParameters, - Object dynamicPayload) { + Map messageParameters, + Map expressionVariables, + String interpolatedMessage, + Class rootBeanClass, + T rootBean, + Object leafBeanInstance, + Object value, + Path propertyPath, + ConstraintDescriptor constraintDescriptor, + Object[] executableParameters, + Object dynamicPayload) { return new ConstraintViolationImpl<>( messageTemplate, messageParameters, @@ -99,7 +94,6 @@ public static ConstraintViolation forParameterValidation(String messageTe value, propertyPath, constraintDescriptor, - elementType, executableParameters, null, dynamicPayload @@ -107,18 +101,17 @@ public static ConstraintViolation forParameterValidation(String messageTe } public static ConstraintViolation forReturnValueValidation(String messageTemplate, - Map messageParameters, - Map expressionVariables, - String interpolatedMessage, - Class rootBeanClass, - T rootBean, - Object leafBeanInstance, - Object value, - Path propertyPath, - ConstraintDescriptor constraintDescriptor, - ElementType elementType, - Object executableReturnValue, - Object dynamicPayload) { + Map messageParameters, + Map expressionVariables, + String interpolatedMessage, + Class rootBeanClass, + T rootBean, + Object leafBeanInstance, + Object value, + Path propertyPath, + ConstraintDescriptor constraintDescriptor, + Object executableReturnValue, + Object dynamicPayload) { return new ConstraintViolationImpl<>( messageTemplate, messageParameters, @@ -130,7 +123,6 @@ public static ConstraintViolation forReturnValueValidation(String message value, propertyPath, constraintDescriptor, - elementType, null, executableReturnValue, dynamicPayload @@ -147,7 +139,6 @@ private ConstraintViolationImpl(String messageTemplate, Object value, Path propertyPath, ConstraintDescriptor constraintDescriptor, - ElementType elementType, Object[] executableParameters, Object executableReturnValue, Object dynamicPayload) { @@ -161,7 +152,6 @@ private ConstraintViolationImpl(String messageTemplate, this.leafBeanInstance = leafBeanInstance; this.constraintDescriptor = constraintDescriptor; this.rootBeanClass = rootBeanClass; - this.elementType = elementType; this.executableParameters = executableParameters; this.executableReturnValue = executableReturnValue; this.dynamicPayload = dynamicPayload; @@ -296,10 +286,6 @@ public boolean equals(Object o) { if ( constraintDescriptor != null ? !constraintDescriptor.equals( that.constraintDescriptor ) : that.constraintDescriptor != null ) { return false; } - if ( elementType != null ? !elementType.equals( that.elementType ) : that.elementType != null ) { - return false; - } - return true; } @@ -331,7 +317,6 @@ private int createHashCode() { result = 31 * result + System.identityHashCode( value ); result = 31 * result + ( constraintDescriptor != null ? constraintDescriptor.hashCode() : 0 ); result = 31 * result + ( messageTemplate != null ? messageTemplate.hashCode() : 0 ); - result = 31 * result + ( elementType != null ? elementType.hashCode() : 0 ); return result; } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/DefaultPropertyNodeNameProvider.java b/engine/src/main/java/org/hibernate/validator/internal/engine/DefaultPropertyNodeNameProvider.java new file mode 100644 index 0000000000..d7dc7d74ab --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/DefaultPropertyNodeNameProvider.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.engine; + +import java.io.Serializable; + +import org.hibernate.validator.spi.nodenameprovider.Property; +import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider; + +/** + * A default {@link PropertyNodeNameProvider} implementation which returns the property name. + * + * @author Damir Alibegovic + */ +public class DefaultPropertyNodeNameProvider implements PropertyNodeNameProvider, Serializable { + @Override + public String getName(Property property) { + return property.getName(); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/MessageInterpolatorContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/MessageInterpolatorContext.java index 2c951be52b..df9f9c4b79 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/MessageInterpolatorContext.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/MessageInterpolatorContext.java @@ -11,6 +11,7 @@ import java.lang.invoke.MethodHandles; import java.util.Map; +import javax.validation.Path; import javax.validation.metadata.ConstraintDescriptor; import org.hibernate.validator.internal.util.logging.Log; @@ -33,19 +34,22 @@ public class MessageInterpolatorContext implements HibernateMessageInterpolatorC private final ConstraintDescriptor constraintDescriptor; private final Object validatedValue; private final Class rootBeanType; + private final Path propertyPath; @Immutable private final Map messageParameters; @Immutable private final Map expressionVariables; public MessageInterpolatorContext(ConstraintDescriptor constraintDescriptor, - Object validatedValue, - Class rootBeanType, - Map messageParameters, - Map expressionVariables) { + Object validatedValue, + Class rootBeanType, + Path propertyPath, + Map messageParameters, + Map expressionVariables) { this.constraintDescriptor = constraintDescriptor; this.validatedValue = validatedValue; this.rootBeanType = rootBeanType; + this.propertyPath = propertyPath; this.messageParameters = toImmutableMap( messageParameters ); this.expressionVariables = toImmutableMap( expressionVariables ); } @@ -75,6 +79,11 @@ public Map getExpressionVariables() { return expressionVariables; } + @Override + public Path getPropertyPath() { + return propertyPath; + } + @Override public T unwrap(Class type) { //allow unwrapping into public super types @@ -122,6 +131,8 @@ public String toString() { sb.append( "MessageInterpolatorContext" ); sb.append( "{constraintDescriptor=" ).append( constraintDescriptor ); sb.append( ", validatedValue=" ).append( validatedValue ); + sb.append( ", rootBeanType=" ).append( rootBeanType.getName() ); + sb.append( ", propertyPath=" ).append( propertyPath ); sb.append( ", messageParameters=" ).append( messageParameters ); sb.append( ", expressionVariables=" ).append( expressionVariables ); sb.append( '}' ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/MethodValidationConfiguration.java b/engine/src/main/java/org/hibernate/validator/internal/engine/MethodValidationConfiguration.java index 5a04e4bfc7..b9f945fdaf 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/MethodValidationConfiguration.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/MethodValidationConfiguration.java @@ -21,7 +21,7 @@ import org.hibernate.validator.internal.util.stereotypes.Immutable; /** - * These properties modify the behavior of the {@code Validator} with respect to the Bean Validation + * These properties modify the behavior of the {@code Validator} with respect to the Jakarta Bean Validation * specification section 5.6.5. In particular: *

  * "Out of the box, a conforming Bean Validation provider must throw a
@@ -164,7 +164,7 @@ public Builder(MethodValidationConfiguration template) {
 		 * Define whether overriding methods that override constraints should throw a {@code ConstraintDefinitionException}.
 		 * The default value is {@code false}, i.e. do not allow.
 		 *
-		 * See Section 5.6.5 of the JSR-380 Specification, specifically
+		 * See Section 5.6.5 of the Jakarta Bean Validation Specification, specifically
 		 * 
 		 * "In sub types (be it sub classes/interfaces or interface implementations), no parameter constraints may
 		 * be declared on overridden or implemented methods, nor may parameters be marked for cascaded validation.
@@ -202,7 +202,7 @@ public Builder allowMultipleCascadedValidationOnReturnValues(boolean allow) {
 		 * Define whether parallel methods that define constraints should throw a {@code ConstraintDefinitionException}. The
 		 * default value is {@code false}, i.e. do not allow.
 		 *
-		 * See Section 5.6.5 of the JSR-380 Specification, specifically
+		 * See Section 5.6.5 of the Jakarta Bean Validation Specification, specifically
 		 * "If a sub type overrides/implements a method originally defined in several parallel types of the hierarchy
 		 * (e.g. two interfaces not extending each other, or a class and an interface not implemented by said class),
 		 * no parameter constraints may be declared for that method at all nor parameters be marked for cascaded validation.
diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/PredefinedScopeConfigurationImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/PredefinedScopeConfigurationImpl.java
new file mode 100644
index 0000000000..8d8b53a986
--- /dev/null
+++ b/engine/src/main/java/org/hibernate/validator/internal/engine/PredefinedScopeConfigurationImpl.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.engine;
+
+import java.util.Locale;
+import java.util.Set;
+
+import javax.validation.spi.BootstrapState;
+import javax.validation.spi.ConfigurationState;
+import javax.validation.spi.ValidationProvider;
+
+import org.hibernate.validator.PredefinedScopeHibernateValidatorConfiguration;
+import org.hibernate.validator.internal.util.CollectionHelper;
+import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer;
+
+/**
+ * @author Guillaume Smet
+ */
+public class PredefinedScopeConfigurationImpl extends AbstractConfigurationImpl
+		implements PredefinedScopeHibernateValidatorConfiguration, ConfigurationState {
+
+	private Set> beanClassesToInitialize;
+
+	private BeanMetaDataClassNormalizer beanMetaDataClassNormalizer;
+
+	public PredefinedScopeConfigurationImpl(BootstrapState state) {
+		super( state );
+	}
+
+	public PredefinedScopeConfigurationImpl(ValidationProvider validationProvider) {
+		super( validationProvider );
+	}
+
+	@Override
+	public PredefinedScopeHibernateValidatorConfiguration initializeBeanMetaData(Set> beanMetaDataToInitialize) {
+		beanClassesToInitialize = CollectionHelper.toImmutableSet( beanMetaDataToInitialize );
+		return thisAsT();
+	}
+
+	public Set> getBeanClassesToInitialize() {
+		return beanClassesToInitialize;
+	}
+
+	@Override
+	public PredefinedScopeHibernateValidatorConfiguration initializeLocales(Set locales) {
+		setLocalesToInitialize( CollectionHelper.toImmutableSet( locales ) );
+		return thisAsT();
+	}
+
+	@Override
+	public PredefinedScopeHibernateValidatorConfiguration beanMetaDataClassNormalizer(BeanMetaDataClassNormalizer beanMetaDataClassNormalizer) {
+		this.beanMetaDataClassNormalizer = beanMetaDataClassNormalizer;
+		return thisAsT();
+	}
+
+	public BeanMetaDataClassNormalizer getBeanMetaDataClassNormalizer() {
+		return beanMetaDataClassNormalizer;
+	}
+}
diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/PredefinedScopeValidatorContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/PredefinedScopeValidatorContextImpl.java
new file mode 100644
index 0000000000..6951658a47
--- /dev/null
+++ b/engine/src/main/java/org/hibernate/validator/internal/engine/PredefinedScopeValidatorContextImpl.java
@@ -0,0 +1,108 @@
+/*
+ * 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.engine;
+
+import java.time.Duration;
+
+import javax.validation.ClockProvider;
+import javax.validation.ConstraintValidatorFactory;
+import javax.validation.MessageInterpolator;
+import javax.validation.ParameterNameProvider;
+import javax.validation.TraversableResolver;
+import javax.validation.Validator;
+import javax.validation.valueextraction.ValueExtractor;
+
+import org.hibernate.validator.HibernateValidatorContext;
+
+/**
+ * @author Guillaume Smet
+ */
+public class PredefinedScopeValidatorContextImpl implements HibernateValidatorContext {
+
+	private final PredefinedScopeValidatorFactoryImpl validatorFactory;
+
+	private final ValidatorFactoryScopedContext.Builder validatorFactoryScopedContextBuilder;
+
+	public PredefinedScopeValidatorContextImpl(PredefinedScopeValidatorFactoryImpl validatorFactory) {
+		this.validatorFactoryScopedContextBuilder = new ValidatorFactoryScopedContext.Builder( validatorFactory.getValidatorFactoryScopedContext() );
+		this.validatorFactory = validatorFactory;
+	}
+
+	@Override
+	public HibernateValidatorContext messageInterpolator(MessageInterpolator messageInterpolator) {
+		throw new IllegalStateException( "Defining a Validator-specific message interpolator is not supported by the predefined scope ValidatorFactory." );
+	}
+
+	@Override
+	public HibernateValidatorContext traversableResolver(TraversableResolver traversableResolver) {
+		validatorFactoryScopedContextBuilder.setTraversableResolver( traversableResolver );
+		return this;
+	}
+
+	@Override
+	public HibernateValidatorContext constraintValidatorFactory(ConstraintValidatorFactory factory) {
+		throw new IllegalStateException( "Defining a Validator-specific constraint validator factory is not supported by the predefined scope ValidatorFactory." );
+	}
+
+	@Override
+	public HibernateValidatorContext parameterNameProvider(ParameterNameProvider parameterNameProvider) {
+		throw new IllegalStateException( "Defining a Validator-specific parameter name provider is not supported by the predefined scope ValidatorFactory." );
+	}
+
+	@Override
+	public HibernateValidatorContext clockProvider(ClockProvider clockProvider) {
+		throw new IllegalStateException( "Defining a Validator-specific clock provider is not supported by the predefined scope ValidatorFactory." );
+	}
+
+	@Override
+	public HibernateValidatorContext addValueExtractor(ValueExtractor extractor) {
+		throw new IllegalStateException( "Adding Validator-specific value extractors is not supported by the predefined scope ValidatorFactory." );
+	}
+
+	@Override
+	public HibernateValidatorContext failFast(boolean failFast) {
+		validatorFactoryScopedContextBuilder.setFailFast( failFast );
+		return this;
+	}
+
+	@Override
+	public HibernateValidatorContext allowOverridingMethodAlterParameterConstraint(boolean allow) {
+		throw new IllegalStateException( "Altering method validation configuration is not supported by the predefined scope ValidatorFactory." );
+	}
+
+	@Override
+	public HibernateValidatorContext allowMultipleCascadedValidationOnReturnValues(boolean allow) {
+		throw new IllegalStateException( "Altering method validation configuration is not supported by the predefined scope ValidatorFactory." );
+	}
+
+	@Override
+	public HibernateValidatorContext allowParallelMethodsDefineParameterConstraints(boolean allow) {
+		throw new IllegalStateException( "Altering method validation configuration is not supported by the predefined scope ValidatorFactory." );
+	}
+
+	@Override
+	public HibernateValidatorContext enableTraversableResolverResultCache(boolean enabled) {
+		validatorFactoryScopedContextBuilder.setTraversableResolverResultCacheEnabled( enabled );
+		return this;
+	}
+
+	@Override
+	public HibernateValidatorContext temporalValidationTolerance(Duration temporalValidationTolerance) {
+		throw new IllegalStateException( "Defining a Validator-specific temporal validation tolerance is not supported by the predefined scope ValidatorFactory." );
+	}
+
+	@Override
+	public HibernateValidatorContext constraintValidatorPayload(Object dynamicPayload) {
+		validatorFactoryScopedContextBuilder.setConstraintValidatorPayload( dynamicPayload );
+		return this;
+	}
+
+	@Override
+	public Validator getValidator() {
+		return validatorFactory.createValidator( validatorFactoryScopedContextBuilder.build() );
+	}
+}
diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/PredefinedScopeValidatorFactoryImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/PredefinedScopeValidatorFactoryImpl.java
new file mode 100644
index 0000000000..03cbdb8962
--- /dev/null
+++ b/engine/src/main/java/org/hibernate/validator/internal/engine/PredefinedScopeValidatorFactoryImpl.java
@@ -0,0 +1,293 @@
+/*
+ * 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.engine;
+
+import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineAllowMultipleCascadedValidationOnReturnValues;
+import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineAllowOverridingMethodAlterParameterConstraint;
+import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineAllowParallelMethodsDefineParameterConstraints;
+import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineConstraintMappings;
+import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineConstraintValidatorPayload;
+import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineExternalClassLoader;
+import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineFailFast;
+import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineScriptEvaluatorFactory;
+import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineTemporalValidationTolerance;
+import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineTraversableResolverResultCacheEnabled;
+import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.logValidatorFactoryScopedConfiguration;
+import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.registerCustomConstraintValidators;
+import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineBeanMetaDataClassNormalizer;
+import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList;
+
+import java.lang.invoke.MethodHandles;
+import java.time.Duration;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.validation.ClockProvider;
+import javax.validation.ConstraintValidatorFactory;
+import javax.validation.MessageInterpolator;
+import javax.validation.ParameterNameProvider;
+import javax.validation.TraversableResolver;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.spi.ConfigurationState;
+
+import org.hibernate.validator.HibernateValidatorContext;
+import org.hibernate.validator.HibernateValidatorFactory;
+import org.hibernate.validator.PredefinedScopeHibernateValidatorFactory;
+import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping;
+import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager;
+import org.hibernate.validator.internal.engine.constraintvalidation.PredefinedScopeConstraintValidatorManagerImpl;
+import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator;
+import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager;
+import org.hibernate.validator.internal.metadata.PredefinedScopeBeanMetaDataManager;
+import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
+import org.hibernate.validator.internal.metadata.provider.MetaDataProvider;
+import org.hibernate.validator.internal.metadata.provider.ProgrammaticMetaDataProvider;
+import org.hibernate.validator.internal.metadata.provider.XmlMetaDataProvider;
+import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper;
+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.logging.Log;
+import org.hibernate.validator.internal.util.logging.LoggerFactory;
+import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy;
+import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory;
+
+/**
+ * Factory returning initialized {@code Validator} instances.
+ * 

+ * This factory is designed to support a predefined scope of bean classes to validate and constraint validators. + */ +public class PredefinedScopeValidatorFactoryImpl implements PredefinedScopeHibernateValidatorFactory { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + /** + * Context containing all {@link ValidatorFactory} level helpers and configuration properties. + */ + private final ValidatorFactoryScopedContext validatorFactoryScopedContext; + + /** + * The constraint validator manager for this factory. + */ + private final ConstraintValidatorManager constraintValidatorManager; + + /** + * Hibernate Validator specific flags to relax constraints on parameters. + */ + private final MethodValidationConfiguration methodValidationConfiguration; + + private final PredefinedScopeBeanMetaDataManager beanMetaDataManager; + + private final ValueExtractorManager valueExtractorManager; + + private final GetterPropertySelectionStrategy getterPropertySelectionStrategy; + + private final ValidationOrderGenerator validationOrderGenerator; + + public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState) { + Contracts.assertTrue( configurationState instanceof PredefinedScopeConfigurationImpl, "Only PredefinedScopeConfigurationImpl is supported." ); + + ClassLoader externalClassLoader = determineExternalClassLoader( configurationState ); + + PredefinedScopeConfigurationImpl hibernateSpecificConfig = (PredefinedScopeConfigurationImpl) configurationState; + + Map properties = configurationState.getProperties(); + + this.methodValidationConfiguration = new MethodValidationConfiguration.Builder() + .allowOverridingMethodAlterParameterConstraint( + determineAllowOverridingMethodAlterParameterConstraint( hibernateSpecificConfig, properties ) + ).allowMultipleCascadedValidationOnReturnValues( + determineAllowMultipleCascadedValidationOnReturnValues( hibernateSpecificConfig, properties ) + ).allowParallelMethodsDefineParameterConstraints( + determineAllowParallelMethodsDefineParameterConstraints( hibernateSpecificConfig, properties ) + ).build(); + + this.validatorFactoryScopedContext = new ValidatorFactoryScopedContext( + configurationState.getMessageInterpolator(), + configurationState.getTraversableResolver(), + new ExecutableParameterNameProvider( configurationState.getParameterNameProvider() ), + configurationState.getClockProvider(), + determineTemporalValidationTolerance( configurationState, properties ), + determineScriptEvaluatorFactory( configurationState, properties, externalClassLoader ), + determineFailFast( hibernateSpecificConfig, properties ), + determineTraversableResolverResultCacheEnabled( hibernateSpecificConfig, properties ), + determineConstraintValidatorPayload( hibernateSpecificConfig ) + ); + + this.constraintValidatorManager = new PredefinedScopeConstraintValidatorManagerImpl( + configurationState.getConstraintValidatorFactory(), + this.validatorFactoryScopedContext.getConstraintValidatorInitializationContext() + ); + + this.validationOrderGenerator = new ValidationOrderGenerator(); + + this.getterPropertySelectionStrategy = ValidatorFactoryConfigurationHelper.determineGetterPropertySelectionStrategy( hibernateSpecificConfig, properties, externalClassLoader ); + + this.valueExtractorManager = new ValueExtractorManager( configurationState.getValueExtractors() ); + ConstraintHelper constraintHelper = new ConstraintHelper(); + TypeResolutionHelper typeResolutionHelper = new TypeResolutionHelper(); + + ConstraintCreationContext constraintCreationContext = new ConstraintCreationContext( constraintHelper, + constraintValidatorManager, typeResolutionHelper, valueExtractorManager ); + + ExecutableHelper executableHelper = new ExecutableHelper( typeResolutionHelper ); + JavaBeanHelper javaBeanHelper = new JavaBeanHelper( ValidatorFactoryConfigurationHelper.determineGetterPropertySelectionStrategy( hibernateSpecificConfig, properties, externalClassLoader ), + ValidatorFactoryConfigurationHelper.determinePropertyNodeNameProvider( hibernateSpecificConfig, properties, externalClassLoader ) ); + + // HV-302; don't load XmlMappingParser if not necessary + XmlMetaDataProvider xmlMetaDataProvider; + if ( configurationState.getMappingStreams().isEmpty() ) { + xmlMetaDataProvider = null; + } + else { + xmlMetaDataProvider = new XmlMetaDataProvider( + constraintCreationContext, javaBeanHelper, configurationState.getMappingStreams(), externalClassLoader + ); + } + + Set constraintMappings = Collections.unmodifiableSet( + determineConstraintMappings( + typeResolutionHelper, + configurationState, + javaBeanHelper, + externalClassLoader + ) + ); + + registerCustomConstraintValidators( constraintMappings, constraintHelper ); + + this.beanMetaDataManager = new PredefinedScopeBeanMetaDataManager( + constraintCreationContext, + executableHelper, + validatorFactoryScopedContext.getParameterNameProvider(), + javaBeanHelper, + validationOrderGenerator, + buildMetaDataProviders( constraintCreationContext, xmlMetaDataProvider, constraintMappings ), + methodValidationConfiguration, + determineBeanMetaDataClassNormalizer( hibernateSpecificConfig ), + hibernateSpecificConfig.getBeanClassesToInitialize() + ); + + if ( LOG.isDebugEnabled() ) { + logValidatorFactoryScopedConfiguration( validatorFactoryScopedContext ); + } + } + + @Override + public Validator getValidator() { + return createValidator( validatorFactoryScopedContext ); + } + + @Override + public MessageInterpolator getMessageInterpolator() { + return validatorFactoryScopedContext.getMessageInterpolator(); + } + + @Override + public TraversableResolver getTraversableResolver() { + return validatorFactoryScopedContext.getTraversableResolver(); + } + + @Override + public ConstraintValidatorFactory getConstraintValidatorFactory() { + return constraintValidatorManager.getDefaultConstraintValidatorFactory(); + } + + @Override + public ParameterNameProvider getParameterNameProvider() { + return validatorFactoryScopedContext.getParameterNameProvider().getDelegate(); + } + + public ExecutableParameterNameProvider getExecutableParameterNameProvider() { + return validatorFactoryScopedContext.getParameterNameProvider(); + } + + @Override + public ClockProvider getClockProvider() { + return validatorFactoryScopedContext.getClockProvider(); + } + + @Override + public ScriptEvaluatorFactory getScriptEvaluatorFactory() { + return validatorFactoryScopedContext.getScriptEvaluatorFactory(); + } + + @Override + public Duration getTemporalValidationTolerance() { + return validatorFactoryScopedContext.getTemporalValidationTolerance(); + } + + @Override + public GetterPropertySelectionStrategy getGetterPropertySelectionStrategy() { + return getterPropertySelectionStrategy; + } + + public boolean isFailFast() { + return validatorFactoryScopedContext.isFailFast(); + } + + public boolean isTraversableResolverResultCacheEnabled() { + return validatorFactoryScopedContext.isTraversableResolverResultCacheEnabled(); + } + + @Override + public T unwrap(Class type) { + // allow unwrapping into public super types + if ( type.isAssignableFrom( PredefinedScopeHibernateValidatorFactory.class ) + || type.isAssignableFrom( HibernateValidatorFactory.class ) ) { + return type.cast( this ); + } + throw LOG.getTypeNotSupportedForUnwrappingException( type ); + } + + @Override + public HibernateValidatorContext usingContext() { + return new PredefinedScopeValidatorContextImpl( this ); + } + + @Override + public void close() { + constraintValidatorManager.clear(); + beanMetaDataManager.clear(); + validatorFactoryScopedContext.getScriptEvaluatorFactory().clear(); + valueExtractorManager.clear(); + } + + public ValidatorFactoryScopedContext getValidatorFactoryScopedContext() { + return this.validatorFactoryScopedContext; + } + + Validator createValidator(ValidatorFactoryScopedContext validatorFactoryScopedContext) { + return new ValidatorImpl( + constraintValidatorManager.getDefaultConstraintValidatorFactory(), + beanMetaDataManager, + valueExtractorManager, + constraintValidatorManager, + validationOrderGenerator, + validatorFactoryScopedContext + ); + } + + private static List buildMetaDataProviders( + ConstraintCreationContext constraintCreationContext, + XmlMetaDataProvider xmlMetaDataProvider, + Set constraintMappings) { + List metaDataProviders = newArrayList(); + if ( xmlMetaDataProvider != null ) { + metaDataProviders.add( xmlMetaDataProvider ); + } + + if ( !constraintMappings.isEmpty() ) { + metaDataProviders.add( new ProgrammaticMetaDataProvider( constraintCreationContext, constraintMappings ) ); + } + return metaDataProviders; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ServiceLoaderBasedConstraintMappingContributor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ServiceLoaderBasedConstraintMappingContributor.java index a4ebb12d7d..2d4b4a4444 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ServiceLoaderBasedConstraintMappingContributor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ServiceLoaderBasedConstraintMappingContributor.java @@ -60,7 +60,7 @@ public void createConstraintMappings(ConstraintMappingBuilder builder) { ConstraintValidator.class ) ); for ( ConstraintValidator constraintValidator : discoveredConstraintValidators ) { - Class constraintValidatorClass = constraintValidator.getClass(); + Class constraintValidatorClass = constraintValidator.getClass(); Class annotationType = determineAnnotationType( constraintValidatorClass ); List> validators = customValidators.get( annotationType ); @@ -90,7 +90,8 @@ private void registerConstraintDefinition(ConstraintMappi } } - private Class determineAnnotationType(Class constraintValidatorClass) { + @SuppressWarnings("rawtypes") + private Class determineAnnotationType(Class constraintValidatorClass) { ResolvedType resolvedType = typeResolutionHelper.getTypeResolver() .resolve( constraintValidatorClass ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidationContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidationContext.java deleted file mode 100644 index 640217e794..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidationContext.java +++ /dev/null @@ -1,860 +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.engine; - -import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; - -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Executable; -import java.time.Duration; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import javax.validation.ClockProvider; -import javax.validation.ConstraintValidatorFactory; -import javax.validation.ConstraintViolation; -import javax.validation.MessageInterpolator; -import javax.validation.ParameterNameProvider; -import javax.validation.Path; -import javax.validation.TraversableResolver; -import javax.validation.ValidationException; -import javax.validation.Validator; -import javax.validation.metadata.ConstraintDescriptor; - -import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; -import org.hibernate.validator.internal.engine.ValidatorFactoryImpl.ValidatorFactoryScopedContext; -import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorContextImpl; -import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; -import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintViolationCreationContext; -import org.hibernate.validator.internal.engine.path.PathImpl; -import org.hibernate.validator.internal.metadata.BeanMetaDataManager; -import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; -import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData; -import org.hibernate.validator.internal.metadata.core.MetaConstraint; -import org.hibernate.validator.internal.metadata.facets.Validatable; -import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; -import org.hibernate.validator.internal.util.logging.Log; -import org.hibernate.validator.internal.util.logging.LoggerFactory; -import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; - -/** - * Context object keeping track of all required data for a validation call. - * - * We use this object to collect all failing constraints, but also to have access to resources like - * constraint validator factory, message interpolator, traversable resolver, etc. - * - * @author Hardy Ferentschik - * @author Emmanuel Bernard - * @author Gunnar Morling - * @author Guillaume Smet - */ -public class ValidationContext { - - private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - - /** - * The current validation operation (e.g. bean validation, parameter validation). - */ - private final ValidationOperation validationOperation; - - /** - * Caches and manages life cycle of constraint validator instances. - */ - private final ConstraintValidatorManager constraintValidatorManager; - - /** - * The root bean of the validation. - */ - private final T rootBean; - - /** - * The root bean class of the validation. - */ - private final Class rootBeanClass; - - /** - * The metadata of the root bean. - */ - private final BeanMetaData rootBeanMetaData; - - /** - * The method of the current validation call in case of executable validation. - */ - private final Executable executable; - - /** - * The validated parameters in case of executable parameter validation. - */ - private final Object[] executableParameters; - - /** - * The validated return value in case of executable return value validation. - */ - private final Object executableReturnValue; - - /** - * The metadata of the Executable. Will be non empty if we are in the case of method validation and the method is constrained. - */ - private final Optional executableMetaData; - - /** - * The set of already processed meta constraints per bean - path ({@link BeanPathMetaConstraintProcessedUnit}). - */ - private final Set processedPathUnits; - - /** - * The set of already processed groups per bean ({@link BeanGroupProcessedUnit}). - */ - private final Set processedGroupUnits; - - /** - * Maps an object to a list of paths in which it has been validated. The objects are the bean instances. - */ - private final Map> processedPathsPerBean; - - /** - * Contains all failing constraints so far. - */ - private final Set> failingConstraintViolations; - - /** - * The constraint factory which should be used in this context. - */ - private final ConstraintValidatorFactory constraintValidatorFactory; - - /** - * Context containing all {@link Validator} level helpers and configuration properties. - */ - private final ValidatorScopedContext validatorScopedContext; - - /** - * Allows a JPA provider to decide whether a property should be validated. - */ - private final TraversableResolver traversableResolver; - - /** - * The constraint validator initialization context. - */ - private final HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext; - - /** - * Indicates if the tracking of already validated bean should be disabled. - */ - private final boolean disableAlreadyValidatedBeanTracking; - - /** - * The name of the validated (leaf) property in case of a validateProperty()/validateValue() call. - */ - private String validatedProperty; - - private ValidationContext(ValidationOperation validationOperation, - ConstraintValidatorManager constraintValidatorManager, - ConstraintValidatorFactory constraintValidatorFactory, - ValidatorScopedContext validatorScopedContext, - TraversableResolver traversableResolver, - HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext, - T rootBean, - Class rootBeanClass, - BeanMetaData rootBeanMetaData, - Executable executable, - Object[] executableParameters, - Object executableReturnValue, - Optional executableMetaData) { - this.validationOperation = validationOperation; - - this.constraintValidatorManager = constraintValidatorManager; - this.validatorScopedContext = validatorScopedContext; - this.constraintValidatorFactory = constraintValidatorFactory; - this.traversableResolver = traversableResolver; - this.constraintValidatorInitializationContext = constraintValidatorInitializationContext; - - this.rootBean = rootBean; - this.rootBeanClass = rootBeanClass; - this.rootBeanMetaData = rootBeanMetaData; - this.executable = executable; - this.executableParameters = executableParameters; - this.executableReturnValue = executableReturnValue; - - this.processedGroupUnits = new HashSet<>(); - this.processedPathUnits = new HashSet<>(); - this.processedPathsPerBean = new IdentityHashMap<>(); - this.failingConstraintViolations = newHashSet(); - - this.executableMetaData = executableMetaData; - - this.disableAlreadyValidatedBeanTracking = buildDisableAlreadyValidatedBeanTracking( validationOperation, rootBeanMetaData, executableMetaData ); - } - - public static ValidationContextBuilder getValidationContextBuilder( - BeanMetaDataManager beanMetaDataManager, - ConstraintValidatorManager constraintValidatorManager, - ConstraintValidatorFactory constraintValidatorFactory, - ValidatorScopedContext validatorScopedContext, - TraversableResolver traversableResolver, - HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext) { - - return new ValidationContextBuilder( - beanMetaDataManager, - constraintValidatorManager, - constraintValidatorFactory, - validatorScopedContext, - traversableResolver, - constraintValidatorInitializationContext - ); - } - - public T getRootBean() { - return rootBean; - } - - public Class getRootBeanClass() { - return rootBeanClass; - } - - public BeanMetaData getRootBeanMetaData() { - return rootBeanMetaData; - } - - public Executable getExecutable() { - return executable; - } - - public Optional getExecutableMetaData() { - return executableMetaData; - } - - public TraversableResolver getTraversableResolver() { - return traversableResolver; - } - - public boolean isFailFastModeEnabled() { - return validatorScopedContext.isFailFast(); - } - - public ConstraintValidatorManager getConstraintValidatorManager() { - return constraintValidatorManager; - } - - /** - * Returns a list with the current executable's parameter names as retrieved - * from the current {@link ParameterNameProvider}. - * - * @return The current executable's parameter names,if this context was - * created for parameter validation, {@code null} otherwise. - */ - public List getParameterNames() { - if ( !ValidationOperation.PARAMETER_VALIDATION.equals( validationOperation ) ) { - return null; - } - - return validatorScopedContext.getParameterNameProvider().getParameterNames( executable ); - } - - public ClockProvider getClockProvider() { - return validatorScopedContext.getClockProvider(); - } - - public Object getConstraintValidatorPayload() { - return validatorScopedContext.getConstraintValidatorPayload(); - } - - public HibernateConstraintValidatorInitializationContext getConstraintValidatorInitializationContext() { - return constraintValidatorInitializationContext; - } - - public Set> createConstraintViolations(ValueContext localContext, - ConstraintValidatorContextImpl constraintValidatorContext) { - - return constraintValidatorContext.getConstraintViolationCreationContexts().stream() - .map( c -> createConstraintViolation( localContext, c, constraintValidatorContext.getConstraintDescriptor() ) ) - .collect( Collectors.toSet() ); - } - - public ConstraintValidatorFactory getConstraintValidatorFactory() { - return constraintValidatorFactory; - } - - public boolean isBeanAlreadyValidated(Object value, Class group, PathImpl path) { - if ( disableAlreadyValidatedBeanTracking ) { - return false; - } - - boolean alreadyValidated; - alreadyValidated = isAlreadyValidatedForCurrentGroup( value, group ); - - if ( alreadyValidated ) { - alreadyValidated = isAlreadyValidatedForPath( value, path ); - } - - return alreadyValidated; - } - - public void markCurrentBeanAsProcessed(ValueContext valueContext) { - if ( disableAlreadyValidatedBeanTracking ) { - return; - } - - markCurrentBeanAsProcessedForCurrentGroup( valueContext.getCurrentBean(), valueContext.getCurrentGroup() ); - markCurrentBeanAsProcessedForCurrentPath( valueContext.getCurrentBean(), valueContext.getPropertyPath() ); - } - - public void addConstraintFailures(Set> failingConstraintViolations) { - this.failingConstraintViolations.addAll( failingConstraintViolations ); - } - - public Set> getFailingConstraints() { - return failingConstraintViolations; - } - - - public ConstraintViolation createConstraintViolation(ValueContext localContext, ConstraintViolationCreationContext constraintViolationCreationContext, ConstraintDescriptor descriptor) { - String messageTemplate = constraintViolationCreationContext.getMessage(); - String interpolatedMessage = interpolate( - messageTemplate, - localContext.getCurrentValidatedValue(), - descriptor, - constraintViolationCreationContext.getMessageParameters(), - constraintViolationCreationContext.getExpressionVariables() - ); - // at this point we make a copy of the path to avoid side effects - Path path = PathImpl.createCopy( constraintViolationCreationContext.getPath() ); - Object dynamicPayload = constraintViolationCreationContext.getDynamicPayload(); - - switch ( validationOperation ) { - case PARAMETER_VALIDATION: - return ConstraintViolationImpl.forParameterValidation( - messageTemplate, - constraintViolationCreationContext.getMessageParameters(), - constraintViolationCreationContext.getExpressionVariables(), - interpolatedMessage, - getRootBeanClass(), - getRootBean(), - localContext.getCurrentBean(), - localContext.getCurrentValidatedValue(), - path, - descriptor, - localContext.getElementType(), - executableParameters, - dynamicPayload - ); - case RETURN_VALUE_VALIDATION: - return ConstraintViolationImpl.forReturnValueValidation( - messageTemplate, - constraintViolationCreationContext.getMessageParameters(), - constraintViolationCreationContext.getExpressionVariables(), - interpolatedMessage, - getRootBeanClass(), - getRootBean(), - localContext.getCurrentBean(), - localContext.getCurrentValidatedValue(), - path, - descriptor, - localContext.getElementType(), - executableReturnValue, - dynamicPayload - ); - default: - return ConstraintViolationImpl.forBeanValidation( - messageTemplate, - constraintViolationCreationContext.getMessageParameters(), - constraintViolationCreationContext.getExpressionVariables(), - interpolatedMessage, - getRootBeanClass(), - getRootBean(), - localContext.getCurrentBean(), - localContext.getCurrentValidatedValue(), - path, - descriptor, - localContext.getElementType(), - dynamicPayload - ); - } - } - - public boolean hasMetaConstraintBeenProcessed(Object bean, Path path, MetaConstraint metaConstraint) { - // this is only useful if the constraint is defined for more than 1 group as in the case it's only - // defined for one group, there is no chance it's going to be called twice. - if ( metaConstraint.isDefinedForOneGroupOnly() ) { - return false; - } - - return processedPathUnits.contains( new BeanPathMetaConstraintProcessedUnit( bean, path, metaConstraint ) ); - } - - public void markConstraintProcessed(Object bean, Path path, MetaConstraint metaConstraint) { - // this is only useful if the constraint is defined for more than 1 group as in the case it's only - // defined for one group, there is no chance it's going to be called twice. - if ( metaConstraint.isDefinedForOneGroupOnly() ) { - return; - } - - processedPathUnits.add( new BeanPathMetaConstraintProcessedUnit( bean, path, metaConstraint ) ); - } - - public String getValidatedProperty() { - return validatedProperty; - } - - public void setValidatedProperty(String validatedProperty) { - this.validatedProperty = validatedProperty; - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder(); - sb.append( "ValidationContext" ); - sb.append( "{rootBean=" ).append( rootBean ); - sb.append( '}' ); - return sb.toString(); - } - - private static boolean buildDisableAlreadyValidatedBeanTracking(ValidationOperation validationOperation, BeanMetaData rootBeanMetaData, - Optional executableMetaData) { - Validatable validatable; - switch ( validationOperation ) { - case BEAN_VALIDATION: - case PROPERTY_VALIDATION: - case VALUE_VALIDATION: - // note that in the case of property and value validation, we are considering the root bean, whereas we - // could consider the bean of the property or the value. We don't really have the info here though so it - // will do for now. - validatable = rootBeanMetaData; - break; - case PARAMETER_VALIDATION: - if ( !executableMetaData.isPresent() ) { - // the method is unconstrained so there's no need to worry about the tracking - return false; - } - validatable = executableMetaData.get().getValidatableParametersMetaData(); - break; - case RETURN_VALUE_VALIDATION: - if ( !executableMetaData.isPresent() ) { - // the method is unconstrained so there's no need to worry about the tracking - return false; - } - validatable = executableMetaData.get().getReturnValueMetaData(); - break; - default: - return false; - } - - return !validatable.hasCascadables(); - } - - private String interpolate(String messageTemplate, - Object validatedValue, - ConstraintDescriptor descriptor, - Map messageParameters, - Map expressionVariables) { - MessageInterpolatorContext context = new MessageInterpolatorContext( - descriptor, - validatedValue, - getRootBeanClass(), - messageParameters, - expressionVariables - ); - - try { - return validatorScopedContext.getMessageInterpolator().interpolate( - messageTemplate, - context - ); - } - catch (ValidationException ve) { - throw ve; - } - catch (Exception e) { - throw LOG.getExceptionOccurredDuringMessageInterpolationException( e ); - } - } - - private boolean isAlreadyValidatedForPath(Object value, PathImpl path) { - Set pathSet = processedPathsPerBean.get( value ); - if ( pathSet == null ) { - return false; - } - - for ( PathImpl p : pathSet ) { - if ( path.isRootPath() || p.isRootPath() || isSubPathOf( path, p ) || isSubPathOf( p, path ) ) { - return true; - } - } - - return false; - } - - private boolean isSubPathOf(Path p1, Path p2) { - Iterator p1Iter = p1.iterator(); - Iterator p2Iter = p2.iterator(); - while ( p1Iter.hasNext() ) { - Path.Node p1Node = p1Iter.next(); - if ( !p2Iter.hasNext() ) { - return false; - } - Path.Node p2Node = p2Iter.next(); - if ( !p1Node.equals( p2Node ) ) { - return false; - } - } - return true; - } - - private boolean isAlreadyValidatedForCurrentGroup(Object value, Class group) { - return processedGroupUnits.contains( new BeanGroupProcessedUnit( value, group ) ); - } - - private void markCurrentBeanAsProcessedForCurrentPath(Object bean, PathImpl path) { - // HV-1031 The path object is mutated as we traverse the object tree, hence copy it before saving it - processedPathsPerBean.computeIfAbsent( bean, b -> new HashSet<>() ) - .add( PathImpl.createCopy( path ) ); - } - - private void markCurrentBeanAsProcessedForCurrentGroup(Object bean, Class group) { - processedGroupUnits.add( new BeanGroupProcessedUnit( bean, group ) ); - } - - /** - * Builder for creating {@link ValidationContext}s suited for the different kinds of validation. - * - * @author Gunnar Morling - */ - public static class ValidationContextBuilder { - private final BeanMetaDataManager beanMetaDataManager; - private final ConstraintValidatorManager constraintValidatorManager; - private final ConstraintValidatorFactory constraintValidatorFactory; - private final TraversableResolver traversableResolver; - private final HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext; - private final ValidatorScopedContext validatorScopedContext; - - private ValidationContextBuilder( - BeanMetaDataManager beanMetaDataManager, - ConstraintValidatorManager constraintValidatorManager, - ConstraintValidatorFactory constraintValidatorFactory, - ValidatorScopedContext validatorScopedContext, - TraversableResolver traversableResolver, - HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext) { - this.beanMetaDataManager = beanMetaDataManager; - this.constraintValidatorManager = constraintValidatorManager; - this.constraintValidatorFactory = constraintValidatorFactory; - this.traversableResolver = traversableResolver; - this.constraintValidatorInitializationContext = constraintValidatorInitializationContext; - this.validatorScopedContext = validatorScopedContext; - } - - public ValidationContext forValidate(T rootBean) { - @SuppressWarnings("unchecked") - Class rootBeanClass = (Class) rootBean.getClass(); - return new ValidationContext<>( - ValidationOperation.BEAN_VALIDATION, - constraintValidatorManager, - constraintValidatorFactory, - validatorScopedContext, - traversableResolver, - constraintValidatorInitializationContext, - rootBean, - rootBeanClass, - beanMetaDataManager.getBeanMetaData( rootBeanClass ), - null, //executable - null, //executable parameters - null, //executable return value - null //executable metadata - ); - } - - public ValidationContext forValidateProperty(T rootBean) { - @SuppressWarnings("unchecked") - Class rootBeanClass = (Class) rootBean.getClass(); - return new ValidationContext<>( - ValidationOperation.PROPERTY_VALIDATION, - constraintValidatorManager, - constraintValidatorFactory, - validatorScopedContext, - traversableResolver, - constraintValidatorInitializationContext, - rootBean, - rootBeanClass, - beanMetaDataManager.getBeanMetaData( rootBeanClass ), - null, //executable - null, //executable parameters - null, //executable return value - null //executable metadata - ); - } - - public ValidationContext forValidateValue(Class rootBeanClass) { - return new ValidationContext<>( - ValidationOperation.VALUE_VALIDATION, - constraintValidatorManager, - constraintValidatorFactory, - validatorScopedContext, - traversableResolver, - constraintValidatorInitializationContext, - null, //root bean - rootBeanClass, - beanMetaDataManager.getBeanMetaData( rootBeanClass ), - null, //executable - null, //executable parameters - null, //executable return value - null //executable metadata - ); - } - - public ValidationContext forValidateParameters( - ExecutableParameterNameProvider parameterNameProvider, - T rootBean, - Executable executable, - Object[] executableParameters) { - @SuppressWarnings("unchecked") - Class rootBeanClass = rootBean != null ? (Class) rootBean.getClass() : (Class) executable.getDeclaringClass(); - BeanMetaData rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass ); - - return new ValidationContext<>( - ValidationOperation.PARAMETER_VALIDATION, - constraintValidatorManager, - constraintValidatorFactory, - validatorScopedContext, - traversableResolver, - constraintValidatorInitializationContext, - rootBean, - rootBeanClass, - rootBeanMetaData, - executable, - executableParameters, - null, //executable return value - rootBeanMetaData.getMetaDataFor( executable ) - ); - } - - public ValidationContext forValidateReturnValue( - T rootBean, - Executable executable, - Object executableReturnValue) { - @SuppressWarnings("unchecked") - Class rootBeanClass = rootBean != null ? (Class) rootBean.getClass() : (Class) executable.getDeclaringClass(); - BeanMetaData rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass ); - return new ValidationContext<>( - ValidationOperation.RETURN_VALUE_VALIDATION, - constraintValidatorManager, - constraintValidatorFactory, - validatorScopedContext, - traversableResolver, - constraintValidatorInitializationContext, - rootBean, - rootBeanClass, - beanMetaDataManager.getBeanMetaData( rootBeanClass ), - executable, - null, //executable parameters - executableReturnValue, - rootBeanMetaData.getMetaDataFor( executable ) - ); - } - } - - private static final class BeanGroupProcessedUnit { - - // these fields are final but we don't mark them as final as an optimization - private Object bean; - private Class group; - private int hashCode; - - private BeanGroupProcessedUnit(Object bean, Class group) { - this.bean = bean; - this.group = group; - this.hashCode = createHashCode(); - } - - @Override - public boolean equals(Object o) { - if ( this == o ) { - return true; - } - - // No need to check if the class matches because of how this class is used in the set. - BeanGroupProcessedUnit that = (BeanGroupProcessedUnit) o; - - if ( bean != that.bean ) { // instance equality - return false; - } - if ( !group.equals( that.group ) ) { - return false; - } - - return true; - } - - @Override - public int hashCode() { - return hashCode; - } - - private int createHashCode() { - int result = System.identityHashCode( bean ); - result = 31 * result + group.hashCode(); - return result; - } - } - - private static final class BeanPathMetaConstraintProcessedUnit { - - // these fields are final but we don't mark them as final as an optimization - private Object bean; - private Path path; - private MetaConstraint metaConstraint; - private int hashCode; - - private BeanPathMetaConstraintProcessedUnit(Object bean, Path path, MetaConstraint metaConstraint) { - this.bean = bean; - this.path = path; - this.metaConstraint = metaConstraint; - this.hashCode = createHashCode(); - } - - @Override - public boolean equals(Object o) { - if ( this == o ) { - return true; - } - - // No need to check if the class matches because of how this class is used in the set. - BeanPathMetaConstraintProcessedUnit that = (BeanPathMetaConstraintProcessedUnit) o; - - if ( bean != that.bean ) { // instance equality - return false; - } - if ( metaConstraint != that.metaConstraint ) { - return false; - } - if ( !path.equals( that.path ) ) { - return false; - } - - return true; - } - - @Override - public int hashCode() { - return hashCode; - } - - private int createHashCode() { - int result = System.identityHashCode( bean ); - result = 31 * result + path.hashCode(); - result = 31 * result + System.identityHashCode( metaConstraint ); - return result; - } - } - - /** - * Context object storing the {@link Validator} level helper and configuration properties. - *

- * There should be only one per {@code Validator} instance. - */ - static class ValidatorScopedContext { - - /** - * The message interpolator. - */ - private final MessageInterpolator messageInterpolator; - - /** - * The parameter name provider. - */ - private final ExecutableParameterNameProvider parameterNameProvider; - - /** - * Provider for the current time when validating {@code @Future} or {@code @Past} - */ - private final ClockProvider clockProvider; - - /** - * Defines the temporal validation tolerance i.e. the allowed margin of error when comparing date/time in temporal - * constraints. - */ - private final Duration temporalValidationTolerance; - - /** - * Used to get the {@code ScriptEvaluatorFactory} when validating {@code @ScriptAssert} and - * {@code @ParameterScriptAssert} constraints. - */ - private final ScriptEvaluatorFactory scriptEvaluatorFactory; - - /** - * Hibernate Validator specific flag to abort validation on first constraint violation. - */ - private final boolean failFast; - - /** - * Hibernate Validator specific flag to disable the {@code TraversableResolver} result cache. - */ - private final boolean traversableResolverResultCacheEnabled; - - /** - * Hibernate Validator specific payload passed to the constraint validators. - */ - private final Object constraintValidatorPayload; - - ValidatorScopedContext(ValidatorFactoryScopedContext validatorFactoryScopedContext) { - this.messageInterpolator = validatorFactoryScopedContext.getMessageInterpolator(); - this.parameterNameProvider = validatorFactoryScopedContext.getParameterNameProvider(); - this.clockProvider = validatorFactoryScopedContext.getClockProvider(); - this.temporalValidationTolerance = validatorFactoryScopedContext.getTemporalValidationTolerance(); - this.scriptEvaluatorFactory = validatorFactoryScopedContext.getScriptEvaluatorFactory(); - this.failFast = validatorFactoryScopedContext.isFailFast(); - this.traversableResolverResultCacheEnabled = validatorFactoryScopedContext.isTraversableResolverResultCacheEnabled(); - this.constraintValidatorPayload = validatorFactoryScopedContext.getConstraintValidatorPayload(); - } - - public MessageInterpolator getMessageInterpolator() { - return this.messageInterpolator; - } - - public ExecutableParameterNameProvider getParameterNameProvider() { - return this.parameterNameProvider; - } - - public ClockProvider getClockProvider() { - return this.clockProvider; - } - - public Duration getTemporalValidationTolerance() { - return this.temporalValidationTolerance; - } - - public ScriptEvaluatorFactory getScriptEvaluatorFactory() { - return this.scriptEvaluatorFactory; - } - - public boolean isFailFast() { - return this.failFast; - } - - public boolean isTraversableResolverResultCacheEnabled() { - return this.traversableResolverResultCacheEnabled; - } - - public Object getConstraintValidatorPayload() { - return this.constraintValidatorPayload; - } - } - - /** - * The different validation operations that can occur. - */ - private enum ValidationOperation { - BEAN_VALIDATION, - PROPERTY_VALIDATION, - VALUE_VALIDATION, - PARAMETER_VALIDATION, - RETURN_VALUE_VALIDATION - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorContextImpl.java index 38b4b8b405..cee1fd75f4 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorContextImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorContextImpl.java @@ -20,7 +20,6 @@ import javax.validation.valueextraction.ValueExtractor; import org.hibernate.validator.HibernateValidatorContext; -import org.hibernate.validator.internal.engine.ValidatorFactoryImpl.ValidatorFactoryScopedContext; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.util.logging.Log; @@ -41,7 +40,7 @@ public class ValidatorContextImpl implements HibernateValidatorContext { private ConstraintValidatorFactory constraintValidatorFactory; private final ValidatorFactoryScopedContext.Builder validatorFactoryScopedContextBuilder; - private final ValueExtractorManager valueExtractorManager; + private final ConstraintCreationContext constraintCreationContext; private final MethodValidationConfiguration.Builder methodValidationConfigurationBuilder; private final Map valueExtractorDescriptors; @@ -49,8 +48,8 @@ public ValidatorContextImpl(ValidatorFactoryImpl validatorFactory) { this.validatorFactoryScopedContextBuilder = new ValidatorFactoryScopedContext.Builder( validatorFactory.getValidatorFactoryScopedContext() ); this.validatorFactory = validatorFactory; this.constraintValidatorFactory = validatorFactory.getConstraintValidatorFactory(); + this.constraintCreationContext = validatorFactory.getConstraintCreationContext(); this.methodValidationConfigurationBuilder = new MethodValidationConfiguration.Builder( validatorFactory.getMethodValidationConfiguration() ); - this.valueExtractorManager = validatorFactory.getValueExtractorManager(); this.valueExtractorDescriptors = new HashMap<>(); } @@ -147,9 +146,12 @@ public HibernateValidatorContext constraintValidatorPayload(Object dynamicPayloa public Validator getValidator() { return validatorFactory.createValidator( constraintValidatorFactory, - valueExtractorDescriptors.isEmpty() ? valueExtractorManager : new ValueExtractorManager( valueExtractorManager, valueExtractorDescriptors ), + valueExtractorDescriptors.isEmpty() + ? constraintCreationContext + : new ConstraintCreationContext( constraintCreationContext.getConstraintHelper(), + constraintCreationContext.getConstraintValidatorManager(), constraintCreationContext.getTypeResolutionHelper(), + new ValueExtractorManager( constraintCreationContext.getValueExtractorManager(), valueExtractorDescriptors ) ), validatorFactoryScopedContextBuilder.build(), - methodValidationConfigurationBuilder.build() - ); + methodValidationConfigurationBuilder.build() ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryConfigurationHelper.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryConfigurationHelper.java new file mode 100644 index 0000000000..635d6a61bb --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryConfigurationHelper.java @@ -0,0 +1,385 @@ +/* + * 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.engine; + +import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; +import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; + +import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandles; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.time.Duration; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.validation.spi.ConfigurationState; + +import org.hibernate.validator.HibernateValidatorConfiguration; +import org.hibernate.validator.cfg.ConstraintMapping; +import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping; +import org.hibernate.validator.internal.engine.constraintdefinition.ConstraintDefinitionContribution; +import org.hibernate.validator.internal.engine.scripting.DefaultScriptEvaluatorFactory; +import org.hibernate.validator.internal.metadata.DefaultBeanMetaDataClassNormalizer; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; +import org.hibernate.validator.internal.util.StringHelper; +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.privilegedactions.GetClassLoader; +import org.hibernate.validator.internal.util.privilegedactions.LoadClass; +import org.hibernate.validator.internal.util.privilegedactions.NewInstance; +import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer; +import org.hibernate.validator.spi.cfg.ConstraintMappingContributor; +import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider; +import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy; +import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; + +final class ValidatorFactoryConfigurationHelper { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private ValidatorFactoryConfigurationHelper() { + } + + static ClassLoader determineExternalClassLoader(ConfigurationState configurationState) { + return ( configurationState instanceof AbstractConfigurationImpl ) + ? ( (AbstractConfigurationImpl) configurationState ).getExternalClassLoader() + : null; + } + + static Set determineConstraintMappings(TypeResolutionHelper typeResolutionHelper, + ConfigurationState configurationState, JavaBeanHelper javaBeanHelper, ClassLoader externalClassLoader) { + Set constraintMappings = newHashSet(); + + if ( configurationState instanceof AbstractConfigurationImpl ) { + AbstractConfigurationImpl hibernateConfiguration = (AbstractConfigurationImpl) configurationState; + + // programmatic config + /* We add these first so that constraint mapping created through DefaultConstraintMappingBuilder will take + * these programmatically defined mappings into account when checking for constraint definition uniqueness + */ + constraintMappings.addAll( hibernateConfiguration.getProgrammaticMappings() ); + + // service loader based config + ConstraintMappingContributor serviceLoaderBasedContributor = new ServiceLoaderBasedConstraintMappingContributor( + typeResolutionHelper, + externalClassLoader != null ? externalClassLoader : run( GetClassLoader.fromContext() ) ); + DefaultConstraintMappingBuilder builder = new DefaultConstraintMappingBuilder( javaBeanHelper, constraintMappings ); + serviceLoaderBasedContributor.createConstraintMappings( builder ); + } + + // XML-defined constraint mapping contributors + List contributors = determinePropertyConfiguredConstraintMappingContributors( configurationState.getProperties(), + externalClassLoader ); + + for ( ConstraintMappingContributor contributor : contributors ) { + DefaultConstraintMappingBuilder builder = new DefaultConstraintMappingBuilder( javaBeanHelper, constraintMappings ); + contributor.createConstraintMappings( builder ); + } + + return constraintMappings; + } + + static boolean checkPropertiesForBoolean(Map properties, String propertyKey, boolean programmaticValue) { + boolean value = programmaticValue; + String propertyStringValue = properties.get( propertyKey ); + if ( propertyStringValue != null ) { + value = Boolean.valueOf( propertyStringValue ); + } + return value; + } + + /** + * Returns a list with {@link ConstraintMappingContributor}s configured via the + * {@link HibernateValidatorConfiguration#CONSTRAINT_MAPPING_CONTRIBUTORS} property. + * + * Also takes into account the deprecated {@link HibernateValidatorConfiguration#CONSTRAINT_MAPPING_CONTRIBUTOR} + * property. + * + * @param properties the properties used to bootstrap the factory + * + * @return a list with property-configured {@link ConstraintMappingContributor}s; May be empty but never {@code null} + */ + static List determinePropertyConfiguredConstraintMappingContributors( + Map properties, ClassLoader externalClassLoader) { + @SuppressWarnings("deprecation") + String deprecatedPropertyValue = properties.get( HibernateValidatorConfiguration.CONSTRAINT_MAPPING_CONTRIBUTOR ); + String propertyValue = properties.get( HibernateValidatorConfiguration.CONSTRAINT_MAPPING_CONTRIBUTORS ); + + if ( StringHelper.isNullOrEmptyString( deprecatedPropertyValue ) && StringHelper.isNullOrEmptyString( propertyValue ) ) { + return Collections.emptyList(); + } + + StringBuilder assembledPropertyValue = new StringBuilder(); + if ( !StringHelper.isNullOrEmptyString( deprecatedPropertyValue ) ) { + assembledPropertyValue.append( deprecatedPropertyValue ); + } + if ( !StringHelper.isNullOrEmptyString( propertyValue ) ) { + if ( assembledPropertyValue.length() > 0 ) { + assembledPropertyValue.append( "," ); + } + assembledPropertyValue.append( propertyValue ); + } + + String[] contributorNames = assembledPropertyValue.toString().split( "," ); + List contributors = newArrayList( contributorNames.length ); + + for ( String contributorName : contributorNames ) { + @SuppressWarnings("unchecked") + Class contributorType = (Class) run( + LoadClass.action( contributorName, externalClassLoader ) ); + contributors.add( run( NewInstance.action( contributorType, "constraint mapping contributor class" ) ) ); + } + + return contributors; + } + + static boolean determineAllowParallelMethodsDefineParameterConstraints(AbstractConfigurationImpl hibernateSpecificConfig, Map properties) { + return checkPropertiesForBoolean( + properties, + HibernateValidatorConfiguration.ALLOW_PARALLEL_METHODS_DEFINE_PARAMETER_CONSTRAINTS, + hibernateSpecificConfig != null ? hibernateSpecificConfig.getMethodValidationConfiguration().isAllowParallelMethodsDefineParameterConstraints() : false + ); + } + + static boolean determineAllowMultipleCascadedValidationOnReturnValues(AbstractConfigurationImpl hibernateSpecificConfig, Map properties) { + return checkPropertiesForBoolean( + properties, + HibernateValidatorConfiguration.ALLOW_MULTIPLE_CASCADED_VALIDATION_ON_RESULT, + hibernateSpecificConfig != null ? hibernateSpecificConfig.getMethodValidationConfiguration().isAllowMultipleCascadedValidationOnReturnValues() : false + ); + } + + static boolean determineAllowOverridingMethodAlterParameterConstraint(AbstractConfigurationImpl hibernateSpecificConfig, Map properties) { + return checkPropertiesForBoolean( + properties, + HibernateValidatorConfiguration.ALLOW_PARAMETER_CONSTRAINT_OVERRIDE, + hibernateSpecificConfig != null ? hibernateSpecificConfig.getMethodValidationConfiguration().isAllowOverridingMethodAlterParameterConstraint() : false + ); + } + + static boolean determineTraversableResolverResultCacheEnabled(AbstractConfigurationImpl configuration, Map properties) { + return checkPropertiesForBoolean( + properties, + HibernateValidatorConfiguration.ENABLE_TRAVERSABLE_RESOLVER_RESULT_CACHE, + configuration != null ? configuration.isTraversableResolverResultCacheEnabled() : true + ); + } + + static boolean determineFailFast(AbstractConfigurationImpl configuration, Map properties) { + // check whether fail fast is programmatically enabled + boolean tmpFailFast = configuration != null ? configuration.getFailFast() : false; + + String propertyStringValue = properties.get( HibernateValidatorConfiguration.FAIL_FAST ); + if ( propertyStringValue != null ) { + boolean configurationValue = Boolean.valueOf( propertyStringValue ); + // throw an exception if the programmatic value is true and it overrides a false configured value + if ( tmpFailFast && !configurationValue ) { + throw LOG.getInconsistentFailFastConfigurationException(); + } + tmpFailFast = configurationValue; + } + + return tmpFailFast; + } + + static ScriptEvaluatorFactory determineScriptEvaluatorFactory(ConfigurationState configurationState, Map properties, + ClassLoader externalClassLoader) { + if ( configurationState instanceof AbstractConfigurationImpl ) { + AbstractConfigurationImpl hibernateSpecificConfig = (AbstractConfigurationImpl) configurationState; + if ( hibernateSpecificConfig.getScriptEvaluatorFactory() != null ) { + LOG.usingScriptEvaluatorFactory( hibernateSpecificConfig.getScriptEvaluatorFactory().getClass() ); + return hibernateSpecificConfig.getScriptEvaluatorFactory(); + } + } + + String scriptEvaluatorFactoryFqcn = properties.get( HibernateValidatorConfiguration.SCRIPT_EVALUATOR_FACTORY_CLASSNAME ); + if ( scriptEvaluatorFactoryFqcn != null ) { + try { + @SuppressWarnings("unchecked") + Class clazz = (Class) run( + LoadClass.action( scriptEvaluatorFactoryFqcn, externalClassLoader ) + ); + ScriptEvaluatorFactory scriptEvaluatorFactory = run( NewInstance.action( clazz, "script evaluator factory class" ) ); + LOG.usingScriptEvaluatorFactory( clazz ); + + return scriptEvaluatorFactory; + } + catch (Exception e) { + throw LOG.getUnableToInstantiateScriptEvaluatorFactoryClassException( scriptEvaluatorFactoryFqcn, e ); + } + } + + return new DefaultScriptEvaluatorFactory( externalClassLoader ); + } + + static Duration determineTemporalValidationTolerance(ConfigurationState configurationState, Map properties) { + if ( configurationState instanceof AbstractConfigurationImpl ) { + AbstractConfigurationImpl hibernateSpecificConfig = (AbstractConfigurationImpl) configurationState; + if ( hibernateSpecificConfig.getTemporalValidationTolerance() != null ) { + LOG.logTemporalValidationTolerance( hibernateSpecificConfig.getTemporalValidationTolerance() ); + return hibernateSpecificConfig.getTemporalValidationTolerance(); + } + } + String temporalValidationToleranceProperty = properties.get( HibernateValidatorConfiguration.TEMPORAL_VALIDATION_TOLERANCE ); + if ( temporalValidationToleranceProperty != null ) { + try { + Duration tolerance = Duration.ofMillis( Long.parseLong( temporalValidationToleranceProperty ) ).abs(); + LOG.logTemporalValidationTolerance( tolerance ); + return tolerance; + } + catch (Exception e) { + throw LOG.getUnableToParseTemporalValidationToleranceException( temporalValidationToleranceProperty, e ); + } + } + + return Duration.ZERO; + } + + static Object determineConstraintValidatorPayload(ConfigurationState configurationState) { + if ( configurationState instanceof AbstractConfigurationImpl ) { + AbstractConfigurationImpl hibernateSpecificConfig = (AbstractConfigurationImpl) configurationState; + if ( hibernateSpecificConfig.getConstraintValidatorPayload() != null ) { + LOG.logConstraintValidatorPayload( hibernateSpecificConfig.getConstraintValidatorPayload() ); + return hibernateSpecificConfig.getConstraintValidatorPayload(); + } + } + + return null; + } + + static GetterPropertySelectionStrategy determineGetterPropertySelectionStrategy(AbstractConfigurationImpl hibernateSpecificConfig, Map properties, + ClassLoader externalClassLoader) { + if ( hibernateSpecificConfig.getGetterPropertySelectionStrategy() != null ) { + LOG.usingGetterPropertySelectionStrategy( hibernateSpecificConfig.getGetterPropertySelectionStrategy().getClass() ); + return hibernateSpecificConfig.getGetterPropertySelectionStrategy(); + } + + String getterPropertySelectionStrategyFqcn = properties.get( HibernateValidatorConfiguration.GETTER_PROPERTY_SELECTION_STRATEGY_CLASSNAME ); + if ( getterPropertySelectionStrategyFqcn != null ) { + try { + @SuppressWarnings("unchecked") + Class clazz = (Class) run( + LoadClass.action( getterPropertySelectionStrategyFqcn, externalClassLoader ) + ); + GetterPropertySelectionStrategy getterPropertySelectionStrategy = run( NewInstance.action( clazz, "getter property selection strategy class" ) ); + LOG.usingGetterPropertySelectionStrategy( clazz ); + + return getterPropertySelectionStrategy; + } + catch (Exception e) { + throw LOG.getUnableToInstantiateGetterPropertySelectionStrategyClassException( getterPropertySelectionStrategyFqcn, e ); + } + } + + return new DefaultGetterPropertySelectionStrategy(); + } + + static BeanMetaDataClassNormalizer determineBeanMetaDataClassNormalizer(PredefinedScopeConfigurationImpl hibernateSpecificConfig) { + if ( hibernateSpecificConfig.getBeanMetaDataClassNormalizer() != null ) { + return hibernateSpecificConfig.getBeanMetaDataClassNormalizer(); + } + + return new DefaultBeanMetaDataClassNormalizer(); + } + + static PropertyNodeNameProvider determinePropertyNodeNameProvider(AbstractConfigurationImpl hibernateSpecificConfig, Map properties, + ClassLoader externalClassLoader) { + if ( hibernateSpecificConfig.getPropertyNodeNameProvider() != null ) { + LOG.usingPropertyNodeNameProvider( hibernateSpecificConfig.getPropertyNodeNameProvider().getClass() ); + + return hibernateSpecificConfig.getPropertyNodeNameProvider(); + } + + String propertyNodeNameProviderFqcn = properties.get( HibernateValidatorConfiguration.PROPERTY_NODE_NAME_PROVIDER_CLASSNAME ); + if ( propertyNodeNameProviderFqcn != null ) { + try { + @SuppressWarnings("unchecked") + Class clazz = (Class) run( LoadClass.action( propertyNodeNameProviderFqcn, externalClassLoader ) ); + PropertyNodeNameProvider propertyNodeNameProvider = run( NewInstance.action( clazz, "property node name provider class" ) ); + LOG.usingPropertyNodeNameProvider( clazz ); + + return propertyNodeNameProvider; + } + catch (Exception e) { + throw LOG.getUnableToInstantiatePropertyNodeNameProviderClassException( propertyNodeNameProviderFqcn, e ); + } + } + + return new DefaultPropertyNodeNameProvider(); + } + + static void registerCustomConstraintValidators(Set constraintMappings, + ConstraintHelper constraintHelper) { + Set> definedConstraints = newHashSet(); + for ( DefaultConstraintMapping constraintMapping : constraintMappings ) { + for ( ConstraintDefinitionContribution contribution : constraintMapping.getConstraintDefinitionContributions() ) { + processConstraintDefinitionContribution( contribution, constraintHelper, definedConstraints ); + } + } + } + + static void processConstraintDefinitionContribution( + ConstraintDefinitionContribution constraintDefinitionContribution, ConstraintHelper constraintHelper, + Set> definedConstraints) { + Class constraintType = constraintDefinitionContribution.getConstraintType(); + if ( definedConstraints.contains( constraintType ) ) { + throw LOG.getConstraintHasAlreadyBeenConfiguredViaProgrammaticApiException( constraintType ); + } + definedConstraints.add( constraintType ); + constraintHelper.putValidatorDescriptors( + constraintType, + constraintDefinitionContribution.getValidatorDescriptors(), + constraintDefinitionContribution.includeExisting() + ); + } + + static void logValidatorFactoryScopedConfiguration(ValidatorFactoryScopedContext context) { + LOG.logValidatorFactoryScopedConfiguration( context.getMessageInterpolator().getClass(), "message interpolator" ); + LOG.logValidatorFactoryScopedConfiguration( context.getTraversableResolver().getClass(), "traversable resolver" ); + LOG.logValidatorFactoryScopedConfiguration( context.getParameterNameProvider().getClass(), "parameter name provider" ); + LOG.logValidatorFactoryScopedConfiguration( context.getClockProvider().getClass(), "clock provider" ); + LOG.logValidatorFactoryScopedConfiguration( context.getScriptEvaluatorFactory().getClass(), "script evaluator factory" ); + } + + /** + * 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 T run(PrivilegedAction action) { + return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); + } + + /** + * The one and only {@link ConstraintMappingContributor.ConstraintMappingBuilder} implementation. + */ + private static class DefaultConstraintMappingBuilder + implements ConstraintMappingContributor.ConstraintMappingBuilder { + + private final JavaBeanHelper javaBeanHelper; + private final Set mappings; + + public DefaultConstraintMappingBuilder(JavaBeanHelper javaBeanHelper, Set mappings) { + this.javaBeanHelper = javaBeanHelper; + this.mappings = mappings; + } + + @Override + public ConstraintMapping addConstraintMapping() { + DefaultConstraintMapping mapping = new DefaultConstraintMapping( javaBeanHelper ); + mappings.add( mapping ); + return mapping; + } + } +} 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 a50baef0fb..9fb2a7705b 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 @@ -6,13 +6,21 @@ */ package org.hibernate.validator.internal.engine; +import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineAllowMultipleCascadedValidationOnReturnValues; +import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineAllowOverridingMethodAlterParameterConstraint; +import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineAllowParallelMethodsDefineParameterConstraints; +import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineConstraintMappings; +import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineConstraintValidatorPayload; +import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineExternalClassLoader; +import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineFailFast; +import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineScriptEvaluatorFactory; +import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineTemporalValidationTolerance; +import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineTraversableResolverResultCacheEnabled; +import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.logValidatorFactoryScopedConfiguration; +import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.registerCustomConstraintValidators; import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; -import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; -import java.lang.annotation.Annotation; import java.lang.invoke.MethodHandles; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.time.Duration; import java.util.Collections; import java.util.List; @@ -30,36 +38,28 @@ import javax.validation.ValidatorFactory; import javax.validation.spi.ConfigurationState; -import org.hibernate.validator.HibernateValidatorConfiguration; import org.hibernate.validator.HibernateValidatorContext; import org.hibernate.validator.HibernateValidatorFactory; -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.engine.constraintdefinition.ConstraintDefinitionContribution; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; -import org.hibernate.validator.internal.engine.constraintvalidation.HibernateConstraintValidatorInitializationContextImpl; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManagerImpl; 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.BeanMetaDataManagerImpl; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; import org.hibernate.validator.internal.metadata.provider.ProgrammaticMetaDataProvider; import org.hibernate.validator.internal.metadata.provider.XmlMetaDataProvider; -import org.hibernate.validator.internal.util.Contracts; +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.StringHelper; 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.privilegedactions.GetClassLoader; -import org.hibernate.validator.internal.util.privilegedactions.LoadClass; -import org.hibernate.validator.internal.util.privilegedactions.NewInstance; import org.hibernate.validator.internal.util.stereotypes.Immutable; import org.hibernate.validator.internal.util.stereotypes.ThreadSafe; -import org.hibernate.validator.spi.cfg.ConstraintMappingContributor; +import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy; import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; /** @@ -83,11 +83,6 @@ public class ValidatorFactoryImpl implements HibernateValidatorFactory { */ private final ValidatorFactoryScopedContext validatorFactoryScopedContext; - /** - * The constraint validator manager for this factory. - */ - private final ConstraintValidatorManager constraintValidatorManager; - /** * Programmatic constraints passed via the Hibernate Validator specific API. Empty if there are * no programmatic constraints @@ -96,14 +91,9 @@ public class ValidatorFactoryImpl implements HibernateValidatorFactory { private final Set constraintMappings; /** - * Helper for dealing with built-in validators and determining custom constraint annotations. - */ - private final ConstraintHelper constraintHelper; - - /** - * Used for resolving type parameters. Thread-safe. + * The constraint creation context containing all the helpers necessary to the constraint creation. */ - private final TypeResolutionHelper typeResolutionHelper; + private final ConstraintCreationContext constraintCreationContext; /** * Used for discovering overridden methods. Thread-safe. @@ -128,55 +118,29 @@ public class ValidatorFactoryImpl implements HibernateValidatorFactory { * provider. See also HV-659. */ @ThreadSafe - private final ConcurrentMap beanMetaDataManagers; + private final ConcurrentMap beanMetaDataManagers = new ConcurrentHashMap<>(); - private final ValueExtractorManager valueExtractorManager; + private final JavaBeanHelper javaBeanHelper; private final ValidationOrderGenerator validationOrderGenerator; public ValidatorFactoryImpl(ConfigurationState configurationState) { - ClassLoader externalClassLoader = getExternalClassLoader( configurationState ); - - this.valueExtractorManager = new ValueExtractorManager( configurationState.getValueExtractors() ); - this.beanMetaDataManagers = new ConcurrentHashMap<>(); - this.constraintHelper = new ConstraintHelper(); - this.typeResolutionHelper = new TypeResolutionHelper(); - this.executableHelper = new ExecutableHelper( typeResolutionHelper ); + ClassLoader externalClassLoader = determineExternalClassLoader( configurationState ); ConfigurationImpl hibernateSpecificConfig = null; if ( configurationState instanceof ConfigurationImpl ) { hibernateSpecificConfig = (ConfigurationImpl) configurationState; } - // HV-302; don't load XmlMappingParser if not necessary - if ( configurationState.getMappingStreams().isEmpty() ) { - this.xmlMetaDataProvider = null; - } - else { - this.xmlMetaDataProvider = new XmlMetaDataProvider( - constraintHelper, typeResolutionHelper, valueExtractorManager, configurationState.getMappingStreams(), externalClassLoader - ); - } - - this.constraintMappings = Collections.unmodifiableSet( - getConstraintMappings( - typeResolutionHelper, - configurationState, - externalClassLoader - ) - ); - - registerCustomConstraintValidators( constraintMappings, constraintHelper ); - Map properties = configurationState.getProperties(); this.methodValidationConfiguration = new MethodValidationConfiguration.Builder() .allowOverridingMethodAlterParameterConstraint( - getAllowOverridingMethodAlterParameterConstraint( hibernateSpecificConfig, properties ) + determineAllowOverridingMethodAlterParameterConstraint( hibernateSpecificConfig, properties ) ).allowMultipleCascadedValidationOnReturnValues( - getAllowMultipleCascadedValidationOnReturnValues( hibernateSpecificConfig, properties ) + determineAllowMultipleCascadedValidationOnReturnValues( hibernateSpecificConfig, properties ) ).allowParallelMethodsDefineParameterConstraints( - getAllowParallelMethodsDefineParameterConstraints( hibernateSpecificConfig, properties ) + determineAllowParallelMethodsDefineParameterConstraints( hibernateSpecificConfig, properties ) ).build(); this.validatorFactoryScopedContext = new ValidatorFactoryScopedContext( @@ -184,67 +148,59 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) { configurationState.getTraversableResolver(), new ExecutableParameterNameProvider( configurationState.getParameterNameProvider() ), configurationState.getClockProvider(), - getTemporalValidationTolerance( configurationState, properties ), - getScriptEvaluatorFactory( configurationState, properties, externalClassLoader ), - getFailFast( hibernateSpecificConfig, properties ), - getTraversableResolverResultCacheEnabled( hibernateSpecificConfig, properties ), - getConstraintValidatorPayload( hibernateSpecificConfig ) + determineTemporalValidationTolerance( configurationState, properties ), + determineScriptEvaluatorFactory( configurationState, properties, externalClassLoader ), + determineFailFast( hibernateSpecificConfig, properties ), + determineTraversableResolverResultCacheEnabled( hibernateSpecificConfig, properties ), + determineConstraintValidatorPayload( hibernateSpecificConfig ) ); - this.constraintValidatorManager = new ConstraintValidatorManager( + ConstraintValidatorManager constraintValidatorManager = new ConstraintValidatorManagerImpl( configurationState.getConstraintValidatorFactory(), this.validatorFactoryScopedContext.getConstraintValidatorInitializationContext() ); this.validationOrderGenerator = new ValidationOrderGenerator(); - if ( LOG.isDebugEnabled() ) { - logValidatorFactoryScopedConfiguration( validatorFactoryScopedContext ); - } - } + ValueExtractorManager valueExtractorManager = new ValueExtractorManager( configurationState.getValueExtractors() ); + ConstraintHelper constraintHelper = new ConstraintHelper(); + TypeResolutionHelper typeResolutionHelper = new TypeResolutionHelper(); - private static ClassLoader getExternalClassLoader(ConfigurationState configurationState) { - return ( configurationState instanceof ConfigurationImpl ) ? ( (ConfigurationImpl) configurationState ).getExternalClassLoader() : null; - } + this.constraintCreationContext = new ConstraintCreationContext( constraintHelper, constraintValidatorManager, typeResolutionHelper, valueExtractorManager ); - private static Set getConstraintMappings(TypeResolutionHelper typeResolutionHelper, - ConfigurationState configurationState, ClassLoader externalClassLoader) { - Set constraintMappings = newHashSet(); + this.executableHelper = new ExecutableHelper( typeResolutionHelper ); + this.javaBeanHelper = new JavaBeanHelper( ValidatorFactoryConfigurationHelper.determineGetterPropertySelectionStrategy( hibernateSpecificConfig, properties, externalClassLoader ), + ValidatorFactoryConfigurationHelper.determinePropertyNodeNameProvider( hibernateSpecificConfig, properties, externalClassLoader ) ); - if ( configurationState instanceof ConfigurationImpl ) { - ConfigurationImpl hibernateConfiguration = (ConfigurationImpl) configurationState; - - // programmatic config - /* We add these first so that constraint mapping created through DefaultConstraintMappingBuilder will take - * these programmatically defined mappings into account when checking for constraint definition uniqueness - */ - constraintMappings.addAll( hibernateConfiguration.getProgrammaticMappings() ); - - // service loader based config - ConstraintMappingContributor serviceLoaderBasedContributor = new ServiceLoaderBasedConstraintMappingContributor( - typeResolutionHelper, - externalClassLoader != null ? externalClassLoader : run( GetClassLoader.fromContext() ) ); - DefaultConstraintMappingBuilder builder = new DefaultConstraintMappingBuilder( constraintMappings ); - serviceLoaderBasedContributor.createConstraintMappings( builder ); + // HV-302; don't load XmlMappingParser if not necessary + if ( configurationState.getMappingStreams().isEmpty() ) { + this.xmlMetaDataProvider = null; + } + else { + this.xmlMetaDataProvider = new XmlMetaDataProvider( constraintCreationContext, javaBeanHelper, configurationState.getMappingStreams(), externalClassLoader ); } - // XML-defined constraint mapping contributors - List contributors = getPropertyConfiguredConstraintMappingContributors( configurationState.getProperties(), - externalClassLoader ); + this.constraintMappings = Collections.unmodifiableSet( + determineConstraintMappings( + typeResolutionHelper, + configurationState, + javaBeanHelper, + externalClassLoader + ) + ); + + registerCustomConstraintValidators( constraintMappings, constraintHelper ); - for ( ConstraintMappingContributor contributor : contributors ) { - DefaultConstraintMappingBuilder builder = new DefaultConstraintMappingBuilder( constraintMappings ); - contributor.createConstraintMappings( builder ); + if ( LOG.isDebugEnabled() ) { + logValidatorFactoryScopedConfiguration( validatorFactoryScopedContext ); } - - return constraintMappings; } @Override public Validator getValidator() { return createValidator( - constraintValidatorManager.getDefaultConstraintValidatorFactory(), - valueExtractorManager, + constraintCreationContext.getConstraintValidatorManager().getDefaultConstraintValidatorFactory(), + constraintCreationContext, validatorFactoryScopedContext, methodValidationConfiguration ); @@ -262,7 +218,7 @@ public TraversableResolver getTraversableResolver() { @Override public ConstraintValidatorFactory getConstraintValidatorFactory() { - return constraintValidatorManager.getDefaultConstraintValidatorFactory(); + return constraintCreationContext.getConstraintValidatorManager().getDefaultConstraintValidatorFactory(); } @Override @@ -289,6 +245,11 @@ public Duration getTemporalValidationTolerance() { return validatorFactoryScopedContext.getTemporalValidationTolerance(); } + @Override + public GetterPropertySelectionStrategy getGetterPropertySelectionStrategy() { + return javaBeanHelper.getGetterPropertySelectionStrategy(); + } + public boolean isFailFast() { return validatorFactoryScopedContext.isFailFast(); } @@ -301,8 +262,8 @@ public boolean isTraversableResolverResultCacheEnabled() { return validatorFactoryScopedContext.isTraversableResolverResultCacheEnabled(); } - ValueExtractorManager getValueExtractorManager() { - return valueExtractorManager; + ConstraintCreationContext getConstraintCreationContext() { + return constraintCreationContext; } @Override @@ -321,13 +282,13 @@ public HibernateValidatorContext usingContext() { @Override public void close() { - constraintValidatorManager.clear(); - constraintHelper.clear(); + constraintCreationContext.getConstraintValidatorManager().clear(); + constraintCreationContext.getConstraintHelper().clear(); for ( BeanMetaDataManager beanMetaDataManager : beanMetaDataManagers.values() ) { beanMetaDataManager.clear(); } validatorFactoryScopedContext.getScriptEvaluatorFactory().clear(); - valueExtractorManager.clear(); + constraintCreationContext.getValueExtractorManager().clear(); } public ValidatorFactoryScopedContext getValidatorFactoryScopedContext() { @@ -335,35 +296,33 @@ public ValidatorFactoryScopedContext getValidatorFactoryScopedContext() { } Validator createValidator(ConstraintValidatorFactory constraintValidatorFactory, - ValueExtractorManager valueExtractorManager, + ConstraintCreationContext constraintCreationContext, ValidatorFactoryScopedContext validatorFactoryScopedContext, MethodValidationConfiguration methodValidationConfiguration) { - BeanMetaDataManager beanMetaDataManager = beanMetaDataManagers.computeIfAbsent( - new BeanMetaDataManagerKey( validatorFactoryScopedContext.getParameterNameProvider(), valueExtractorManager, methodValidationConfiguration ), - key -> new BeanMetaDataManager( - constraintHelper, + new BeanMetaDataManagerKey( validatorFactoryScopedContext.getParameterNameProvider(), constraintCreationContext.getValueExtractorManager(), methodValidationConfiguration ), + key -> new BeanMetaDataManagerImpl( + constraintCreationContext, executableHelper, - typeResolutionHelper, validatorFactoryScopedContext.getParameterNameProvider(), - valueExtractorManager, + javaBeanHelper, validationOrderGenerator, - buildDataProviders(), + buildMetaDataProviders(), methodValidationConfiguration ) - ); + ); return new ValidatorImpl( constraintValidatorFactory, beanMetaDataManager, - valueExtractorManager, - constraintValidatorManager, + constraintCreationContext.getValueExtractorManager(), + constraintCreationContext.getConstraintValidatorManager(), validationOrderGenerator, validatorFactoryScopedContext ); } - private List buildDataProviders() { + private List buildMetaDataProviders() { List metaDataProviders = newArrayList(); if ( xmlMetaDataProvider != null ) { metaDataProviders.add( xmlMetaDataProvider ); @@ -372,9 +331,7 @@ private List buildDataProviders() { if ( !constraintMappings.isEmpty() ) { metaDataProviders.add( new ProgrammaticMetaDataProvider( - constraintHelper, - typeResolutionHelper, - valueExtractorManager, + constraintCreationContext, constraintMappings ) ); @@ -382,236 +339,6 @@ private List buildDataProviders() { return metaDataProviders; } - private static boolean checkPropertiesForBoolean(Map properties, String propertyKey, boolean programmaticValue) { - boolean value = programmaticValue; - String propertyStringValue = properties.get( propertyKey ); - if ( propertyStringValue != null ) { - value = Boolean.valueOf( propertyStringValue ); - } - return value; - } - - /** - * Returns a list with {@link ConstraintMappingContributor}s configured via the - * {@link HibernateValidatorConfiguration#CONSTRAINT_MAPPING_CONTRIBUTORS} property. - * - * Also takes into account the deprecated {@link HibernateValidatorConfiguration#CONSTRAINT_MAPPING_CONTRIBUTOR} - * property. - * - * @param properties the properties used to bootstrap the factory - * - * @return a list with property-configured {@link ConstraintMappingContributor}s; May be empty but never {@code null} - */ - private static List getPropertyConfiguredConstraintMappingContributors( - Map properties, ClassLoader externalClassLoader) { - String deprecatedPropertyValue = properties.get( HibernateValidatorConfiguration.CONSTRAINT_MAPPING_CONTRIBUTOR ); - String propertyValue = properties.get( HibernateValidatorConfiguration.CONSTRAINT_MAPPING_CONTRIBUTORS ); - - if ( StringHelper.isNullOrEmptyString( deprecatedPropertyValue ) && StringHelper.isNullOrEmptyString( propertyValue ) ) { - return Collections.emptyList(); - } - - StringBuilder assembledPropertyValue = new StringBuilder(); - if ( !StringHelper.isNullOrEmptyString( deprecatedPropertyValue ) ) { - assembledPropertyValue.append( deprecatedPropertyValue ); - } - if ( !StringHelper.isNullOrEmptyString( propertyValue ) ) { - if ( assembledPropertyValue.length() > 0 ) { - assembledPropertyValue.append( "," ); - } - assembledPropertyValue.append( propertyValue ); - } - - String[] contributorNames = assembledPropertyValue.toString().split( "," ); - List contributors = newArrayList( contributorNames.length ); - - for ( String contributorName : contributorNames ) { - @SuppressWarnings("unchecked") - Class contributorType = (Class) run( - LoadClass.action( contributorName, externalClassLoader ) ); - contributors.add( run( NewInstance.action( contributorType, "constraint mapping contributor class" ) ) ); - } - - return contributors; - } - - private static boolean getAllowParallelMethodsDefineParameterConstraints(ConfigurationImpl hibernateSpecificConfig, Map properties) { - return checkPropertiesForBoolean( - properties, - HibernateValidatorConfiguration.ALLOW_PARALLEL_METHODS_DEFINE_PARAMETER_CONSTRAINTS, - hibernateSpecificConfig != null ? hibernateSpecificConfig.getMethodValidationConfiguration().isAllowParallelMethodsDefineParameterConstraints() : false - ); - } - - private static boolean getAllowMultipleCascadedValidationOnReturnValues(ConfigurationImpl hibernateSpecificConfig, Map properties) { - return checkPropertiesForBoolean( - properties, - HibernateValidatorConfiguration.ALLOW_MULTIPLE_CASCADED_VALIDATION_ON_RESULT, - hibernateSpecificConfig != null ? hibernateSpecificConfig.getMethodValidationConfiguration().isAllowMultipleCascadedValidationOnReturnValues() : false - ); - } - - private static boolean getAllowOverridingMethodAlterParameterConstraint(ConfigurationImpl hibernateSpecificConfig, Map properties) { - return checkPropertiesForBoolean( - properties, - HibernateValidatorConfiguration.ALLOW_PARAMETER_CONSTRAINT_OVERRIDE, - hibernateSpecificConfig != null ? hibernateSpecificConfig.getMethodValidationConfiguration().isAllowOverridingMethodAlterParameterConstraint() : false - ); - } - - private static boolean getTraversableResolverResultCacheEnabled(ConfigurationImpl configuration, Map properties) { - return checkPropertiesForBoolean( - properties, - HibernateValidatorConfiguration.ENABLE_TRAVERSABLE_RESOLVER_RESULT_CACHE, - configuration != null ? configuration.isTraversableResolverResultCacheEnabled() : true - ); - } - - private static boolean getFailFast(ConfigurationImpl configuration, Map properties) { - // check whether fail fast is programmatically enabled - boolean tmpFailFast = configuration != null ? configuration.getFailFast() : false; - - String propertyStringValue = properties.get( HibernateValidatorConfiguration.FAIL_FAST ); - if ( propertyStringValue != null ) { - boolean configurationValue = Boolean.valueOf( propertyStringValue ); - // throw an exception if the programmatic value is true and it overrides a false configured value - if ( tmpFailFast && !configurationValue ) { - throw LOG.getInconsistentFailFastConfigurationException(); - } - tmpFailFast = configurationValue; - } - - return tmpFailFast; - } - - private static ScriptEvaluatorFactory getScriptEvaluatorFactory(ConfigurationState configurationState, Map properties, - ClassLoader externalClassLoader) { - if ( configurationState instanceof ConfigurationImpl ) { - ConfigurationImpl hibernateSpecificConfig = (ConfigurationImpl) configurationState; - if ( hibernateSpecificConfig.getScriptEvaluatorFactory() != null ) { - LOG.usingScriptEvaluatorFactory( hibernateSpecificConfig.getScriptEvaluatorFactory().getClass() ); - return hibernateSpecificConfig.getScriptEvaluatorFactory(); - } - } - - String scriptEvaluatorFactoryFqcn = properties.get( HibernateValidatorConfiguration.SCRIPT_EVALUATOR_FACTORY_CLASSNAME ); - if ( scriptEvaluatorFactoryFqcn != null ) { - try { - @SuppressWarnings("unchecked") - Class clazz = (Class) run( - LoadClass.action( scriptEvaluatorFactoryFqcn, externalClassLoader ) - ); - ScriptEvaluatorFactory scriptEvaluatorFactory = run( NewInstance.action( clazz, "script evaluator factory class" ) ); - LOG.usingScriptEvaluatorFactory( clazz ); - - return scriptEvaluatorFactory; - } - catch (Exception e) { - throw LOG.getUnableToInstantiateScriptEvaluatorFactoryClassException( scriptEvaluatorFactoryFqcn, e ); - } - } - - return new DefaultScriptEvaluatorFactory( externalClassLoader ); - } - - private Duration getTemporalValidationTolerance(ConfigurationState configurationState, Map properties) { - if ( configurationState instanceof ConfigurationImpl ) { - ConfigurationImpl hibernateSpecificConfig = (ConfigurationImpl) configurationState; - if ( hibernateSpecificConfig.getTemporalValidationTolerance() != null ) { - LOG.logTemporalValidationTolerance( hibernateSpecificConfig.getTemporalValidationTolerance() ); - return hibernateSpecificConfig.getTemporalValidationTolerance(); - } - } - String temporalValidationToleranceProperty = properties.get( HibernateValidatorConfiguration.TEMPORAL_VALIDATION_TOLERANCE ); - if ( temporalValidationToleranceProperty != null ) { - try { - Duration tolerance = Duration.ofMillis( Long.parseLong( temporalValidationToleranceProperty ) ).abs(); - LOG.logTemporalValidationTolerance( tolerance ); - return tolerance; - } - catch (Exception e) { - throw LOG.getUnableToParseTemporalValidationToleranceException( temporalValidationToleranceProperty, e ); - } - } - - return Duration.ZERO; - } - - private Object getConstraintValidatorPayload(ConfigurationState configurationState) { - if ( configurationState instanceof ConfigurationImpl ) { - ConfigurationImpl hibernateSpecificConfig = (ConfigurationImpl) configurationState; - if ( hibernateSpecificConfig.getConstraintValidatorPayload() != null ) { - LOG.logConstraintValidatorPayload( hibernateSpecificConfig.getConstraintValidatorPayload() ); - return hibernateSpecificConfig.getConstraintValidatorPayload(); - } - } - - return null; - } - - private static void registerCustomConstraintValidators(Set constraintMappings, - ConstraintHelper constraintHelper) { - Set> definedConstraints = newHashSet(); - for ( DefaultConstraintMapping constraintMapping : constraintMappings ) { - for ( ConstraintDefinitionContribution contribution : constraintMapping.getConstraintDefinitionContributions() ) { - processConstraintDefinitionContribution( contribution, constraintHelper, definedConstraints ); - } - } - } - - private static void processConstraintDefinitionContribution( - ConstraintDefinitionContribution constraintDefinitionContribution, ConstraintHelper constraintHelper, - Set> definedConstraints) { - Class constraintType = constraintDefinitionContribution.getConstraintType(); - if ( definedConstraints.contains( constraintType ) ) { - throw LOG.getConstraintHasAlreadyBeenConfiguredViaProgrammaticApiException( constraintType ); - } - definedConstraints.add( constraintType ); - constraintHelper.putValidatorDescriptors( - constraintType, - constraintDefinitionContribution.getValidatorDescriptors(), - constraintDefinitionContribution.includeExisting() - ); - } - - private static void logValidatorFactoryScopedConfiguration(ValidatorFactoryScopedContext context) { - LOG.logValidatorFactoryScopedConfiguration( context.getMessageInterpolator().getClass(), "message interpolator" ); - LOG.logValidatorFactoryScopedConfiguration( context.getTraversableResolver().getClass(), "traversable resolver" ); - LOG.logValidatorFactoryScopedConfiguration( context.getParameterNameProvider().getClass(), "parameter name provider" ); - LOG.logValidatorFactoryScopedConfiguration( context.getClockProvider().getClass(), "clock provider" ); - LOG.logValidatorFactoryScopedConfiguration( context.getScriptEvaluatorFactory().getClass(), "script evaluator factory" ); - } - - /** - * 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 T run(PrivilegedAction action) { - return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); - } - - /** - * The one and only {@link ConstraintMappingContributor.ConstraintMappingBuilder} implementation. - */ - private static class DefaultConstraintMappingBuilder - implements ConstraintMappingContributor.ConstraintMappingBuilder { - private final Set mappings; - - public DefaultConstraintMappingBuilder(Set mappings) { - super(); - this.mappings = mappings; - } - - @Override - public ConstraintMapping addConstraintMapping() { - DefaultConstraintMapping mapping = new DefaultConstraintMapping(); - mappings.add( mapping ); - return mapping; - } - } - private static class BeanMetaDataManagerKey { private final ExecutableParameterNameProvider parameterNameProvider; private final ValueExtractorManager valueExtractorManager; @@ -663,257 +390,4 @@ public String toString() { + ", methodValidationConfiguration=" + methodValidationConfiguration + "]"; } } - - static class ValidatorFactoryScopedContext { - /** - * The default message interpolator for this factory. - */ - private final MessageInterpolator messageInterpolator; - - /** - * The default traversable resolver for this factory. - */ - private final TraversableResolver traversableResolver; - - /** - * The default parameter name provider for this factory. - */ - private final ExecutableParameterNameProvider parameterNameProvider; - - /** - * Provider for the current time when validating {@code @Future} or {@code @Past} - */ - private final ClockProvider clockProvider; - - /** - * Defines the temporal validation tolerance i.e. the allowed margin of error when comparing date/time in temporal - * constraints. - */ - private final Duration temporalValidationTolerance; - - /** - * Used to get the {@code ScriptEvaluatorFactory} when validating {@code @ScriptAssert} and - * {@code @ParameterScriptAssert} constraints. - */ - private final ScriptEvaluatorFactory scriptEvaluatorFactory; - - /** - * Hibernate Validator specific flag to abort validation on first constraint violation. - */ - private final boolean failFast; - - /** - * Hibernate Validator specific flag to disable the {@code TraversableResolver} result cache. - */ - private final boolean traversableResolverResultCacheEnabled; - - /** - * The constraint validator payload. - */ - private final Object constraintValidatorPayload; - - /** - * The constraint validator initialization context. - */ - private final HibernateConstraintValidatorInitializationContextImpl constraintValidatorInitializationContext; - - private ValidatorFactoryScopedContext(MessageInterpolator messageInterpolator, - TraversableResolver traversableResolver, - ExecutableParameterNameProvider parameterNameProvider, - ClockProvider clockProvider, - Duration temporalValidationTolerance, - ScriptEvaluatorFactory scriptEvaluatorFactory, - boolean failFast, - boolean traversableResolverResultCacheEnabled, - Object constraintValidatorPayload) { - this( messageInterpolator, traversableResolver, parameterNameProvider, clockProvider, temporalValidationTolerance, scriptEvaluatorFactory, failFast, - traversableResolverResultCacheEnabled, constraintValidatorPayload, - new HibernateConstraintValidatorInitializationContextImpl( scriptEvaluatorFactory, clockProvider, - temporalValidationTolerance ) ); - } - - private ValidatorFactoryScopedContext(MessageInterpolator messageInterpolator, - TraversableResolver traversableResolver, - ExecutableParameterNameProvider parameterNameProvider, - ClockProvider clockProvider, - Duration temporalValidationTolerance, - ScriptEvaluatorFactory scriptEvaluatorFactory, - boolean failFast, - boolean traversableResolverResultCacheEnabled, - Object constraintValidatorPayload, - HibernateConstraintValidatorInitializationContextImpl constraintValidatorInitializationContext) { - this.messageInterpolator = messageInterpolator; - this.traversableResolver = traversableResolver; - this.parameterNameProvider = parameterNameProvider; - this.clockProvider = clockProvider; - this.temporalValidationTolerance = temporalValidationTolerance; - this.scriptEvaluatorFactory = scriptEvaluatorFactory; - this.failFast = failFast; - this.traversableResolverResultCacheEnabled = traversableResolverResultCacheEnabled; - this.constraintValidatorPayload = constraintValidatorPayload; - this.constraintValidatorInitializationContext = constraintValidatorInitializationContext; - } - - public MessageInterpolator getMessageInterpolator() { - return this.messageInterpolator; - } - - public TraversableResolver getTraversableResolver() { - return this.traversableResolver; - } - - public ExecutableParameterNameProvider getParameterNameProvider() { - return this.parameterNameProvider; - } - - public ClockProvider getClockProvider() { - return this.clockProvider; - } - - public Duration getTemporalValidationTolerance() { - return this.temporalValidationTolerance; - } - - public ScriptEvaluatorFactory getScriptEvaluatorFactory() { - return this.scriptEvaluatorFactory; - } - - public boolean isFailFast() { - return this.failFast; - } - - public boolean isTraversableResolverResultCacheEnabled() { - return this.traversableResolverResultCacheEnabled; - } - - public Object getConstraintValidatorPayload() { - return this.constraintValidatorPayload; - } - - public HibernateConstraintValidatorInitializationContext getConstraintValidatorInitializationContext() { - return this.constraintValidatorInitializationContext; - } - - static class Builder { - private final ValidatorFactoryScopedContext defaultContext; - - private MessageInterpolator messageInterpolator; - private TraversableResolver traversableResolver; - private ExecutableParameterNameProvider parameterNameProvider; - private ClockProvider clockProvider; - private ScriptEvaluatorFactory scriptEvaluatorFactory; - private Duration temporalValidationTolerance; - private boolean failFast; - private boolean traversableResolverResultCacheEnabled; - private Object constraintValidatorPayload; - private HibernateConstraintValidatorInitializationContextImpl constraintValidatorInitializationContext; - - Builder(ValidatorFactoryScopedContext defaultContext) { - Contracts.assertNotNull( defaultContext, "Default context cannot be null." ); - - this.defaultContext = defaultContext; - this.messageInterpolator = defaultContext.messageInterpolator; - this.traversableResolver = defaultContext.traversableResolver; - this.parameterNameProvider = defaultContext.parameterNameProvider; - this.clockProvider = defaultContext.clockProvider; - this.scriptEvaluatorFactory = defaultContext.scriptEvaluatorFactory; - this.temporalValidationTolerance = defaultContext.temporalValidationTolerance; - this.failFast = defaultContext.failFast; - this.traversableResolverResultCacheEnabled = defaultContext.traversableResolverResultCacheEnabled; - this.constraintValidatorPayload = defaultContext.constraintValidatorPayload; - this.constraintValidatorInitializationContext = defaultContext.constraintValidatorInitializationContext; - } - - public Builder setMessageInterpolator(MessageInterpolator messageInterpolator) { - if ( messageInterpolator == null ) { - this.messageInterpolator = defaultContext.messageInterpolator; - } - else { - this.messageInterpolator = messageInterpolator; - } - - return this; - } - - public Builder setTraversableResolver(TraversableResolver traversableResolver) { - if ( traversableResolver == null ) { - this.traversableResolver = defaultContext.traversableResolver; - } - else { - this.traversableResolver = traversableResolver; - } - return this; - } - - public Builder setParameterNameProvider(ParameterNameProvider parameterNameProvider) { - if ( parameterNameProvider == null ) { - this.parameterNameProvider = defaultContext.parameterNameProvider; - } - else { - this.parameterNameProvider = new ExecutableParameterNameProvider( parameterNameProvider ); - } - return this; - } - - public Builder setClockProvider(ClockProvider clockProvider) { - if ( clockProvider == null ) { - this.clockProvider = defaultContext.clockProvider; - } - else { - this.clockProvider = clockProvider; - } - return this; - } - - public Builder setTemporalValidationTolerance(Duration temporalValidationTolerance) { - this.temporalValidationTolerance = temporalValidationTolerance == null ? Duration.ZERO : temporalValidationTolerance.abs(); - return this; - } - - public Builder setScriptEvaluatorFactory(ScriptEvaluatorFactory scriptEvaluatorFactory) { - if ( scriptEvaluatorFactory == null ) { - this.scriptEvaluatorFactory = defaultContext.scriptEvaluatorFactory; - } - else { - this.scriptEvaluatorFactory = scriptEvaluatorFactory; - } - return this; - } - - public Builder setFailFast(boolean failFast) { - this.failFast = failFast; - return this; - } - - public Builder setTraversableResolverResultCacheEnabled(boolean traversableResolverResultCacheEnabled) { - this.traversableResolverResultCacheEnabled = traversableResolverResultCacheEnabled; - return this; - } - - public Builder setConstraintValidatorPayload(Object constraintValidatorPayload) { - this.constraintValidatorPayload = constraintValidatorPayload; - return this; - } - - public ValidatorFactoryScopedContext build() { - return new ValidatorFactoryScopedContext( - messageInterpolator, - traversableResolver, - parameterNameProvider, - clockProvider, - temporalValidationTolerance, - scriptEvaluatorFactory, - failFast, - traversableResolverResultCacheEnabled, - constraintValidatorPayload, - HibernateConstraintValidatorInitializationContextImpl.of( - constraintValidatorInitializationContext, - scriptEvaluatorFactory, - clockProvider, - temporalValidationTolerance - ) - ); - } - } - } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryScopedContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryScopedContext.java new file mode 100644 index 0000000000..fa87eb016d --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryScopedContext.java @@ -0,0 +1,273 @@ +/* + * 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.engine; + +import java.time.Duration; + +import javax.validation.ClockProvider; +import javax.validation.MessageInterpolator; +import javax.validation.ParameterNameProvider; +import javax.validation.TraversableResolver; + +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; +import org.hibernate.validator.internal.engine.constraintvalidation.HibernateConstraintValidatorInitializationContextImpl; +import org.hibernate.validator.internal.util.Contracts; +import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; +import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; + +public class ValidatorFactoryScopedContext { + /** + * The default message interpolator for this factory. + */ + private final MessageInterpolator messageInterpolator; + + /** + * The default traversable resolver for this factory. + */ + private final TraversableResolver traversableResolver; + + /** + * The default parameter name provider for this factory. + */ + private final ExecutableParameterNameProvider parameterNameProvider; + + /** + * Provider for the current time when validating {@code @Future} or {@code @Past} + */ + private final ClockProvider clockProvider; + + /** + * Defines the temporal validation tolerance i.e. the allowed margin of error when comparing date/time in temporal + * constraints. + */ + private final Duration temporalValidationTolerance; + + /** + * Used to get the {@code ScriptEvaluatorFactory} when validating {@code @ScriptAssert} and + * {@code @ParameterScriptAssert} constraints. + */ + private final ScriptEvaluatorFactory scriptEvaluatorFactory; + + /** + * Hibernate Validator specific flag to abort validation on first constraint violation. + */ + private final boolean failFast; + + /** + * Hibernate Validator specific flag to disable the {@code TraversableResolver} result cache. + */ + private final boolean traversableResolverResultCacheEnabled; + + /** + * The constraint validator payload. + */ + private final Object constraintValidatorPayload; + + /** + * The constraint validator initialization context. + */ + private final HibernateConstraintValidatorInitializationContextImpl constraintValidatorInitializationContext; + + ValidatorFactoryScopedContext(MessageInterpolator messageInterpolator, + TraversableResolver traversableResolver, + ExecutableParameterNameProvider parameterNameProvider, + ClockProvider clockProvider, + Duration temporalValidationTolerance, + ScriptEvaluatorFactory scriptEvaluatorFactory, + boolean failFast, + boolean traversableResolverResultCacheEnabled, + Object constraintValidatorPayload) { + this( messageInterpolator, traversableResolver, parameterNameProvider, clockProvider, temporalValidationTolerance, scriptEvaluatorFactory, failFast, + traversableResolverResultCacheEnabled, constraintValidatorPayload, + new HibernateConstraintValidatorInitializationContextImpl( scriptEvaluatorFactory, clockProvider, + temporalValidationTolerance ) ); + } + + private ValidatorFactoryScopedContext(MessageInterpolator messageInterpolator, + TraversableResolver traversableResolver, + ExecutableParameterNameProvider parameterNameProvider, + ClockProvider clockProvider, + Duration temporalValidationTolerance, + ScriptEvaluatorFactory scriptEvaluatorFactory, + boolean failFast, + boolean traversableResolverResultCacheEnabled, + Object constraintValidatorPayload, + HibernateConstraintValidatorInitializationContextImpl constraintValidatorInitializationContext) { + this.messageInterpolator = messageInterpolator; + this.traversableResolver = traversableResolver; + this.parameterNameProvider = parameterNameProvider; + this.clockProvider = clockProvider; + this.temporalValidationTolerance = temporalValidationTolerance; + this.scriptEvaluatorFactory = scriptEvaluatorFactory; + this.failFast = failFast; + this.traversableResolverResultCacheEnabled = traversableResolverResultCacheEnabled; + this.constraintValidatorPayload = constraintValidatorPayload; + this.constraintValidatorInitializationContext = constraintValidatorInitializationContext; + } + + public MessageInterpolator getMessageInterpolator() { + return this.messageInterpolator; + } + + public TraversableResolver getTraversableResolver() { + return this.traversableResolver; + } + + public ExecutableParameterNameProvider getParameterNameProvider() { + return this.parameterNameProvider; + } + + public ClockProvider getClockProvider() { + return this.clockProvider; + } + + public Duration getTemporalValidationTolerance() { + return this.temporalValidationTolerance; + } + + public ScriptEvaluatorFactory getScriptEvaluatorFactory() { + return this.scriptEvaluatorFactory; + } + + public boolean isFailFast() { + return this.failFast; + } + + public boolean isTraversableResolverResultCacheEnabled() { + return this.traversableResolverResultCacheEnabled; + } + + public Object getConstraintValidatorPayload() { + return this.constraintValidatorPayload; + } + + public HibernateConstraintValidatorInitializationContext getConstraintValidatorInitializationContext() { + return this.constraintValidatorInitializationContext; + } + + static class Builder { + private final ValidatorFactoryScopedContext defaultContext; + + private MessageInterpolator messageInterpolator; + private TraversableResolver traversableResolver; + private ExecutableParameterNameProvider parameterNameProvider; + private ClockProvider clockProvider; + private ScriptEvaluatorFactory scriptEvaluatorFactory; + private Duration temporalValidationTolerance; + private boolean failFast; + private boolean traversableResolverResultCacheEnabled; + private Object constraintValidatorPayload; + private HibernateConstraintValidatorInitializationContextImpl constraintValidatorInitializationContext; + + Builder(ValidatorFactoryScopedContext defaultContext) { + Contracts.assertNotNull( defaultContext, "Default context cannot be null." ); + + this.defaultContext = defaultContext; + this.messageInterpolator = defaultContext.messageInterpolator; + this.traversableResolver = defaultContext.traversableResolver; + this.parameterNameProvider = defaultContext.parameterNameProvider; + this.clockProvider = defaultContext.clockProvider; + this.scriptEvaluatorFactory = defaultContext.scriptEvaluatorFactory; + this.temporalValidationTolerance = defaultContext.temporalValidationTolerance; + this.failFast = defaultContext.failFast; + this.traversableResolverResultCacheEnabled = defaultContext.traversableResolverResultCacheEnabled; + this.constraintValidatorPayload = defaultContext.constraintValidatorPayload; + this.constraintValidatorInitializationContext = defaultContext.constraintValidatorInitializationContext; + } + + public ValidatorFactoryScopedContext.Builder setMessageInterpolator(MessageInterpolator messageInterpolator) { + if ( messageInterpolator == null ) { + this.messageInterpolator = defaultContext.messageInterpolator; + } + else { + this.messageInterpolator = messageInterpolator; + } + + return this; + } + + public ValidatorFactoryScopedContext.Builder setTraversableResolver(TraversableResolver traversableResolver) { + if ( traversableResolver == null ) { + this.traversableResolver = defaultContext.traversableResolver; + } + else { + this.traversableResolver = traversableResolver; + } + return this; + } + + public ValidatorFactoryScopedContext.Builder setParameterNameProvider(ParameterNameProvider parameterNameProvider) { + if ( parameterNameProvider == null ) { + this.parameterNameProvider = defaultContext.parameterNameProvider; + } + else { + this.parameterNameProvider = new ExecutableParameterNameProvider( parameterNameProvider ); + } + return this; + } + + public ValidatorFactoryScopedContext.Builder setClockProvider(ClockProvider clockProvider) { + if ( clockProvider == null ) { + this.clockProvider = defaultContext.clockProvider; + } + else { + this.clockProvider = clockProvider; + } + return this; + } + + public ValidatorFactoryScopedContext.Builder setTemporalValidationTolerance(Duration temporalValidationTolerance) { + this.temporalValidationTolerance = temporalValidationTolerance == null ? Duration.ZERO : temporalValidationTolerance.abs(); + return this; + } + + public ValidatorFactoryScopedContext.Builder setScriptEvaluatorFactory(ScriptEvaluatorFactory scriptEvaluatorFactory) { + if ( scriptEvaluatorFactory == null ) { + this.scriptEvaluatorFactory = defaultContext.scriptEvaluatorFactory; + } + else { + this.scriptEvaluatorFactory = scriptEvaluatorFactory; + } + return this; + } + + public ValidatorFactoryScopedContext.Builder setFailFast(boolean failFast) { + this.failFast = failFast; + return this; + } + + public ValidatorFactoryScopedContext.Builder setTraversableResolverResultCacheEnabled(boolean traversableResolverResultCacheEnabled) { + this.traversableResolverResultCacheEnabled = traversableResolverResultCacheEnabled; + return this; + } + + public ValidatorFactoryScopedContext.Builder setConstraintValidatorPayload(Object constraintValidatorPayload) { + this.constraintValidatorPayload = constraintValidatorPayload; + return this; + } + + public ValidatorFactoryScopedContext build() { + return new ValidatorFactoryScopedContext( + messageInterpolator, + traversableResolver, + parameterNameProvider, + clockProvider, + temporalValidationTolerance, + scriptEvaluatorFactory, + failFast, + traversableResolverResultCacheEnabled, + constraintValidatorPayload, + HibernateConstraintValidatorInitializationContextImpl.of( + constraintValidatorInitializationContext, + scriptEvaluatorFactory, + clockProvider, + temporalValidationTolerance + ) + ); + } + } +} 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 7b9319c61e..c34c2a7e22 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 @@ -8,7 +8,6 @@ import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES; -import java.lang.annotation.ElementType; import java.lang.invoke.MethodHandles; import java.lang.reflect.Constructor; import java.lang.reflect.Executable; @@ -20,7 +19,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -36,9 +34,6 @@ import javax.validation.valueextraction.ValueExtractor; import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; -import org.hibernate.validator.internal.engine.ValidationContext.ValidationContextBuilder; -import org.hibernate.validator.internal.engine.ValidationContext.ValidatorScopedContext; -import org.hibernate.validator.internal.engine.ValidatorFactoryImpl.ValidatorFactoryScopedContext; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; import org.hibernate.validator.internal.engine.groups.Group; import org.hibernate.validator.internal.engine.groups.GroupWithInheritance; @@ -48,6 +43,13 @@ import org.hibernate.validator.internal.engine.path.NodeImpl; import org.hibernate.validator.internal.engine.path.PathImpl; import org.hibernate.validator.internal.engine.resolver.TraversableResolvers; +import org.hibernate.validator.internal.engine.validationcontext.BaseBeanValidationContext; +import org.hibernate.validator.internal.engine.validationcontext.ExecutableValidationContext; +import org.hibernate.validator.internal.engine.validationcontext.ValidationContextBuilder; +import org.hibernate.validator.internal.engine.validationcontext.ValidatorScopedContext; +import org.hibernate.validator.internal.engine.valuecontext.BeanValueContext; +import org.hibernate.validator.internal.engine.valuecontext.ValueContext; +import org.hibernate.validator.internal.engine.valuecontext.ValueContexts; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorHelper; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; @@ -62,10 +64,7 @@ import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.facets.Cascadable; import org.hibernate.validator.internal.metadata.facets.Validatable; -import org.hibernate.validator.internal.metadata.location.ConstraintLocation; -import org.hibernate.validator.internal.metadata.location.FieldConstraintLocation; -import org.hibernate.validator.internal.metadata.location.GetterConstraintLocation; -import org.hibernate.validator.internal.metadata.location.TypeArgumentConstraintLocation; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ReflectionHelper; @@ -103,7 +102,7 @@ public class ValidatorImpl implements Validator, ExecutableValidator { /** * {@link TraversableResolver} as passed to the constructor of this instance. - * Never use it directly, always use {@link #getCachingTraversableResolver()} to retrieved the single threaded caching wrapper. + * Never use it directly, always use {@link TraversableResolvers#wrapWithCachingForSingleValidation(TraversableResolver, boolean)} to retrieved the single threaded caching wrapper. */ private final TraversableResolver traversableResolver; @@ -152,14 +151,18 @@ public final Set> validate(T object, Class... grou Contracts.assertNotNull( object, MESSAGES.validatedObjectMustNotBeNull() ); sanityCheckGroups( groups ); - ValidationContext validationContext = getValidationContextBuilder().forValidate( object ); + @SuppressWarnings("unchecked") + Class rootBeanClass = (Class) object.getClass(); + BeanMetaData rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass ); - if ( !validationContext.getRootBeanMetaData().hasConstraints() ) { + if ( !rootBeanMetaData.hasConstraints() ) { return Collections.emptySet(); } + BaseBeanValidationContext validationContext = getValidationContextBuilder().forValidate( rootBeanClass, rootBeanMetaData, object ); + ValidationOrder validationOrder = determineGroupValidationOrder( groups ); - ValueContext valueContext = ValueContext.getLocalExecutionContext( + BeanValueContext valueContext = ValueContexts.getLocalExecutionContextForBean( validatorScopedContext.getParameterNameProvider(), object, validationContext.getRootBeanMetaData(), @@ -175,14 +178,19 @@ public final Set> validateProperty(T object, String p sanityCheckPropertyPath( propertyName ); sanityCheckGroups( groups ); - ValidationContext validationContext = getValidationContextBuilder().forValidateProperty( object ); + @SuppressWarnings("unchecked") + Class rootBeanClass = (Class) object.getClass(); + BeanMetaData rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass ); - if ( !validationContext.getRootBeanMetaData().hasConstraints() ) { + if ( !rootBeanMetaData.hasConstraints() ) { return Collections.emptySet(); } PathImpl propertyPath = PathImpl.createPathFromString( propertyName ); - ValueContext valueContext = getValueContextForPropertyValidation( validationContext, propertyPath ); + BaseBeanValidationContext validationContext = getValidationContextBuilder().forValidateProperty( rootBeanClass, rootBeanMetaData, object, + propertyPath ); + + BeanValueContext valueContext = getValueContextForPropertyValidation( validationContext, propertyPath ); if ( valueContext.getCurrentBean() == null ) { throw LOG.getUnableToReachPropertyToValidateException( validationContext.getRootBean(), propertyPath ); @@ -199,18 +207,21 @@ public final Set> validateValue(Class beanType, St sanityCheckPropertyPath( propertyName ); sanityCheckGroups( groups ); - ValidationContext validationContext = getValidationContextBuilder().forValidateValue( beanType ); + BeanMetaData rootBeanMetaData = beanMetaDataManager.getBeanMetaData( beanType ); - if ( !validationContext.getRootBeanMetaData().hasConstraints() ) { + if ( !rootBeanMetaData.hasConstraints() ) { return Collections.emptySet(); } + PathImpl propertyPath = PathImpl.createPathFromString( propertyName ); + BaseBeanValidationContext validationContext = getValidationContextBuilder().forValidateValue( beanType, rootBeanMetaData, propertyPath ); + ValidationOrder validationOrder = determineGroupValidationOrder( groups ); return validateValueInContext( validationContext, value, - PathImpl.createPathFromString( propertyName ), + propertyPath, validationOrder ); } @@ -251,17 +262,22 @@ public Set> validateReturnValue(T object, Method meth private Set> validateParameters(T object, Executable executable, Object[] parameterValues, Class... groups) { sanityCheckGroups( groups ); - ValidationContext validationContext = getValidationContextBuilder().forValidateParameters( - validatorScopedContext.getParameterNameProvider(), + @SuppressWarnings("unchecked") + Class rootBeanClass = object != null ? (Class) object.getClass() : (Class) executable.getDeclaringClass(); + BeanMetaData rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass ); + + if ( !rootBeanMetaData.hasConstraints() ) { + return Collections.emptySet(); + } + + ExecutableValidationContext validationContext = getValidationContextBuilder().forValidateParameters( + rootBeanClass, + rootBeanMetaData, object, executable, parameterValues ); - if ( !validationContext.getRootBeanMetaData().hasConstraints() ) { - return Collections.emptySet(); - } - ValidationOrder validationOrder = determineGroupValidationOrder( groups ); validateParametersInContext( validationContext, parameterValues, validationOrder ); @@ -272,16 +288,22 @@ private Set> validateParameters(T object, Executable private Set> validateReturnValue(T object, Executable executable, Object returnValue, Class... groups) { sanityCheckGroups( groups ); - ValidationContext validationContext = getValidationContextBuilder().forValidateReturnValue( + @SuppressWarnings("unchecked") + Class rootBeanClass = object != null ? (Class) object.getClass() : (Class) executable.getDeclaringClass(); + BeanMetaData rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass ); + + if ( !rootBeanMetaData.hasConstraints() ) { + return Collections.emptySet(); + } + + ExecutableValidationContext validationContext = getValidationContextBuilder().forValidateReturnValue( + rootBeanClass, + rootBeanMetaData, object, executable, returnValue ); - if ( !validationContext.getRootBeanMetaData().hasConstraints() ) { - return Collections.emptySet(); - } - ValidationOrder validationOrder = determineGroupValidationOrder( groups ); validateReturnValueInContext( validationContext, object, returnValue, validationOrder ); @@ -312,14 +334,12 @@ public ExecutableValidator forExecutables() { } private ValidationContextBuilder getValidationContextBuilder() { - return ValidationContext.getValidationContextBuilder( - beanMetaDataManager, + return new ValidationContextBuilder( constraintValidatorManager, constraintValidatorFactory, validatorScopedContext, TraversableResolvers.wrapWithCachingForSingleValidation( traversableResolver, validatorScopedContext.isTraversableResolverResultCacheEnabled() ), constraintValidatorInitializationContext - ); } @@ -352,22 +372,22 @@ private ValidationOrder determineGroupValidationOrder(Class[] groups) { /** * Validates the given object using the available context information. + * * @param validationContext the global validation context * @param valueContext the current validation context * @param validationOrder Contains the information which and in which order groups have to be executed - * * @param The root bean type * * @return Set of constraint violations or the empty set if there were no violations. */ - private Set> validateInContext(ValidationContext validationContext, ValueContext valueContext, + private Set> validateInContext(BaseBeanValidationContext validationContext, BeanValueContext valueContext, ValidationOrder validationOrder) { if ( valueContext.getCurrentBean() == null ) { return Collections.emptySet(); } BeanMetaData beanMetaData = valueContext.getCurrentBeanMetaData(); - if ( beanMetaData.defaultGroupSequenceIsRedefined() ) { + if ( beanMetaData.isDefaultGroupSequenceRedefined() ) { validationOrder.assertDefaultGroupSequenceIsExpandable( beanMetaData.getDefaultGroupSequence( valueContext.getCurrentBean() ) ); } @@ -420,7 +440,7 @@ private Set> validateInContext(ValidationContext validationContext, ValueContext valueContext) { + private void validateConstraintsForCurrentGroup(BaseBeanValidationContext validationContext, BeanValueContext valueContext) { // we are not validating the default group there is nothing special to consider. If we are validating the default // group sequence we have to consider that a class in the hierarchy could redefine the default group sequence. if ( !valueContext.validatingDefault() ) { @@ -431,14 +451,14 @@ private void validateConstraintsForCurrentGroup(ValidationContext validationC } } - private void validateConstraintsForDefaultGroup(ValidationContext validationContext, ValueContext valueContext) { + private void validateConstraintsForDefaultGroup(BaseBeanValidationContext validationContext, BeanValueContext valueContext) { 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 = beanMetaDataManager.getBeanMetaData( clazz ); - boolean defaultGroupSequenceIsRedefined = hostingBeanMetaData.defaultGroupSequenceIsRedefined(); + 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 ) { @@ -451,8 +471,11 @@ private void validateConstraintsForDefaultGroup(ValidationContext validat for ( Group defaultSequenceMember : groupOfGroups ) { validationSuccessful = validateConstraintsForSingleDefaultGroupElement( validationContext, valueContext, validatedInterfaces, clazz, - metaConstraints, defaultSequenceMember ); + metaConstraints, defaultSequenceMember ) && validationSuccessful; } + + validationContext.markCurrentBeanAsProcessed( valueContext ); + if ( !validationSuccessful ) { break; } @@ -464,10 +487,9 @@ private void validateConstraintsForDefaultGroup(ValidationContext validat Set> metaConstraints = hostingBeanMetaData.getDirectMetaConstraints(); validateConstraintsForSingleDefaultGroupElement( validationContext, valueContext, validatedInterfaces, clazz, metaConstraints, Group.DEFAULT_GROUP ); + validationContext.markCurrentBeanAsProcessed( valueContext ); } - validationContext.markCurrentBeanAsProcessed( valueContext ); - // all constraints in the hierarchy has been validated, stop validation. if ( defaultGroupSequenceIsRedefined ) { break; @@ -475,7 +497,7 @@ private void validateConstraintsForDefaultGroup(ValidationContext validat } } - private boolean validateConstraintsForSingleDefaultGroupElement(ValidationContext validationContext, ValueContext valueContext, final Map, Class> validatedInterfaces, + private boolean validateConstraintsForSingleDefaultGroupElement(BaseBeanValidationContext validationContext, ValueContext valueContext, final Map, Class> validatedInterfaces, Class clazz, Set> metaConstraints, Group defaultSequenceMember) { boolean validationSuccessful = true; @@ -503,12 +525,12 @@ private boolean validateConstraintsForSingleDefaultGroupElement(ValidationCo return validationSuccessful; } - private void validateConstraintsForNonDefaultGroup(ValidationContext validationContext, ValueContext valueContext) { + private void validateConstraintsForNonDefaultGroup(BaseBeanValidationContext validationContext, BeanValueContext valueContext) { validateMetaConstraints( validationContext, valueContext, valueContext.getCurrentBean(), valueContext.getCurrentBeanMetaData().getMetaConstraints() ); validationContext.markCurrentBeanAsProcessed( valueContext ); } - private void validateMetaConstraints(ValidationContext validationContext, ValueContext valueContext, Object parent, + private void validateMetaConstraints(BaseBeanValidationContext validationContext, ValueContext valueContext, Object parent, Iterable> constraints) { for ( MetaConstraint metaConstraint : constraints ) { @@ -519,8 +541,8 @@ private void validateMetaConstraints(ValidationContext validationContext, Val } } - private boolean validateMetaConstraint(ValidationContext validationContext, ValueContext valueContext, Object parent, MetaConstraint metaConstraint) { - ValueContext.ValueState originalValueState = valueContext.getCurrentValueState(); + private boolean validateMetaConstraint(BaseBeanValidationContext validationContext, ValueContext valueContext, Object parent, MetaConstraint metaConstraint) { + BeanValueContext.ValueState originalValueState = valueContext.getCurrentValueState(); valueContext.appendNode( metaConstraint.getLocation() ); boolean success = true; @@ -548,15 +570,15 @@ private boolean validateMetaConstraint(ValidationContext validationContext, V * @param validationContext The execution context * @param valueContext Collected information for single validation */ - private void validateCascadedConstraints(ValidationContext validationContext, ValueContext valueContext) { + private void validateCascadedConstraints(BaseBeanValidationContext validationContext, ValueContext valueContext) { Validatable validatable = valueContext.getCurrentValidatable(); - ValueContext.ValueState originalValueState = valueContext.getCurrentValueState(); + BeanValueContext.ValueState originalValueState = valueContext.getCurrentValueState(); for ( Cascadable cascadable : validatable.getCascadables() ) { valueContext.appendNode( cascadable ); - ElementType elementType = cascadable.getElementType(); - if ( isCascadeRequired( validationContext, valueContext.getCurrentBean(), valueContext.getPropertyPath(), elementType ) ) { + if ( isCascadeRequired( validationContext, valueContext.getCurrentBean(), valueContext.getPropertyPath(), + cascadable.getConstraintLocationKind() ) ) { Object value = getCascadableValue( validationContext, valueContext.getCurrentBean(), cascadable ); CascadingMetaData cascadingMetaData = cascadable.getCascadingMetaData(); @@ -585,27 +607,29 @@ private void validateCascadedConstraints(ValidationContext validationContext, } } - private void validateCascadedAnnotatedObjectForCurrentGroup(Object value, ValidationContext validationContext, ValueContext valueContext, + private void validateCascadedAnnotatedObjectForCurrentGroup(Object value, BaseBeanValidationContext validationContext, ValueContext valueContext, CascadingMetaData cascadingMetaData) { - if ( validationContext.isBeanAlreadyValidated( value, valueContext.getCurrentGroup(), valueContext.getPropertyPath() ) || + // We need to convert the group before checking if the bean was processed or not + // as group defines the processed status. + Class originalGroup = valueContext.getCurrentGroup(); + Class currentGroup = cascadingMetaData.convertGroup( originalGroup ); + + if ( validationContext.isBeanAlreadyValidated( value, currentGroup, valueContext.getPropertyPath() ) || shouldFailFast( validationContext ) ) { return; } - Class originalGroup = valueContext.getCurrentGroup(); - Class currentGroup = cascadingMetaData.convertGroup( originalGroup ); - // expand the group only if was created by group conversion; // otherwise we're looping through the right validation order // already and need only to pass the current element ValidationOrder validationOrder = validationOrderGenerator.getValidationOrder( currentGroup, currentGroup != originalGroup ); - ValueContext cascadedValueContext = buildNewLocalExecutionContext( valueContext, value ); + BeanValueContext cascadedValueContext = buildNewLocalExecutionContext( valueContext, value ); validateInContext( validationContext, cascadedValueContext, validationOrder ); } - private void validateCascadedContainerElementsForCurrentGroup(Object value, ValidationContext validationContext, ValueContext valueContext, + private void validateCascadedContainerElementsForCurrentGroup(Object value, BaseBeanValidationContext validationContext, ValueContext valueContext, List containerElementTypesCascadingMetaData) { for ( ContainerCascadingMetaData cascadingMetaData : containerElementTypesCascadingMetaData ) { if ( !cascadingMetaData.isMarkedForCascadingOnAnnotatedObjectOrContainerElements() ) { @@ -630,11 +654,11 @@ private void validateCascadedContainerElementsForCurrentGroup(Object value, Vali private class CascadingValueReceiver implements ValueExtractor.ValueReceiver { - private final ValidationContext validationContext; + private final BaseBeanValidationContext validationContext; private final ValueContext valueContext; private final ContainerCascadingMetaData cascadingMetaData; - public CascadingValueReceiver(ValidationContext validationContext, ValueContext valueContext, ContainerCascadingMetaData cascadingMetaData) { + public CascadingValueReceiver(BaseBeanValidationContext validationContext, ValueContext valueContext, ContainerCascadingMetaData cascadingMetaData) { this.validationContext = validationContext; this.valueContext = valueContext; this.cascadingMetaData = cascadingMetaData; @@ -664,21 +688,23 @@ public void keyedValue(String nodeName, Object key, Object value) { } private void doValidate(Object value, String nodeName) { + // We need to convert the group before checking if the bean was processed or not + // as group defines the processed status. + Class originalGroup = valueContext.getCurrentGroup(); + Class currentGroup = cascadingMetaData.convertGroup( originalGroup ); + if ( value == null || - validationContext.isBeanAlreadyValidated( value, valueContext.getCurrentGroup(), valueContext.getPropertyPath() ) || + validationContext.isBeanAlreadyValidated( value, currentGroup, valueContext.getPropertyPath() ) || shouldFailFast( validationContext ) ) { return; } - Class originalGroup = valueContext.getCurrentGroup(); - Class currentGroup = cascadingMetaData.convertGroup( originalGroup ); - // expand the group only if was created by group conversion; // otherwise we're looping through the right validation order // already and need only to pass the current element ValidationOrder validationOrder = validationOrderGenerator.getValidationOrder( currentGroup, currentGroup != originalGroup ); - ValueContext cascadedValueContext = buildNewLocalExecutionContext( valueContext, value ); + BeanValueContext cascadedValueContext = buildNewLocalExecutionContext( valueContext, value ); if ( cascadingMetaData.getDeclaredContainerClass() != null ) { cascadedValueContext.setTypeParameter( cascadingMetaData.getDeclaredContainerClass(), cascadingMetaData.getDeclaredTypeParameterIndex() ); @@ -705,7 +731,7 @@ private void doValidate(Object value, String nodeName) { } } - private void validateCascadedContainerElementsInContext(Object value, ValidationContext validationContext, ValueContext valueContext, + private void validateCascadedContainerElementsInContext(Object value, BaseBeanValidationContext validationContext, ValueContext valueContext, ContainerCascadingMetaData cascadingMetaData, ValidationOrder validationOrder) { Iterator groupIterator = validationOrder.getGroupIterator(); while ( groupIterator.hasNext() ) { @@ -740,36 +766,28 @@ private void validateCascadedContainerElementsInContext(Object value, Validation } } - 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() - ); - } + private BeanValueContext buildNewLocalExecutionContext(ValueContext valueContext, Object value) { + BeanValueContext newValueContext; + Contracts.assertNotNull( value, "value cannot be null" ); + BeanMetaData beanMetaData = beanMetaDataManager.getBeanMetaData( value.getClass() ); + newValueContext = ValueContexts.getLocalExecutionContextForBean( + validatorScopedContext.getParameterNameProvider(), + value, + beanMetaData, + valueContext.getPropertyPath() + ); + newValueContext.setCurrentValidatedValue( value ); return newValueContext; } - private Set> validateValueInContext(ValidationContext validationContext, Object value, PathImpl propertyPath, + private Set> validateValueInContext(BaseBeanValidationContext validationContext, Object value, PathImpl propertyPath, ValidationOrder validationOrder) { - ValueContext valueContext = getValueContextForValueValidation( validationContext, propertyPath ); + BeanValueContext valueContext = getValueContextForValueValidation( validationContext.getRootBeanClass(), propertyPath ); valueContext.setCurrentValidatedValue( value ); BeanMetaData beanMetaData = valueContext.getCurrentBeanMetaData(); - if ( beanMetaData.defaultGroupSequenceIsRedefined() ) { + if ( beanMetaData.isDefaultGroupSequenceRedefined() ) { validationOrder.assertDefaultGroupSequenceIsExpandable( beanMetaData.getDefaultGroupSequence( null ) ); } @@ -806,7 +824,7 @@ private Set> validateValueInContext(ValidationContext return validationContext.getFailingConstraints(); } - private void validateParametersInContext(ValidationContext validationContext, + private void validateParametersInContext(ExecutableValidationContext validationContext, Object[] parameterValues, ValidationOrder validationOrder) { BeanMetaData beanMetaData = validationContext.getRootBeanMetaData(); @@ -831,7 +849,7 @@ private void validateParametersInContext(ValidationContext validationCont ); } - if ( beanMetaData.defaultGroupSequenceIsRedefined() ) { + if ( beanMetaData.isDefaultGroupSequenceRedefined() ) { validationOrder.assertDefaultGroupSequenceIsExpandable( beanMetaData.getDefaultGroupSequence( validationContext.getRootBean() @@ -848,8 +866,7 @@ private void validateParametersInContext(ValidationContext validationCont } } - ValueContext cascadingValueContext = ValueContext.getLocalExecutionContext( - beanMetaDataManager, + ValueContext cascadingValueContext = ValueContexts.getLocalExecutionContextForExecutable( validatorScopedContext.getParameterNameProvider(), parameterValues, executableMetaData.getValidatableParametersMetaData(), @@ -894,7 +911,7 @@ private void validateParametersInContext(ValidationContext validationCont } } - private void validateParametersForGroup(ValidationContext validationContext, ExecutableMetaData executableMetaData, Object[] parameterValues, + private void validateParametersForGroup(ExecutableValidationContext validationContext, ExecutableMetaData executableMetaData, Object[] parameterValues, Group group) { Contracts.assertNotNull( executableMetaData, "executableMetaData may not be null" ); @@ -931,7 +948,7 @@ private void validateParametersForGroup(ValidationContext validationConte } } - private void validateParametersForSingleGroup(ValidationContext validationContext, Object[] parameterValues, ExecutableMetaData executableMetaData, Class currentValidatedGroup) { + private void validateParametersForSingleGroup(ExecutableValidationContext validationContext, Object[] parameterValues, ExecutableMetaData executableMetaData, Class currentValidatedGroup) { if ( !executableMetaData.getCrossParameterConstraints().isEmpty() ) { ValueContext valueContext = getExecutableValueContext( validationContext.getRootBean(), executableMetaData, executableMetaData.getValidatableParametersMetaData(), currentValidatedGroup @@ -981,31 +998,19 @@ private void validateParametersForSingleGroup(ValidationContext validatio private ValueContext getExecutableValueContext(T object, ExecutableMetaData executableMetaData, Validatable validatable, Class group) { ValueContext valueContext; - if ( object != null ) { - valueContext = ValueContext.getLocalExecutionContext( - beanMetaDataManager, - validatorScopedContext.getParameterNameProvider(), - object, - validatable, - PathImpl.createPathForExecutable( executableMetaData ) - ); - } - else { - valueContext = ValueContext.getLocalExecutionContext( - beanMetaDataManager, - validatorScopedContext.getParameterNameProvider(), - (Class) null, //the type is not required in this case (only for cascaded validation) - validatable, - PathImpl.createPathForExecutable( executableMetaData ) - ); - } + valueContext = ValueContexts.getLocalExecutionContextForExecutable( + validatorScopedContext.getParameterNameProvider(), + object, + validatable, + PathImpl.createPathForExecutable( executableMetaData ) + ); valueContext.setCurrentGroup( group ); return valueContext; } - private void validateReturnValueInContext(ValidationContext validationContext, T bean, V value, ValidationOrder validationOrder) { + private void validateReturnValueInContext(ExecutableValidationContext validationContext, T bean, V value, ValidationOrder validationOrder) { BeanMetaData beanMetaData = validationContext.getRootBeanMetaData(); Optional executableMetaDataOptional = validationContext.getExecutableMetaData(); @@ -1017,7 +1022,7 @@ private void validateReturnValueInContext(ValidationContext validation ExecutableMetaData executableMetaData = executableMetaDataOptional.get(); - if ( beanMetaData.defaultGroupSequenceIsRedefined() ) { + if ( beanMetaData.isDefaultGroupSequenceRedefined() ) { validationOrder.assertDefaultGroupSequenceIsExpandable( beanMetaData.getDefaultGroupSequence( bean ) ); } @@ -1033,9 +1038,10 @@ private void validateReturnValueInContext(ValidationContext validation ValueContext cascadingValueContext = null; - if ( value != null ) { - cascadingValueContext = ValueContext.getLocalExecutionContext( - beanMetaDataManager, + boolean isCascadingRequired = value != null && executableMetaData.isCascading(); + + if ( isCascadingRequired ) { + cascadingValueContext = ValueContexts.getLocalExecutionContextForExecutable( validatorScopedContext.getParameterNameProvider(), value, executableMetaData.getReturnValueMetaData(), @@ -1065,7 +1071,7 @@ private void validateReturnValueInContext(ValidationContext validation return; } - if ( value != null ) { + if ( isCascadingRequired ) { cascadingValueContext.setCurrentGroup( group.getDefiningClass() ); validateCascadedConstraints( validationContext, cascadingValueContext ); @@ -1083,7 +1089,7 @@ private void validateReturnValueInContext(ValidationContext validation } //TODO GM: if possible integrate with validateParameterForGroup() - private void validateReturnValueForGroup(ValidationContext validationContext, ExecutableMetaData executableMetaData, T bean, Object value, + private void validateReturnValueForGroup(BaseBeanValidationContext validationContext, ExecutableMetaData executableMetaData, T bean, Object value, Group group) { Contracts.assertNotNull( executableMetaData, "executableMetaData may not be null" ); @@ -1121,7 +1127,7 @@ private void validateReturnValueForGroup(ValidationContext validationCont } } - private void validateReturnValueForSingleGroup(ValidationContext validationContext, ExecutableMetaData executableMetaData, T bean, Object value, Class oneGroup) { + private void validateReturnValueForSingleGroup(BaseBeanValidationContext validationContext, ExecutableMetaData executableMetaData, T bean, Object value, Class oneGroup) { // validate constraints at return value itself ValueContext valueContext = getExecutableValueContext( executableMetaData.getKind() == ElementKind.CONSTRUCTOR ? value : bean, @@ -1141,10 +1147,11 @@ private void validateReturnValueForSingleGroup(ValidationContext validati * * @param validationContext The validation context. * @param propertyPath The property path for which constraints have to be collected. + * * @return Returns an instance of {@code ValueContext} which describes the local validation context associated to - * the given property path. + * the given property path. */ - private ValueContext getValueContextForPropertyValidation(ValidationContext validationContext, PathImpl propertyPath) { + private BeanValueContext getValueContextForPropertyValidation(BaseBeanValidationContext validationContext, PathImpl propertyPath) { Class clazz = validationContext.getRootBeanClass(); BeanMetaData beanMetaData = validationContext.getRootBeanMetaData(); Object value = validationContext.getRootBean(); @@ -1204,10 +1211,9 @@ else if ( propertyPathNode.getKey() != null ) { throw LOG.getInvalidPropertyPathException( clazz, propertyPath.asString() ); } - validationContext.setValidatedProperty( propertyMetaData.getName() ); propertyPath.removeLeafNode(); - return ValueContext.getLocalExecutionContext( validatorScopedContext.getParameterNameProvider(), value, beanMetaData, propertyPath ); + return ValueContexts.getLocalExecutionContextForBean( validatorScopedContext.getParameterNameProvider(), value, beanMetaData, propertyPath ); } /** @@ -1216,14 +1222,14 @@ else if ( propertyPathNode.getKey() != null ) { * We are only able to use the static types as we don't have the value. *

* - * @param validationContext The validation context. + * @param rootBeanClass The class of the root bean. * @param propertyPath The property path for which constraints have to be collected. * @return Returns an instance of {@code ValueContext} which describes the local validation context associated to * the given property path. */ - private ValueContext getValueContextForValueValidation(ValidationContext validationContext, + private BeanValueContext getValueContextForValueValidation(Class rootBeanClass, PathImpl propertyPath) { - Class clazz = validationContext.getRootBeanClass(); + Class clazz = rootBeanClass; BeanMetaData beanMetaData = null; PropertyMetaData propertyMetaData = null; @@ -1257,19 +1263,19 @@ private ValueContext getValueContextForValueValidation(ValidationConte throw LOG.getInvalidPropertyPathException( clazz, propertyPath.asString() ); } - validationContext.setValidatedProperty( propertyMetaData.getName() ); propertyPath.removeLeafNode(); - return ValueContext.getLocalExecutionContext( validatorScopedContext.getParameterNameProvider(), clazz, beanMetaData, propertyPath ); + return ValueContexts.getLocalExecutionContextForValueValidation( validatorScopedContext.getParameterNameProvider(), beanMetaData, propertyPath ); } - private boolean isValidationRequired(ValidationContext validationContext, + private boolean isValidationRequired(BaseBeanValidationContext validationContext, ValueContext valueContext, MetaConstraint metaConstraint) { - // validateProperty()/validateValue() call, but this constraint is for another property - if ( validationContext.getValidatedProperty() != null && - !Objects.equals( validationContext.getValidatedProperty(), getPropertyName( metaConstraint.getLocation() ) ) ) { - return false; + // check if this validation context is qualified to validate the current meta constraint. + // For instance, in the case of validateProperty()/validateValue(), the current meta constraint + // could be for another property and, in this case, we don't validate it. + if ( !validationContext.appliesTo( metaConstraint ) ) { + return false; } if ( validationContext.hasMetaConstraintBeenProcessed( valueContext.getCurrentBean(), @@ -1286,23 +1292,24 @@ private boolean isValidationRequired(ValidationContext validationContext, validationContext, valueContext.getCurrentBean(), valueContext.getPropertyPath(), - metaConstraint.getElementType() + metaConstraint.getConstraintLocationKind() ); } - private boolean isReachable(ValidationContext validationContext, Object traversableObject, PathImpl path, ElementType type) { - if ( needToCallTraversableResolver( path, type ) ) { + private boolean isReachable(BaseBeanValidationContext validationContext, Object traversableObject, PathImpl path, + ConstraintLocationKind constraintLocationKind) { + if ( needToCallTraversableResolver( path, constraintLocationKind ) ) { return true; } - Path pathToObject = path.getPathWithoutLeafNode(); + Path pathToObject = PathImpl.createCopyWithoutLeafNode( path ); try { return validationContext.getTraversableResolver().isReachable( traversableObject, path.getLeafNode(), validationContext.getRootBeanClass(), pathToObject, - type + constraintLocationKind.getElementType() ); } catch (RuntimeException e) { @@ -1310,35 +1317,36 @@ private boolean isReachable(ValidationContext validationContext, Object trave } } - private boolean needToCallTraversableResolver(PathImpl path, ElementType type) { + private boolean needToCallTraversableResolver(PathImpl path, ConstraintLocationKind constraintLocationKind) { // as the TraversableResolver interface is designed right now it does not make sense to call it when // there is no traversable object hosting the property to be accessed. For this reason we don't call the resolver // for class level constraints (ElementType.TYPE) or top level method parameters or return values. // see also BV expert group discussion - http://lists.jboss.org/pipermail/beanvalidation-dev/2013-January/000722.html - return isClassLevelConstraint( type ) + return isClassLevelConstraint( constraintLocationKind ) || isCrossParameterValidation( path ) || isParameterValidation( path ) || isReturnValueValidation( path ); } - private boolean isCascadeRequired(ValidationContext validationContext, Object traversableObject, PathImpl path, ElementType type) { - if ( needToCallTraversableResolver( path, type ) ) { + private boolean isCascadeRequired(BaseBeanValidationContext validationContext, Object traversableObject, PathImpl path, + ConstraintLocationKind constraintLocationKind) { + if ( needToCallTraversableResolver( path, constraintLocationKind ) ) { return true; } - boolean isReachable = isReachable( validationContext, traversableObject, path, type ); + boolean isReachable = isReachable( validationContext, traversableObject, path, constraintLocationKind ); if ( !isReachable ) { return false; } - Path pathToObject = path.getPathWithoutLeafNode(); + Path pathToObject = PathImpl.createCopyWithoutLeafNode( path ); try { return validationContext.getTraversableResolver().isCascadable( traversableObject, path.getLeafNode(), validationContext.getRootBeanClass(), pathToObject, - type + constraintLocationKind.getElementType() ); } catch (RuntimeException e) { @@ -1346,8 +1354,8 @@ private boolean isCascadeRequired(ValidationContext validationContext, Object } } - private boolean isClassLevelConstraint(ElementType type) { - return ElementType.TYPE.equals( type ); + private boolean isClassLevelConstraint(ConstraintLocationKind constraintLocationKind) { + return ConstraintLocationKind.TYPE.equals( constraintLocationKind ); } private boolean isCrossParameterValidation(PathImpl path) { @@ -1362,11 +1370,11 @@ private boolean isReturnValueValidation(PathImpl path) { return path.getLeafNode().getKind() == ElementKind.RETURN_VALUE; } - private boolean shouldFailFast(ValidationContext validationContext) { + private boolean shouldFailFast(BaseBeanValidationContext validationContext) { return validationContext.isFailFastModeEnabled() && !validationContext.getFailingConstraints().isEmpty(); } - private PropertyMetaData getBeanPropertyMetaData(BeanMetaData beanMetaData, Path.Node propertyNode ) { + private PropertyMetaData getBeanPropertyMetaData(BeanMetaData beanMetaData, Path.Node propertyNode) { if ( !ElementKind.PROPERTY.equals( propertyNode.getKind() ) ) { throw LOG.getInvalidPropertyPathException( beanMetaData.getBeanClass(), propertyNode.getName() ); } @@ -1374,22 +1382,7 @@ private PropertyMetaData getBeanPropertyMetaData(BeanMetaData beanMetaData, P return beanMetaData.getMetaDataFor( propertyNode.getName() ); } - private Object getCascadableValue(ValidationContext validationContext, Object object, Cascadable cascadable) { + private Object getCascadableValue(BaseBeanValidationContext validationContext, Object object, Cascadable cascadable) { return cascadable.getValue( object ); } - - private String getPropertyName(ConstraintLocation location) { - if ( location instanceof TypeArgumentConstraintLocation ) { - location = ( (TypeArgumentConstraintLocation) location ).getOuterDelegate(); - } - - if ( location instanceof FieldConstraintLocation ) { - return ( (FieldConstraintLocation) location ).getPropertyName(); - } - else if ( location instanceof GetterConstraintLocation ) { - return ( (GetterConstraintLocation) location ).getPropertyName(); - } - - return null; - } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/AbstractConstraintValidatorManagerImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/AbstractConstraintValidatorManagerImpl.java new file mode 100644 index 0000000000..a8c14621ec --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/AbstractConstraintValidatorManagerImpl.java @@ -0,0 +1,178 @@ +/* + * 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.engine.constraintvalidation; + +import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; + +import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import javax.validation.ConstraintDeclarationException; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorFactory; +import javax.validation.metadata.ConstraintDescriptor; + +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidator; +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; +import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; +import org.hibernate.validator.internal.util.TypeHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; + +/** + * @author Guillaume Smet + */ +public abstract class AbstractConstraintValidatorManagerImpl implements ConstraintValidatorManager { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + /** + * The explicit or implicit default constraint validator factory. We always cache {@code ConstraintValidator} + * instances if they are created via the default instance and with the default initialization context. Constraint + * validator instances created via other factory instances (specified eg via {@code ValidatorFactory#usingContext()} + * or initialization context are only cached for the most recently used factory and context. + */ + private final ConstraintValidatorFactory defaultConstraintValidatorFactory; + + /** + * The explicit or implicit default constraint validator initialization context. We always cache + * {@code ConstraintValidator} instances if they are created via the default instance and with the default context. + * Constraint validator instances created via other factory instances (specified eg via + * {@code ValidatorFactory#usingContext()} or initialization context are only cached for the most recently used + * factory and context. + */ + private final HibernateConstraintValidatorInitializationContext defaultConstraintValidatorInitializationContext; + + /** + * Creates a new {@code ConstraintValidatorManager}. + * + * @param defaultConstraintValidatorFactory the default validator factory + * @param defaultConstraintValidatorInitializationContext the default initialization context + */ + public AbstractConstraintValidatorManagerImpl(ConstraintValidatorFactory defaultConstraintValidatorFactory, + HibernateConstraintValidatorInitializationContext defaultConstraintValidatorInitializationContext) { + this.defaultConstraintValidatorFactory = defaultConstraintValidatorFactory; + this.defaultConstraintValidatorInitializationContext = defaultConstraintValidatorInitializationContext; + } + + @Override + public ConstraintValidatorFactory getDefaultConstraintValidatorFactory() { + return defaultConstraintValidatorFactory; + } + + @Override + public HibernateConstraintValidatorInitializationContext getDefaultConstraintValidatorInitializationContext() { + return defaultConstraintValidatorInitializationContext; + } + + protected ConstraintValidator createAndInitializeValidator( + Type validatedValueType, + ConstraintDescriptorImpl descriptor, + ConstraintValidatorFactory constraintValidatorFactory, + HibernateConstraintValidatorInitializationContext initializationContext) { + + ConstraintValidatorDescriptor validatorDescriptor = findMatchingValidatorDescriptor( descriptor, validatedValueType ); + ConstraintValidator constraintValidator; + + if ( validatorDescriptor == null ) { + return null; + } + + constraintValidator = validatorDescriptor.newInstance( constraintValidatorFactory ); + initializeValidator( descriptor, constraintValidator, initializationContext ); + + return constraintValidator; + } + + /** + * Runs the validator resolution algorithm. + * + * @param validatedValueType The type of the value to be validated (the type of the member/class the constraint was placed on). + * + * @return The class of a matching validator. + */ + private ConstraintValidatorDescriptor findMatchingValidatorDescriptor(ConstraintDescriptorImpl descriptor, Type validatedValueType) { + Map> availableValidatorDescriptors = TypeHelper.getValidatorTypes( + descriptor.getAnnotationType(), + descriptor.getMatchingConstraintValidatorDescriptors() + ); + + List discoveredSuitableTypes = findSuitableValidatorTypes( validatedValueType, availableValidatorDescriptors.keySet() ); + resolveAssignableTypes( discoveredSuitableTypes ); + + if ( discoveredSuitableTypes.size() == 0 ) { + return null; + } + + if ( discoveredSuitableTypes.size() > 1 ) { + throw LOG.getMoreThanOneValidatorFoundForTypeException( validatedValueType, discoveredSuitableTypes ); + } + + Type suitableType = discoveredSuitableTypes.get( 0 ); + return availableValidatorDescriptors.get( suitableType ); + } + + private List findSuitableValidatorTypes(Type type, Iterable availableValidatorTypes) { + List determinedSuitableTypes = newArrayList(); + for ( Type validatorType : availableValidatorTypes ) { + if ( TypeHelper.isAssignable( validatorType, type ) + && !determinedSuitableTypes.contains( validatorType ) ) { + determinedSuitableTypes.add( validatorType ); + } + } + return determinedSuitableTypes; + } + + private void initializeValidator( + ConstraintDescriptor descriptor, + ConstraintValidator constraintValidator, + HibernateConstraintValidatorInitializationContext initializationContext) { + try { + if ( constraintValidator instanceof HibernateConstraintValidator ) { + ( (HibernateConstraintValidator) constraintValidator ).initialize( descriptor, initializationContext ); + } + constraintValidator.initialize( descriptor.getAnnotation() ); + } + catch (RuntimeException e) { + if ( e instanceof ConstraintDeclarationException ) { + throw e; + } + throw LOG.getUnableToInitializeConstraintValidatorException( constraintValidator.getClass(), e ); + } + } + + /** + * Tries to reduce all assignable classes down to a single class. + * + * @param assignableTypes The set of all classes which are assignable to the class of the value to be validated and + * which are handled by at least one of the validators for the specified constraint. + */ + private void resolveAssignableTypes(List assignableTypes) { + if ( assignableTypes.size() == 0 || assignableTypes.size() == 1 ) { + return; + } + + List typesToRemove = new ArrayList<>(); + do { + typesToRemove.clear(); + Type type = assignableTypes.get( 0 ); + for ( int i = 1; i < assignableTypes.size(); i++ ) { + if ( TypeHelper.isAssignable( type, assignableTypes.get( i ) ) ) { + typesToRemove.add( type ); + } + else if ( TypeHelper.isAssignable( assignableTypes.get( i ), type ) ) { + typesToRemove.add( assignableTypes.get( i ) ); + } + } + assignableTypes.removeAll( typesToRemove ); + } while ( typesToRemove.size() > 0 ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ClassBasedValidatorDescriptor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ClassBasedValidatorDescriptor.java index ab1bc49ece..fd5f8c96ce 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ClassBasedValidatorDescriptor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ClassBasedValidatorDescriptor.java @@ -25,28 +25,38 @@ * Represents an implementation of {@link ConstraintValidator}. * * @author Gunnar Morling + * @author Guillaume Smet */ class ClassBasedValidatorDescriptor implements ConstraintValidatorDescriptor { - private static final long serialVersionUID = -8207687559460098548L; private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); private final Class> validatorClass; private final Type validatedType; private final EnumSet validationTargets; - public ClassBasedValidatorDescriptor(Class> validatorClass) { + private ClassBasedValidatorDescriptor(Class> validatorClass) { this.validatorClass = validatorClass; - this.validatedType = TypeHelper.extractType( validatorClass ); + this.validatedType = TypeHelper.extractValidatedType( validatorClass ); this.validationTargets = determineValidationTargets( validatorClass ); } + public static ClassBasedValidatorDescriptor of(Class> validatorClass, + Class registeredConstraintAnnotationType) { + Type definedConstraintAnnotationType = TypeHelper.extractConstraintType( validatorClass ); + if ( !registeredConstraintAnnotationType.equals( definedConstraintAnnotationType ) ) { + throw LOG.getConstraintValidatorDefinitionConstraintMismatchException( validatorClass, registeredConstraintAnnotationType, + definedConstraintAnnotationType ); + } + + return new ClassBasedValidatorDescriptor( validatorClass ); + } + private static EnumSet determineValidationTargets(Class> validatorClass) { SupportedValidationTarget supportedTargetAnnotation = validatorClass.getAnnotation( - SupportedValidationTarget.class - ); + SupportedValidationTarget.class ); - //by default constraints target the annotated element + // by default constraints target the annotated element if ( supportedTargetAnnotation == null ) { return EnumSet.of( ValidationTarget.ANNOTATED_ELEMENT ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ComposingConstraintTree.java b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ComposingConstraintTree.java index 4d72ef1c10..ea6db39743 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ComposingConstraintTree.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ComposingConstraintTree.java @@ -9,22 +9,21 @@ import static org.hibernate.validator.constraints.CompositionType.ALL_FALSE; import static org.hibernate.validator.constraints.CompositionType.AND; import static org.hibernate.validator.constraints.CompositionType.OR; -import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; import java.lang.annotation.Annotation; import java.lang.invoke.MethodHandles; import java.lang.reflect.Type; -import java.util.Collections; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; -import java.util.Set; +import java.util.Optional; import java.util.stream.Collectors; import javax.validation.ConstraintValidator; -import javax.validation.ConstraintViolation; import org.hibernate.validator.constraints.CompositionType; -import org.hibernate.validator.internal.engine.ValidationContext; -import org.hibernate.validator.internal.engine.ValueContext; +import org.hibernate.validator.internal.engine.validationcontext.ValidationContext; +import org.hibernate.validator.internal.engine.valuecontext.ValueContext; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.internal.util.logging.Log; @@ -48,34 +47,34 @@ class ComposingConstraintTree extends ConstraintTree { @Immutable private final List> children; - public ComposingConstraintTree(ConstraintDescriptorImpl descriptor, Type validatedValueType) { - super( descriptor, validatedValueType ); + public ComposingConstraintTree(ConstraintValidatorManager constraintValidatorManager, ConstraintDescriptorImpl descriptor, Type validatedValueType) { + super( constraintValidatorManager, descriptor, validatedValueType ); this.children = descriptor.getComposingConstraintImpls().stream() - .map( desc -> createConstraintTree( desc ) ) + .map( desc -> createConstraintTree( constraintValidatorManager, desc ) ) .collect( Collectors.collectingAndThen( Collectors.toList(), CollectionHelper::toImmutableList ) ); } - private ConstraintTree createConstraintTree(ConstraintDescriptorImpl composingDescriptor) { + private ConstraintTree createConstraintTree(ConstraintValidatorManager constraintValidatorManager, ConstraintDescriptorImpl composingDescriptor) { if ( composingDescriptor.getComposingConstraintImpls().isEmpty() ) { - return new SimpleConstraintTree<>( composingDescriptor, getValidatedValueType() ); + return new SimpleConstraintTree<>( constraintValidatorManager, composingDescriptor, getValidatedValueType() ); } else { - return new ComposingConstraintTree<>( composingDescriptor, getValidatedValueType() ); + return new ComposingConstraintTree<>( constraintValidatorManager, composingDescriptor, getValidatedValueType() ); } } @Override - protected void validateConstraints(ValidationContext validationContext, + protected void validateConstraints(ValidationContext validationContext, ValueContext valueContext, - Set> constraintViolations) { + Collection violatedConstraintValidatorContexts) { CompositionResult compositionResult = validateComposingConstraints( - validationContext, valueContext, constraintViolations + validationContext, valueContext, violatedConstraintValidatorContexts ); - Set> localViolations; + Optional violatedLocalConstraintValidatorContext; // After all children are validated the actual ConstraintValidator of the constraint itself is executed - if ( mainConstraintNeedsEvaluation( validationContext, constraintViolations ) ) { + if ( mainConstraintNeedsEvaluation( validationContext, violatedConstraintValidatorContexts ) ) { if ( LOG.isTraceEnabled() ) { LOG.tracef( @@ -89,17 +88,12 @@ protected void validateConstraints(ValidationContext validationContext, ConstraintValidator validator = getInitializedConstraintValidator( validationContext, valueContext ); // create a constraint validator context - ConstraintValidatorContextImpl constraintValidatorContext = new ConstraintValidatorContextImpl( - validationContext.getParameterNames(), - validationContext.getClockProvider(), - valueContext.getPropertyPath(), - descriptor, - validationContext.getConstraintValidatorPayload() + ConstraintValidatorContextImpl constraintValidatorContext = validationContext.createConstraintValidatorContextFor( + descriptor, valueContext.getPropertyPath() ); // validate - localViolations = validateSingleConstraint( - validationContext, + violatedLocalConstraintValidatorContext = validateSingleConstraint( valueContext, constraintValidatorContext, validator @@ -107,7 +101,7 @@ protected void validateConstraints(ValidationContext validationContext, // We re-evaluate the boolean composition by taking into consideration also the violations // from the local constraintValidator - if ( localViolations.isEmpty() ) { + if ( !violatedLocalConstraintValidatorContext.isPresent() ) { compositionResult.setAtLeastOneTrue( true ); } else { @@ -115,24 +109,24 @@ protected void validateConstraints(ValidationContext validationContext, } } else { - localViolations = Collections.emptySet(); + violatedLocalConstraintValidatorContext = Optional.empty(); } - if ( !passesCompositionTypeRequirement( constraintViolations, compositionResult ) ) { + if ( !passesCompositionTypeRequirement( violatedConstraintValidatorContexts, compositionResult ) ) { prepareFinalConstraintViolations( - validationContext, valueContext, constraintViolations, localViolations + validationContext, valueContext, violatedConstraintValidatorContexts, violatedLocalConstraintValidatorContext ); } } - private boolean mainConstraintNeedsEvaluation(ValidationContext executionContext, - Set> constraintViolations) { + private boolean mainConstraintNeedsEvaluation(ValidationContext validationContext, + Collection violatedConstraintValidatorContexts) { // we are dealing with a composing constraint with no validator for the main constraint if ( !descriptor.getComposingConstraints().isEmpty() && descriptor.getMatchingConstraintValidatorDescriptors().isEmpty() ) { return false; } - if ( constraintViolations.isEmpty() ) { + if ( violatedConstraintValidatorContexts.isEmpty() ) { return true; } @@ -142,7 +136,7 @@ private boolean mainConstraintNeedsEvaluation(ValidationContext execution } // explicit fail fast mode - if ( executionContext.isFailFastModeEnabled() ) { + if ( validationContext.isFailFastModeEnabled() ) { return false; } @@ -153,33 +147,29 @@ private boolean mainConstraintNeedsEvaluation(ValidationContext execution * Before the final constraint violations can be reported back we need to check whether we have a composing * constraint whose result should be reported as single violation. * - * @param executionContext meta data about top level validation + * @param validationContext meta data about top level validation * @param valueContext meta data for currently validated value - * @param constraintViolations used to accumulate constraint violations - * @param localViolations set of constraint violations of top level constraint + * @param violatedConstraintValidatorContexts used to accumulate constraint validator contexts that cause constraint violations + * @param localConstraintValidatorContext an optional of constraint violations of top level constraint */ - private void prepareFinalConstraintViolations(ValidationContext executionContext, + private void prepareFinalConstraintViolations(ValidationContext validationContext, ValueContext valueContext, - Set> constraintViolations, - Set> localViolations) { + Collection violatedConstraintValidatorContexts, + Optional localConstraintValidatorContext) { if ( reportAsSingleViolation() ) { // We clear the current violations list anyway - constraintViolations.clear(); + violatedConstraintValidatorContexts.clear(); // But then we need to distinguish whether the local ConstraintValidator has reported // violations or not (or if there is no local ConstraintValidator at all). // If not we create a violation // using the error message in the annotation declaration at top level. - if ( localViolations.isEmpty() ) { - final String message = getDescriptor().getMessageTemplate(); - ConstraintViolationCreationContext constraintViolationCreationContext = new ConstraintViolationCreationContext( - message, - valueContext.getPropertyPath() - ); - ConstraintViolation violation = executionContext.createConstraintViolation( - valueContext, constraintViolationCreationContext, descriptor + if ( !localConstraintValidatorContext.isPresent() ) { + violatedConstraintValidatorContexts.add( + validationContext.createConstraintValidatorContextFor( + descriptor, valueContext.getPropertyPath() + ) ); - constraintViolations.add( violation ); } } @@ -191,28 +181,30 @@ private void prepareFinalConstraintViolations(ValidationContext execution // as checked in test CustomErrorMessage.java // If no violations have been reported from the local ConstraintValidator, or no such validator exists, // then we just add an empty list. - constraintViolations.addAll( localViolations ); + if ( localConstraintValidatorContext.isPresent() ) { + violatedConstraintValidatorContexts.add( localConstraintValidatorContext.get() ); + } } /** * Validates all composing constraints recursively. * - * @param executionContext Meta data about top level validation + * @param validationContext Meta data about top level validation * @param valueContext Meta data for currently validated value - * @param constraintViolations Used to accumulate constraint violations + * @param violatedConstraintValidatorContexts Used to accumulate constraint validator contexts that cause constraint violations * * @return Returns an instance of {@code CompositionResult} relevant for boolean composition of constraints */ - private CompositionResult validateComposingConstraints(ValidationContext executionContext, + private CompositionResult validateComposingConstraints(ValidationContext validationContext, ValueContext valueContext, - Set> constraintViolations) { + Collection violatedConstraintValidatorContexts) { CompositionResult compositionResult = new CompositionResult( true, false ); for ( ConstraintTree tree : children ) { - Set> tmpViolations = newHashSet( 5 ); - tree.validateConstraints( executionContext, valueContext, tmpViolations ); - constraintViolations.addAll( tmpViolations ); + List tmpConstraintValidatorContexts = new ArrayList<>( 5 ); + tree.validateConstraints( validationContext, valueContext, tmpConstraintValidatorContexts ); + violatedConstraintValidatorContexts.addAll( tmpConstraintValidatorContexts ); - if ( tmpViolations.isEmpty() ) { + if ( tmpConstraintValidatorContexts.isEmpty() ) { compositionResult.setAtLeastOneTrue( true ); // no need to further validate constraints, because at least one validation passed if ( descriptor.getCompositionType() == OR ) { @@ -222,7 +214,7 @@ private CompositionResult validateComposingConstraints(ValidationContext else { compositionResult.setAllTrue( false ); if ( descriptor.getCompositionType() == AND - && ( executionContext.isFailFastModeEnabled() || descriptor.isReportAsSingleViolation() ) ) { + && ( validationContext.isFailFastModeEnabled() || descriptor.isReportAsSingleViolation() ) ) { break; } } @@ -230,7 +222,7 @@ private CompositionResult validateComposingConstraints(ValidationContext return compositionResult; } - private boolean passesCompositionTypeRequirement(Set constraintViolations, CompositionResult compositionResult) { + private boolean passesCompositionTypeRequirement(Collection constraintViolations, CompositionResult compositionResult) { CompositionType compositionType = getDescriptor().getCompositionType(); boolean passedValidation = false; switch ( compositionType ) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintTree.java b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintTree.java index 3a5e8a08b0..ecf5f75f9c 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintTree.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintTree.java @@ -6,22 +6,20 @@ */ package org.hibernate.validator.internal.engine.constraintvalidation; -import static org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager.DUMMY_CONSTRAINT_VALIDATOR; -import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; - import java.lang.annotation.Annotation; import java.lang.invoke.MethodHandles; import java.lang.reflect.Type; -import java.util.Collections; -import java.util.Set; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; import javax.validation.ConstraintDeclarationException; import javax.validation.ConstraintValidator; -import javax.validation.ConstraintViolation; import javax.validation.ValidationException; -import org.hibernate.validator.internal.engine.ValidationContext; -import org.hibernate.validator.internal.engine.ValueContext; +import org.hibernate.validator.internal.engine.valuecontext.ValueContext; +import org.hibernate.validator.internal.engine.validationcontext.ValidationContext; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; @@ -48,37 +46,47 @@ public abstract class ConstraintTree { private final Type validatedValueType; - /** - * Either the initialized constraint validator for the default constraint validator factory or - * {@link ConstraintValidatorManager#DUMMY_CONSTRAINT_VALIDATOR}. - */ - private volatile ConstraintValidator constraintValidatorForDefaultConstraintValidatorFactoryAndInitializationContext; + private volatile ConstraintValidator defaultInitializedConstraintValidator; - protected ConstraintTree(ConstraintDescriptorImpl descriptor, Type validatedValueType) { + protected ConstraintTree(ConstraintValidatorManager constraintValidatorManager, ConstraintDescriptorImpl descriptor, Type validatedValueType) { this.descriptor = descriptor; this.validatedValueType = validatedValueType; + + if ( constraintValidatorManager.isPredefinedScope() ) { + this.defaultInitializedConstraintValidator = constraintValidatorManager.getInitializedValidator( validatedValueType, + descriptor, + constraintValidatorManager.getDefaultConstraintValidatorFactory(), + constraintValidatorManager.getDefaultConstraintValidatorInitializationContext() ); + } } - public static ConstraintTree of(ConstraintDescriptorImpl composingDescriptor, Type validatedValueType) { + public static ConstraintTree of(ConstraintValidatorManager constraintValidatorManager, + ConstraintDescriptorImpl composingDescriptor, Type validatedValueType) { if ( composingDescriptor.getComposingConstraintImpls().isEmpty() ) { - return new SimpleConstraintTree<>( composingDescriptor, validatedValueType ); + return new SimpleConstraintTree<>( constraintValidatorManager, composingDescriptor, validatedValueType ); } else { - return new ComposingConstraintTree<>( composingDescriptor, validatedValueType ); + return new ComposingConstraintTree<>( constraintValidatorManager, composingDescriptor, validatedValueType ); } } - public final boolean validateConstraints(ValidationContext executionContext, ValueContext valueContext) { - Set> constraintViolations = newHashSet( 5 ); - validateConstraints( executionContext, valueContext, constraintViolations ); - if ( !constraintViolations.isEmpty() ) { - executionContext.addConstraintFailures( constraintViolations ); + public final boolean validateConstraints(ValidationContext validationContext, ValueContext valueContext) { + List violatedConstraintValidatorContexts = new ArrayList<>( 5 ); + validateConstraints( validationContext, valueContext, violatedConstraintValidatorContexts ); + if ( !violatedConstraintValidatorContexts.isEmpty() ) { + for ( ConstraintValidatorContextImpl constraintValidatorContext : violatedConstraintValidatorContexts ) { + for ( ConstraintViolationCreationContext constraintViolationCreationContext : constraintValidatorContext.getConstraintViolationCreationContexts() ) { + validationContext.addConstraintFailure( + valueContext, constraintViolationCreationContext, constraintValidatorContext.getConstraintDescriptor() + ); + } + } return false; } return true; } - protected abstract void validateConstraints(ValidationContext executionContext, ValueContext valueContext, Set> constraintViolations); + protected abstract void validateConstraints(ValidationContext validationContext, ValueContext valueContext, Collection violatedConstraintValidatorContexts); public final ConstraintDescriptorImpl getDescriptor() { return descriptor; @@ -109,58 +117,59 @@ private ValidationException getExceptionForNullValidator(Type validatedValueType } } - protected final ConstraintValidator getInitializedConstraintValidator(ValidationContext validationContext, ValueContext valueContext) { + protected final ConstraintValidator getInitializedConstraintValidator(ValidationContext validationContext, ValueContext valueContext) { ConstraintValidator validator; - if ( validationContext.getConstraintValidatorFactory() == validationContext.getConstraintValidatorManager().getDefaultConstraintValidatorFactory() - && validationContext.getConstraintValidatorInitializationContext() == validationContext.getConstraintValidatorManager() - .getDefaultConstraintValidatorInitializationContext() ) { - validator = constraintValidatorForDefaultConstraintValidatorFactoryAndInitializationContext; - - if ( validator == null ) { - synchronized ( this ) { - validator = constraintValidatorForDefaultConstraintValidatorFactoryAndInitializationContext; - if ( validator == null ) { - validator = getInitializedConstraintValidator( validationContext ); - - constraintValidatorForDefaultConstraintValidatorFactoryAndInitializationContext = validator; + if ( validationContext.getConstraintValidatorManager().isPredefinedScope() ) { + validator = defaultInitializedConstraintValidator; + } + else { + if ( validationContext.getConstraintValidatorFactory() == validationContext.getConstraintValidatorManager().getDefaultConstraintValidatorFactory() + && validationContext.getConstraintValidatorInitializationContext() == validationContext.getConstraintValidatorManager() + .getDefaultConstraintValidatorInitializationContext() ) { + validator = defaultInitializedConstraintValidator; + + if ( validator == null ) { + synchronized ( this ) { + validator = defaultInitializedConstraintValidator; + if ( validator == null ) { + validator = validationContext.getConstraintValidatorManager().getInitializedValidator( + validatedValueType, + descriptor, + validationContext.getConstraintValidatorManager().getDefaultConstraintValidatorFactory(), + validationContext.getConstraintValidatorManager().getDefaultConstraintValidatorInitializationContext() ); + + defaultInitializedConstraintValidator = validator; + } } } } - } - else { - // For now, we don't cache the result in the ConstraintTree if we don't use the default constraint validator - // factory. Creating a lot of CHM for that cache might not be a good idea and we prefer being conservative - // for now. Note that we have the ConstraintValidatorManager cache that mitigates the situation. - // If you come up with a use case where it makes sense, please reach out to us. - validator = getInitializedConstraintValidator( validationContext ); + else { + // For now, we don't cache the result in the ConstraintTree if we don't use the default constraint validator + // factory. Creating a lot of CHM for that cache might not be a good idea and we prefer being conservative + // for now. Note that we have the ConstraintValidatorManager cache that mitigates the situation. + // If you come up with a use case where it makes sense, please reach out to us. + validator = validationContext.getConstraintValidatorManager().getInitializedValidator( + validatedValueType, + descriptor, + validationContext.getConstraintValidatorFactory(), + validationContext.getConstraintValidatorInitializationContext() + ); + } } - if ( validator == DUMMY_CONSTRAINT_VALIDATOR ) { + if ( validator == null ) { throw getExceptionForNullValidator( validatedValueType, valueContext.getPropertyPath().asString() ); } return validator; } - @SuppressWarnings("unchecked") - private ConstraintValidator getInitializedConstraintValidator(ValidationContext validationContext) { - ConstraintValidator validator = validationContext.getConstraintValidatorManager().getInitializedValidator( - validatedValueType, - descriptor, - validationContext.getConstraintValidatorFactory(), - validationContext.getConstraintValidatorInitializationContext() - ); - - if ( validator != null ) { - return validator; - } - else { - return (ConstraintValidator) DUMMY_CONSTRAINT_VALIDATOR; - } - } - - protected final Set> validateSingleConstraint(ValidationContext executionContext, + /** + * @return an {@link Optional#empty()} if there is no violation or a corresponding {@link ConstraintValidatorContextImpl} + * otherwise. + */ + protected final Optional validateSingleConstraint( ValueContext valueContext, ConstraintValidatorContextImpl constraintValidatorContext, ConstraintValidator validator) { @@ -179,11 +188,9 @@ protected final Set> validateSingleConstraint(Vali if ( !isValid ) { //We do not add these violations yet, since we don't know how they are //going to influence the final boolean evaluation - return executionContext.createConstraintViolations( - valueContext, constraintValidatorContext - ); + return Optional.of( constraintValidatorContext ); } - return Collections.emptySet(); + return Optional.empty(); } @Override diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorContextImpl.java index ff5c9c14d8..8af5369371 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorContextImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorContextImpl.java @@ -45,7 +45,6 @@ public class ConstraintValidatorContextImpl implements HibernateConstraintValida private Map messageParameters; private Map expressionVariables; - private final List methodParameterNames; private final ClockProvider clockProvider; private final PathImpl basePath; private final ConstraintDescriptor constraintDescriptor; @@ -54,9 +53,11 @@ public class ConstraintValidatorContextImpl implements HibernateConstraintValida private Object dynamicPayload; private final Object constraintValidatorPayload; - public ConstraintValidatorContextImpl(List methodParameterNames, ClockProvider clockProvider, - PathImpl propertyPath, ConstraintDescriptor constraintDescriptor, Object constraintValidatorPayload) { - this.methodParameterNames = methodParameterNames; + public ConstraintValidatorContextImpl( + ClockProvider clockProvider, + PathImpl propertyPath, + ConstraintDescriptor constraintDescriptor, + Object constraintValidatorPayload) { this.clockProvider = clockProvider; this.basePath = propertyPath; this.constraintDescriptor = constraintDescriptor; @@ -74,11 +75,10 @@ public final String getDefaultConstraintMessageTemplate() { } @Override - public final ConstraintViolationBuilder buildConstraintViolationWithTemplate(String messageTemplate) { + public ConstraintViolationBuilder buildConstraintViolationWithTemplate(String messageTemplate) { return new ConstraintViolationBuilderImpl( - methodParameterNames, messageTemplate, - PathImpl.createCopy( basePath ) + getCopyOfBasePath() ); } @@ -161,6 +161,10 @@ public final List getConstraintViolationCrea return CollectionHelper.toImmutableList( returnedConstraintViolationCreationContexts ); } + protected final PathImpl getCopyOfBasePath() { + return PathImpl.createCopy( basePath ); + } + private ConstraintViolationCreationContext getDefaultConstraintViolationCreationContext() { return new ConstraintViolationCreationContext( getDefaultConstraintMessageTemplate(), @@ -171,10 +175,6 @@ private ConstraintViolationCreationContext getDefaultConstraintViolationCreation ); } - public List getMethodParameterNames() { - return methodParameterNames; - } - private abstract class NodeBuilderBase { protected final String messageTemplate; @@ -202,16 +202,14 @@ public ConstraintValidatorContext addConstraintViolation() { } } - private class ConstraintViolationBuilderImpl extends NodeBuilderBase implements ConstraintViolationBuilder { - - private final List methodParameterNames; + protected class ConstraintViolationBuilderImpl extends NodeBuilderBase implements ConstraintViolationBuilder { - private ConstraintViolationBuilderImpl(List methodParameterNames, String template, PathImpl path) { + protected ConstraintViolationBuilderImpl(String template, PathImpl path) { super( template, path ); - this.methodParameterNames = methodParameterNames; } @Override + @Deprecated public NodeBuilderDefinedContext addNode(String name) { dropLeafNodeIfRequired(); propertyPath.addPropertyNode( name ); @@ -233,14 +231,7 @@ public LeafNodeBuilderCustomizableContext addBeanNode() { @Override public NodeBuilderDefinedContext addParameterNode(int index) { - if ( propertyPath.getLeafNode().getKind() != ElementKind.CROSS_PARAMETER ) { - throw LOG.getParameterNodeAddedForNonCrossParameterConstraintException( propertyPath ); - } - - dropLeafNodeIfRequired(); - propertyPath.addParameterNode( methodParameterNames.get( index ), index ); - - return new NodeBuilder( messageTemplate, propertyPath ); + throw LOG.getParameterNodeAddedForNonCrossParameterConstraintException( propertyPath ); } @Override @@ -251,22 +242,20 @@ public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(St } /** - * In case nodes are added from within a class-level or cross-parameter - * constraint, the node representing the constraint element will be - * dropped. inIterable(), getKey() etc. + * In case nodes are added from within a class-level constraint, the node representing + * the constraint element will be dropped. inIterable(), getKey() etc. */ private void dropLeafNodeIfRequired() { - if ( propertyPath.getLeafNode().getKind() == ElementKind.BEAN || propertyPath.getLeafNode() - .getKind() == ElementKind.CROSS_PARAMETER ) { - propertyPath = propertyPath.getPathWithoutLeafNode(); + if ( propertyPath.getLeafNode().getKind() == ElementKind.BEAN ) { + propertyPath = PathImpl.createCopyWithoutLeafNode( propertyPath ); } } } - private class NodeBuilder extends NodeBuilderBase + protected class NodeBuilder extends NodeBuilderBase implements NodeBuilderDefinedContext, LeafNodeBuilderDefinedContext, ContainerElementNodeBuilderDefinedContext { - private NodeBuilder(String template, PathImpl path) { + protected NodeBuilder(String template, PathImpl path) { super( template, path ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorDescriptor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorDescriptor.java index 7132ad1c9a..cb4da5b2ec 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorDescriptor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorDescriptor.java @@ -44,8 +44,9 @@ public interface ConstraintValidatorDescriptor { */ ConstraintValidator newInstance(ConstraintValidatorFactory constraintValidatorFactory); - static ConstraintValidatorDescriptor forClass(Class> validatorClass) { - return new ClassBasedValidatorDescriptor<>( validatorClass ); + static ConstraintValidatorDescriptor forClass(Class> validatorClass, + Class constraintAnnotationType) { + return ClassBasedValidatorDescriptor.of( validatorClass, constraintAnnotationType ); } static ConstraintValidatorDescriptor forLambda(Class annotationType, Type validatedType, ValidationCallable lambda) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorManager.java b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorManager.java index 0b090e3a38..d6e34c4bf8 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorManager.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorManager.java @@ -6,106 +6,21 @@ */ package org.hibernate.validator.internal.engine.constraintvalidation; -import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; - import java.lang.annotation.Annotation; -import java.lang.invoke.MethodHandles; import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.ConcurrentHashMap; -import javax.validation.ConstraintDeclarationException; import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; import javax.validation.ConstraintValidatorFactory; -import javax.validation.constraints.Null; -import javax.validation.metadata.ConstraintDescriptor; -import org.hibernate.validator.constraintvalidation.HibernateConstraintValidator; import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; -import org.hibernate.validator.internal.util.Contracts; -import org.hibernate.validator.internal.util.TypeHelper; -import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; -import org.hibernate.validator.internal.util.logging.Log; -import org.hibernate.validator.internal.util.logging.LoggerFactory; /** * Manager in charge of providing and caching initialized {@code ConstraintValidator} instances. * * @author Hardy Ferentschik */ -public class ConstraintValidatorManager { - - private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - - /** - * Dummy {@code ConstraintValidator} used as placeholder for the case that for a given context there exists - * no matching constraint validator instance - */ - static ConstraintValidator DUMMY_CONSTRAINT_VALIDATOR = new ConstraintValidator() { - - @Override - public boolean isValid(Object value, ConstraintValidatorContext context) { - return false; - } - }; - - /** - * The explicit or implicit default constraint validator factory. We always cache {@code ConstraintValidator} - * instances if they are created via the default instance and with the default initialization context. Constraint - * validator instances created via other factory instances (specified eg via {@code ValidatorFactory#usingContext()} - * or initialization context are only cached for the most recently used factory and context. - */ - private final ConstraintValidatorFactory defaultConstraintValidatorFactory; - - /** - * The explicit or implicit default constraint validator initialization context. We always cache - * {@code ConstraintValidator} instances if they are created via the default instance and with the default context. - * Constraint validator instances created via other factory instances (specified eg via - * {@code ValidatorFactory#usingContext()} or initialization context are only cached for the most recently used - * factory and context. - */ - private final HibernateConstraintValidatorInitializationContext defaultConstraintValidatorInitializationContext; - - /** - * The most recently used non default constraint validator factory. - */ - private volatile ConstraintValidatorFactory mostRecentlyUsedNonDefaultConstraintValidatorFactory; - - /** - * The most recently used non default constraint validator initialization context. - */ - private volatile HibernateConstraintValidatorInitializationContext mostRecentlyUsedNonDefaultConstraintValidatorInitializationContext; - - /** - * Used for synchronizing access to {@link #mostRecentlyUsedNonDefaultConstraintValidatorFactory} (which can be - * null itself). - */ - private final Object mostRecentlyUsedNonDefaultConstraintValidatorFactoryAndInitializationContextMutex = new Object(); - - /** - * Cache of initialized {@code ConstraintValidator} instances keyed against validated type, annotation, - * constraint validator factory and constraint validator initialization context ({@code CacheKey}). - */ - private final ConcurrentHashMap> constraintValidatorCache; - - /** - * Creates a new {@code ConstraintValidatorManager}. - * - * @param defaultConstraintValidatorFactory the default validator factory - * @param defaultConstraintValidatorInitializationContext the default initialization context - */ - public ConstraintValidatorManager(ConstraintValidatorFactory defaultConstraintValidatorFactory, - HibernateConstraintValidatorInitializationContext defaultConstraintValidatorInitializationContext) { - this.defaultConstraintValidatorFactory = defaultConstraintValidatorFactory; - this.defaultConstraintValidatorInitializationContext = defaultConstraintValidatorInitializationContext; - this.constraintValidatorCache = new ConcurrentHashMap<>(); - } +public interface ConstraintValidatorManager { /** * @param validatedValueType the type of the value to be validated. Cannot be {@code null}. @@ -117,257 +32,17 @@ public ConstraintValidatorManager(ConstraintValidatorFactory defaultConstraintVa * @return an initialized constraint validator for the given type and annotation of the value to be validated. * {@code null} is returned if no matching constraint validator could be found. */ - public ConstraintValidator getInitializedValidator( + ConstraintValidator getInitializedValidator( Type validatedValueType, ConstraintDescriptorImpl descriptor, ConstraintValidatorFactory constraintValidatorFactory, - HibernateConstraintValidatorInitializationContext initializationContext) { - Contracts.assertNotNull( validatedValueType ); - Contracts.assertNotNull( descriptor ); - Contracts.assertNotNull( constraintValidatorFactory ); - Contracts.assertNotNull( initializationContext ); - - CacheKey key = new CacheKey( descriptor.getAnnotationDescriptor(), validatedValueType, constraintValidatorFactory, initializationContext ); - - @SuppressWarnings("unchecked") - ConstraintValidator constraintValidator = (ConstraintValidator) constraintValidatorCache.get( key ); - - if ( constraintValidator == null ) { - constraintValidator = createAndInitializeValidator( validatedValueType, descriptor, constraintValidatorFactory, initializationContext ); - constraintValidator = cacheValidator( key, constraintValidator ); - } - else { - LOG.tracef( "Constraint validator %s found in cache.", constraintValidator ); - } - - return DUMMY_CONSTRAINT_VALIDATOR == constraintValidator ? null : constraintValidator; - } - - private ConstraintValidator cacheValidator(CacheKey key, - ConstraintValidator constraintValidator) { - // we only cache constraint validator instances for the default and most recently used factory - if ( ( key.getConstraintValidatorFactory() != defaultConstraintValidatorFactory - && key.getConstraintValidatorFactory() != mostRecentlyUsedNonDefaultConstraintValidatorFactory ) || - ( key.getConstraintValidatorInitializationContext() != defaultConstraintValidatorInitializationContext - && key.getConstraintValidatorInitializationContext() != mostRecentlyUsedNonDefaultConstraintValidatorInitializationContext ) ) { - - synchronized ( mostRecentlyUsedNonDefaultConstraintValidatorFactoryAndInitializationContextMutex ) { - if ( key.constraintValidatorFactory != mostRecentlyUsedNonDefaultConstraintValidatorFactory || - key.constraintValidatorInitializationContext != mostRecentlyUsedNonDefaultConstraintValidatorInitializationContext ) { - clearEntries( mostRecentlyUsedNonDefaultConstraintValidatorFactory, mostRecentlyUsedNonDefaultConstraintValidatorInitializationContext ); - mostRecentlyUsedNonDefaultConstraintValidatorFactory = key.getConstraintValidatorFactory(); - mostRecentlyUsedNonDefaultConstraintValidatorInitializationContext = key.getConstraintValidatorInitializationContext(); - } - } - } - - @SuppressWarnings("unchecked") - ConstraintValidator cached = (ConstraintValidator) constraintValidatorCache.putIfAbsent( key, constraintValidator ); - - return cached != null ? cached : constraintValidator; - } - - @SuppressWarnings("unchecked") - private ConstraintValidator createAndInitializeValidator( - Type validatedValueType, - ConstraintDescriptorImpl descriptor, - ConstraintValidatorFactory constraintValidatorFactory, - HibernateConstraintValidatorInitializationContext initializationContext) { - - ConstraintValidatorDescriptor validatorDescriptor = findMatchingValidatorDescriptor( descriptor, validatedValueType ); - ConstraintValidator constraintValidator; - - if ( validatorDescriptor == null ) { - constraintValidator = (ConstraintValidator) DUMMY_CONSTRAINT_VALIDATOR; - } - else { - constraintValidator = validatorDescriptor.newInstance( constraintValidatorFactory ); - initializeValidator( descriptor, constraintValidator, initializationContext ); - } - - return constraintValidator; - } - - private void clearEntries(ConstraintValidatorFactory constraintValidatorFactory, HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext) { - Iterator>> cacheEntries = constraintValidatorCache.entrySet().iterator(); - - while ( cacheEntries.hasNext() ) { - Entry> cacheEntry = cacheEntries.next(); - if ( cacheEntry.getKey().getConstraintValidatorFactory() == constraintValidatorFactory && - cacheEntry.getKey().getConstraintValidatorInitializationContext() == constraintValidatorInitializationContext ) { - constraintValidatorFactory.releaseInstance( cacheEntry.getValue() ); - cacheEntries.remove(); - } - } - } - - public void clear() { - for ( Map.Entry> entry : constraintValidatorCache.entrySet() ) { - entry.getKey().getConstraintValidatorFactory().releaseInstance( entry.getValue() ); - } - constraintValidatorCache.clear(); - } - - public ConstraintValidatorFactory getDefaultConstraintValidatorFactory() { - return defaultConstraintValidatorFactory; - } - - public HibernateConstraintValidatorInitializationContext getDefaultConstraintValidatorInitializationContext() { - return defaultConstraintValidatorInitializationContext; - } - - public int numberOfCachedConstraintValidatorInstances() { - return constraintValidatorCache.size(); - } - - /** - * Runs the validator resolution algorithm. - * - * @param validatedValueType The type of the value to be validated (the type of the member/class the constraint was placed on). - * - * @return The class of a matching validator. - */ - private ConstraintValidatorDescriptor findMatchingValidatorDescriptor(ConstraintDescriptorImpl descriptor, Type validatedValueType) { - Map> availableValidatorDescriptors = TypeHelper.getValidatorTypes( - descriptor.getAnnotationType(), - descriptor.getMatchingConstraintValidatorDescriptors() - ); - - List discoveredSuitableTypes = findSuitableValidatorTypes( validatedValueType, availableValidatorDescriptors.keySet() ); - resolveAssignableTypes( discoveredSuitableTypes ); - - if ( discoveredSuitableTypes.size() == 0 ) { - return null; - } - - if ( discoveredSuitableTypes.size() > 1 ) { - throw LOG.getMoreThanOneValidatorFoundForTypeException( validatedValueType, discoveredSuitableTypes ); - } - - Type suitableType = discoveredSuitableTypes.get( 0 ); - return availableValidatorDescriptors.get( suitableType ); - } - - private List findSuitableValidatorTypes(Type type, Iterable availableValidatorTypes) { - List determinedSuitableTypes = newArrayList(); - for ( Type validatorType : availableValidatorTypes ) { - if ( TypeHelper.isAssignable( validatorType, type ) - && !determinedSuitableTypes.contains( validatorType ) ) { - determinedSuitableTypes.add( validatorType ); - } - } - return determinedSuitableTypes; - } - - private void initializeValidator( - ConstraintDescriptor descriptor, - ConstraintValidator constraintValidator, - HibernateConstraintValidatorInitializationContext initializationContext) { - try { - if ( constraintValidator instanceof HibernateConstraintValidator ) { - ( (HibernateConstraintValidator) constraintValidator ).initialize( descriptor, initializationContext ); - } - constraintValidator.initialize( descriptor.getAnnotation() ); - } - catch (RuntimeException e) { - if ( e instanceof ConstraintDeclarationException ) { - throw e; - } - throw LOG.getUnableToInitializeConstraintValidatorException( constraintValidator.getClass(), e ); - } - } - - /** - * Tries to reduce all assignable classes down to a single class. - * - * @param assignableTypes The set of all classes which are assignable to the class of the value to be validated and - * which are handled by at least one of the validators for the specified constraint. - */ - private void resolveAssignableTypes(List assignableTypes) { - if ( assignableTypes.size() == 0 || assignableTypes.size() == 1 ) { - return; - } - - List typesToRemove = new ArrayList<>(); - do { - typesToRemove.clear(); - Type type = assignableTypes.get( 0 ); - for ( int i = 1; i < assignableTypes.size(); i++ ) { - if ( TypeHelper.isAssignable( type, assignableTypes.get( i ) ) ) { - typesToRemove.add( type ); - } - else if ( TypeHelper.isAssignable( assignableTypes.get( i ), type ) ) { - typesToRemove.add( assignableTypes.get( i ) ); - } - } - assignableTypes.removeAll( typesToRemove ); - } while ( typesToRemove.size() > 0 ); - } - - private static final class CacheKey { - // These members are not final for optimization purposes - private ConstraintAnnotationDescriptor annotationDescriptor; - private Type validatedType; - private ConstraintValidatorFactory constraintValidatorFactory; - private HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext; - private int hashCode; - - private CacheKey(ConstraintAnnotationDescriptor annotationDescriptor, Type validatorType, ConstraintValidatorFactory constraintValidatorFactory, - HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext) { - this.annotationDescriptor = annotationDescriptor; - this.validatedType = validatorType; - this.constraintValidatorFactory = constraintValidatorFactory; - this.constraintValidatorInitializationContext = constraintValidatorInitializationContext; - this.hashCode = createHashCode(); - } - - public ConstraintValidatorFactory getConstraintValidatorFactory() { - return constraintValidatorFactory; - } - - public HibernateConstraintValidatorInitializationContext getConstraintValidatorInitializationContext() { - return constraintValidatorInitializationContext; - } - - @Override - public boolean equals(Object o) { - if ( this == o ) { - return true; - } - // no need to check for the type here considering it's only used in a typed map - if ( o == null ) { - return false; - } - - CacheKey other = (CacheKey) o; + HibernateConstraintValidatorInitializationContext initializationContext); - if ( !annotationDescriptor.equals( other.annotationDescriptor ) ) { - return false; - } - if ( !validatedType.equals( other.validatedType ) ) { - return false; - } - if ( !constraintValidatorFactory.equals( other.constraintValidatorFactory ) ) { - return false; - } - if ( !constraintValidatorInitializationContext.equals( other.constraintValidatorInitializationContext ) ) { - return false; - } + void clear(); - return true; - } + ConstraintValidatorFactory getDefaultConstraintValidatorFactory(); - @Override - public int hashCode() { - return hashCode; - } + HibernateConstraintValidatorInitializationContext getDefaultConstraintValidatorInitializationContext(); - private int createHashCode() { - int result = annotationDescriptor.hashCode(); - result = 31 * result + validatedType.hashCode(); - result = 31 * result + constraintValidatorFactory.hashCode(); - result = 31 * result + constraintValidatorInitializationContext.hashCode(); - return result; - } - } + boolean isPredefinedScope(); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorManagerImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorManagerImpl.java new file mode 100644 index 0000000000..bce18a9b8d --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorManagerImpl.java @@ -0,0 +1,243 @@ +/* + * 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.engine.constraintvalidation; + +import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Type; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.ConstraintValidatorFactory; +import javax.validation.constraints.Null; + +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; +import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; +import org.hibernate.validator.internal.util.Contracts; +import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; + +/** + * Default implementation of the {@link ConstraintValidatorManager}. + * + * @author Hardy Ferentschik + * @author Guillaume Smet + */ +public class ConstraintValidatorManagerImpl extends AbstractConstraintValidatorManagerImpl { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + /** + * Dummy {@code ConstraintValidator} used as placeholder for the case that for a given context there exists + * no matching constraint validator instance + */ + private static final ConstraintValidator DUMMY_CONSTRAINT_VALIDATOR = new ConstraintValidator() { + + @Override + public boolean isValid(Object value, ConstraintValidatorContext context) { + return false; + } + }; + + /** + * The most recently used non default constraint validator factory. + */ + private volatile ConstraintValidatorFactory mostRecentlyUsedNonDefaultConstraintValidatorFactory; + + /** + * The most recently used non default constraint validator initialization context. + */ + private volatile HibernateConstraintValidatorInitializationContext mostRecentlyUsedNonDefaultConstraintValidatorInitializationContext; + + /** + * Used for synchronizing access to {@link #mostRecentlyUsedNonDefaultConstraintValidatorFactory} (which can be + * null itself). + */ + private final Object mostRecentlyUsedNonDefaultConstraintValidatorFactoryAndInitializationContextMutex = new Object(); + + /** + * Cache of initialized {@code ConstraintValidator} instances keyed against validated type, annotation, + * constraint validator factory and constraint validator initialization context ({@code CacheKey}). + */ + private final ConcurrentHashMap> constraintValidatorCache; + + /** + * Creates a new {@code ConstraintValidatorManager}. + * + * @param defaultConstraintValidatorFactory the default validator factory + * @param defaultConstraintValidatorInitializationContext the default initialization context + */ + public ConstraintValidatorManagerImpl(ConstraintValidatorFactory defaultConstraintValidatorFactory, + HibernateConstraintValidatorInitializationContext defaultConstraintValidatorInitializationContext) { + super( defaultConstraintValidatorFactory, defaultConstraintValidatorInitializationContext ); + this.constraintValidatorCache = new ConcurrentHashMap<>(); + } + + @Override + public boolean isPredefinedScope() { + return false; + } + + /** + * @param validatedValueType the type of the value to be validated. Cannot be {@code null}. + * @param descriptor the constraint descriptor for which to get an initialized constraint validator. Cannot be {@code null} + * @param constraintValidatorFactory constraint factory used to instantiate the constraint validator. Cannot be {@code null}. + * @param initializationContext context used on constraint validator initialization + * @param the annotation type + * + * @return an initialized constraint validator for the given type and annotation of the value to be validated. + * {@code null} is returned if no matching constraint validator could be found. + */ + @Override + public ConstraintValidator getInitializedValidator( + Type validatedValueType, + ConstraintDescriptorImpl descriptor, + ConstraintValidatorFactory constraintValidatorFactory, + HibernateConstraintValidatorInitializationContext initializationContext) { + Contracts.assertNotNull( validatedValueType ); + Contracts.assertNotNull( descriptor ); + Contracts.assertNotNull( constraintValidatorFactory ); + Contracts.assertNotNull( initializationContext ); + + CacheKey key = new CacheKey( descriptor.getAnnotationDescriptor(), validatedValueType, constraintValidatorFactory, initializationContext ); + + @SuppressWarnings("unchecked") + ConstraintValidator constraintValidator = (ConstraintValidator) constraintValidatorCache.get( key ); + + if ( constraintValidator == null ) { + constraintValidator = createAndInitializeValidator( validatedValueType, descriptor, constraintValidatorFactory, initializationContext ); + constraintValidator = cacheValidator( key, constraintValidator ); + } + else { + LOG.tracef( "Constraint validator %s found in cache.", constraintValidator ); + } + + return DUMMY_CONSTRAINT_VALIDATOR == constraintValidator ? null : constraintValidator; + } + + private ConstraintValidator cacheValidator(CacheKey key, + ConstraintValidator constraintValidator) { + // we only cache constraint validator instances for the default and most recently used factory + if ( ( key.getConstraintValidatorFactory() != getDefaultConstraintValidatorFactory() + && key.getConstraintValidatorFactory() != mostRecentlyUsedNonDefaultConstraintValidatorFactory ) || + ( key.getConstraintValidatorInitializationContext() != getDefaultConstraintValidatorInitializationContext() + && key.getConstraintValidatorInitializationContext() != mostRecentlyUsedNonDefaultConstraintValidatorInitializationContext ) ) { + + synchronized ( mostRecentlyUsedNonDefaultConstraintValidatorFactoryAndInitializationContextMutex ) { + if ( key.constraintValidatorFactory != mostRecentlyUsedNonDefaultConstraintValidatorFactory || + key.constraintValidatorInitializationContext != mostRecentlyUsedNonDefaultConstraintValidatorInitializationContext ) { + clearEntries( mostRecentlyUsedNonDefaultConstraintValidatorFactory, mostRecentlyUsedNonDefaultConstraintValidatorInitializationContext ); + mostRecentlyUsedNonDefaultConstraintValidatorFactory = key.getConstraintValidatorFactory(); + mostRecentlyUsedNonDefaultConstraintValidatorInitializationContext = key.getConstraintValidatorInitializationContext(); + } + } + } + + @SuppressWarnings("unchecked") + ConstraintValidator cached = (ConstraintValidator) constraintValidatorCache.putIfAbsent( key, + constraintValidator != null ? constraintValidator : DUMMY_CONSTRAINT_VALIDATOR ); + + return cached != null ? cached : constraintValidator; + } + + private void clearEntries(ConstraintValidatorFactory constraintValidatorFactory, HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext) { + Iterator>> cacheEntries = constraintValidatorCache.entrySet().iterator(); + + while ( cacheEntries.hasNext() ) { + Entry> cacheEntry = cacheEntries.next(); + if ( cacheEntry.getKey().getConstraintValidatorFactory() == constraintValidatorFactory && + cacheEntry.getKey().getConstraintValidatorInitializationContext() == constraintValidatorInitializationContext ) { + constraintValidatorFactory.releaseInstance( cacheEntry.getValue() ); + cacheEntries.remove(); + } + } + } + + @Override + public void clear() { + for ( Map.Entry> entry : constraintValidatorCache.entrySet() ) { + entry.getKey().getConstraintValidatorFactory().releaseInstance( entry.getValue() ); + } + constraintValidatorCache.clear(); + } + + public int numberOfCachedConstraintValidatorInstances() { + return constraintValidatorCache.size(); + } + + private static final class CacheKey { + // These members are not final for optimization purposes + private ConstraintAnnotationDescriptor annotationDescriptor; + private Type validatedType; + private ConstraintValidatorFactory constraintValidatorFactory; + private HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext; + private int hashCode; + + private CacheKey(ConstraintAnnotationDescriptor annotationDescriptor, Type validatorType, ConstraintValidatorFactory constraintValidatorFactory, + HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext) { + this.annotationDescriptor = annotationDescriptor; + this.validatedType = validatorType; + this.constraintValidatorFactory = constraintValidatorFactory; + this.constraintValidatorInitializationContext = constraintValidatorInitializationContext; + this.hashCode = createHashCode(); + } + + public ConstraintValidatorFactory getConstraintValidatorFactory() { + return constraintValidatorFactory; + } + + public HibernateConstraintValidatorInitializationContext getConstraintValidatorInitializationContext() { + return constraintValidatorInitializationContext; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + // no need to check for the type here considering it's only used in a typed map + if ( o == null ) { + return false; + } + + CacheKey other = (CacheKey) o; + + if ( !annotationDescriptor.equals( other.annotationDescriptor ) ) { + return false; + } + if ( !validatedType.equals( other.validatedType ) ) { + return false; + } + if ( !constraintValidatorFactory.equals( other.constraintValidatorFactory ) ) { + return false; + } + if ( !constraintValidatorInitializationContext.equals( other.constraintValidatorInitializationContext ) ) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return hashCode; + } + + private int createHashCode() { + int result = annotationDescriptor.hashCode(); + result = 31 * result + validatedType.hashCode(); + result = 31 * result + constraintValidatorFactory.hashCode(); + result = 31 * result + constraintValidatorInitializationContext.hashCode(); + return result; + } + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/CrossParameterConstraintValidatorContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/CrossParameterConstraintValidatorContextImpl.java new file mode 100644 index 0000000000..2ac491a84d --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/CrossParameterConstraintValidatorContextImpl.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.engine.constraintvalidation; + +import java.util.List; + +import javax.validation.ClockProvider; +import javax.validation.ElementKind; +import javax.validation.metadata.ConstraintDescriptor; + +import org.hibernate.validator.constraintvalidation.HibernateCrossParameterConstraintValidatorContext; +import org.hibernate.validator.internal.engine.path.PathImpl; +import org.hibernate.validator.internal.util.Contracts; + +/** + * @author Marko Bekhta + */ +public class CrossParameterConstraintValidatorContextImpl extends ConstraintValidatorContextImpl implements HibernateCrossParameterConstraintValidatorContext { + + private final List methodParameterNames; + + public CrossParameterConstraintValidatorContextImpl(List methodParameterNames, ClockProvider clockProvider, PathImpl propertyPath, ConstraintDescriptor constraintDescriptor, Object constraintValidatorPayload) { + super( clockProvider, propertyPath, constraintDescriptor, constraintValidatorPayload ); + Contracts.assertTrue( propertyPath.getLeafNode().getKind() == ElementKind.CROSS_PARAMETER, "Context can only be used for corss parameter validation" ); + this.methodParameterNames = methodParameterNames; + } + + @Override + public final ConstraintViolationBuilder buildConstraintViolationWithTemplate(String messageTemplate) { + return new CrossParameterConstraintViolationBuilderImpl( + methodParameterNames, + messageTemplate, + getCopyOfBasePath() + ); + } + + @Override + public List getMethodParameterNames() { + return methodParameterNames; + } + + @Override + public T unwrap(Class type) { + //allow unwrapping into public super types + if ( type.isAssignableFrom( HibernateCrossParameterConstraintValidatorContext.class ) ) { + return type.cast( this ); + } + return super.unwrap( type ); + } + + private class CrossParameterConstraintViolationBuilderImpl extends ConstraintViolationBuilderImpl { + + private final List methodParameterNames; + + private CrossParameterConstraintViolationBuilderImpl(List methodParameterNames, String template, PathImpl path) { + super( template, path ); + this.methodParameterNames = methodParameterNames; + } + + @Override + public NodeBuilderDefinedContext addParameterNode(int index) { + dropLeafNode(); + propertyPath.addParameterNode( methodParameterNames.get( index ), index ); + + return new NodeBuilder( messageTemplate, propertyPath ); + } + + private void dropLeafNode() { + propertyPath = PathImpl.createCopyWithoutLeafNode( propertyPath ); + } + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/PredefinedScopeConstraintValidatorManagerImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/PredefinedScopeConstraintValidatorManagerImpl.java new file mode 100644 index 0000000000..f341f51689 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/PredefinedScopeConstraintValidatorManagerImpl.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.engine.constraintvalidation; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorFactory; + +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; +import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; +import org.hibernate.validator.internal.util.Contracts; + +/** + * Implementation of the {@link ConstraintValidatorManager} for the predefined scope ValidatorFactory. + * + * @author Guillaume Smet + */ +public class PredefinedScopeConstraintValidatorManagerImpl extends AbstractConstraintValidatorManagerImpl { + + /** + * Creates a new {@code ConstraintValidatorManager}. + * + * @param defaultConstraintValidatorFactory the default validator factory + * @param defaultConstraintValidatorInitializationContext the default initialization context + */ + public PredefinedScopeConstraintValidatorManagerImpl( + ConstraintValidatorFactory defaultConstraintValidatorFactory, + HibernateConstraintValidatorInitializationContext defaultConstraintValidatorInitializationContext + ) { + super( defaultConstraintValidatorFactory, defaultConstraintValidatorInitializationContext ); + } + + /** + * @param validatedValueType the type of the value to be validated. Cannot be {@code null}. + * @param descriptor the constraint descriptor for which to get an initialized constraint validator. Cannot be {@code null} + * @param constraintValidatorFactory constraint factory used to instantiate the constraint validator. Cannot be {@code null}. + * @param initializationContext context used on constraint validator initialization + * @param the annotation type + * + * @return an initialized constraint validator for the given type and annotation of the value to be validated. + * {@code null} is returned if no matching constraint validator could be found. + */ + @Override + public ConstraintValidator getInitializedValidator( + Type validatedValueType, + ConstraintDescriptorImpl descriptor, + ConstraintValidatorFactory constraintValidatorFactory, + HibernateConstraintValidatorInitializationContext initializationContext) { + Contracts.assertNotNull( validatedValueType ); + Contracts.assertNotNull( descriptor ); + Contracts.assertNotNull( constraintValidatorFactory ); + Contracts.assertNotNull( initializationContext ); + + return createAndInitializeValidator( validatedValueType, descriptor, constraintValidatorFactory, initializationContext ); + } + + @Override + public boolean isPredefinedScope() { + return true; + } + + @Override + public void clear() { + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/SimpleConstraintTree.java b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/SimpleConstraintTree.java index 586c398708..aae2dbb8ad 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/SimpleConstraintTree.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/SimpleConstraintTree.java @@ -9,13 +9,12 @@ import java.lang.annotation.Annotation; import java.lang.invoke.MethodHandles; import java.lang.reflect.Type; -import java.util.Set; +import java.util.Collection; import javax.validation.ConstraintValidator; -import javax.validation.ConstraintViolation; -import org.hibernate.validator.internal.engine.ValidationContext; -import org.hibernate.validator.internal.engine.ValueContext; +import org.hibernate.validator.internal.engine.validationcontext.ValidationContext; +import org.hibernate.validator.internal.engine.valuecontext.ValueContext; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; @@ -34,14 +33,14 @@ class SimpleConstraintTree extends ConstraintTree { private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - public SimpleConstraintTree(ConstraintDescriptorImpl descriptor, Type validatedValueType) { - super( descriptor, validatedValueType ); + public SimpleConstraintTree(ConstraintValidatorManager constraintValidatorManager, ConstraintDescriptorImpl descriptor, Type validatedValueType) { + super( constraintValidatorManager, descriptor, validatedValueType ); } @Override - protected void validateConstraints(ValidationContext validationContext, + protected void validateConstraints(ValidationContext validationContext, ValueContext valueContext, - Set> constraintViolations) { + Collection violatedConstraintValidatorContexts) { if ( LOG.isTraceEnabled() ) { LOG.tracef( @@ -55,22 +54,13 @@ protected void validateConstraints(ValidationContext validationContext, ConstraintValidator validator = getInitializedConstraintValidator( validationContext, valueContext ); // create a constraint validator context - ConstraintValidatorContextImpl constraintValidatorContext = new ConstraintValidatorContextImpl( - validationContext.getParameterNames(), - validationContext.getClockProvider(), - valueContext.getPropertyPath(), - descriptor, - validationContext.getConstraintValidatorPayload() + ConstraintValidatorContextImpl constraintValidatorContext = validationContext.createConstraintValidatorContextFor( + descriptor, valueContext.getPropertyPath() ); // validate - constraintViolations.addAll( - validateSingleConstraint( - validationContext, - valueContext, - constraintValidatorContext, - validator - ) - ); + if ( validateSingleConstraint( valueContext, constraintValidatorContext, validator ).isPresent() ) { + violatedConstraintValidatorContexts.add( constraintValidatorContext ); + } } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/el/SimpleELContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/el/SimpleELContext.java index c4d2518b53..8647e34837 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/el/SimpleELContext.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/el/SimpleELContext.java @@ -15,7 +15,6 @@ import javax.el.MapELResolver; import javax.el.ResourceBundleELResolver; import javax.el.StandardELContext; -import javax.el.StaticFieldELResolver; /** * @author Hardy Ferentschik @@ -25,7 +24,6 @@ public class SimpleELContext extends StandardELContext { private static final ELResolver DEFAULT_RESOLVER = new CompositeELResolver() { { add( new RootResolver() ); - add( new StaticFieldELResolver() ); add( new ArrayELResolver( true ) ); add( new ListELResolver( true ) ); add( new MapELResolver( true ) ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/package-info.java b/engine/src/main/java/org/hibernate/validator/internal/engine/package-info.java index eda64a88af..d154589477 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/package-info.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/package-info.java @@ -6,6 +6,6 @@ */ /** - * Implementations for the core interfaces of JSR-380. + * Implementations for the core interfaces of Jakarta Bean Validation. */ package org.hibernate.validator.internal.engine; diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/path/PathImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/path/PathImpl.java index 0a7bd6bb8c..d83a9e8f7e 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/path/PathImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/path/PathImpl.java @@ -105,12 +105,13 @@ public static PathImpl createCopy(PathImpl path) { return new PathImpl( path ); } - public boolean isRootPath() { - return nodeList.size() == 1 && nodeList.get( 0 ).getName() == null; + public static PathImpl createCopyWithoutLeafNode(PathImpl path) { + return new PathImpl( path.nodeList.subList( 0, path.nodeList.size() - 1 ) ); } - public PathImpl getPathWithoutLeafNode() { - return new PathImpl( nodeList.subList( 0, nodeList.size() - 1 ) ); + + public boolean isRootPath() { + return nodeList.size() == 1 && nodeList.get( 0 ).getName() == null; } public NodeImpl addPropertyNode(String nodeName) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/resolver/JPATraversableResolver.java b/engine/src/main/java/org/hibernate/validator/internal/engine/resolver/JPATraversableResolver.java index b1d401ea9a..c4ed299cab 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/resolver/JPATraversableResolver.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/resolver/JPATraversableResolver.java @@ -21,11 +21,13 @@ * query the reachability of a property. * This resolver will be automatically enabled if JPA 2 is on the classpath and the default {@code TraversableResolver} is * used. + *

+ * This class needs to be public as it's instantiated via a privileged action that is not in this package. * * @author Hardy Ferentschik * @author Emmanuel Bernard */ -class JPATraversableResolver implements TraversableResolver { +public class JPATraversableResolver implements TraversableResolver { private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/resolver/TraversableResolvers.java b/engine/src/main/java/org/hibernate/validator/internal/engine/resolver/TraversableResolvers.java index f1392120bd..e7c513b884 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/resolver/TraversableResolvers.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/resolver/TraversableResolvers.java @@ -106,10 +106,7 @@ public static TraversableResolver getDefault() { return run( NewInstance.action( jpaAwareResolverClass, "" ) ); } catch (ValidationException e) { - LOG.debugf( - "Unable to load or instantiate JPA aware resolver %s. All properties will per default be traversable.", - JPA_AWARE_TRAVERSABLE_RESOLVER_CLASS_NAME - ); + LOG.logUnableToLoadOrInstantiateJPAAwareResolver( JPA_AWARE_TRAVERSABLE_RESOLVER_CLASS_NAME ); return getTraverseAllTraversableResolver(); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/AbstractValidationContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/AbstractValidationContext.java new file mode 100644 index 0000000000..870f0b2ad7 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/AbstractValidationContext.java @@ -0,0 +1,499 @@ +/* + * 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.engine.validationcontext; + +import java.lang.invoke.MethodHandles; +import java.util.Collections; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import javax.validation.ConstraintValidatorFactory; +import javax.validation.ConstraintViolation; +import javax.validation.Path; +import javax.validation.TraversableResolver; +import javax.validation.ValidationException; +import javax.validation.Validator; +import javax.validation.metadata.ConstraintDescriptor; + +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; +import org.hibernate.validator.internal.engine.MessageInterpolatorContext; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorContextImpl; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintViolationCreationContext; +import org.hibernate.validator.internal.engine.path.PathImpl; +import org.hibernate.validator.internal.engine.valuecontext.ValueContext; +import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.util.stereotypes.Lazy; + +/** + * Context object keeping track of all required data for a validation call. + *

+ * We use this object to collect all failing constraints, but also to have access to resources like + * constraint validator factory, message interpolator, traversable resolver, etc. + * + * @author Hardy Ferentschik + * @author Emmanuel Bernard + * @author Gunnar Morling + * @author Guillaume Smet + * @author Marko Bekhta + */ +abstract class AbstractValidationContext implements BaseBeanValidationContext { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + /** + * Caches and manages life cycle of constraint validator instances. + */ + private final ConstraintValidatorManager constraintValidatorManager; + + /** + * The root bean of the validation. + */ + private final T rootBean; + + /** + * The root bean class of the validation. + */ + private final Class rootBeanClass; + + /** + * The metadata of the root bean. + */ + private final BeanMetaData rootBeanMetaData; + + /** + * The constraint factory which should be used in this context. + */ + private final ConstraintValidatorFactory constraintValidatorFactory; + + /** + * Context containing all {@link Validator} level helpers and configuration properties. + */ + protected final ValidatorScopedContext validatorScopedContext; + + /** + * Allows a JPA provider to decide whether a property should be validated. + */ + private final TraversableResolver traversableResolver; + + /** + * The constraint validator initialization context. + */ + private final HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext; + + /** + * Indicates if the tracking of already validated bean should be disabled. + */ + private final boolean disableAlreadyValidatedBeanTracking; + + /** + * The set of already processed meta constraints per bean - path ({@link BeanPathMetaConstraintProcessedUnit}). + */ + @Lazy + private Set processedPathUnits; + + /** + * The set of already processed groups per bean ({@link BeanGroupProcessedUnit}). + */ + @Lazy + private Set processedGroupUnits; + + /** + * Maps an object to a list of paths in which it has been validated. The objects are the bean instances. + */ + @Lazy + private Map> processedPathsPerBean; + + /** + * Contains all failing constraints so far. + */ + @Lazy + private Set> failingConstraintViolations; + + protected AbstractValidationContext( + ConstraintValidatorManager constraintValidatorManager, + ConstraintValidatorFactory constraintValidatorFactory, + ValidatorScopedContext validatorScopedContext, + TraversableResolver traversableResolver, + HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext, + T rootBean, + Class rootBeanClass, + BeanMetaData rootBeanMetaData, + boolean disableAlreadyValidatedBeanTracking + ) { + this.constraintValidatorManager = constraintValidatorManager; + this.validatorScopedContext = validatorScopedContext; + this.constraintValidatorFactory = constraintValidatorFactory; + this.traversableResolver = traversableResolver; + this.constraintValidatorInitializationContext = constraintValidatorInitializationContext; + + this.rootBean = rootBean; + this.rootBeanClass = rootBeanClass; + this.rootBeanMetaData = rootBeanMetaData; + + this.disableAlreadyValidatedBeanTracking = disableAlreadyValidatedBeanTracking; + } + + @Override + public T getRootBean() { + return rootBean; + } + + @Override + public Class getRootBeanClass() { + return rootBeanClass; + } + + @Override + public BeanMetaData getRootBeanMetaData() { + return rootBeanMetaData; + } + + @Override + public TraversableResolver getTraversableResolver() { + return traversableResolver; + } + + @Override + public boolean isFailFastModeEnabled() { + return validatorScopedContext.isFailFast(); + } + + @Override + public ConstraintValidatorManager getConstraintValidatorManager() { + return constraintValidatorManager; + } + + @Override + public HibernateConstraintValidatorInitializationContext getConstraintValidatorInitializationContext() { + return constraintValidatorInitializationContext; + } + + @Override + public ConstraintValidatorFactory getConstraintValidatorFactory() { + return constraintValidatorFactory; + } + + @Override + public boolean isBeanAlreadyValidated(Object value, Class group, PathImpl path) { + if ( disableAlreadyValidatedBeanTracking ) { + return false; + } + + boolean alreadyValidated; + alreadyValidated = isAlreadyValidatedForCurrentGroup( value, group ); + + if ( alreadyValidated ) { + alreadyValidated = isAlreadyValidatedForPath( value, path ); + } + + return alreadyValidated; + } + + @Override + public void markCurrentBeanAsProcessed(ValueContext valueContext) { + if ( disableAlreadyValidatedBeanTracking ) { + return; + } + + markCurrentBeanAsProcessedForCurrentGroup( valueContext.getCurrentBean(), valueContext.getCurrentGroup() ); + markCurrentBeanAsProcessedForCurrentPath( valueContext.getCurrentBean(), valueContext.getPropertyPath() ); + } + + @Override + public Set> getFailingConstraints() { + if ( failingConstraintViolations == null ) { + return Collections.emptySet(); + } + + return failingConstraintViolations; + } + + @Override + public void addConstraintFailure( + ValueContext valueContext, + ConstraintViolationCreationContext constraintViolationCreationContext, + ConstraintDescriptor descriptor + ) { + String messageTemplate = constraintViolationCreationContext.getMessage(); + String interpolatedMessage = interpolate( + messageTemplate, + valueContext.getCurrentValidatedValue(), + descriptor, + constraintViolationCreationContext.getPath(), + constraintViolationCreationContext.getMessageParameters(), + constraintViolationCreationContext.getExpressionVariables() + ); + // at this point we make a copy of the path to avoid side effects + Path path = PathImpl.createCopy( constraintViolationCreationContext.getPath() ); + + getInitializedFailingConstraintViolations().add( + createConstraintViolation( + messageTemplate, + interpolatedMessage, + path, + descriptor, + valueContext, + constraintViolationCreationContext + ) + ); + } + + protected abstract ConstraintViolation createConstraintViolation( + String messageTemplate, + String interpolatedMessage, + Path propertyPath, + ConstraintDescriptor constraintDescriptor, + ValueContext valueContext, + ConstraintViolationCreationContext constraintViolationCreationContext); + + @Override + public boolean hasMetaConstraintBeenProcessed(Object bean, Path path, MetaConstraint metaConstraint) { + // this is only useful if the constraint is defined for more than 1 group as in the case it's only + // defined for one group, there is no chance it's going to be called twice. + if ( metaConstraint.isDefinedForOneGroupOnly() ) { + return false; + } + + return getInitializedProcessedPathUnits().contains( new BeanPathMetaConstraintProcessedUnit( bean, path, metaConstraint ) ); + } + + @Override + public void markConstraintProcessed(Object bean, Path path, MetaConstraint metaConstraint) { + // this is only useful if the constraint is defined for more than 1 group as in the case it's only + // defined for one group, there is no chance it's going to be called twice. + if ( metaConstraint.isDefinedForOneGroupOnly() ) { + return; + } + + getInitializedProcessedPathUnits().add( new BeanPathMetaConstraintProcessedUnit( bean, path, metaConstraint ) ); + } + + @Override + public ConstraintValidatorContextImpl createConstraintValidatorContextFor(ConstraintDescriptorImpl constraintDescriptor, PathImpl path) { + return new ConstraintValidatorContextImpl( + validatorScopedContext.getClockProvider(), + path, + constraintDescriptor, + validatorScopedContext.getConstraintValidatorPayload() + ); + } + + @Override + public abstract String toString(); + + private String interpolate( + String messageTemplate, + Object validatedValue, + ConstraintDescriptor descriptor, + Path path, + Map messageParameters, + Map expressionVariables) { + MessageInterpolatorContext context = new MessageInterpolatorContext( + descriptor, + validatedValue, + getRootBeanClass(), + path, + messageParameters, + expressionVariables + ); + + try { + return validatorScopedContext.getMessageInterpolator().interpolate( + messageTemplate, + context + ); + } + catch (ValidationException ve) { + throw ve; + } + catch (Exception e) { + throw LOG.getExceptionOccurredDuringMessageInterpolationException( e ); + } + } + + private boolean isAlreadyValidatedForPath(Object value, PathImpl path) { + Set pathSet = getInitializedProcessedPathsPerBean().get( value ); + if ( pathSet == null ) { + return false; + } + + for ( PathImpl p : pathSet ) { + if ( path.isRootPath() || p.isRootPath() || isSubPathOf( path, p ) || isSubPathOf( p, path ) ) { + return true; + } + } + + return false; + } + + private boolean isSubPathOf(Path p1, Path p2) { + Iterator p1Iter = p1.iterator(); + Iterator p2Iter = p2.iterator(); + while ( p1Iter.hasNext() ) { + Path.Node p1Node = p1Iter.next(); + if ( !p2Iter.hasNext() ) { + return false; + } + Path.Node p2Node = p2Iter.next(); + if ( !p1Node.equals( p2Node ) ) { + return false; + } + } + return true; + } + + private boolean isAlreadyValidatedForCurrentGroup(Object value, Class group) { + return getInitializedProcessedGroupUnits().contains( new BeanGroupProcessedUnit( value, group ) ); + } + + private void markCurrentBeanAsProcessedForCurrentPath(Object bean, PathImpl path) { + // HV-1031 The path object is mutated as we traverse the object tree, hence copy it before saving it + Map> processedPathsPerBean = getInitializedProcessedPathsPerBean(); + + Set processedPaths = processedPathsPerBean.get( bean ); + if ( processedPaths == null ) { + processedPaths = new HashSet<>(); + processedPathsPerBean.put( bean, processedPaths ); + } + + processedPaths.add( PathImpl.createCopy( path ) ); + } + + private void markCurrentBeanAsProcessedForCurrentGroup(Object bean, Class group) { + getInitializedProcessedGroupUnits().add( new BeanGroupProcessedUnit( bean, group ) ); + } + + private Set getInitializedProcessedPathUnits() { + if ( processedPathUnits == null ) { + processedPathUnits = new HashSet<>(); + } + return processedPathUnits; + } + + private Set getInitializedProcessedGroupUnits() { + if ( processedGroupUnits == null ) { + processedGroupUnits = new HashSet<>(); + } + return processedGroupUnits; + } + + private Map> getInitializedProcessedPathsPerBean() { + if ( processedPathsPerBean == null ) { + processedPathsPerBean = new IdentityHashMap<>(); + } + return processedPathsPerBean; + } + + private Set> getInitializedFailingConstraintViolations() { + if ( failingConstraintViolations == null ) { + failingConstraintViolations = new HashSet<>(); + } + return failingConstraintViolations; + } + + private static final class BeanPathMetaConstraintProcessedUnit { + + // these fields are final but we don't mark them as final as an optimization + private Object bean; + private Path path; + private MetaConstraint metaConstraint; + private int hashCode; + + BeanPathMetaConstraintProcessedUnit(Object bean, Path path, MetaConstraint metaConstraint) { + this.bean = bean; + this.path = path; + this.metaConstraint = metaConstraint; + this.hashCode = createHashCode(); + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + + // No need to check if the class matches because of how this class is used in the set. + BeanPathMetaConstraintProcessedUnit that = (BeanPathMetaConstraintProcessedUnit) o; + + if ( bean != that.bean ) { // instance equality + return false; + } + if ( metaConstraint != that.metaConstraint ) { + return false; + } + if ( !path.equals( that.path ) ) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return hashCode; + } + + private int createHashCode() { + int result = System.identityHashCode( bean ); + result = 31 * result + path.hashCode(); + result = 31 * result + System.identityHashCode( metaConstraint ); + return result; + } + } + + private static final class BeanGroupProcessedUnit { + + // these fields are final but we don't mark them as final as an optimization + private Object bean; + private Class group; + private int hashCode; + + BeanGroupProcessedUnit(Object bean, Class group) { + this.bean = bean; + this.group = group; + this.hashCode = createHashCode(); + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + + // No need to check if the class matches because of how this class is used in the set. + BeanGroupProcessedUnit that = (BeanGroupProcessedUnit) o; + + if ( bean != that.bean ) { // instance equality + return false; + } + if ( !group.equals( that.group ) ) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return hashCode; + } + + private int createHashCode() { + int result = System.identityHashCode( bean ); + result = 31 * result + group.hashCode(); + return result; + } + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/BaseBeanValidationContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/BaseBeanValidationContext.java new file mode 100644 index 0000000000..5656460d4a --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/BaseBeanValidationContext.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.engine.validationcontext; + +import javax.validation.Path; +import javax.validation.TraversableResolver; +import javax.validation.Validator; + +import org.hibernate.validator.internal.engine.ValidatorImpl; +import org.hibernate.validator.internal.engine.path.PathImpl; +import org.hibernate.validator.internal.engine.valuecontext.ValueContext; +import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; + +/** + * Interface that exposes contextual information required for a validation call related to a bean. + *

+ * Provides ability to collect failing constraints and gives access to resources like constraint validator factory, + * traversable resolver, etc. + * + * @author Hardy Ferentschik + * @author Emmanuel Bernard + * @author Gunnar Morling + * @author Guillaume Smet + */ +public interface BaseBeanValidationContext extends ValidationContext { + + T getRootBean(); + + Class getRootBeanClass(); + + BeanMetaData getRootBeanMetaData(); + + TraversableResolver getTraversableResolver(); + + boolean isBeanAlreadyValidated(Object value, Class group, PathImpl path); + + void markCurrentBeanAsProcessed(ValueContext valueContext); + + boolean hasMetaConstraintBeenProcessed(Object bean, Path path, MetaConstraint metaConstraint); + + void markConstraintProcessed(Object bean, Path path, MetaConstraint metaConstraint); + + /** + * @return {@code true} if current validation context can and should process passed meta constraint. Is used in + * {@link ValidatorImpl} to check if validation is required in case of calls to + * {@link Validator#validateValue(Class, String, Object, Class[])} or + * {@link Validator#validateProperty(Object, String, Class[])}. In these cases, as we iterate through all meta + * constraints of the bean, we expect those that are not defined for the validated property. + */ + default boolean appliesTo(MetaConstraint metaConstraint) { + return true; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/BeanValidationContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/BeanValidationContext.java new file mode 100644 index 0000000000..ab7aa153d8 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/BeanValidationContext.java @@ -0,0 +1,77 @@ +/* + * 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.engine.validationcontext; + +import javax.validation.ConstraintValidatorFactory; +import javax.validation.ConstraintViolation; +import javax.validation.Path; +import javax.validation.TraversableResolver; +import javax.validation.metadata.ConstraintDescriptor; + +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; +import org.hibernate.validator.internal.engine.ConstraintViolationImpl; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintViolationCreationContext; +import org.hibernate.validator.internal.engine.valuecontext.ValueContext; +import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; + +/** + * Implementation of {@link AbstractValidationContext} for the validation of a bean. + * + * @author Marko Bekhta + */ +class BeanValidationContext extends AbstractValidationContext { + + BeanValidationContext( + ConstraintValidatorManager constraintValidatorManager, + ConstraintValidatorFactory constraintValidatorFactory, + ValidatorScopedContext validatorScopedContext, + TraversableResolver traversableResolver, + HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext, + T rootBean, + Class rootBeanClass, + BeanMetaData rootBeanMetaData + ) { + super( constraintValidatorManager, constraintValidatorFactory, validatorScopedContext, traversableResolver, constraintValidatorInitializationContext, + rootBean, rootBeanClass, rootBeanMetaData, buildDisableAlreadyValidatedBeanTracking( rootBeanMetaData ) + ); + } + + private static boolean buildDisableAlreadyValidatedBeanTracking(BeanMetaData rootBeanMetaData) { + return !rootBeanMetaData.hasCascadables(); + } + + @Override + protected ConstraintViolation createConstraintViolation( + String messageTemplate, String interpolatedMessage, Path propertyPath, + ConstraintDescriptor constraintDescriptor, ValueContext localContext, + ConstraintViolationCreationContext constraintViolationCreationContext) { + return ConstraintViolationImpl.forBeanValidation( + messageTemplate, + constraintViolationCreationContext.getMessageParameters(), + constraintViolationCreationContext.getExpressionVariables(), + interpolatedMessage, + getRootBeanClass(), + getRootBean(), + localContext.getCurrentBean(), + localContext.getCurrentValidatedValue(), + propertyPath, + constraintDescriptor, + constraintViolationCreationContext.getDynamicPayload() + ); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder( getClass().getSimpleName() ); + sb.append( '{' ); + sb.append( "rootBeanClass=" ).append( getRootBeanClass() ); + sb.append( '}' ); + return sb.toString(); + } + +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ExecutableValidationContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ExecutableValidationContext.java new file mode 100644 index 0000000000..aaf30447b7 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ExecutableValidationContext.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.engine.validationcontext; + +import java.lang.reflect.Executable; +import java.util.Optional; + +import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData; + +/** + * Extension of {@link BaseBeanValidationContext} for executable validation. + * + * @author Marko Bekhta + */ +public interface ExecutableValidationContext extends BaseBeanValidationContext { + + Executable getExecutable(); + + Optional getExecutableMetaData(); + +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ParameterExecutableValidationContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ParameterExecutableValidationContext.java new file mode 100644 index 0000000000..0cdf280854 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ParameterExecutableValidationContext.java @@ -0,0 +1,152 @@ +/* + * 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.engine.validationcontext; + +import java.lang.reflect.Executable; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import javax.validation.ConstraintValidatorFactory; +import javax.validation.ConstraintViolation; +import javax.validation.Path; +import javax.validation.TraversableResolver; +import javax.validation.metadata.ConstraintDescriptor; + +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; +import org.hibernate.validator.internal.engine.ConstraintViolationImpl; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorContextImpl; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintViolationCreationContext; +import org.hibernate.validator.internal.engine.constraintvalidation.CrossParameterConstraintValidatorContextImpl; +import org.hibernate.validator.internal.engine.path.PathImpl; +import org.hibernate.validator.internal.engine.valuecontext.ValueContext; +import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; +import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData; +import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; +import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl.ConstraintType; + +/** + * Implementation of {@link AbstractValidationContext} for executable's parameter validation. + * + * @author Marko Bekhta + */ +public class ParameterExecutableValidationContext extends AbstractValidationContext + implements ExecutableValidationContext { + + /** + * The method of the current validation call. + */ + private final Executable executable; + + /** + * The validated parameters. + */ + private final Object[] executableParameters; + + /** + * The metadata of the Executable. Will be non empty if the method is constrained. + */ + private final Optional executableMetaData; + + ParameterExecutableValidationContext( + ConstraintValidatorManager constraintValidatorManager, + ConstraintValidatorFactory constraintValidatorFactory, + ValidatorScopedContext validatorScopedContext, + TraversableResolver traversableResolver, + HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext, + T rootBean, + Class rootBeanClass, + BeanMetaData rootBeanMetaData, + Executable executable, + Optional executableMetaData, + Object[] executableParameters + ) { + super( constraintValidatorManager, constraintValidatorFactory, validatorScopedContext, traversableResolver, + constraintValidatorInitializationContext, rootBean, rootBeanClass, rootBeanMetaData, + buildDisableAlreadyValidatedBeanTracking( executableMetaData ) + ); + this.executable = executable; + this.executableMetaData = executableMetaData; + this.executableParameters = executableParameters; + } + + @Override + public Executable getExecutable() { + return executable; + } + + @Override + public Optional getExecutableMetaData() { + return executableMetaData; + } + + private static boolean buildDisableAlreadyValidatedBeanTracking(Optional executableMetaData) { + if ( !executableMetaData.isPresent() ) { + // the method is unconstrained so there's no need to worry about the tracking + return false; + } + + return !executableMetaData.get().getReturnValueMetaData().hasCascadables(); + } + + @Override + public ConstraintValidatorContextImpl createConstraintValidatorContextFor(ConstraintDescriptorImpl constraintDescriptor, PathImpl path) { + if ( ConstraintType.CROSS_PARAMETER.equals( constraintDescriptor.getConstraintType() ) ) { + return new CrossParameterConstraintValidatorContextImpl( + getParameterNames(), + validatorScopedContext.getClockProvider(), + path, + constraintDescriptor, + validatorScopedContext.getConstraintValidatorPayload() + ); + } + + return new ConstraintValidatorContextImpl( + validatorScopedContext.getClockProvider(), + path, + constraintDescriptor, + validatorScopedContext.getConstraintValidatorPayload() + ); + } + + @Override + protected ConstraintViolation createConstraintViolation( + String messageTemplate, String interpolatedMessage, Path propertyPath, ConstraintDescriptor constraintDescriptor, ValueContext valueContext, + ConstraintViolationCreationContext constraintViolationCreationContext) { + return ConstraintViolationImpl.forParameterValidation( + messageTemplate, + constraintViolationCreationContext.getMessageParameters(), + constraintViolationCreationContext.getExpressionVariables(), + interpolatedMessage, + getRootBeanClass(), + getRootBean(), + valueContext.getCurrentBean(), + valueContext.getCurrentValidatedValue(), + propertyPath, + constraintDescriptor, + executableParameters, + constraintViolationCreationContext.getDynamicPayload() + ); + } + + private List getParameterNames() { + return validatorScopedContext.getParameterNameProvider().getParameterNames( executable ); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder( getClass().getSimpleName() ); + sb.append( '{' ); + sb.append( "rootBeanClass=" ).append( getRootBeanClass() ); + sb.append( ", executable=" ).append( executable ); + sb.append( ", executableParameters=" ).append( Arrays.toString( executableParameters ) ); + sb.append( ", executableMetaData=" ).append( executableMetaData ); + sb.append( '}' ); + return sb.toString(); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/PropertyValidationContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/PropertyValidationContext.java new file mode 100644 index 0000000000..7151bcd59d --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/PropertyValidationContext.java @@ -0,0 +1,110 @@ +/* + * 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.engine.validationcontext; + +import java.util.Objects; + +import javax.validation.ConstraintValidatorFactory; +import javax.validation.ConstraintViolation; +import javax.validation.Path; +import javax.validation.TraversableResolver; +import javax.validation.Validator; +import javax.validation.metadata.ConstraintDescriptor; + +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; +import org.hibernate.validator.internal.engine.ConstraintViolationImpl; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintViolationCreationContext; +import org.hibernate.validator.internal.engine.valuecontext.ValueContext; +import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.location.AbstractPropertyConstraintLocation; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.metadata.location.TypeArgumentConstraintLocation; + +/** + * Implementation of {@link AbstractValidationContext} for property/value validation + * (used in calls to {@link Validator#validateProperty(Object, String, Class[])} or + * {@link Validator#validateValue(Class, String, Object, Class[])}). + * + * @author Marko Bekhta + */ +class PropertyValidationContext extends AbstractValidationContext { + + /** + * The name of the validated (leaf) property in case of a validateProperty()/validateValue() call. + */ + private String validatedProperty; + + PropertyValidationContext( + ConstraintValidatorManager constraintValidatorManager, + ConstraintValidatorFactory constraintValidatorFactory, + ValidatorScopedContext validatorScopedContext, + TraversableResolver traversableResolver, + HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext, + T rootBean, + Class rootBeanClass, + BeanMetaData rootBeanMetaData, + String validatedProperty + ) { + super( constraintValidatorManager, constraintValidatorFactory, validatorScopedContext, traversableResolver, constraintValidatorInitializationContext, + rootBean, rootBeanClass, rootBeanMetaData, buildDisableAlreadyValidatedBeanTracking( rootBeanMetaData ) + ); + this.validatedProperty = validatedProperty; + } + + private static boolean buildDisableAlreadyValidatedBeanTracking(BeanMetaData rootBeanMetaData) { + return !rootBeanMetaData.hasCascadables(); + } + + @Override + public boolean appliesTo(MetaConstraint metaConstraint) { + return Objects.equals( validatedProperty, getPropertyName( metaConstraint.getLocation() ) ); + } + + private String getPropertyName(ConstraintLocation location) { + if ( location instanceof TypeArgumentConstraintLocation ) { + location = ( (TypeArgumentConstraintLocation) location ).getOuterDelegate(); + } + + if ( location instanceof AbstractPropertyConstraintLocation ) { + return ( (AbstractPropertyConstraintLocation) location ).getPropertyName(); + } + + return null; + } + + @Override + protected ConstraintViolation createConstraintViolation( + String messageTemplate, String interpolatedMessage, Path propertyPath, + ConstraintDescriptor constraintDescriptor, ValueContext localContext, + ConstraintViolationCreationContext constraintViolationCreationContext) { + return ConstraintViolationImpl.forBeanValidation( + messageTemplate, + constraintViolationCreationContext.getMessageParameters(), + constraintViolationCreationContext.getExpressionVariables(), + interpolatedMessage, + getRootBeanClass(), + getRootBean(), + localContext.getCurrentBean(), + localContext.getCurrentValidatedValue(), + propertyPath, + constraintDescriptor, + constraintViolationCreationContext.getDynamicPayload() + ); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder( getClass().getSimpleName() ); + sb.append( '{' ); + sb.append( "rootBeanClass=" ).append( getRootBeanClass() ); + sb.append( ", validatedProperty='" ).append( validatedProperty ).append( '\'' ); + sb.append( '}' ); + return sb.toString(); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ReturnValueExecutableValidationContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ReturnValueExecutableValidationContext.java new file mode 100644 index 0000000000..0c7715db79 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ReturnValueExecutableValidationContext.java @@ -0,0 +1,120 @@ +/* + * 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.engine.validationcontext; + +import java.lang.reflect.Executable; +import java.util.Optional; + +import javax.validation.ConstraintValidatorFactory; +import javax.validation.ConstraintViolation; +import javax.validation.Path; +import javax.validation.TraversableResolver; +import javax.validation.metadata.ConstraintDescriptor; + +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; +import org.hibernate.validator.internal.engine.ConstraintViolationImpl; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintViolationCreationContext; +import org.hibernate.validator.internal.engine.valuecontext.ValueContext; +import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; +import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData; + +/** + * Implementation of {@link AbstractValidationContext} for executable's return value validation. + * + * @author Marko Bekhta + */ +public class ReturnValueExecutableValidationContext extends AbstractValidationContext + implements ExecutableValidationContext { + + /** + * The method of the current validation call. + */ + private final Executable executable; + + /** + * The validated return value. + */ + private final Object executableReturnValue; + + /** + * The metadata of the Executable. Will be non empty if the method is constrained. + */ + private final Optional executableMetaData; + + ReturnValueExecutableValidationContext( + ConstraintValidatorManager constraintValidatorManager, + ConstraintValidatorFactory constraintValidatorFactory, + ValidatorScopedContext validatorScopedContext, + TraversableResolver traversableResolver, + HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext, + T rootBean, + Class rootBeanClass, + BeanMetaData rootBeanMetaData, + Executable executable, + Optional executableMetaData, + Object executableReturnValue + ) { + super( constraintValidatorManager, constraintValidatorFactory, validatorScopedContext, traversableResolver, + constraintValidatorInitializationContext, rootBean, rootBeanClass, rootBeanMetaData, + buildDisableAlreadyValidatedBeanTracking( executableMetaData ) + ); + this.executable = executable; + this.executableMetaData = executableMetaData; + this.executableReturnValue = executableReturnValue; + } + + @Override + public Executable getExecutable() { + return executable; + } + + @Override + public Optional getExecutableMetaData() { + return executableMetaData; + } + + private static boolean buildDisableAlreadyValidatedBeanTracking(Optional executableMetaData) { + if ( !executableMetaData.isPresent() ) { + // the method is unconstrained so there's no need to worry about the tracking + return false; + } + + return !executableMetaData.get().getReturnValueMetaData().hasCascadables(); + } + + @Override + protected ConstraintViolation createConstraintViolation(String messageTemplate, String interpolatedMessage, Path propertyPath, ConstraintDescriptor constraintDescriptor, ValueContext valueContext, + ConstraintViolationCreationContext constraintViolationCreationContext) { + return ConstraintViolationImpl.forReturnValueValidation( + messageTemplate, + constraintViolationCreationContext.getMessageParameters(), + constraintViolationCreationContext.getExpressionVariables(), + interpolatedMessage, + getRootBeanClass(), + getRootBean(), + valueContext.getCurrentBean(), + valueContext.getCurrentValidatedValue(), + propertyPath, + constraintDescriptor, + executableReturnValue, + constraintViolationCreationContext.getDynamicPayload() + ); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder( getClass().getSimpleName() ); + sb.append( '{' ); + sb.append( "rootBeanClass=" ).append( getRootBeanClass() ); + sb.append( ", executable=" ).append( executable ); + sb.append( ", executableReturnValue=" ).append( executableReturnValue ); + sb.append( ", executableMetaData=" ).append( executableMetaData ); + sb.append( '}' ); + return sb.toString(); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ValidationContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ValidationContext.java new file mode 100644 index 0000000000..29792a23bf --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ValidationContext.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.engine.validationcontext; + +import java.util.Set; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.ConstraintValidatorFactory; +import javax.validation.ConstraintViolation; +import javax.validation.metadata.ConstraintDescriptor; + +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; +import org.hibernate.validator.internal.engine.valuecontext.ValueContext; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorContextImpl; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintViolationCreationContext; +import org.hibernate.validator.internal.engine.path.PathImpl; +import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; + +/** + * Context object interface keeping track of all required data for operations inside {@link ConstraintTree} + * and its subclasses. + *

+ * Allows to collect all failing constraints, creates {@link ConstraintValidatorContext}s based on the constraint + * descriptors, and exposes other resources needed to initialize a new {@link ConstraintValidator}. + * + * @author Hardy Ferentschik + * @author Emmanuel Bernard + * @author Gunnar Morling + * @author Guillaume Smet + * @author Marko Bekhta + */ +public interface ValidationContext { + + boolean isFailFastModeEnabled(); + + ConstraintValidatorManager getConstraintValidatorManager(); + + HibernateConstraintValidatorInitializationContext getConstraintValidatorInitializationContext(); + + ConstraintValidatorFactory getConstraintValidatorFactory(); + + void addConstraintFailure( + ValueContext valueContext, + ConstraintViolationCreationContext constraintViolationCreationContext, + ConstraintDescriptor descriptor + ); + + Set> getFailingConstraints(); + + ConstraintValidatorContextImpl createConstraintValidatorContextFor(ConstraintDescriptorImpl constraintDescriptor, PathImpl path); +} 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 new file mode 100644 index 0000000000..1d1d388d30 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ValidationContextBuilder.java @@ -0,0 +1,128 @@ +/* + * 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.engine.validationcontext; + +import java.lang.reflect.Executable; + +import javax.validation.ConstraintValidatorFactory; +import javax.validation.TraversableResolver; + +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.aggregated.BeanMetaData; + +/** + * Builder for creating {@link AbstractValidationContext}s suited for the different kinds of validation. + * + * @author Gunnar Morling + * @author Marko Bekhta + */ +public class ValidationContextBuilder { + + private final ConstraintValidatorManager constraintValidatorManager; + private final ConstraintValidatorFactory constraintValidatorFactory; + private final TraversableResolver traversableResolver; + private final HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext; + private final ValidatorScopedContext validatorScopedContext; + + public ValidationContextBuilder( + ConstraintValidatorManager constraintValidatorManager, + ConstraintValidatorFactory constraintValidatorFactory, + ValidatorScopedContext validatorScopedContext, + TraversableResolver traversableResolver, + HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext) { + this.constraintValidatorManager = constraintValidatorManager; + this.constraintValidatorFactory = constraintValidatorFactory; + this.traversableResolver = traversableResolver; + this.constraintValidatorInitializationContext = constraintValidatorInitializationContext; + this.validatorScopedContext = validatorScopedContext; + } + + public BaseBeanValidationContext forValidate(Class rootBeanClass, BeanMetaData rootBeanMetaData, T rootBean) { + return new BeanValidationContext<>( + constraintValidatorManager, + constraintValidatorFactory, + validatorScopedContext, + traversableResolver, + constraintValidatorInitializationContext, + rootBean, + rootBeanClass, + rootBeanMetaData + ); + } + + public BaseBeanValidationContext forValidateProperty(Class rootBeanClass, BeanMetaData rootBeanMetaData, T rootBean, PathImpl propertyPath) { + return new PropertyValidationContext<>( + constraintValidatorManager, + constraintValidatorFactory, + validatorScopedContext, + traversableResolver, + constraintValidatorInitializationContext, + rootBean, + rootBeanClass, + rootBeanMetaData, + propertyPath.getLeafNode().getName() + ); + } + + public BaseBeanValidationContext forValidateValue(Class rootBeanClass, BeanMetaData rootBeanMetaData, PathImpl propertyPath) { + return new PropertyValidationContext<>( + constraintValidatorManager, + constraintValidatorFactory, + validatorScopedContext, + traversableResolver, + constraintValidatorInitializationContext, + null, //root bean + rootBeanClass, + rootBeanMetaData, + propertyPath.getLeafNode().getName() + ); + } + + public ExecutableValidationContext forValidateParameters( + Class rootBeanClass, + BeanMetaData rootBeanMetaData, + T rootBean, + Executable executable, + Object[] executableParameters) { + return new ParameterExecutableValidationContext<>( + constraintValidatorManager, + constraintValidatorFactory, + validatorScopedContext, + traversableResolver, + constraintValidatorInitializationContext, + rootBean, + rootBeanClass, + rootBeanMetaData, + executable, + rootBeanMetaData.getMetaDataFor( executable ), + executableParameters + ); + } + + public ExecutableValidationContext forValidateReturnValue( + Class rootBeanClass, + BeanMetaData rootBeanMetaData, + T rootBean, + Executable executable, + Object executableReturnValue) { + return new ReturnValueExecutableValidationContext<>( + constraintValidatorManager, + constraintValidatorFactory, + validatorScopedContext, + traversableResolver, + constraintValidatorInitializationContext, + rootBean, + rootBeanClass, + rootBeanMetaData, + executable, + rootBeanMetaData.getMetaDataFor( executable ), + executableReturnValue + ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ValidatorScopedContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ValidatorScopedContext.java new file mode 100644 index 0000000000..04953bc7b7 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ValidatorScopedContext.java @@ -0,0 +1,110 @@ +/* + * 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.engine.validationcontext; + +import java.time.Duration; + +import javax.validation.ClockProvider; +import javax.validation.MessageInterpolator; +import javax.validation.Validator; + +import org.hibernate.validator.internal.engine.ValidatorFactoryScopedContext; +import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; +import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; + +/** + * Context object storing the {@link Validator} level helper and configuration properties. + *

+ * There should be only one per {@code Validator} instance. + */ +public class ValidatorScopedContext { + + /** + * The message interpolator. + */ + private final MessageInterpolator messageInterpolator; + + /** + * The parameter name provider. + */ + private final ExecutableParameterNameProvider parameterNameProvider; + + /** + * Provider for the current time when validating {@code @Future} or {@code @Past} + */ + private final ClockProvider clockProvider; + + /** + * Defines the temporal validation tolerance i.e. the allowed margin of error when comparing date/time in temporal + * constraints. + */ + private final Duration temporalValidationTolerance; + + /** + * Used to get the {@code ScriptEvaluatorFactory} when validating {@code @ScriptAssert} and + * {@code @ParameterScriptAssert} constraints. + */ + private final ScriptEvaluatorFactory scriptEvaluatorFactory; + + /** + * Hibernate Validator specific flag to abort validation on first constraint violation. + */ + private final boolean failFast; + + /** + * Hibernate Validator specific flag to disable the {@code TraversableResolver} result cache. + */ + private final boolean traversableResolverResultCacheEnabled; + + /** + * Hibernate Validator specific payload passed to the constraint validators. + */ + private final Object constraintValidatorPayload; + + public ValidatorScopedContext(ValidatorFactoryScopedContext validatorFactoryScopedContext) { + this.messageInterpolator = validatorFactoryScopedContext.getMessageInterpolator(); + this.parameterNameProvider = validatorFactoryScopedContext.getParameterNameProvider(); + this.clockProvider = validatorFactoryScopedContext.getClockProvider(); + this.temporalValidationTolerance = validatorFactoryScopedContext.getTemporalValidationTolerance(); + this.scriptEvaluatorFactory = validatorFactoryScopedContext.getScriptEvaluatorFactory(); + this.failFast = validatorFactoryScopedContext.isFailFast(); + this.traversableResolverResultCacheEnabled = validatorFactoryScopedContext.isTraversableResolverResultCacheEnabled(); + this.constraintValidatorPayload = validatorFactoryScopedContext.getConstraintValidatorPayload(); + } + + public MessageInterpolator getMessageInterpolator() { + return this.messageInterpolator; + } + + public ExecutableParameterNameProvider getParameterNameProvider() { + return this.parameterNameProvider; + } + + public ClockProvider getClockProvider() { + return this.clockProvider; + } + + public Duration getTemporalValidationTolerance() { + return this.temporalValidationTolerance; + } + + public ScriptEvaluatorFactory getScriptEvaluatorFactory() { + return this.scriptEvaluatorFactory; + } + + public boolean isFailFast() { + return this.failFast; + } + + public boolean isTraversableResolverResultCacheEnabled() { + return this.traversableResolverResultCacheEnabled; + } + + public Object getConstraintValidatorPayload() { + return this.constraintValidatorPayload; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/package-info.java b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/package-info.java new file mode 100644 index 0000000000..dbf3047e80 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/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 . + */ +/** + * Contains various implementation of validation contexts and supporting classes. + */ +package org.hibernate.validator.internal.engine.validationcontext; diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valuecontext/BeanValueContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valuecontext/BeanValueContext.java new file mode 100644 index 0000000000..ced53a7403 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valuecontext/BeanValueContext.java @@ -0,0 +1,31 @@ +/* + * 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.engine.valuecontext; + +import org.hibernate.validator.internal.engine.path.PathImpl; +import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; +import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; + +/** + * @author Marko Bekhta + */ +public class BeanValueContext extends ValueContext { + + /** + * The metadata of the current bean. + */ + private final BeanMetaData currentBeanMetaData; + + BeanValueContext(ExecutableParameterNameProvider parameterNameProvider, T currentBean, BeanMetaData currentBeanMetaData, PathImpl propertyPath) { + super( parameterNameProvider, currentBean, currentBeanMetaData, propertyPath ); + this.currentBeanMetaData = currentBeanMetaData; + } + + public final BeanMetaData getCurrentBeanMetaData() { + return currentBeanMetaData; + } +} 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/ValueContext.java similarity index 61% rename from engine/src/main/java/org/hibernate/validator/internal/engine/ValueContext.java rename to engine/src/main/java/org/hibernate/validator/internal/engine/valuecontext/ValueContext.java index 88ba7f7d35..b70df17703 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValueContext.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valuecontext/ValueContext.java @@ -4,9 +4,8 @@ * License: Apache License, Version 2.0 * See the license.txt file in the root directory or . */ -package org.hibernate.validator.internal.engine; +package org.hibernate.validator.internal.engine.valuecontext; -import java.lang.annotation.ElementType; import java.lang.reflect.TypeVariable; import javax.validation.groups.Default; @@ -14,11 +13,10 @@ 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.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.facets.Cascadable; import org.hibernate.validator.internal.metadata.facets.Validatable; import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.TypeVariables; @@ -39,16 +37,6 @@ public class ValueContext { */ private final T currentBean; - /** - * The class of the current bean. - */ - private final Class currentBeanType; - - /** - * The metadata of the current bean. - */ - private final BeanMetaData currentBeanMetaData; - /** * The current property path we are validating. */ @@ -67,41 +55,13 @@ public class ValueContext { private final Validatable currentValidatable; /** - * The {@code ElementType} the constraint was defined on + * The {@code ConstraintLocationKind} the constraint was defined on */ - private ElementType elementType; - - public static ValueContext getLocalExecutionContext(BeanMetaDataManager beanMetaDataManager, - 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 ); - } - - @SuppressWarnings("unchecked") - public static ValueContext getLocalExecutionContext(ExecutableParameterNameProvider parameterNameProvider, T value, - BeanMetaData currentBeanMetaData, PathImpl propertyPath) { - Class rootBeanType = (Class) value.getClass(); - return new ValueContext<>( parameterNameProvider, value, rootBeanType, (BeanMetaData) currentBeanMetaData, currentBeanMetaData, propertyPath ); - } - - public static ValueContext getLocalExecutionContext(BeanMetaDataManager beanMetaDataManager, - ExecutableParameterNameProvider parameterNameProvider, Class rootBeanType, Validatable validatable, PathImpl propertyPath) { - BeanMetaData rootBeanMetaData = rootBeanType != null ? beanMetaDataManager.getBeanMetaData( rootBeanType ) : null; - return new ValueContext<>( parameterNameProvider, null, rootBeanType, rootBeanMetaData, validatable, propertyPath ); - } - - @SuppressWarnings("unchecked") - public static ValueContext getLocalExecutionContext(ExecutableParameterNameProvider parameterNameProvider, Class currentBeanType, - BeanMetaData currentBeanMetaData, PathImpl propertyPath) { - return new ValueContext<>( parameterNameProvider, null, currentBeanType, (BeanMetaData) currentBeanMetaData, currentBeanMetaData, propertyPath ); - } + private ConstraintLocationKind constraintLocationKind; - private ValueContext(ExecutableParameterNameProvider parameterNameProvider, T currentBean, Class currentBeanType, BeanMetaData currentBeanMetaData, Validatable validatable, PathImpl propertyPath) { + ValueContext(ExecutableParameterNameProvider parameterNameProvider, T currentBean, Validatable validatable, PathImpl propertyPath) { this.parameterNameProvider = parameterNameProvider; this.currentBean = currentBean; - this.currentBeanType = currentBeanType; - this.currentBeanMetaData = currentBeanMetaData; this.currentValidatable = validatable; this.propertyPath = propertyPath; } @@ -118,14 +78,6 @@ public final T getCurrentBean() { return currentBean; } - public final Class getCurrentBeanType() { - return currentBeanType; - } - - public final BeanMetaData getCurrentBeanMetaData() { - return currentBeanMetaData; - } - public Validatable getCurrentValidatable() { return currentValidatable; } @@ -199,12 +151,12 @@ public final boolean validatingDefault() { return getCurrentGroup() != null && getCurrentGroup().getName().equals( Default.class.getName() ); } - public final ElementType getElementType() { - return elementType; + public final ConstraintLocationKind getConstraintLocationKind() { + return constraintLocationKind; } - public final void setElementType(ElementType elementType) { - this.elementType = elementType; + public final void setConstraintLocationKind(ConstraintLocationKind constraintLocationKind) { + this.constraintLocationKind = constraintLocationKind; } public final ValueState getCurrentValueState() { @@ -212,8 +164,8 @@ public final ValueState getCurrentValueState() { } public final void resetValueState(ValueState valueState) { - this.propertyPath = valueState.propertyPath; - this.currentValue = valueState.currentValue; + this.propertyPath = valueState.getPropertyPath(); + this.currentValue = valueState.getCurrentValue(); } @Override @@ -221,11 +173,10 @@ public String toString() { final StringBuilder sb = new StringBuilder(); sb.append( "ValueContext" ); sb.append( "{currentBean=" ).append( currentBean ); - sb.append( ", currentBeanType=" ).append( currentBeanType ); sb.append( ", propertyPath=" ).append( propertyPath ); sb.append( ", currentGroup=" ).append( currentGroup ); sb.append( ", currentValue=" ).append( currentValue ); - sb.append( ", elementType=" ).append( elementType ); + sb.append( ", constraintLocationKind=" ).append( constraintLocationKind ); sb.append( '}' ); return sb.toString(); } @@ -241,9 +192,17 @@ public static class ValueState { private final V currentValue; - private ValueState(PathImpl propertyPath, V currentValue) { + ValueState(PathImpl propertyPath, V currentValue) { this.propertyPath = propertyPath; this.currentValue = currentValue; } + + public PathImpl getPropertyPath() { + return propertyPath; + } + + public V getCurrentValue() { + return currentValue; + } } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valuecontext/ValueContexts.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valuecontext/ValueContexts.java new file mode 100644 index 0000000000..de4cd0f39a --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valuecontext/ValueContexts.java @@ -0,0 +1,50 @@ +/* + * 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.engine.valuecontext; + +import org.hibernate.validator.internal.engine.path.PathImpl; +import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; +import org.hibernate.validator.internal.metadata.facets.Validatable; +import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; + +/** + * @author Marko Bekhta + */ +public final class ValueContexts { + + private ValueContexts() { + } + + /** + * Creates a value context for validating an executable. Can be applied to both parameter and + * return value validation. Does not require a bean metadata information. + */ + public static ValueContext getLocalExecutionContextForExecutable( + ExecutableParameterNameProvider parameterNameProvider, + T value, + Validatable validatable, + PathImpl propertyPath) { + return new ValueContext<>( parameterNameProvider, value, validatable, propertyPath ); + } + + @SuppressWarnings("unchecked") + public static BeanValueContext getLocalExecutionContextForBean( + ExecutableParameterNameProvider parameterNameProvider, + T value, + BeanMetaData currentBeanMetaData, + PathImpl propertyPath) { + return new BeanValueContext<>( parameterNameProvider, value, (BeanMetaData) currentBeanMetaData, propertyPath ); + } + + @SuppressWarnings("unchecked") + public static BeanValueContext getLocalExecutionContextForValueValidation( + ExecutableParameterNameProvider parameterNameProvider, + BeanMetaData currentBeanMetaData, + PathImpl propertyPath) { + return new BeanValueContext<>( parameterNameProvider, null, (BeanMetaData) currentBeanMetaData, propertyPath ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ListPropertyValueExtractor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ListPropertyValueExtractor.java index 59b691b8fc..bcccb67566 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ListPropertyValueExtractor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ListPropertyValueExtractor.java @@ -11,6 +11,7 @@ import javax.validation.valueextraction.ExtractedValue; import javax.validation.valueextraction.ValueExtractor; +import org.hibernate.validator.internal.IgnoreForbiddenApisErrors; import org.hibernate.validator.internal.engine.path.NodeImpl; import javafx.beans.property.ListProperty; @@ -25,6 +26,7 @@ * @author Guillaume Smet */ @SuppressWarnings("restriction") +@IgnoreForbiddenApisErrors(reason = "Usage of JavaFX classes") class ListPropertyValueExtractor implements ValueExtractor> { static final ValueExtractorDescriptor DESCRIPTOR = new ValueExtractorDescriptor( new ListPropertyValueExtractor() ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/MapPropertyKeyExtractor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/MapPropertyKeyExtractor.java index e2b507fae0..bbd5dc7bc4 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/MapPropertyKeyExtractor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/MapPropertyKeyExtractor.java @@ -11,6 +11,7 @@ import javax.validation.valueextraction.ExtractedValue; import javax.validation.valueextraction.ValueExtractor; +import org.hibernate.validator.internal.IgnoreForbiddenApisErrors; import org.hibernate.validator.internal.engine.path.NodeImpl; import javafx.beans.property.MapProperty; @@ -25,6 +26,7 @@ * @author Guillaume Smet */ @SuppressWarnings("restriction") +@IgnoreForbiddenApisErrors(reason = "Usage of JavaFX classes") class MapPropertyKeyExtractor implements ValueExtractor> { static final ValueExtractorDescriptor DESCRIPTOR = new ValueExtractorDescriptor( new MapPropertyKeyExtractor() ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/MapPropertyValueExtractor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/MapPropertyValueExtractor.java index 0db1b74ba4..179ed8e51b 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/MapPropertyValueExtractor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/MapPropertyValueExtractor.java @@ -11,6 +11,7 @@ import javax.validation.valueextraction.ExtractedValue; import javax.validation.valueextraction.ValueExtractor; +import org.hibernate.validator.internal.IgnoreForbiddenApisErrors; import org.hibernate.validator.internal.engine.path.NodeImpl; import javafx.beans.property.MapProperty; @@ -25,6 +26,7 @@ * @author Guillaume Smet */ @SuppressWarnings("restriction") +@IgnoreForbiddenApisErrors(reason = "Usage of JavaFX classes") class MapPropertyValueExtractor implements ValueExtractor> { static final ValueExtractorDescriptor DESCRIPTOR = new ValueExtractorDescriptor( new MapPropertyValueExtractor() ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ObservableValueValueExtractor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ObservableValueValueExtractor.java index 986ed2b559..12232824d0 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ObservableValueValueExtractor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ObservableValueValueExtractor.java @@ -10,6 +10,8 @@ import javax.validation.valueextraction.UnwrapByDefault; import javax.validation.valueextraction.ValueExtractor; +import org.hibernate.validator.internal.IgnoreForbiddenApisErrors; + import javafx.beans.value.ObservableValue; /** @@ -18,6 +20,7 @@ * @author Gunnar Morling */ @SuppressWarnings("restriction") +@IgnoreForbiddenApisErrors(reason = "Usage of JavaFX classes") @UnwrapByDefault class ObservableValueValueExtractor implements ValueExtractor> { diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlyListPropertyValueExtractor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlyListPropertyValueExtractor.java index e91f838030..e003c52245 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlyListPropertyValueExtractor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlyListPropertyValueExtractor.java @@ -11,6 +11,7 @@ import javax.validation.valueextraction.ExtractedValue; import javax.validation.valueextraction.ValueExtractor; +import org.hibernate.validator.internal.IgnoreForbiddenApisErrors; import org.hibernate.validator.internal.engine.path.NodeImpl; import javafx.beans.property.ReadOnlyListProperty; @@ -25,6 +26,7 @@ * @author Guillaume Smet */ @SuppressWarnings("restriction") +@IgnoreForbiddenApisErrors(reason = "Usage of JavaFX classes") class ReadOnlyListPropertyValueExtractor implements ValueExtractor> { static final ValueExtractorDescriptor DESCRIPTOR = new ValueExtractorDescriptor( new ReadOnlyListPropertyValueExtractor() ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlyMapPropertyKeyExtractor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlyMapPropertyKeyExtractor.java index 6632054deb..c98cefd76f 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlyMapPropertyKeyExtractor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlyMapPropertyKeyExtractor.java @@ -11,6 +11,7 @@ import javax.validation.valueextraction.ExtractedValue; import javax.validation.valueextraction.ValueExtractor; +import org.hibernate.validator.internal.IgnoreForbiddenApisErrors; import org.hibernate.validator.internal.engine.path.NodeImpl; import javafx.beans.property.ReadOnlyMapProperty; @@ -25,6 +26,7 @@ * @author Guillaume Smet */ @SuppressWarnings("restriction") +@IgnoreForbiddenApisErrors(reason = "Usage of JavaFX classes") class ReadOnlyMapPropertyKeyExtractor implements ValueExtractor> { static final ValueExtractorDescriptor DESCRIPTOR = new ValueExtractorDescriptor( new ReadOnlyMapPropertyKeyExtractor() ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlyMapPropertyValueExtractor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlyMapPropertyValueExtractor.java index 4217177d4d..e843400dd0 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlyMapPropertyValueExtractor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlyMapPropertyValueExtractor.java @@ -11,6 +11,7 @@ import javax.validation.valueextraction.ExtractedValue; import javax.validation.valueextraction.ValueExtractor; +import org.hibernate.validator.internal.IgnoreForbiddenApisErrors; import org.hibernate.validator.internal.engine.path.NodeImpl; import javafx.beans.property.ReadOnlyMapProperty; @@ -25,6 +26,7 @@ * @author Guillaume Smet */ @SuppressWarnings("restriction") +@IgnoreForbiddenApisErrors(reason = "Usage of JavaFX classes") class ReadOnlyMapPropertyValueExtractor implements ValueExtractor> { static final ValueExtractorDescriptor DESCRIPTOR = new ValueExtractorDescriptor( new ReadOnlyMapPropertyValueExtractor() ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlySetPropertyValueExtractor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlySetPropertyValueExtractor.java index 1366eebdd0..18635dc017 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlySetPropertyValueExtractor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlySetPropertyValueExtractor.java @@ -11,6 +11,7 @@ import javax.validation.valueextraction.ExtractedValue; import javax.validation.valueextraction.ValueExtractor; +import org.hibernate.validator.internal.IgnoreForbiddenApisErrors; import org.hibernate.validator.internal.engine.path.NodeImpl; import javafx.beans.property.ReadOnlySetProperty; @@ -25,6 +26,7 @@ * @author Guillaume Smet */ @SuppressWarnings("restriction") +@IgnoreForbiddenApisErrors(reason = "Usage of JavaFX classes") class ReadOnlySetPropertyValueExtractor implements ValueExtractor> { static final ValueExtractorDescriptor DESCRIPTOR = new ValueExtractorDescriptor( new ReadOnlySetPropertyValueExtractor() ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/SetPropertyValueExtractor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/SetPropertyValueExtractor.java index e7847ddd0e..bcd0d9b55b 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/SetPropertyValueExtractor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/SetPropertyValueExtractor.java @@ -11,6 +11,7 @@ import javax.validation.valueextraction.ExtractedValue; import javax.validation.valueextraction.ValueExtractor; +import org.hibernate.validator.internal.IgnoreForbiddenApisErrors; import org.hibernate.validator.internal.engine.path.NodeImpl; import javafx.beans.property.SetProperty; @@ -25,6 +26,7 @@ * @author Guillaume Smet */ @SuppressWarnings("restriction") +@IgnoreForbiddenApisErrors(reason = "Usage of JavaFX classes") class SetPropertyValueExtractor implements ValueExtractor> { static final ValueExtractorDescriptor DESCRIPTOR = new ValueExtractorDescriptor( new SetPropertyValueExtractor() ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ValueExtractorManager.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ValueExtractorManager.java index 4532c0b7ad..af427ff571 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ValueExtractorManager.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ValueExtractorManager.java @@ -36,10 +36,16 @@ public class ValueExtractorManager { @Immutable public static final Set SPEC_DEFINED_EXTRACTORS; + /** + * Set this environment variable to true to ensure the JavaFX integrations are disabled. + * Normally the JavaFX extensions are enabled if and only if JavaFX is found on classpath. + */ + private static final String HIBERNATE_VALIDATOR_FORCE_DISABLE_JAVAFX_INTEGRATION = "org.hibernate.validator.force-disable-javafx-integration"; + static { LinkedHashSet specDefinedExtractors = new LinkedHashSet<>(); - if ( isJavaFxInClasspath() ) { + if ( isJavaFxExtensionsEnabled() ) { specDefinedExtractors.add( ObservableValueValueExtractor.DESCRIPTOR ); specDefinedExtractors.add( ListPropertyValueExtractor.DESCRIPTOR ); specDefinedExtractors.add( ReadOnlyListPropertyValueExtractor.DESCRIPTOR ); @@ -177,8 +183,26 @@ public boolean equals(Object obj) { return registeredValueExtractors.equals( other.registeredValueExtractors ); } + private static boolean isJavaFxExtensionsEnabled() { + if ( isJavaFxForcefullyDisabled() ) { + return false; + } + else { + return isJavaFxInClasspath(); + } + } + + private static boolean isJavaFxForcefullyDisabled() { + return run( new PrivilegedAction() { + @Override + public Boolean run() { + return Boolean.valueOf( Boolean.getBoolean( HIBERNATE_VALIDATOR_FORCE_DISABLE_JAVAFX_INTEGRATION ) ); + } + } ); + } + private static boolean isJavaFxInClasspath() { - return isClassPresent( "javafx.application.Application", false ); + return isClassPresent( "javafx.beans.value.ObservableValue", false ); } private static boolean isClassPresent(String className, boolean fallbackOnTCCL) { 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..4285b34da2 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 @@ -47,8 +47,6 @@ public class ValueExtractorResolver { private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - private static final Object NON_CONTAINER_VALUE = new Object(); - @Immutable private final Set registeredValueExtractors; @@ -56,7 +54,7 @@ public class ValueExtractorResolver { private final ConcurrentHashMap, Set> possibleValueExtractorsByRuntimeType = new ConcurrentHashMap<>(); - private final ConcurrentHashMap, Object> nonContainerTypes = new ConcurrentHashMap<>(); + private final Set> nonContainerTypes = Collections.newSetFromMap( new ConcurrentHashMap<>() ); ValueExtractorResolver(Set valueExtractors) { this.registeredValueExtractors = CollectionHelper.toImmutableSet( valueExtractors ); @@ -303,26 +301,27 @@ private Set getRuntimeCompliantValueExtractors(Class valueExtractorDescriptors = possibleValueExtractorsByRuntimeType.get( runtimeType ); - if ( valueExtractorDescriptors == null ) { - //otherwise we just look for maximally specific extractors for the runtime type - Set possibleValueExtractors = potentialValueExtractorDescriptors - .stream() - .filter( e -> TypeHelper.isAssignable( e.getContainerType(), runtimeType ) ) - .collect( Collectors.toSet() ); - - valueExtractorDescriptors = getMaximallySpecificValueExtractors( possibleValueExtractors ); + + if ( valueExtractorDescriptors != null ) { + return valueExtractorDescriptors; } + Set possibleValueExtractors = potentialValueExtractorDescriptors + .stream() + .filter( e -> TypeHelper.isAssignable( e.getContainerType(), runtimeType ) ) + .collect( Collectors.toSet() ); + + valueExtractorDescriptors = getMaximallySpecificValueExtractors( possibleValueExtractors ); + if ( valueExtractorDescriptors.isEmpty() ) { - nonContainerTypes.put( runtimeType, NON_CONTAINER_VALUE ); - } - else { - Set extractorDescriptorsToCache = CollectionHelper.toImmutableSet( valueExtractorDescriptors ); - possibleValueExtractorsByRuntimeType.put( runtimeType, extractorDescriptorsToCache ); - return extractorDescriptorsToCache; + nonContainerTypes.add( runtimeType ); + return Collections.emptySet(); } - return valueExtractorDescriptors; + Set valueExtractorDescriptorsToCache = CollectionHelper.toImmutableSet( valueExtractorDescriptors ); + Set cachedValueExtractorDescriptors = possibleValueExtractorsByRuntimeType.putIfAbsent( runtimeType, + valueExtractorDescriptorsToCache ); + return cachedValueExtractorDescriptors != null ? cachedValueExtractorDescriptors : valueExtractorDescriptorsToCache; } private Set getRuntimeAndContainerElementCompliantValueExtractorsFromPossibleCandidates(Type declaredType, @@ -334,32 +333,34 @@ private Set getRuntimeAndContainerElementCompliantValu ValueExtractorCacheKey cacheKey = new ValueExtractorCacheKey( runtimeType, typeParameter ); Set valueExtractorDescriptors = possibleValueExtractorsByRuntimeTypeAndTypeParameter.get( cacheKey ); - if ( valueExtractorDescriptors == null ) { - boolean isInternal = TypeVariables.isInternal( typeParameter ); - Class erasedDeclaredType = TypeHelper.getErasedReferenceType( declaredType ); - Set possibleValueExtractors = valueExtractorCandidates - .stream() - .filter( e -> TypeHelper.isAssignable( e.getContainerType(), runtimeType ) ) - .filter( extractorDescriptor -> - checkValueExtractorTypeCompatibility( - typeParameter, isInternal, erasedDeclaredType, extractorDescriptor - ) - ).collect( Collectors.toSet() ); + if ( valueExtractorDescriptors != null ) { + return valueExtractorDescriptors; + } + + boolean isInternal = TypeVariables.isInternal( typeParameter ); + Class erasedDeclaredType = TypeHelper.getErasedReferenceType( declaredType ); - valueExtractorDescriptors = getMaximallySpecificValueExtractors( possibleValueExtractors ); + Set possibleValueExtractors = valueExtractorCandidates + .stream() + .filter( e -> TypeHelper.isAssignable( e.getContainerType(), runtimeType ) ) + .filter( extractorDescriptor -> + checkValueExtractorTypeCompatibility( + typeParameter, isInternal, erasedDeclaredType, extractorDescriptor + ) + ).collect( Collectors.toSet() ); - if ( valueExtractorDescriptors.isEmpty() ) { - nonContainerTypes.put( runtimeType, NON_CONTAINER_VALUE ); - } - else { - Set extractorDescriptorsToCache = CollectionHelper.toImmutableSet( valueExtractorDescriptors ); - possibleValueExtractorsByRuntimeTypeAndTypeParameter.put( cacheKey, extractorDescriptorsToCache ); - return extractorDescriptorsToCache; - } + valueExtractorDescriptors = getMaximallySpecificValueExtractors( possibleValueExtractors ); + + if ( valueExtractorDescriptors.isEmpty() ) { + nonContainerTypes.add( runtimeType ); + return Collections.emptySet(); } - return valueExtractorDescriptors; + Set valueExtractorDescriptorsToCache = CollectionHelper.toImmutableSet( valueExtractorDescriptors ); + Set cachedValueExtractorDescriptors = possibleValueExtractorsByRuntimeTypeAndTypeParameter.putIfAbsent( cacheKey, + valueExtractorDescriptorsToCache ); + return cachedValueExtractorDescriptors != null ? cachedValueExtractorDescriptors : valueExtractorDescriptorsToCache; } private boolean checkValueExtractorTypeCompatibility(TypeVariable typeParameter, boolean isInternal, Class erasedDeclaredType, diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/BeanMetaDataManager.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/BeanMetaDataManager.java index 5a5abbf095..127b7a3652 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/BeanMetaDataManager.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/BeanMetaDataManager.java @@ -6,223 +6,17 @@ */ package org.hibernate.validator.internal.metadata; -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.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.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; /** * 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 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; - - /** - * Additional metadata providers used for meta data retrieval if - * the XML and/or programmatic configuration is used. - */ - 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 BeanMetaDataManager(ConstraintHelper constraintHelper, - ExecutableHelper executableHelper, - TypeResolutionHelper typeResolutionHelper, - ExecutableParameterNameProvider parameterNameProvider, - ValueExtractorManager valueExtractorManager, - 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.metaDataProviders = newArrayList(); - this.metaDataProviders.addAll( optionalMetaDataProviders ); - - 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(); - AnnotationMetaDataProvider defaultProvider = new AnnotationMetaDataProvider( - constraintHelper, - typeResolutionHelper, - valueExtractorManager, - annotationProcessingOptions - ); - - this.metaDataProviders.add( defaultProvider ); - } - - @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() { - AnnotationProcessingOptions options = new AnnotationProcessingOptionsImpl(); - for ( MetaDataProvider metaDataProvider : metaDataProviders ) { - 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(); +public interface BeanMetaDataManager { - for ( Class clazz : ClassHierarchyHelper.getHierarchy( beanClass ) ) { - BeanConfiguration configuration = provider.getBeanConfiguration( clazz ); - if ( configuration != null ) { - configurations.add( configuration ); - } - } + BeanMetaData getBeanMetaData(Class beanClass); - return configurations; - } + void clear(); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/BeanMetaDataManagerImpl.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/BeanMetaDataManagerImpl.java new file mode 100644 index 0000000000..722d92aa2f --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/BeanMetaDataManagerImpl.java @@ -0,0 +1,232 @@ +/* + * 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; + +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 org.hibernate.validator.internal.engine.ConstraintCreationContext; +import org.hibernate.validator.internal.engine.MethodValidationConfiguration; +import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; +import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; +import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +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.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 BeanMetaDataManagerImpl implements 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; + + /** + * Additional metadata providers used for meta data retrieval if + * the XML and/or programmatic configuration is used. + */ + @Immutable + private final List metaDataProviders; + + /** + * The constraint creation context containing all the helpers necessary to the constraint creation. + */ + private final ConstraintCreationContext constraintCreationContext; + + 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 BeanMetaDataManagerImpl(ConstraintCreationContext constraintCreationContext, + ExecutableHelper executableHelper, + ExecutableParameterNameProvider parameterNameProvider, + JavaBeanHelper javaBeanHelper, + ValidationOrderGenerator validationOrderGenerator, + List optionalMetaDataProviders, + MethodValidationConfiguration methodValidationConfiguration) { + this.constraintCreationContext = constraintCreationContext; + this.executableHelper = executableHelper; + 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( + constraintCreationContext, + 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 ); + } + + @Override + @SuppressWarnings("unchecked") + public BeanMetaData getBeanMetaData(Class beanClass) { + Contracts.assertNotNull( beanClass, MESSAGES.beanTypeCannotBeNull() ); + + // First, let's do a simple lookup as it's the default case + BeanMetaData beanMetaData = (BeanMetaData) beanMetaDataCache.get( beanClass ); + + if ( beanMetaData != null ) { + return beanMetaData; + } + + beanMetaData = createBeanMetaData( beanClass ); + BeanMetaData previousBeanMetaData = (BeanMetaData) beanMetaDataCache.putIfAbsent( beanClass, beanMetaData ); + + // we return the previous value if not null + if ( previousBeanMetaData != null ) { + return previousBeanMetaData; + } + + return beanMetaData; + } + + @Override + 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( + constraintCreationContext, executableHelper, 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/DefaultBeanMetaDataClassNormalizer.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/DefaultBeanMetaDataClassNormalizer.java new file mode 100644 index 0000000000..a40a987786 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/DefaultBeanMetaDataClassNormalizer.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.metadata; + +import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer; + +/** + * The default implementation of {@link BeanMetaDataClassNormalizer}. + *

+ * Simply returns the provided class. + * + * @author Guillaume Smet + */ +public class DefaultBeanMetaDataClassNormalizer implements BeanMetaDataClassNormalizer { + + @Override + public Class normalize(Class beanClass) { + return beanClass; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/PredefinedScopeBeanMetaDataManager.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/PredefinedScopeBeanMetaDataManager.java new file mode 100644 index 0000000000..b7215b0637 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/PredefinedScopeBeanMetaDataManager.java @@ -0,0 +1,366 @@ +/* + * 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; + +import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; + +import java.lang.annotation.ElementType; +import java.lang.reflect.Executable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import javax.validation.metadata.BeanDescriptor; +import javax.validation.metadata.ConstraintDescriptor; +import javax.validation.metadata.ConstructorDescriptor; +import javax.validation.metadata.ElementDescriptor.ConstraintFinder; +import javax.validation.metadata.MethodDescriptor; +import javax.validation.metadata.MethodType; +import javax.validation.metadata.PropertyDescriptor; +import javax.validation.metadata.Scope; + +import org.hibernate.validator.internal.engine.ConstraintCreationContext; +import org.hibernate.validator.internal.engine.MethodValidationConfiguration; +import org.hibernate.validator.internal.engine.groups.Sequence; +import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; +import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; +import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataBuilder; +import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl; +import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData; +import org.hibernate.validator.internal.metadata.aggregated.PropertyMetaData; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.facets.Cascadable; +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.ExecutableHelper; +import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; +import org.hibernate.validator.internal.util.classhierarchy.ClassHierarchyHelper; +import org.hibernate.validator.internal.util.classhierarchy.Filters; +import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer; + +public class PredefinedScopeBeanMetaDataManager implements BeanMetaDataManager { + + private final BeanMetaDataClassNormalizer beanMetaDataClassNormalizer; + + /** + * Used to cache the constraint meta data for validated entities. + */ + private final ConcurrentMap, BeanMetaData> beanMetaDataMap = new ConcurrentHashMap<>(); + + public PredefinedScopeBeanMetaDataManager(ConstraintCreationContext constraintCreationContext, + ExecutableHelper executableHelper, + ExecutableParameterNameProvider parameterNameProvider, + JavaBeanHelper javaBeanHelper, + ValidationOrderGenerator validationOrderGenerator, + List optionalMetaDataProviders, + MethodValidationConfiguration methodValidationConfiguration, + BeanMetaDataClassNormalizer beanMetaDataClassNormalizer, + Set> beanClassesToInitialize) { + AnnotationProcessingOptions annotationProcessingOptions = getAnnotationProcessingOptionsFromNonDefaultProviders( optionalMetaDataProviders ); + AnnotationMetaDataProvider defaultProvider = new AnnotationMetaDataProvider( + constraintCreationContext, + javaBeanHelper, + annotationProcessingOptions + ); + + List metaDataProviders = 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. + metaDataProviders.add( defaultProvider ); + metaDataProviders.addAll( optionalMetaDataProviders ); + + for ( Class validatedClass : beanClassesToInitialize ) { + Class normalizedValidatedClass = beanMetaDataClassNormalizer.normalize( validatedClass ); + + @SuppressWarnings("unchecked") + List> classHierarchy = (List>) (Object) ClassHierarchyHelper.getHierarchy( normalizedValidatedClass, Filters.excludeInterfaces() ); + + // note that the hierarchy also contains the initial class + for ( Class hierarchyElement : classHierarchy ) { + if ( this.beanMetaDataMap.containsKey( hierarchyElement ) ) { + continue; + } + + this.beanMetaDataMap.put( hierarchyElement, + createBeanMetaData( constraintCreationContext, executableHelper, parameterNameProvider, + javaBeanHelper, validationOrderGenerator, optionalMetaDataProviders, methodValidationConfiguration, + metaDataProviders, hierarchyElement ) ); + } + } + + this.beanMetaDataClassNormalizer = beanMetaDataClassNormalizer; + } + + @SuppressWarnings("unchecked") + @Override + public BeanMetaData getBeanMetaData(Class beanClass) { + Class normalizedBeanClass = beanMetaDataClassNormalizer.normalize( beanClass ); + BeanMetaData beanMetaData = (BeanMetaData) beanMetaDataMap.get( normalizedBeanClass ); + if ( beanMetaData == null ) { + // note that if at least one element of the hierarchy is constrained, the child classes should really be initialized + // otherwise they will be considered unconstrained. + beanMetaData = (BeanMetaData) beanMetaDataMap.computeIfAbsent( normalizedBeanClass, UninitializedBeanMetaData::new ); + } + return beanMetaData; + } + + @Override + public void clear() { + beanMetaDataMap.clear(); + } + + /** + * 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 static BeanMetaDataImpl createBeanMetaData(ConstraintCreationContext constraintCreationContext, + ExecutableHelper executableHelper, + ExecutableParameterNameProvider parameterNameProvider, + JavaBeanHelper javaBeanHelper, + ValidationOrderGenerator validationOrderGenerator, + List optionalMetaDataProviders, + MethodValidationConfiguration methodValidationConfiguration, + List metaDataProviders, + Class clazz) { + BeanMetaDataBuilder builder = BeanMetaDataBuilder.getInstance( + constraintCreationContext, executableHelper, 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 static 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 static 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; + } + + private static class UninitializedBeanMetaData implements BeanMetaData { + + private final Class beanClass; + + private final BeanDescriptor beanDescriptor; + + private final List> classHierarchy; + + @SuppressWarnings("unchecked") + private UninitializedBeanMetaData(Class beanClass) { + this.beanClass = beanClass; + this.classHierarchy = (List>) (Object) ClassHierarchyHelper.getHierarchy( beanClass, Filters.excludeInterfaces() ); + this.beanDescriptor = new UninitializedBeanDescriptor( beanClass ); + } + + @Override + public Iterable getCascadables() { + return Collections.emptyList(); + } + + @Override + public boolean hasCascadables() { + return false; + } + + @Override + public Class getBeanClass() { + return beanClass; + } + + @Override + public boolean hasConstraints() { + return false; + } + + @Override + public BeanDescriptor getBeanDescriptor() { + return beanDescriptor; + } + + @Override + public PropertyMetaData getMetaDataFor(String propertyName) { + throw new IllegalStateException( "Metadata has not been initialized for bean of type " + beanClass.getName() ); + } + + @Override + public List> getDefaultGroupSequence(T beanState) { + throw new IllegalStateException( "Metadata has not been initialized for bean of type " + beanClass.getName() ); + } + + @Override + public Iterator getDefaultValidationSequence(T beanState) { + throw new IllegalStateException( "Metadata has not been initialized for bean of type " + beanClass.getName() ); + } + + @Override + public boolean isDefaultGroupSequenceRedefined() { + return false; + } + + @Override + public Set> getMetaConstraints() { + return Collections.emptySet(); + } + + @Override + public Set> getDirectMetaConstraints() { + return Collections.emptySet(); + } + + @Override + public Optional getMetaDataFor(Executable executable) throws IllegalArgumentException { + return Optional.empty(); + } + + @Override + public List> getClassHierarchy() { + return classHierarchy; + } + } + + private static class UninitializedBeanDescriptor implements BeanDescriptor { + + private final Class elementClass; + + private UninitializedBeanDescriptor(Class elementClass) { + this.elementClass = elementClass; + } + + @Override + public boolean hasConstraints() { + return false; + } + + @Override + public Class getElementClass() { + return elementClass; + } + + @Override + public Set> getConstraintDescriptors() { + return Collections.emptySet(); + } + + @Override + public ConstraintFinder findConstraints() { + return UninitializedConstaintFinder.INSTANCE; + } + + @Override + public boolean isBeanConstrained() { + return false; + } + + @Override + public PropertyDescriptor getConstraintsForProperty(String propertyName) { + return null; + } + + @Override + public Set getConstrainedProperties() { + return Collections.emptySet(); + } + + @Override + public MethodDescriptor getConstraintsForMethod(String methodName, Class... parameterTypes) { + return null; + } + + @Override + public Set getConstrainedMethods(MethodType methodType, MethodType... methodTypes) { + return Collections.emptySet(); + } + + @Override + public ConstructorDescriptor getConstraintsForConstructor(Class... parameterTypes) { + return null; + } + + @Override + public Set getConstrainedConstructors() { + return Collections.emptySet(); + } + } + + private static class UninitializedConstaintFinder implements ConstraintFinder { + + private static final UninitializedConstaintFinder INSTANCE = new UninitializedConstaintFinder(); + + @Override + public ConstraintFinder unorderedAndMatchingGroups(Class... groups) { + return this; + } + + @Override + public ConstraintFinder lookingAt(Scope scope) { + return this; + } + + @Override + public ConstraintFinder declaredOn(ElementType... types) { + return this; + } + + @Override + public Set> getConstraintDescriptors() { + return Collections.emptySet(); + } + + @Override + public boolean hasConstraints() { + return false; + } + } +} 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 new file mode 100644 index 0000000000..aea6cb0558 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/AbstractPropertyCascadable.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.aggregated; + +import java.lang.reflect.Type; + +import org.hibernate.validator.internal.engine.path.PathImpl; +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.facets.Cascadable; +import org.hibernate.validator.internal.properties.Field; +import org.hibernate.validator.internal.properties.Getter; +import org.hibernate.validator.internal.properties.Property; +import org.hibernate.validator.internal.properties.PropertyAccessor; + +/** + * A {@link Cascadable} backed by a property of a Java bean. + * + * @author Gunnar Morling + * @author Marko Bekhta + */ +public abstract class AbstractPropertyCascadable implements Cascadable { + + private final T property; + private final PropertyAccessor propertyAccessor; + private final Type cascadableType; + private final CascadingMetaData cascadingMetaData; + + AbstractPropertyCascadable(T property, CascadingMetaData cascadingMetaData) { + this.property = property; + this.propertyAccessor = property.createAccessor(); + this.cascadableType = property.getType(); + this.cascadingMetaData = cascadingMetaData; + } + + @Override + public Type getCascadableType() { + return cascadableType; + } + + @Override + public Object getValue(Object parent) { + return propertyAccessor.getValueFrom( parent ); + } + + @Override + public void appendTo(PathImpl path) { + path.addPropertyNode( property.getResolvedPropertyName() ); + } + + @Override + public CascadingMetaData getCascadingMetaData() { + return cascadingMetaData; + } + + public abstract static class AbstractBuilder implements Cascadable.Builder { + + private final ValueExtractorManager valueExtractorManager; + private final T property; + private CascadingMetaDataBuilder cascadingMetaDataBuilder; + + protected AbstractBuilder(ValueExtractorManager valueExtractorManager, T property, CascadingMetaDataBuilder cascadingMetaDataBuilder) { + this.valueExtractorManager = valueExtractorManager; + this.property = property; + this.cascadingMetaDataBuilder = cascadingMetaDataBuilder; + } + + @Override + public void mergeCascadingMetaData(CascadingMetaDataBuilder cascadingMetaData) { + this.cascadingMetaDataBuilder = this.cascadingMetaDataBuilder.merge( cascadingMetaData ); + } + + @Override + public Cascadable build() { + return create( property, cascadingMetaDataBuilder.build( valueExtractorManager, property ) ); + } + + protected abstract Cascadable create(T property, CascadingMetaData build); + + public static Cascadable.Builder builder(ValueExtractorManager valueExtractorManager, Property property, + CascadingMetaDataBuilder cascadingMetaDataBuilder) { + if ( property instanceof Field ) { + return new FieldCascadable.Builder( valueExtractorManager, (Field) property, cascadingMetaDataBuilder ); + } + else if ( property instanceof Getter ) { + return new GetterCascadable.Builder( valueExtractorManager, (Getter) property, cascadingMetaDataBuilder ); + } + throw new IllegalStateException( "It should be either a field or a getter." ); + } + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaData.java index 3a9f5a00dc..fdeac0ffa5 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaData.java @@ -79,7 +79,7 @@ public interface BeanMetaData extends Validatable { /** * @return {@code true} if the entity redefines the default group sequence, {@code false} otherwise. */ - boolean defaultGroupSequenceIsRedefined(); + boolean isDefaultGroupSequenceRedefined(); /** * @return A set of {@code MetaConstraint} instances encapsulating the information of all the constraints 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..4f266dfa27 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataBuilder.java @@ -0,0 +1,300 @@ +/* + * 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.ConstraintCreationContext; +import org.hibernate.validator.internal.engine.MethodValidationConfiguration; +import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; +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.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 ConstraintCreationContext constraintCreationContext; + private final ValidationOrderGenerator validationOrderGenerator; + private final Class beanClass; + private final Set builders = newHashSet(); + private final ExecutableHelper executableHelper; + private final ExecutableParameterNameProvider parameterNameProvider; + private final MethodValidationConfiguration methodValidationConfiguration; + + private ConfigurationSource sequenceSource; + private ConfigurationSource providerSource; + private List> defaultGroupSequence; + private DefaultGroupSequenceProvider defaultGroupSequenceProvider; + + + private BeanMetaDataBuilder( + ConstraintCreationContext constraintCreationContext, + ExecutableHelper executableHelper, + ExecutableParameterNameProvider parameterNameProvider, + ValidationOrderGenerator validationOrderGenerator, + Class beanClass, + MethodValidationConfiguration methodValidationConfiguration) { + this.beanClass = beanClass; + this.constraintCreationContext = constraintCreationContext; + this.validationOrderGenerator = validationOrderGenerator; + this.executableHelper = executableHelper; + this.parameterNameProvider = parameterNameProvider; + this.methodValidationConfiguration = methodValidationConfiguration; + } + + public static BeanMetaDataBuilder getInstance( + ConstraintCreationContext constraintCreationContext, + ExecutableHelper executableHelper, + ExecutableParameterNameProvider parameterNameProvider, + ValidationOrderGenerator validationOrderGenerator, + Class beanClass, + MethodValidationConfiguration methodValidationConfiguration) { + return new BeanMetaDataBuilder<>( + constraintCreationContext, + executableHelper, + 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, + constraintCreationContext, + executableHelper, + 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 ConstraintCreationContext constraintCreationContext; + private final ExecutableHelper executableHelper; + 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, + ConstraintCreationContext constraintCreationContext, + ExecutableHelper executableHelper, + ExecutableParameterNameProvider parameterNameProvider, + MethodValidationConfiguration methodValidationConfiguration + ) { + this.beanClass = beanClass; + this.constrainedElement = constrainedElement; + this.constraintCreationContext = constraintCreationContext; + this.executableHelper = executableHelper; + this.parameterNameProvider = parameterNameProvider; + this.methodValidationConfiguration = methodValidationConfiguration; + + switch ( constrainedElement.getKind() ) { + case FIELD: + ConstrainedField constrainedField = (ConstrainedField) constrainedElement; + metaDataBuilder = new PropertyMetaData.Builder( + beanClass, + constrainedField, + constraintCreationContext + ); + 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, + constraintCreationContext, + executableHelper, + parameterNameProvider, + methodValidationConfiguration + ); + } + + if ( constrainedElement.getKind() == ConstrainedElement.ConstrainedElementKind.GETTER ) { + metaDataBuilder = new PropertyMetaData.Builder( + beanClass, + constrainedExecutable, + constraintCreationContext + ); + } + break; + case TYPE: + ConstrainedType constrainedType = (ConstrainedType) constrainedElement; + metaDataBuilder = new ClassMetaData.Builder( + beanClass, + constrainedType, + constraintCreationContext + ); + 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, + constraintCreationContext, + executableHelper, + 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 386ba70b63..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 @@ -9,11 +9,8 @@ import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; -import java.lang.annotation.ElementType; import java.lang.invoke.MethodHandles; import java.lang.reflect.Executable; -import java.lang.reflect.Member; -import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; @@ -30,28 +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.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.metadata.location.ConstraintLocation.ConstraintLocationKind; 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.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 { @@ -134,11 +121,6 @@ public final class BeanMetaDataImpl implements BeanMetaData { @Immutable private final Set cascadedProperties; - /** - * The bean descriptor for this bean. - */ - private final BeanDescriptor beanDescriptor; - /** * The default groups sequence for this bean class. */ @@ -162,6 +144,22 @@ public final class BeanMetaDataImpl implements BeanMetaData { @Immutable private final List> classHierarchyWithoutInterfaces; + /** + * {code true} if the default group sequence is redefined, either via a group sequence redefinition or a group + * sequence provider. + */ + private final boolean defaultGroupSequenceRedefined; + + /** + * The resolved default group sequence. + */ + private final List> resolvedDefaultGroupSequence; + + /** + * The bean descriptor for this bean. Lazily created. + */ + private volatile BeanDescriptor beanDescriptor; + /** * Creates a new {@link BeanMetaDataImpl} * @@ -186,6 +184,7 @@ public BeanMetaDataImpl(Class beanClass, Set tmpUnconstrainedExecutables = newHashSet(); boolean hasConstraints = false; + Set> allMetaConstraints = newHashSet(); for ( ConstraintMetaData constraintMetaData : constraintMetaDataSet ) { boolean elementHasConstraints = constraintMetaData.isCascading() || constraintMetaData.isConstrained(); @@ -194,6 +193,9 @@ public BeanMetaDataImpl(Class beanClass, if ( constraintMetaData.getKind() == ElementKind.PROPERTY ) { propertyMetaDataSet.add( (PropertyMetaData) constraintMetaData ); } + else if ( constraintMetaData.getKind() == ElementKind.BEAN ) { + allMetaConstraints.addAll( ( (ClassMetaData) constraintMetaData ).getAllConstraints() ); + } else { ExecutableMetaData executableMetaData = (ExecutableMetaData) constraintMetaData; if ( elementHasConstraints ) { @@ -206,7 +208,6 @@ public BeanMetaDataImpl(Class beanClass, } Set cascadedProperties = newHashSet(); - Set> allMetaConstraints = newHashSet(); for ( PropertyMetaData propertyMetaData : propertyMetaDataSet ) { propertyMetaDataMap.put( propertyMetaData.getName(), propertyMetaData ); @@ -233,36 +234,9 @@ public BeanMetaDataImpl(Class beanClass, this.executableMetaDataMap = CollectionHelper.toImmutableMap( bySignature( executableMetaDataSet ) ); this.unconstrainedExecutables = CollectionHelper.toImmutableSet( tmpUnconstrainedExecutables ); - boolean defaultGroupSequenceIsRedefined = defaultGroupSequenceIsRedefined(); - List> resolvedDefaultGroupSequence = getDefaultGroupSequence( null ); - - Map propertyDescriptors = getConstrainedPropertiesAsDescriptors( - propertyMetaDataMap, - defaultGroupSequenceIsRedefined, - resolvedDefaultGroupSequence - ); - - Map methodsDescriptors = getConstrainedMethodsAsDescriptors( - executableMetaDataMap, - defaultGroupSequenceIsRedefined, - resolvedDefaultGroupSequence - ); - - Map constructorsDescriptors = getConstrainedConstructorsAsDescriptors( - executableMetaDataMap, - defaultGroupSequenceIsRedefined, - resolvedDefaultGroupSequence - ); - - this.beanDescriptor = new BeanDescriptorImpl( - beanClass, - getClassLevelConstraintsAsDescriptors( allMetaConstraints ), - propertyDescriptors, - methodsDescriptors, - constructorsDescriptors, - defaultGroupSequenceIsRedefined, - resolvedDefaultGroupSequence - ); + // We initialize those elements eagerly so that any eventual error is thrown when bootstrapping the bean metadata + this.defaultGroupSequenceRedefined = this.defaultGroupSequence.size() > 1 || hasDefaultGroupSequenceProvider(); + this.resolvedDefaultGroupSequence = getDefaultGroupSequence( null ); } @Override @@ -277,6 +251,21 @@ public boolean hasConstraints() { @Override public BeanDescriptor getBeanDescriptor() { + BeanDescriptor beanDescriptor = this.beanDescriptor; + + if ( beanDescriptor == null ) { + synchronized (this) { + beanDescriptor = this.beanDescriptor; + + if ( beanDescriptor == null ) { + beanDescriptor = createBeanDescriptor( beanClass, allMetaConstraints, propertyMetaDataMap, executableMetaDataMap, + defaultGroupSequenceRedefined, resolvedDefaultGroupSequence ); + + this.beanDescriptor = beanDescriptor; + } + } + } + return beanDescriptor; } @@ -347,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 { @@ -358,8 +347,8 @@ public Iterator getDefaultValidationSequence(T beanState) { } @Override - public boolean defaultGroupSequenceIsRedefined() { - return defaultGroupSequence.size() > 1 || hasDefaultGroupSequenceProvider(); + public boolean isDefaultGroupSequenceRedefined() { + return defaultGroupSequenceRedefined; } @Override @@ -367,9 +356,41 @@ public List> getClassHierarchy() { return classHierarchyWithoutInterfaces; } + private static BeanDescriptor createBeanDescriptor(Class beanClass, Set> allMetaConstraints, + Map propertyMetaDataMap, Map executableMetaDataMap, boolean defaultGroupSequenceRedefined, + List> resolvedDefaultGroupSequence) { + Map propertyDescriptors = getConstrainedPropertiesAsDescriptors( + propertyMetaDataMap, + defaultGroupSequenceRedefined, + resolvedDefaultGroupSequence + ); + + Map methodsDescriptors = getConstrainedMethodsAsDescriptors( + executableMetaDataMap, + defaultGroupSequenceRedefined, + resolvedDefaultGroupSequence + ); + + Map constructorsDescriptors = getConstrainedConstructorsAsDescriptors( + executableMetaDataMap, + defaultGroupSequenceRedefined, + resolvedDefaultGroupSequence + ); + + return new BeanDescriptorImpl( + beanClass, + getClassLevelConstraintsAsDescriptors( allMetaConstraints ), + propertyDescriptors, + methodsDescriptors, + constructorsDescriptors, + defaultGroupSequenceRedefined, + resolvedDefaultGroupSequence + ); + } + private static Set> getClassLevelConstraintsAsDescriptors(Set> constraints) { return constraints.stream() - .filter( c -> c.getElementType() == ElementType.TYPE ) + .filter( c -> c.getConstraintLocationKind() == ConstraintLocationKind.TYPE ) .map( MetaConstraint::getDescriptor ) .collect( Collectors.toSet() ); } @@ -401,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 ); @@ -537,295 +558,6 @@ public String toString() { + ", defaultGroupSequence=" + getDefaultGroupSequence( null ) + '}'; } - 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 propertyBuilder; - 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; - propertyBuilder = new PropertyMetaData.Builder( - beanClass, - constrainedField, - constraintHelper, - typeResolutionHelper, - valueExtractorManager - ); - break; - case CONSTRUCTOR: - case METHOD: - ConstrainedExecutable constrainedExecutable = (ConstrainedExecutable) constrainedElement; - Member member = constrainedExecutable.getExecutable(); - - // 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 ( !Modifier.isPrivate( member.getModifiers() ) || beanClass == member.getDeclaringClass() ) { - methodBuilder = new ExecutableMetaData.Builder( - beanClass, - constrainedExecutable, - constraintHelper, - executableHelper, - typeResolutionHelper, - valueExtractorManager, - parameterNameProvider, - methodValidationConfiguration - ); - } - - if ( constrainedExecutable.isGetterMethod() ) { - propertyBuilder = new PropertyMetaData.Builder( - beanClass, - constrainedExecutable, - constraintHelper, - typeResolutionHelper, - valueExtractorManager - ); - } - break; - case TYPE: - ConstrainedType constrainedType = (ConstrainedType) constrainedElement; - propertyBuilder = new PropertyMetaData.Builder( - beanClass, - constrainedType, - constraintHelper, - typeResolutionHelper, - valueExtractorManager - ); - break; - } - - this.hashCode = buildHashCode(); - } - - public boolean add(ConstrainedElement constrainedElement) { - boolean added = false; - - if ( methodBuilder != null && methodBuilder.accepts( constrainedElement ) ) { - methodBuilder.add( constrainedElement ); - added = true; - } - - if ( propertyBuilder != null && propertyBuilder.accepts( constrainedElement ) ) { - propertyBuilder.add( constrainedElement ); - - if ( !added && constrainedElement.getKind() == ConstrainedElementKind.METHOD && 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 ( propertyBuilder != null ) { - metaDataSet.add( propertyBuilder.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/ClassMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ClassMetaData.java new file mode 100644 index 0000000000..a9174ad4f1 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ClassMetaData.java @@ -0,0 +1,112 @@ +/* + * 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 java.util.List; +import java.util.Set; + +import javax.validation.ElementKind; + +import org.hibernate.validator.internal.engine.ConstraintCreationContext; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.descriptor.ClassDescriptorImpl; +import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; +import org.hibernate.validator.internal.metadata.raw.ConstrainedType; + +/** + * Represents the constraint related meta data for a type i.e. class-level + * constraints. + * + * @author Gunnar Morling + * @author Guillaume Smet + * @author Marko Bekhta + */ +public class ClassMetaData extends AbstractConstraintMetaData { + + private ClassMetaData(Class beanClass, + Set> constraints, + Set> containerElementsConstraints) { + super( + beanClass.getSimpleName(), + beanClass, + constraints, + containerElementsConstraints, + false, + !constraints.isEmpty() || !containerElementsConstraints.isEmpty() + ); + } + + @Override + public ClassDescriptorImpl asDescriptor(boolean defaultGroupSequenceRedefined, List> defaultGroupSequence) { + return new ClassDescriptorImpl( + getType(), + asDescriptors( getDirectConstraints() ), + defaultGroupSequenceRedefined, + defaultGroupSequence + ); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append( getClass().getSimpleName() ).append( "[ " ); + sb.append( "type=" ).append( getType() ); + sb.append( "]" ); + return sb.toString(); + } + + @Override + public ElementKind getKind() { + return ElementKind.BEAN; + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if ( this == obj ) { + return true; + } + if ( !super.equals( obj ) ) { + return false; + } + if ( getClass() != obj.getClass() ) { + return false; + } + return true; + } + + public static class Builder extends MetaDataBuilder { + public Builder(Class beanClass, ConstrainedType constrainedType, ConstraintCreationContext constraintCreationContext) { + super( beanClass, constraintCreationContext ); + + add( constrainedType ); + } + + @Override + public boolean accepts(ConstrainedElement constrainedElement) { + return constrainedElement.getKind() == ConstrainedElement.ConstrainedElementKind.TYPE; + } + + @Override + public final void add(ConstrainedElement constrainedElement) { + super.add( constrainedElement ); + } + + @Override + public ClassMetaData build() { + return new ClassMetaData( + getBeanClass(), + adaptOriginsAndImplicitGroups( getDirectConstraints() ), + adaptOriginsAndImplicitGroups( getContainerElementConstraints() ) + ); + } + } +} 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 001ac4db3b..be8979bc73 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 @@ -10,9 +10,6 @@ import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Arrays; import java.util.Collections; @@ -24,20 +21,19 @@ import javax.validation.ElementKind; import javax.validation.metadata.ParameterDescriptor; +import org.hibernate.validator.internal.engine.ConstraintCreationContext; import org.hibernate.validator.internal.engine.MethodValidationConfiguration; -import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; 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; import org.hibernate.validator.internal.metadata.descriptor.ExecutableDescriptorImpl; 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.ConstrainedParameter; +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.ReflectionHelper; -import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.stereotypes.Immutable; /** @@ -256,12 +252,11 @@ public static class Builder extends MetaDataBuilder { private final Set signatures = newHashSet(); /** - * Either CONSTRUCTOR or METHOD. + * Either CONSTRUCTOR, METHOD or GETTER. */ - private final ConstrainedElement.ConstrainedElementKind kind; + private final ConstrainedElementKind kind; private final Set constrainedExecutables = newHashSet(); - private Executable executable; - private final boolean isGetterMethod; + private Callable callable; private final Set> crossParameterConstraints = newHashSet(); private final Set rules; private boolean isConstrained = false; @@ -282,20 +277,17 @@ public static class Builder extends MetaDataBuilder { public Builder( Class beanClass, ConstrainedExecutable constrainedExecutable, - ConstraintHelper constraintHelper, + ConstraintCreationContext constraintCreationContext, ExecutableHelper executableHelper, - TypeResolutionHelper typeResolutionHelper, - ValueExtractorManager valueExtractorManager, ExecutableParameterNameProvider parameterNameProvider, MethodValidationConfiguration methodValidationConfiguration) { - super( beanClass, constraintHelper, typeResolutionHelper, valueExtractorManager ); + super( beanClass, constraintCreationContext ); this.executableHelper = executableHelper; this.parameterNameProvider = parameterNameProvider; this.kind = constrainedExecutable.getKind(); - this.executable = constrainedExecutable.getExecutable(); + this.callable = constrainedExecutable.getCallable(); this.rules = methodValidationConfiguration.getConfiguredRuleSet(); - this.isGetterMethod = constrainedExecutable.isGetterMethod(); add( constrainedExecutable ); } @@ -306,21 +298,31 @@ public boolean accepts(ConstrainedElement constrainedElement) { return false; } - Executable candidate = ( (ConstrainedExecutable) constrainedElement ).getExecutable(); + Callable candidate = ( (ConstrainedExecutable) constrainedElement ).getCallable(); //are the locations equal (created by different builders) or //does one of the executables override the other one? - return executable.equals( candidate ) || - overrides( executable, candidate ) || - overrides( candidate, executable ); + return isResolvedToSameMethodInHierarchy( callable, candidate ); } - private boolean overrides(Executable first, Executable other) { - if ( first instanceof Constructor || other instanceof Constructor ) { + private boolean isResolvedToSameMethodInHierarchy(Callable first, Callable other) { + if ( isConstructor( first ) || isConstructor( other ) ) { + return first.equals( other ); + } + + return first.isResolvedToSameMethodInHierarchy( executableHelper, getBeanClass(), other ); + } + + private boolean overrides(Callable first, Callable other) { + if ( isConstructor( first ) || isConstructor( other ) ) { return false; } - return executableHelper.overrides( (Method) first, (Method) other ); + return executableHelper.overrides( first, other ); + } + + private boolean isConstructor(Callable callable) { + return callable.getConstrainedElementKind() == ConstrainedElementKind.CONSTRUCTOR; } @Override @@ -328,7 +330,7 @@ public final void add(ConstrainedElement constrainedElement) { super.add( constrainedElement ); ConstrainedExecutable constrainedExecutable = (ConstrainedExecutable) constrainedElement; - signatures.add( ExecutableHelper.getSignature( constrainedExecutable.getExecutable() ) ); + signatures.add( constrainedExecutable.getCallable().getSignature() ); constrainedExecutables.add( constrainedExecutable ); isConstrained = isConstrained || constrainedExecutable.isConstrained(); @@ -344,11 +346,11 @@ public final void add(ConstrainedElement constrainedElement) { // keep the "lowest" executable in hierarchy to make sure any type parameters declared on super-types (and // used in overridden methods) are resolved for the specific sub-type we are interested in - if ( executable != null && overrides( - constrainedExecutable.getExecutable(), - executable + if ( callable != null && overrides( + constrainedExecutable.getCallable(), + callable ) ) { - executable = constrainedExecutable.getExecutable(); + callable = constrainedExecutable.getCallable(); } } @@ -359,7 +361,7 @@ public final void add(ConstrainedElement constrainedElement) { * @param executable The executable to merge. */ private void addToExecutablesByDeclaringType(ConstrainedExecutable executable) { - Class beanClass = executable.getExecutable().getDeclaringClass(); + Class beanClass = executable.getCallable().getDeclaringClass(); ConstrainedExecutable mergedExecutable = executablesByDeclaringType.get( beanClass ); if ( mergedExecutable != null ) { @@ -377,19 +379,19 @@ public ExecutableMetaData build() { assertCorrectnessOfConfiguration(); return new ExecutableMetaData( - kind == ConstrainedElement.ConstrainedElementKind.CONSTRUCTOR ? executable.getDeclaringClass().getSimpleName() : executable.getName(), - ReflectionHelper.typeOf( executable ), - executable.getParameterTypes(), - kind == ConstrainedElement.ConstrainedElementKind.CONSTRUCTOR ? ElementKind.CONSTRUCTOR : ElementKind.METHOD, - kind == ConstrainedElement.ConstrainedElementKind.CONSTRUCTOR ? Collections.singleton( ExecutableHelper.getSignature( executable ) ) : + callable.getName(), + callable.getType(), + callable.getParameterTypes(), + kind == ConstrainedElementKind.CONSTRUCTOR ? ElementKind.CONSTRUCTOR : ElementKind.METHOD, + kind == ConstrainedElementKind.CONSTRUCTOR ? Collections.singleton( callable.getSignature() ) : CollectionHelper.toImmutableSet( signatures ), adaptOriginsAndImplicitGroups( getDirectConstraints() ), adaptOriginsAndImplicitGroups( getContainerElementConstraints() ), findParameterMetaData(), adaptOriginsAndImplicitGroups( crossParameterConstraints ), - cascadingMetaDataBuilder.build( valueExtractorManager, executable ), + cascadingMetaDataBuilder.build( constraintCreationContext.getValueExtractorManager(), callable ), isConstrained, - isGetterMethod + kind == ConstrainedElementKind.GETTER ); } @@ -410,11 +412,9 @@ private List findParameterMetaData() { for ( ConstrainedParameter oneParameter : oneExecutable.getAllParameterMetaData() ) { parameterBuilders.add( new ParameterMetaData.Builder( - executable.getDeclaringClass(), + callable.getDeclaringClass(), oneParameter, - constraintHelper, - typeResolutionHelper, - valueExtractorManager, + constraintCreationContext, parameterNameProvider ) ); 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 59c433f01c..c8ebe21304 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 @@ -6,111 +6,37 @@ */ package org.hibernate.validator.internal.metadata.aggregated; -import java.lang.annotation.ElementType; -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Field; -import java.lang.reflect.Type; -import java.security.AccessController; -import java.security.PrivilegedAction; - -import org.hibernate.validator.HibernateValidatorPermission; -import org.hibernate.validator.internal.engine.path.PathImpl; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.facets.Cascadable; -import org.hibernate.validator.internal.util.ReflectionHelper; -import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredField; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; +import org.hibernate.validator.internal.properties.Field; /** * A {@link Cascadable} backed by a field of a Java bean. * * @author Gunnar Morling + * @author Marko Bekhta */ -public class FieldCascadable implements Cascadable { - - private final Field field; - private final Type cascadableType; - private final CascadingMetaData cascadingMetaData; +public class FieldCascadable extends AbstractPropertyCascadable { FieldCascadable(Field field, CascadingMetaData cascadingMetaData) { - this.field = field; - this.cascadableType = ReflectionHelper.typeOf( field ); - this.cascadingMetaData = cascadingMetaData; + super( field, cascadingMetaData ); } @Override - public ElementType getElementType() { - return ElementType.FIELD; + public ConstraintLocationKind getConstraintLocationKind() { + return ConstraintLocationKind.FIELD; } - @Override - public Type getCascadableType() { - return cascadableType; - } - - @Override - public Object getValue(Object parent) { - return ReflectionHelper.getValue( field, parent ); - } - - @Override - public void appendTo(PathImpl path) { - path.addPropertyNode( field.getName() ); - } - - @Override - public CascadingMetaData getCascadingMetaData() { - return cascadingMetaData; - } + public static class Builder extends AbstractPropertyCascadable.AbstractBuilder { - public static class Builder implements Cascadable.Builder { - - private final ValueExtractorManager valueExtractorManager; - private final Field field; - private CascadingMetaDataBuilder cascadingMetaDataBuilder; - - public Builder(ValueExtractorManager valueExtractorManager, Field field, CascadingMetaDataBuilder cascadingMetaDataBuilder) { - this.valueExtractorManager = valueExtractorManager; - this.field = field; - this.cascadingMetaDataBuilder = cascadingMetaDataBuilder; - } - - @Override - public void mergeCascadingMetaData(CascadingMetaDataBuilder cascadingMetaData) { - this.cascadingMetaDataBuilder = this.cascadingMetaDataBuilder.merge( cascadingMetaData ); + protected Builder(ValueExtractorManager valueExtractorManager, Field field, CascadingMetaDataBuilder cascadingMetaDataBuilder) { + super( valueExtractorManager, field, cascadingMetaDataBuilder ); } @Override - public FieldCascadable build() { - return new FieldCascadable( getAccessible( field ), cascadingMetaDataBuilder.build( valueExtractorManager, field ) ); - } - - /** - * Returns an accessible version of the given member. Will be the given member itself in case it is accessible, - * otherwise a copy which is set accessible. - */ - private Field getAccessible(Field original) { - if ( ( (AccessibleObject) original ).isAccessible() ) { - return original; - } - - SecurityManager sm = System.getSecurityManager(); - if ( sm != null ) { - sm.checkPermission( HibernateValidatorPermission.ACCESS_PRIVATE_MEMBERS ); - } - - Class clazz = original.getDeclaringClass(); - - return run( GetDeclaredField.andMakeAccessible( clazz, original.getName() ) ); - } - - /** - * 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(); + protected Cascadable create(Field field, CascadingMetaData cascadingMetaData) { + return new FieldCascadable( field, cascadingMetaData ); } } } 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 72854cfa39..ab5da7d89f 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 @@ -6,80 +6,37 @@ */ package org.hibernate.validator.internal.metadata.aggregated; -import java.lang.annotation.ElementType; -import java.lang.reflect.Method; -import java.lang.reflect.Type; - -import org.hibernate.validator.internal.engine.path.PathImpl; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.facets.Cascadable; -import org.hibernate.validator.internal.util.ReflectionHelper; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; +import org.hibernate.validator.internal.properties.Getter; /** - * A {@link Cascadable} backed by a property getter of a Java bean. + * A {@link Cascadable} backed by a getter of a Java bean. * * @author Gunnar Morling + * @author Marko Bekhta */ -public class GetterCascadable implements Cascadable { - - private final Method method; - private final String propertyName; - private final Type cascadableType; - private final CascadingMetaData cascadingMetaData; - - GetterCascadable(Method method, CascadingMetaData cascadingMetaData) { - this.method = method; - this.propertyName = ReflectionHelper.getPropertyName( method ); - this.cascadableType = ReflectionHelper.typeOf( method ); - this.cascadingMetaData = cascadingMetaData; - } - - @Override - public ElementType getElementType() { - return ElementType.METHOD; - } - - @Override - public Type getCascadableType() { - return cascadableType; - } +public class GetterCascadable extends AbstractPropertyCascadable { - @Override - public Object getValue(Object parent) { - return ReflectionHelper.getValue( method, parent ); - } - - @Override - public void appendTo(PathImpl path) { - path.addPropertyNode( propertyName ); + GetterCascadable(Getter getter, CascadingMetaData cascadingMetaData) { + super( getter, cascadingMetaData ); } @Override - public CascadingMetaData getCascadingMetaData() { - return cascadingMetaData; + public ConstraintLocationKind getConstraintLocationKind() { + return ConstraintLocationKind.GETTER; } - public static class Builder implements Cascadable.Builder { + public static class Builder extends AbstractPropertyCascadable.AbstractBuilder { - private final ValueExtractorManager valueExtractorManager; - private final Method method; - private CascadingMetaDataBuilder cascadingMetaDataBuilder; - - // Note: the method passed here has to be accessible: the caller is responsible for that - public Builder(ValueExtractorManager valueExtractorManager, Method method, CascadingMetaDataBuilder cascadingMetaDataBuilder) { - this.valueExtractorManager = valueExtractorManager; - this.method = method; - this.cascadingMetaDataBuilder = cascadingMetaDataBuilder; - } - - @Override - public void mergeCascadingMetaData(CascadingMetaDataBuilder cascadingMetaData) { - this.cascadingMetaDataBuilder = this.cascadingMetaDataBuilder.merge( cascadingMetaData ); + protected Builder(ValueExtractorManager valueExtractorManager, Getter getter, CascadingMetaDataBuilder cascadingMetaDataBuilder) { + super( valueExtractorManager, getter, cascadingMetaDataBuilder ); } @Override - public GetterCascadable build() { - return new GetterCascadable( method, cascadingMetaDataBuilder.build( valueExtractorManager, method ) ); + protected Cascadable create(Getter getter, CascadingMetaData cascadingMetaData) { + return new GetterCascadable( getter, cascadingMetaData ); } } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/MetaDataBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/MetaDataBuilder.java index 92e7c077be..3a094ca28b 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/MetaDataBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/MetaDataBuilder.java @@ -11,14 +11,12 @@ import java.lang.annotation.Annotation; import java.util.Set; -import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.engine.ConstraintCreationContext; import org.hibernate.validator.internal.metadata.core.ConstraintOrigin; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.core.MetaConstraints; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; -import org.hibernate.validator.internal.util.TypeResolutionHelper; /** * Builds {@link ConstraintMetaData} instances for the @@ -29,20 +27,16 @@ */ public abstract class MetaDataBuilder { - protected final ConstraintHelper constraintHelper; - protected final TypeResolutionHelper typeResolutionHelper; - protected final ValueExtractorManager valueExtractorManager; + protected final ConstraintCreationContext constraintCreationContext; private final Class beanClass; private final Set> directConstraints = newHashSet(); private final Set> containerElementsConstraints = newHashSet(); private boolean isCascading = false; - protected MetaDataBuilder(Class beanClass, ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager) { + protected MetaDataBuilder(Class beanClass, ConstraintCreationContext constraintCreationContext) { this.beanClass = beanClass; - this.constraintHelper = constraintHelper; - this.typeResolutionHelper = typeResolutionHelper; - this.valueExtractorManager = valueExtractorManager; + this.constraintCreationContext = constraintCreationContext; } /** @@ -128,16 +122,18 @@ private MetaConstraint adaptOriginAndImplicitGroup(Met Class constraintClass = constraint.getLocation().getDeclaringClass(); ConstraintDescriptorImpl descriptor = new ConstraintDescriptorImpl<>( - constraintHelper, - constraint.getLocation().getMember(), + constraintCreationContext.getConstraintHelper(), + constraint.getLocation().getConstrainable(), constraint.getDescriptor().getAnnotationDescriptor(), - constraint.getElementType(), + constraint.getConstraintLocationKind(), constraintClass.isInterface() ? constraintClass : null, definedIn, constraint.getDescriptor().getConstraintType() ); - return MetaConstraints.create( typeResolutionHelper, valueExtractorManager, descriptor, constraint.getLocation() ); + return MetaConstraints.create( constraintCreationContext.getTypeResolutionHelper(), + constraintCreationContext.getValueExtractorManager(), + constraintCreationContext.getConstraintValidatorManager(), descriptor, constraint.getLocation() ); } /** 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 d175c6e548..fadcceb751 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 @@ -6,8 +6,6 @@ */ package org.hibernate.validator.internal.metadata.aggregated; -import java.lang.annotation.ElementType; -import java.lang.reflect.Executable; import java.lang.reflect.Type; import java.util.List; import java.util.Set; @@ -15,17 +13,17 @@ import javax.validation.ElementKind; import javax.validation.metadata.ParameterDescriptor; +import org.hibernate.validator.internal.engine.ConstraintCreationContext; import org.hibernate.validator.internal.engine.path.PathImpl; -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.ParameterDescriptorImpl; import org.hibernate.validator.internal.metadata.facets.Cascadable; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; import org.hibernate.validator.internal.metadata.raw.ConstrainedElement.ConstrainedElementKind; import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter; +import org.hibernate.validator.internal.properties.Callable; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; -import org.hibernate.validator.internal.util.TypeResolutionHelper; /** * An aggregated view of the constraint related meta data for a single method @@ -64,8 +62,8 @@ public int getIndex() { } @Override - public ElementType getElementType() { - return ElementType.PARAMETER; + public ConstraintLocationKind getConstraintLocationKind() { + return ConstraintLocationKind.PARAMETER; } @Override @@ -112,16 +110,14 @@ public static class Builder extends MetaDataBuilder { private final ExecutableParameterNameProvider parameterNameProvider; private final Type parameterType; private final int parameterIndex; - private Executable executableForNameRetrieval; + private Callable callableForNameRetrieval; private CascadingMetaDataBuilder cascadingMetaDataBuilder; public Builder(Class beanClass, ConstrainedParameter constrainedParameter, - ConstraintHelper constraintHelper, - TypeResolutionHelper typeResolutionHelper, - ValueExtractorManager valueExtractorManager, + ConstraintCreationContext constraintCreationContext, ExecutableParameterNameProvider parameterNameProvider) { - super( beanClass, constraintHelper, typeResolutionHelper, valueExtractorManager ); + super( beanClass, constraintCreationContext ); this.parameterNameProvider = parameterNameProvider; this.parameterType = constrainedParameter.getType(); @@ -156,9 +152,9 @@ public void add(ConstrainedElement constrainedElement) { // use this parent class parameter name instead of the more specific one. // Worse case, we are consistent, best case parameters from parents are more meaningful. // See HV-887 and the associated unit test - if ( executableForNameRetrieval == null || - newConstrainedParameter.getExecutable().getDeclaringClass().isAssignableFrom( executableForNameRetrieval.getDeclaringClass() ) ) { - executableForNameRetrieval = newConstrainedParameter.getExecutable(); + if ( callableForNameRetrieval == null || + newConstrainedParameter.getCallable().getDeclaringClass().isAssignableFrom( callableForNameRetrieval.getDeclaringClass() ) ) { + callableForNameRetrieval = newConstrainedParameter.getCallable(); } } @@ -166,11 +162,11 @@ public void add(ConstrainedElement constrainedElement) { public ParameterMetaData build() { return new ParameterMetaData( parameterIndex, - parameterNameProvider.getParameterNames( executableForNameRetrieval ).get( parameterIndex ), + callableForNameRetrieval.getParameterName( parameterNameProvider, parameterIndex ), parameterType, adaptOriginsAndImplicitGroups( getDirectConstraints() ), adaptOriginsAndImplicitGroups( getContainerElementConstraints() ), - cascadingMetaDataBuilder.build( valueExtractorManager, executableForNameRetrieval ) + cascadingMetaDataBuilder.build( constraintCreationContext.getValueExtractorManager(), callableForNameRetrieval ) ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/PropertyMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/PropertyMetaData.java index c1731a8678..c5cb0683f2 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/PropertyMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/PropertyMetaData.java @@ -6,12 +6,8 @@ */ package org.hibernate.validator.internal.metadata.aggregated; -import java.lang.reflect.Field; -import java.lang.reflect.Member; -import java.lang.reflect.Method; +import java.lang.invoke.MethodHandles; import java.lang.reflect.Type; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.ArrayDeque; import java.util.Collections; import java.util.Deque; @@ -25,24 +21,24 @@ import javax.validation.ElementKind; -import org.hibernate.validator.HibernateValidatorPermission; -import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.engine.ConstraintCreationContext; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.core.MetaConstraints; import org.hibernate.validator.internal.metadata.descriptor.PropertyDescriptorImpl; import org.hibernate.validator.internal.metadata.facets.Cascadable; import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.metadata.location.GetterConstraintLocation; import org.hibernate.validator.internal.metadata.location.TypeArgumentConstraintLocation; 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.Getter; +import org.hibernate.validator.internal.properties.Property; +import org.hibernate.validator.internal.properties.javabean.JavaBeanGetter; import org.hibernate.validator.internal.util.CollectionHelper; -import org.hibernate.validator.internal.util.ReflectionHelper; -import org.hibernate.validator.internal.util.TypeResolutionHelper; -import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredMethod; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; import org.hibernate.validator.internal.util.stereotypes.Immutable; /** @@ -63,6 +59,8 @@ */ public class PropertyMetaData extends AbstractConstraintMetaData { + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + @Immutable private final Set cascadables; @@ -70,8 +68,7 @@ private PropertyMetaData(String propertyName, Type type, Set> constraints, Set> containerElementsConstraints, - Set cascadables, - boolean cascadingProperty) { + Set cascadables) { super( propertyName, type, @@ -150,41 +147,27 @@ public boolean equals(Object obj) { public static class Builder extends MetaDataBuilder { private static final EnumSet SUPPORTED_ELEMENT_KINDS = EnumSet.of( - ConstrainedElementKind.TYPE, ConstrainedElementKind.FIELD, - ConstrainedElementKind.METHOD + ConstrainedElementKind.GETTER ); private final String propertyName; - private final Map cascadableBuilders = new HashMap<>(); + private final Map cascadableBuilders = new HashMap<>(); private final Type propertyType; - private boolean cascadingProperty = false; - private Method getterAccessibleMethod; - - public Builder(Class beanClass, ConstrainedField constrainedField, ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, - ValueExtractorManager valueExtractorManager) { - super( beanClass, constraintHelper, typeResolutionHelper, valueExtractorManager ); - - this.propertyName = constrainedField.getField().getName(); - this.propertyType = ReflectionHelper.typeOf( constrainedField.getField() ); - add( constrainedField ); - } - public Builder(Class beanClass, ConstrainedType constrainedType, ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, - ValueExtractorManager valueExtractorManager) { - super( beanClass, constraintHelper, typeResolutionHelper, valueExtractorManager ); + public Builder(Class beanClass, ConstrainedField constrainedProperty, ConstraintCreationContext constraintCreationContext) { + super( beanClass, constraintCreationContext ); - this.propertyName = null; - this.propertyType = null; - add( constrainedType ); + this.propertyName = constrainedProperty.getField().getName(); + this.propertyType = constrainedProperty.getField().getType(); + add( constrainedProperty ); } - public Builder(Class beanClass, ConstrainedExecutable constrainedMethod, ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, - ValueExtractorManager valueExtractorManager) { - super( beanClass, constraintHelper, typeResolutionHelper, valueExtractorManager ); + public Builder(Class beanClass, ConstrainedExecutable constrainedMethod, ConstraintCreationContext constraintCreationContext) { + super( beanClass, constraintCreationContext ); - this.propertyName = ReflectionHelper.getPropertyName( constrainedMethod.getExecutable() ); - this.propertyType = ReflectionHelper.typeOf( constrainedMethod.getExecutable() ); + this.propertyName = constrainedMethod.getCallable().as( Property.class ).getPropertyName(); + this.propertyType = constrainedMethod.getCallable().getType(); add( constrainedMethod ); } @@ -194,62 +177,60 @@ public boolean accepts(ConstrainedElement constrainedElement) { return false; } - if ( constrainedElement.getKind() == ConstrainedElementKind.METHOD && - !( (ConstrainedExecutable) constrainedElement ).isGetterMethod() ) { - return false; - } - return Objects.equals( getPropertyName( constrainedElement ), propertyName ); } @Override public final void add(ConstrainedElement constrainedElement) { - // if we are in the case of a getter and if we have constraints (either on the annotated object itself or on - // a container element) or cascaded validation, we want to create an accessible version of the getter only once. - if ( constrainedElement.getKind() == ConstrainedElementKind.METHOD && constrainedElement.isConstrained() ) { - getterAccessibleMethod = getAccessible( (Method) ( (ConstrainedExecutable) constrainedElement ).getExecutable() ); - } - super.add( constrainedElement ); - cascadingProperty = cascadingProperty || constrainedElement.getCascadingMetaDataBuilder().isCascading(); - if ( constrainedElement.getCascadingMetaDataBuilder().isMarkedForCascadingOnAnnotatedObjectOrContainerElements() || constrainedElement.getCascadingMetaDataBuilder().hasGroupConversionsOnAnnotatedObjectOrContainerElements() ) { - if ( constrainedElement.getKind() == ConstrainedElementKind.FIELD ) { - Field field = ( (ConstrainedField) constrainedElement ).getField(); - Cascadable.Builder builder = cascadableBuilders.get( field ); - if ( builder == null ) { - builder = new FieldCascadable.Builder( valueExtractorManager, field, constrainedElement.getCascadingMetaDataBuilder() ); - cascadableBuilders.put( field, builder ); + Property property = getConstrainableFromConstrainedElement( constrainedElement ); + + Cascadable.Builder builder = cascadableBuilders.get( property ); + if ( builder == null ) { + builder = AbstractPropertyCascadable.AbstractBuilder.builder( + constraintCreationContext.getValueExtractorManager(), property, + constrainedElement.getCascadingMetaDataBuilder() ); + cascadableBuilders.put( property, builder ); + } + else { + builder.mergeCascadingMetaData( constrainedElement.getCascadingMetaDataBuilder() ); + } + } + } + + private Property getConstrainableFromConstrainedElement(ConstrainedElement constrainedElement) { + switch ( constrainedElement.getKind() ) { + case FIELD: + if ( constrainedElement instanceof ConstrainedField ) { + return ( (ConstrainedField) constrainedElement ).getField(); } else { - builder.mergeCascadingMetaData( constrainedElement.getCascadingMetaDataBuilder() ); + throw LOG.getUnexpectedConstraintElementType( ConstrainedField.class, constrainedElement.getClass() ); } - } - else if ( constrainedElement.getKind() == ConstrainedElementKind.METHOD ) { - Method method = (Method) ( (ConstrainedExecutable) constrainedElement ).getExecutable(); - Cascadable.Builder builder = cascadableBuilders.get( method ); - - if ( builder == null ) { - builder = new GetterCascadable.Builder( valueExtractorManager, getterAccessibleMethod, constrainedElement.getCascadingMetaDataBuilder() ); - cascadableBuilders.put( method, builder ); + case GETTER: + if ( constrainedElement instanceof ConstrainedExecutable ) { + return ( (ConstrainedExecutable) constrainedElement ).getCallable().as( Getter.class ); } else { - builder.mergeCascadingMetaData( constrainedElement.getCascadingMetaDataBuilder() ); + throw LOG.getUnexpectedConstraintElementType( ConstrainedExecutable.class, constrainedElement.getClass() ); } - } + default: + throw LOG.getUnsupportedConstraintElementType( constrainedElement.getKind() ); } } @Override protected Set> adaptConstraints(ConstrainedElement constrainedElement, Set> constraints) { - if ( constraints.isEmpty() || constrainedElement.getKind() != ConstrainedElementKind.METHOD ) { + if ( constraints.isEmpty() || constrainedElement.getKind() != ConstrainedElementKind.GETTER ) { return constraints; } - ConstraintLocation getterConstraintLocation = ConstraintLocation.forGetter( getterAccessibleMethod ); + ConstraintLocation getterConstraintLocation = ConstraintLocation + .forGetter( ( (ConstrainedExecutable) constrainedElement ).getCallable().as( JavaBeanGetter.class ) ); // convert return value locations into getter locations for usage within this meta-data return constraints.stream() @@ -261,8 +242,14 @@ private MetaConstraint withGetterLocation(ConstraintLocation getterConstraint ConstraintLocation converted = null; // fast track if it's a regular constraint - if ( !(constraint.getLocation() instanceof TypeArgumentConstraintLocation) ) { - converted = getterConstraintLocation; + if ( !( constraint.getLocation() instanceof TypeArgumentConstraintLocation ) ) { + // Change the constraint location to a GetterConstraintLocation if it is not already one + if ( constraint.getLocation() instanceof GetterConstraintLocation ) { + converted = constraint.getLocation(); + } + else { + converted = getterConstraintLocation; + } } else { Deque locationStack = new ArrayDeque<>(); @@ -283,7 +270,13 @@ private MetaConstraint withGetterLocation(ConstraintLocation getterConstraint // 2. beginning at the root, transform each location so it references the transformed delegate for ( ConstraintLocation location : locationStack ) { if ( !(location instanceof TypeArgumentConstraintLocation) ) { - converted = getterConstraintLocation; + // Change the constraint location to a GetterConstraintLocation if it is not already one + if ( location instanceof GetterConstraintLocation ) { + converted = location; + } + else { + converted = getterConstraintLocation; + } } else { converted = ConstraintLocation.forTypeArgument( @@ -295,43 +288,22 @@ private MetaConstraint withGetterLocation(ConstraintLocation getterConstraint } } - return MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraint.getDescriptor(), converted ); + return MetaConstraints.create( constraintCreationContext.getTypeResolutionHelper(), + constraintCreationContext.getValueExtractorManager(), + constraintCreationContext.getConstraintValidatorManager(), constraint.getDescriptor(), converted ); } private String getPropertyName(ConstrainedElement constrainedElement) { if ( constrainedElement.getKind() == ConstrainedElementKind.FIELD ) { - return ReflectionHelper.getPropertyName( ( (ConstrainedField) constrainedElement ).getField() ); + return ( (ConstrainedField) constrainedElement ).getField().getPropertyName(); } - else if ( constrainedElement.getKind() == ConstrainedElementKind.METHOD ) { - return ReflectionHelper.getPropertyName( ( (ConstrainedExecutable) constrainedElement ).getExecutable() ); + else if ( constrainedElement.getKind() == ConstrainedElementKind.GETTER ) { + return ( (ConstrainedExecutable) constrainedElement ).getCallable().as( Property.class ).getPropertyName(); } return null; } - /** - * Returns an accessible version of the given member. Will be the given member itself in case it is accessible, - * otherwise a copy which is set accessible. - */ - private Method getAccessible(Method original) { - if ( original.isAccessible() ) { - return original; - } - - SecurityManager sm = System.getSecurityManager(); - if ( sm != null ) { - sm.checkPermission( HibernateValidatorPermission.ACCESS_PRIVATE_MEMBERS ); - } - - Class clazz = original.getDeclaringClass(); - - return run( GetDeclaredMethod.andMakeAccessible( clazz, original.getName() ) ); - } - - private T run(PrivilegedAction action) { - return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); - } - @Override public PropertyMetaData build() { Set cascadables = cascadableBuilders.values() @@ -344,8 +316,7 @@ public PropertyMetaData build() { propertyType, adaptOriginsAndImplicitGroups( getDirectConstraints() ), adaptOriginsAndImplicitGroups( getContainerElementConstraints() ), - cascadables, - cascadingProperty + cascadables ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ReturnValueMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ReturnValueMetaData.java index 6e9062b131..aac44bf57f 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ReturnValueMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ReturnValueMetaData.java @@ -6,7 +6,6 @@ */ package org.hibernate.validator.internal.metadata.aggregated; -import java.lang.annotation.ElementType; import java.lang.reflect.Type; import java.util.Collections; import java.util.List; @@ -20,6 +19,7 @@ import org.hibernate.validator.internal.metadata.descriptor.ReturnValueDescriptorImpl; import org.hibernate.validator.internal.metadata.facets.Cascadable; import org.hibernate.validator.internal.metadata.facets.Validatable; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; import org.hibernate.validator.internal.util.stereotypes.Immutable; /** @@ -67,8 +67,8 @@ public boolean hasCascadables() { } @Override - public ElementType getElementType() { - return ElementType.METHOD; + public ConstraintLocationKind getConstraintLocationKind() { + return ConstraintLocationKind.METHOD; } @Override diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/rule/MethodConfigurationRule.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/rule/MethodConfigurationRule.java index c7d1831283..7b4eeffd7e 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/rule/MethodConfigurationRule.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/rule/MethodConfigurationRule.java @@ -54,8 +54,8 @@ protected boolean isStrictSubType(Class clazz, Class otherClazz) { * {@code otherExecutable}, {@code false} otherwise */ protected boolean isDefinedOnSubType(ConstrainedExecutable executable, ConstrainedExecutable otherExecutable) { - Class clazz = executable.getExecutable().getDeclaringClass(); - Class otherClazz = otherExecutable.getExecutable().getDeclaringClass(); + Class clazz = executable.getCallable().getDeclaringClass(); + Class otherClazz = otherExecutable.getCallable().getDeclaringClass(); return isStrictSubType( clazz, otherClazz ); } @@ -71,8 +71,8 @@ protected boolean isDefinedOnSubType(ConstrainedExecutable executable, Constrain * {@code otherExecutable}, {@code false} otherwise */ protected boolean isDefinedOnParallelType(ConstrainedExecutable executable, ConstrainedExecutable otherExecutable) { - Class clazz = executable.getExecutable().getDeclaringClass(); - Class otherClazz = otherExecutable.getExecutable().getDeclaringClass(); + Class clazz = executable.getCallable().getDeclaringClass(); + Class otherClazz = otherExecutable.getCallable().getDeclaringClass(); return !( clazz.isAssignableFrom( otherClazz ) || otherClazz.isAssignableFrom( clazz ) ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/rule/OverridingMethodMustNotAlterParameterConstraints.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/rule/OverridingMethodMustNotAlterParameterConstraints.java index 2e402c25bb..2b88e5e953 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/rule/OverridingMethodMustNotAlterParameterConstraints.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/rule/OverridingMethodMustNotAlterParameterConstraints.java @@ -22,8 +22,8 @@ public void apply(ConstrainedExecutable method, ConstrainedExecutable otherMetho otherMethod.hasParameterConstraints() && !method.isEquallyParameterConstrained( otherMethod ) ) { throw LOG.getParameterConfigurationAlteredInSubTypeException( - method.getExecutable(), - otherMethod.getExecutable() + method.getCallable(), + otherMethod.getCallable() ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/rule/ParallelMethodsMustNotDefineGroupConversionForCascadedReturnValue.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/rule/ParallelMethodsMustNotDefineGroupConversionForCascadedReturnValue.java index 4546d1278e..69cc7debfb 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/rule/ParallelMethodsMustNotDefineGroupConversionForCascadedReturnValue.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/rule/ParallelMethodsMustNotDefineGroupConversionForCascadedReturnValue.java @@ -25,8 +25,8 @@ public void apply(ConstrainedExecutable method, ConstrainedExecutable otherMetho if ( isDefinedOnParallelType( method, otherMethod ) && isCascaded && hasGroupConversions ) { throw LOG.getMethodsFromParallelTypesMustNotDefineGroupConversionsForCascadedReturnValueException( - method.getExecutable(), - otherMethod.getExecutable() + method.getCallable(), + otherMethod.getCallable() ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/rule/ParallelMethodsMustNotDefineParameterConstraints.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/rule/ParallelMethodsMustNotDefineParameterConstraints.java index 2023e3ab2d..12abf849b4 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/rule/ParallelMethodsMustNotDefineParameterConstraints.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/rule/ParallelMethodsMustNotDefineParameterConstraints.java @@ -21,8 +21,8 @@ public void apply(ConstrainedExecutable method, ConstrainedExecutable otherMetho if ( isDefinedOnParallelType( method, otherMethod ) && ( method.hasParameterConstraints() || otherMethod.hasParameterConstraints() ) ) { throw LOG.getParameterConstraintsDefinedInMethodsFromParallelTypesException( - method.getExecutable(), - otherMethod.getExecutable() + method.getCallable(), + otherMethod.getCallable() ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/rule/ReturnValueMayOnlyBeMarkedOnceAsCascadedPerHierarchyLine.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/rule/ReturnValueMayOnlyBeMarkedOnceAsCascadedPerHierarchyLine.java index 7bac3875e9..f29b70cc94 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/rule/ReturnValueMayOnlyBeMarkedOnceAsCascadedPerHierarchyLine.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/rule/ReturnValueMayOnlyBeMarkedOnceAsCascadedPerHierarchyLine.java @@ -22,8 +22,8 @@ public void apply(ConstrainedExecutable method, ConstrainedExecutable otherMetho otherMethod.getCascadingMetaDataBuilder().isMarkedForCascadingOnAnnotatedObjectOrContainerElements() && ( isDefinedOnSubType( method, otherMethod ) || isDefinedOnSubType( otherMethod, method ) ) ) { throw LOG.getMethodReturnValueMustNotBeMarkedMoreThanOnceForCascadedValidationException( - method.getExecutable(), - otherMethod.getExecutable() + method.getCallable(), + otherMethod.getCallable() ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/rule/VoidMethodsMustNotBeReturnValueConstrained.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/rule/VoidMethodsMustNotBeReturnValueConstrained.java index 3344bd0515..15600a1876 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/rule/VoidMethodsMustNotBeReturnValueConstrained.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/rule/VoidMethodsMustNotBeReturnValueConstrained.java @@ -6,8 +6,6 @@ */ package org.hibernate.validator.internal.metadata.aggregated.rule; -import java.lang.reflect.Method; - import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable; /** @@ -20,11 +18,10 @@ public class VoidMethodsMustNotBeReturnValueConstrained extends MethodConfigurat @Override public void apply(ConstrainedExecutable executable, ConstrainedExecutable otherExecutable) { - if ( ( executable.getExecutable() instanceof Method ) && - ( (Method) executable.getExecutable() ).getReturnType() == void.class && + if ( !executable.getCallable().hasReturnValue() && ( !executable.getConstraints().isEmpty() || executable.getCascadingMetaDataBuilder().isMarkedForCascadingOnAnnotatedObjectOrContainerElements() ) ) { - throw LOG.getVoidMethodsMustNotBeConstrainedException( executable.getExecutable() ); + throw LOG.getVoidMethodsMustNotBeConstrainedException( executable.getCallable() ); } } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/AnnotationProcessingOptions.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/AnnotationProcessingOptions.java index 5e80ed6b4b..dfbc674ff8 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/AnnotationProcessingOptions.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/AnnotationProcessingOptions.java @@ -6,7 +6,7 @@ */ package org.hibernate.validator.internal.metadata.core; -import java.lang.reflect.Member; +import org.hibernate.validator.internal.properties.Constrainable; /** * An {@code AnnotationProcessingOptions} instance keeps track of annotations which should be ignored as configuration source. @@ -18,13 +18,13 @@ public interface AnnotationProcessingOptions { boolean areClassLevelConstraintsIgnoredFor(Class clazz); - boolean areMemberConstraintsIgnoredFor(Member member); + boolean areMemberConstraintsIgnoredFor(Constrainable constrainable); - boolean areReturnValueConstraintsIgnoredFor(Member member); + boolean areReturnValueConstraintsIgnoredFor(Constrainable constrainable); - boolean areCrossParameterConstraintsIgnoredFor(Member member); + boolean areCrossParameterConstraintsIgnoredFor(Constrainable constrainable); - boolean areParameterConstraintsIgnoredFor(Member member, int index); + boolean areParameterConstraintsIgnoredFor(Constrainable constrainable, int index); void merge(AnnotationProcessingOptions annotationProcessingOptions); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/AnnotationProcessingOptionsImpl.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/AnnotationProcessingOptionsImpl.java index 11bc7d6ccf..74576e24e1 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/AnnotationProcessingOptionsImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/AnnotationProcessingOptionsImpl.java @@ -6,15 +6,15 @@ */ package org.hibernate.validator.internal.metadata.core; +import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; + import java.lang.invoke.MethodHandles; -import java.lang.reflect.Member; import java.util.Map; +import org.hibernate.validator.internal.properties.Constrainable; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; -import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; - /** * An {@code AnnotationProcessingOptions} instance keeps track of annotations which should be ignored as configuration source. * The main validation source for Bean Validation is annotation and alternate configuration sources use this class @@ -40,17 +40,17 @@ public class AnnotationProcessingOptionsImpl implements AnnotationProcessingOpti /** * Keeps track of explicitly excluded members (fields and properties). */ - private final Map annotationIgnoredForMembers = newHashMap(); + private final Map annotationIgnoredForMembers = newHashMap(); /** * Keeps track of explicitly excluded return value constraints for methods/constructors. */ - private final Map annotationIgnoresForReturnValues = newHashMap(); + private final Map annotationIgnoresForReturnValues = newHashMap(); /** * Keeps track of explicitly excluded cross parameter constraints for methods/constructors. */ - private final Map annotationIgnoresForCrossParameter = newHashMap(); + private final Map annotationIgnoresForCrossParameter = newHashMap(); /** * Keeps track whether the 'ignore-annotations' flag is set on a method/constructor parameter @@ -58,10 +58,11 @@ public class AnnotationProcessingOptionsImpl implements AnnotationProcessingOpti private final Map annotationIgnoresForMethodParameter = newHashMap(); @Override - public boolean areMemberConstraintsIgnoredFor(Member member) { - Class clazz = member.getDeclaringClass(); - if ( annotationIgnoredForMembers.containsKey( member ) ) { - return annotationIgnoredForMembers.get( member ); + public boolean areMemberConstraintsIgnoredFor(Constrainable constrainable) { + Class clazz = constrainable.getDeclaringClass(); + Boolean annotationIgnoredForMember = annotationIgnoredForMembers.get( constrainable ); + if ( annotationIgnoredForMember != null ) { + return annotationIgnoredForMember; } else { return areAllConstraintAnnotationsIgnoredFor( clazz ); @@ -69,41 +70,45 @@ public boolean areMemberConstraintsIgnoredFor(Member member) { } @Override - public boolean areReturnValueConstraintsIgnoredFor(Member member) { - if ( annotationIgnoresForReturnValues.containsKey( member ) ) { - return annotationIgnoresForReturnValues.get( member ); + public boolean areReturnValueConstraintsIgnoredFor(Constrainable constrainable) { + Boolean annotationIgnoreForReturnValue = annotationIgnoresForReturnValues.get( constrainable ); + if ( annotationIgnoreForReturnValue != null ) { + return annotationIgnoreForReturnValue; } else { - return areMemberConstraintsIgnoredFor( member ); + return areMemberConstraintsIgnoredFor( constrainable ); } } @Override - public boolean areCrossParameterConstraintsIgnoredFor(Member member) { - if ( annotationIgnoresForCrossParameter.containsKey( member ) ) { - return annotationIgnoresForCrossParameter.get( member ); + public boolean areCrossParameterConstraintsIgnoredFor(Constrainable constrainable) { + Boolean annotationIgnoreForCrossParameter = annotationIgnoresForCrossParameter.get( constrainable ); + if ( annotationIgnoreForCrossParameter != null ) { + return annotationIgnoreForCrossParameter; } else { - return areMemberConstraintsIgnoredFor( member ); + return areMemberConstraintsIgnoredFor( constrainable ); } } @Override - public boolean areParameterConstraintsIgnoredFor(Member member, int index) { - ExecutableParameterKey key = new ExecutableParameterKey( member, index ); - if ( annotationIgnoresForMethodParameter.containsKey( key ) ) { - return annotationIgnoresForMethodParameter.get( key ); + public boolean areParameterConstraintsIgnoredFor(Constrainable constrainable, int index) { + ExecutableParameterKey key = new ExecutableParameterKey( constrainable, index ); + Boolean annotationIgnoreForMethodParameter = annotationIgnoresForMethodParameter.get( key ); + if ( annotationIgnoreForMethodParameter != null ) { + return annotationIgnoreForMethodParameter; } else { - return areMemberConstraintsIgnoredFor( member ); + return areMemberConstraintsIgnoredFor( constrainable ); } } @Override public boolean areClassLevelConstraintsIgnoredFor(Class clazz) { boolean ignoreAnnotation; - if ( annotationIgnoresForClasses.containsKey( clazz ) ) { - ignoreAnnotation = annotationIgnoresForClasses.get( clazz ); + Boolean annotationIgnoreForClass = annotationIgnoresForClasses.get( clazz ); + if ( annotationIgnoreForClass != null ) { + ignoreAnnotation = annotationIgnoreForClass; } else { ignoreAnnotation = areAllConstraintAnnotationsIgnoredFor( clazz ); @@ -138,19 +143,19 @@ public void ignoreAnnotationConstraintForClass(Class clazz, Boolean b) { } } - public void ignoreConstraintAnnotationsOnMember(Member member, Boolean b) { + public void ignoreConstraintAnnotationsOnMember(Constrainable member, Boolean b) { annotationIgnoredForMembers.put( member, b ); } - public void ignoreConstraintAnnotationsForReturnValue(Member member, Boolean b) { + public void ignoreConstraintAnnotationsForReturnValue(Constrainable member, Boolean b) { annotationIgnoresForReturnValues.put( member, b ); } - public void ignoreConstraintAnnotationsForCrossParameterConstraint(Member member, Boolean b) { + public void ignoreConstraintAnnotationsForCrossParameterConstraint(Constrainable member, Boolean b) { annotationIgnoresForCrossParameter.put( member, b ); } - public void ignoreConstraintAnnotationsOnParameter(Member member, int index, Boolean b) { + public void ignoreConstraintAnnotationsOnParameter(Constrainable member, int index, Boolean b) { ExecutableParameterKey key = new ExecutableParameterKey( member, index ); annotationIgnoresForMethodParameter.put( key, b ); } @@ -164,11 +169,11 @@ private boolean areAllConstraintAnnotationsIgnoredFor(Class clazz) { } public class ExecutableParameterKey { - private final Member member; + private final Constrainable constrainable; private final int index; - public ExecutableParameterKey(Member member, int index) { - this.member = member; + public ExecutableParameterKey(Constrainable constrainable, int index) { + this.constrainable = constrainable; this.index = index; } @@ -186,7 +191,7 @@ public boolean equals(Object o) { if ( index != that.index ) { return false; } - if ( member != null ? !member.equals( that.member ) : that.member != null ) { + if ( constrainable != null ? !constrainable.equals( that.constrainable ) : that.constrainable != null ) { return false; } @@ -195,7 +200,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - int result = member != null ? member.hashCode() : 0; + int result = constrainable != null ? constrainable.hashCode() : 0; result = 31 * result + index; return result; } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java index 9d0ca63446..b38a5543b3 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java @@ -19,6 +19,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.Function; @@ -55,6 +56,7 @@ import org.hibernate.validator.constraints.CodePointLength; import org.hibernate.validator.constraints.ConstraintComposition; +import org.hibernate.validator.constraints.CreditCardNumber; import org.hibernate.validator.constraints.Currency; import org.hibernate.validator.constraints.EAN; import org.hibernate.validator.constraints.ISBN; @@ -64,12 +66,14 @@ import org.hibernate.validator.constraints.Mod11Check; import org.hibernate.validator.constraints.ModCheck; import org.hibernate.validator.constraints.ParameterScriptAssert; +import org.hibernate.validator.constraints.Range; import org.hibernate.validator.constraints.SafeHtml; import org.hibernate.validator.constraints.ScriptAssert; import org.hibernate.validator.constraints.URL; import org.hibernate.validator.constraints.UniqueElements; import org.hibernate.validator.constraints.br.CNPJ; import org.hibernate.validator.constraints.br.CPF; +import org.hibernate.validator.constraints.br.TituloEleitoral; import org.hibernate.validator.constraints.pl.NIP; import org.hibernate.validator.constraints.pl.PESEL; import org.hibernate.validator.constraints.pl.REGON; @@ -91,6 +95,7 @@ import org.hibernate.validator.internal.constraintvalidators.bv.money.CurrencyValidatorForMonetaryAmount; import org.hibernate.validator.internal.constraintvalidators.bv.money.DecimalMaxValidatorForMonetaryAmount; import org.hibernate.validator.internal.constraintvalidators.bv.money.DecimalMinValidatorForMonetaryAmount; +import org.hibernate.validator.internal.constraintvalidators.bv.money.DigitsValidatorForMonetaryAmount; import org.hibernate.validator.internal.constraintvalidators.bv.money.MaxValidatorForMonetaryAmount; import org.hibernate.validator.internal.constraintvalidators.bv.money.MinValidatorForMonetaryAmount; import org.hibernate.validator.internal.constraintvalidators.bv.money.NegativeOrZeroValidatorForMonetaryAmount; @@ -111,28 +116,40 @@ import org.hibernate.validator.internal.constraintvalidators.bv.notempty.NotEmptyValidatorForMap; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForBigDecimal; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForBigInteger; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForByte; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForDouble; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForFloat; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForInteger; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForLong; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForNumber; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForShort; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForBigDecimal; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForBigInteger; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForByte; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForDouble; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForFloat; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForInteger; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForLong; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForNumber; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForShort; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForBigDecimal; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForBigInteger; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForByte; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForDouble; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForFloat; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForInteger; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForLong; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForNumber; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForShort; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForBigDecimal; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForBigInteger; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForByte; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForDouble; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForFloat; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForInteger; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForLong; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForNumber; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForShort; import org.hibernate.validator.internal.constraintvalidators.bv.number.sign.NegativeOrZeroValidatorForBigDecimal; import org.hibernate.validator.internal.constraintvalidators.bv.number.sign.NegativeOrZeroValidatorForBigInteger; import org.hibernate.validator.internal.constraintvalidators.bv.number.sign.NegativeOrZeroValidatorForByte; @@ -303,6 +320,7 @@ public class ConstraintHelper { private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); private static final String JODA_TIME_CLASS_NAME = "org.joda.time.ReadableInstant"; private static final String JAVA_MONEY_CLASS_NAME = "javax.money.MonetaryAmount"; + private static final String JSOUP_CLASS_NAME = "org.jsoup.Jsoup"; @Immutable private final Map, List>> builtinConstraints; @@ -313,6 +331,7 @@ public class ConstraintHelper { private final ValidatorDescriptorMap validatorDescriptors = new ValidatorDescriptorMap(); + @SuppressWarnings("deprecation") public ConstraintHelper() { Map, List>> tmpConstraints = new HashMap<>(); @@ -325,46 +344,66 @@ public ConstraintHelper() { putConstraints( tmpConstraints, DecimalMax.class, Arrays.asList( DecimalMaxValidatorForBigDecimal.class, DecimalMaxValidatorForBigInteger.class, + DecimalMaxValidatorForByte.class, DecimalMaxValidatorForDouble.class, DecimalMaxValidatorForFloat.class, DecimalMaxValidatorForLong.class, + DecimalMaxValidatorForInteger.class, DecimalMaxValidatorForNumber.class, + DecimalMaxValidatorForShort.class, DecimalMaxValidatorForCharSequence.class, DecimalMaxValidatorForMonetaryAmount.class ) ); putConstraints( tmpConstraints, DecimalMin.class, Arrays.asList( DecimalMinValidatorForBigDecimal.class, DecimalMinValidatorForBigInteger.class, + DecimalMinValidatorForByte.class, DecimalMinValidatorForDouble.class, DecimalMinValidatorForFloat.class, DecimalMinValidatorForLong.class, + DecimalMinValidatorForInteger.class, DecimalMinValidatorForNumber.class, + DecimalMinValidatorForShort.class, DecimalMinValidatorForCharSequence.class, DecimalMinValidatorForMonetaryAmount.class ) ); + putConstraints( tmpConstraints, Digits.class, Arrays.asList( + DigitsValidatorForCharSequence.class, + DigitsValidatorForNumber.class, + DigitsValidatorForMonetaryAmount.class + ) ); } else { putConstraints( tmpConstraints, DecimalMax.class, Arrays.asList( DecimalMaxValidatorForBigDecimal.class, DecimalMaxValidatorForBigInteger.class, + DecimalMaxValidatorForByte.class, DecimalMaxValidatorForDouble.class, DecimalMaxValidatorForFloat.class, DecimalMaxValidatorForLong.class, + DecimalMaxValidatorForInteger.class, DecimalMaxValidatorForNumber.class, + DecimalMaxValidatorForShort.class, DecimalMaxValidatorForCharSequence.class ) ); putConstraints( tmpConstraints, DecimalMin.class, Arrays.asList( DecimalMinValidatorForBigDecimal.class, DecimalMinValidatorForBigInteger.class, + DecimalMinValidatorForByte.class, DecimalMinValidatorForDouble.class, DecimalMinValidatorForFloat.class, DecimalMinValidatorForLong.class, + DecimalMinValidatorForInteger.class, DecimalMinValidatorForNumber.class, + DecimalMinValidatorForShort.class, DecimalMinValidatorForCharSequence.class ) ); + putConstraints( tmpConstraints, Digits.class, Arrays.asList( + DigitsValidatorForCharSequence.class, + DigitsValidatorForNumber.class + ) ); } - putConstraints( tmpConstraints, Digits.class, DigitsValidatorForCharSequence.class, DigitsValidatorForNumber.class ); putConstraint( tmpConstraints, Email.class, EmailValidator.class ); List>> futureValidators = new ArrayList<>( 18 ); @@ -417,26 +456,30 @@ public ConstraintHelper() { putConstraints( tmpConstraints, FutureOrPresent.class, futureOrPresentValidators ); - putConstraint( tmpConstraints, ISBN.class, ISBNValidator.class ); - if ( isJavaMoneyInClasspath() ) { putConstraints( tmpConstraints, Max.class, Arrays.asList( MaxValidatorForBigDecimal.class, MaxValidatorForBigInteger.class, + MaxValidatorForByte.class, MaxValidatorForDouble.class, MaxValidatorForFloat.class, + MaxValidatorForInteger.class, MaxValidatorForLong.class, MaxValidatorForNumber.class, + MaxValidatorForShort.class, MaxValidatorForCharSequence.class, MaxValidatorForMonetaryAmount.class ) ); putConstraints( tmpConstraints, Min.class, Arrays.asList( MinValidatorForBigDecimal.class, MinValidatorForBigInteger.class, + MinValidatorForByte.class, MinValidatorForDouble.class, MinValidatorForFloat.class, + MinValidatorForInteger.class, MinValidatorForLong.class, MinValidatorForNumber.class, + MinValidatorForShort.class, MinValidatorForCharSequence.class, MinValidatorForMonetaryAmount.class ) ); @@ -454,10 +497,13 @@ public ConstraintHelper() { putConstraints( tmpConstraints, Min.class, Arrays.asList( MinValidatorForBigDecimal.class, MinValidatorForBigInteger.class, + MinValidatorForByte.class, MinValidatorForDouble.class, MinValidatorForFloat.class, + MinValidatorForInteger.class, MinValidatorForLong.class, MinValidatorForNumber.class, + MinValidatorForShort.class, MinValidatorForCharSequence.class ) ); } @@ -656,45 +702,63 @@ public ConstraintHelper() { if ( isJavaMoneyInClasspath() ) { putConstraint( tmpConstraints, Currency.class, CurrencyValidatorForMonetaryAmount.class ); } + putConstraint( tmpConstraints, CreditCardNumber.class ); putConstraint( tmpConstraints, DurationMax.class, DurationMaxValidator.class ); putConstraint( tmpConstraints, DurationMin.class, DurationMinValidator.class ); putConstraint( tmpConstraints, EAN.class, EANValidator.class ); putConstraint( tmpConstraints, org.hibernate.validator.constraints.Email.class, org.hibernate.validator.internal.constraintvalidators.hv.EmailValidator.class ); + putConstraint( tmpConstraints, ISBN.class, ISBNValidator.class ); putConstraint( tmpConstraints, Length.class, LengthValidator.class ); putConstraint( tmpConstraints, CodePointLength.class, CodePointLengthValidator.class ); - putConstraint( tmpConstraints, ModCheck.class, ModCheckValidator.class ); putConstraint( tmpConstraints, LuhnCheck.class, LuhnCheckValidator.class ); + putConstraint( tmpConstraints, ModCheck.class, ModCheckValidator.class ); putConstraint( tmpConstraints, Mod10Check.class, Mod10CheckValidator.class ); putConstraint( tmpConstraints, Mod11Check.class, Mod11CheckValidator.class ); - putConstraint( tmpConstraints, REGON.class, REGONValidator.class ); putConstraint( tmpConstraints, NIP.class, NIPValidator.class ); - putConstraint( tmpConstraints, PESEL.class, PESELValidator.class ); putConstraint( tmpConstraints, org.hibernate.validator.constraints.NotBlank.class, org.hibernate.validator.internal.constraintvalidators.hv.NotBlankValidator.class ); + putConstraint( tmpConstraints, org.hibernate.validator.constraints.NotEmpty.class ); putConstraint( tmpConstraints, ParameterScriptAssert.class, ParameterScriptAssertValidator.class ); - putConstraint( tmpConstraints, SafeHtml.class, SafeHtmlValidator.class ); + putConstraint( tmpConstraints, PESEL.class, PESELValidator.class ); + putConstraint( tmpConstraints, Range.class ); + putConstraint( tmpConstraints, REGON.class, REGONValidator.class ); + if ( isJsoupInClasspath() ) { + putConstraint( tmpConstraints, SafeHtml.class, SafeHtmlValidator.class ); + } putConstraint( tmpConstraints, ScriptAssert.class, ScriptAssertValidator.class ); + putConstraint( tmpConstraints, TituloEleitoral.class ); putConstraint( tmpConstraints, UniqueElements.class, UniqueElementsValidator.class ); putConstraint( tmpConstraints, URL.class, URLValidator.class ); this.builtinConstraints = Collections.unmodifiableMap( tmpConstraints ); } - private static void putConstraint(Map, List>> validators, Class constraintType, Class> validatorType) { - validators.put( constraintType, Collections.singletonList( ConstraintValidatorDescriptor.forClass( validatorType ) ) ); + private static void putConstraint(Map, List>> validators, + Class constraintType) { + validators.put( constraintType, Collections.emptyList() ); + } + + private static void putConstraint(Map, List>> validators, + Class constraintType, Class> validatorType) { + validators.put( constraintType, Collections.singletonList( ConstraintValidatorDescriptor.forClass( validatorType, constraintType ) ) ); } - private static void putConstraints(Map, List>> validators, Class constraintType, Class> validatorType1, Class> validatorType2) { - List> descriptors = Stream.of( validatorType1, validatorType2 ) - .map( ConstraintValidatorDescriptor::forClass ) - .collect( Collectors.toList() ); + private static void putConstraints(Map, List>> validators, + Class constraintType, Class> validatorType1, Class> validatorType2) { + List> descriptors = new ArrayList<>( 2 ); + + descriptors.add( ConstraintValidatorDescriptor.forClass( validatorType1, constraintType ) ); + descriptors.add( ConstraintValidatorDescriptor.forClass( validatorType2, constraintType ) ); validators.put( constraintType, CollectionHelper.toImmutableList( descriptors ) ); } - private static void putConstraints(Map, List>> validators, Class constraintType, List>> validatorDescriptors) { - List> descriptors = validatorDescriptors.stream() - .map( ConstraintValidatorDescriptor::forClass ) - .collect( Collectors.toList() ); + private static void putConstraints(Map, List>> validators, + Class constraintType, List>> validatorTypes) { + List> descriptors = new ArrayList<>( validatorTypes.size() ); + + for ( Class> validatorType : validatorTypes ) { + descriptors.add( ConstraintValidatorDescriptor.forClass( validatorType, constraintType ) ); + } validators.put( constraintType, CollectionHelper.toImmutableList( descriptors ) ); } @@ -703,6 +767,10 @@ private boolean isBuiltinConstraint(Class annotationType) return builtinConstraints.containsKey( annotationType ); } + public Set> getBuiltinConstraints() { + return CollectionHelper.toImmutableSet( builtinConstraints.keySet() ); + } + /** * Returns the constraint validator classes for the given constraint * annotation type, as retrieved from @@ -881,7 +949,7 @@ private void assertPayloadParameterExists(Class annotation throw LOG.getConstraintWithoutMandatoryParameterException( PAYLOAD, annotationType.getName() ); } Class[] defaultPayload = (Class[]) method.getDefaultValue(); - if ( defaultPayload.length != 0 ) { + if ( defaultPayload == null || defaultPayload.length != 0 ) { throw LOG.getWrongDefaultValueForPayloadParameterException( annotationType.getName() ); } } @@ -897,7 +965,7 @@ private void assertGroupsParameterExists(Class annotationT throw LOG.getConstraintWithoutMandatoryParameterException( GROUPS, annotationType.getName() ); } Class[] defaultGroups = (Class[]) method.getDefaultValue(); - if ( defaultGroups.length != 0 ) { + if ( defaultGroups == null || defaultGroups.length != 0 ) { throw LOG.getWrongDefaultValueForGroupsParameterException( annotationType.getName() ); } } @@ -971,6 +1039,10 @@ private static boolean isJavaMoneyInClasspath() { return isClassPresent( JAVA_MONEY_CLASS_NAME ); } + private static boolean isJsoupInClasspath() { + return isClassPresent( JSOUP_CLASS_NAME ); + } + /** * Returns the default validators for the given constraint type. * @@ -995,8 +1067,8 @@ private List> getDefault .validatedBy(); return Stream.of( validatedBy ) - .map( c -> ConstraintValidatorDescriptor.forClass( c ) ) - .collect( Collectors.collectingAndThen( Collectors.toList(), CollectionHelper::toImmutableList ) ); + .map( c -> ConstraintValidatorDescriptor.forClass( c, annotationType ) ) + .collect( Collectors.collectingAndThen( Collectors.toList(), CollectionHelper::toImmutableList ) ); } private static boolean isClassPresent(String className) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraint.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraint.java index 48143f07a5..9a4cafcbdb 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraint.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraint.java @@ -7,7 +7,6 @@ package org.hibernate.validator.internal.metadata.core; import java.lang.annotation.Annotation; -import java.lang.annotation.ElementType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.List; @@ -16,13 +15,16 @@ import javax.validation.valueextraction.ValueExtractor; -import org.hibernate.validator.internal.engine.ValidationContext; -import org.hibernate.validator.internal.engine.ValueContext; +import org.hibernate.validator.internal.engine.valuecontext.ValueContext; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; +import org.hibernate.validator.internal.engine.validationcontext.ValidationContext; +import org.hibernate.validator.internal.engine.valuecontext.BeanValueContext; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorHelper; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; import org.hibernate.validator.internal.util.StringHelper; import org.hibernate.validator.internal.util.stereotypes.Immutable; @@ -64,12 +66,13 @@ public class MetaConstraint { /** * @param constraintDescriptor The constraint descriptor for this constraint * @param location meta data about constraint placement - * @param valueExtractorDescriptors the potential {@link ValueExtractor}s used to extract the value to validate + * @param valueExtractionPath the potential {@link ValueExtractor}s used to extract the value to validate * @param validatedValueType the type of the validated element */ - MetaConstraint(ConstraintDescriptorImpl constraintDescriptor, ConstraintLocation location, List valueExtractionPath, + MetaConstraint(ConstraintValidatorManager constraintValidatorManager, ConstraintDescriptorImpl constraintDescriptor, + ConstraintLocation location, List valueExtractionPath, Type validatedValueType) { - this.constraintTree = ConstraintTree.of( constraintDescriptor, validatedValueType ); + this.constraintTree = ConstraintTree.of( constraintValidatorManager, constraintDescriptor, validatedValueType ); this.location = location; this.valueExtractionPath = getValueExtractionPath( valueExtractionPath ); this.hashCode = buildHashCode( constraintDescriptor, location ); @@ -100,8 +103,8 @@ public final ConstraintDescriptorImpl getDescriptor() { return constraintTree.getDescriptor(); } - public final ElementType getElementType() { - return constraintTree.getDescriptor().getElementType(); + public final ConstraintLocationKind getConstraintLocationKind() { + return constraintTree.getDescriptor().getConstraintLocationKind(); } public boolean validateConstraint(ValidationContext validationContext, ValueContext valueContext) { @@ -123,7 +126,7 @@ public boolean validateConstraint(ValidationContext validationContext, ValueC } private boolean doValidateConstraint(ValidationContext executionContext, ValueContext valueContext) { - valueContext.setElementType( getElementType() ); + valueContext.setConstraintLocationKind( getConstraintLocationKind() ); boolean validationResult = constraintTree.validateConstraints( executionContext, valueContext ); return validationResult; @@ -216,7 +219,7 @@ public void keyedValue(String nodeName, Object key, Object value) { } private void doValidate(Object value, String nodeName) { - ValueContext.ValueState originalValueState = valueContext.getCurrentValueState(); + BeanValueContext.ValueState originalValueState = valueContext.getCurrentValueState(); Class containerClass = currentValueExtractionPathNode.getContainerClass(); if ( containerClass != null ) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraints.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraints.java index 0a95ddd10d..50a9893d11 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraints.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraints.java @@ -20,6 +20,7 @@ import javax.validation.metadata.ValidateUnwrappedValue; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorHelper; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; @@ -49,6 +50,7 @@ private MetaConstraints() { } public static MetaConstraint create(TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, + ConstraintValidatorManager constraintValidatorManager, ConstraintDescriptorImpl constraintDescriptor, ConstraintLocation location) { List valueExtractionPath = new ArrayList<>(); @@ -69,7 +71,7 @@ public static MetaConstraint create(TypeResolutionHelp Collections.reverse( valueExtractionPath ); - return new MetaConstraint<>( constraintDescriptor, location, valueExtractionPath, typeOfValidatedElement ); + return new MetaConstraint<>( constraintValidatorManager, constraintDescriptor, location, valueExtractionPath, typeOfValidatedElement ); } private static Type addValueExtractorDescriptorForWrappedValue(TypeResolutionHelper typeResolutionHelper, @@ -159,14 +161,14 @@ private static void addValueExtractorDescriptorForTypeArgumentLocation( ValueExt */ private static Class getWrappedValueType(TypeResolutionHelper typeResolutionHelper, Type declaredType, ValueExtractorDescriptor valueExtractorDescriptor) { ResolvedType resolvedType = typeResolutionHelper.getTypeResolver().resolve( declaredType ); + List resolvedTypeParameters = resolvedType.typeParametersFor( valueExtractorDescriptor.getContainerType() ); - if ( resolvedTypeParameters.isEmpty() ) { + if ( resolvedTypeParameters == null || resolvedTypeParameters.isEmpty() ) { throw LOG.getNoValueExtractorFoundForUnwrapException( declaredType ); } - else { - return resolvedTypeParameters.get( TypeVariables.getTypeParameterIndex( valueExtractorDescriptor.getExtractedTypeParameter() ) ).getErasedType(); - } + + return resolvedTypeParameters.get( TypeVariables.getTypeParameterIndex( valueExtractorDescriptor.getExtractedTypeParameter() ) ).getErasedType(); } private static TypeVariable getContainerClassTypeParameter(Class declaredType, ValueExtractorDescriptor selectedValueExtractorDescriptor) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/descriptor/ClassDescriptorImpl.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/descriptor/ClassDescriptorImpl.java new file mode 100644 index 0000000000..0d1f0017b4 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/descriptor/ClassDescriptorImpl.java @@ -0,0 +1,37 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.metadata.descriptor; + +import java.lang.reflect.Type; +import java.util.List; +import java.util.Set; + +import javax.validation.metadata.ElementDescriptor; + +/** + * Describes a validated type class-level constraints. + * + * @author Marko Bekhta + */ +public class ClassDescriptorImpl extends ElementDescriptorImpl implements ElementDescriptor { + + public ClassDescriptorImpl(Type beanType, + Set> constraints, + boolean defaultGroupSequenceRedefined, + List> defaultGroupSequence) { + super( beanType, constraints, defaultGroupSequenceRedefined, defaultGroupSequence ); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append( getClass().getSimpleName() ); + sb.append( "{beanType=" ).append( getElementClass() ); + sb.append( '}' ); + return sb.toString(); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/descriptor/ConstraintDescriptorImpl.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/descriptor/ConstraintDescriptorImpl.java index 094c6e54b2..372e45dc44 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/descriptor/ConstraintDescriptorImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/descriptor/ConstraintDescriptorImpl.java @@ -12,15 +12,10 @@ import java.io.Serializable; import java.lang.annotation.Annotation; import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.Target; import java.lang.invoke.MethodHandles; -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.Field; -import java.lang.reflect.Member; import java.lang.reflect.Method; import java.security.AccessController; import java.security.PrivilegedAction; @@ -49,6 +44,10 @@ import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorDescriptor; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.ConstraintOrigin; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; +import org.hibernate.validator.internal.properties.Callable; +import org.hibernate.validator.internal.properties.Constrainable; +import org.hibernate.validator.internal.properties.Property; import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.internal.util.StringHelper; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; @@ -130,10 +129,10 @@ public class ConstraintDescriptorImpl implements Constrain private final boolean isReportAsSingleInvalidConstraint; /** - * Describes on which level ({@code TYPE}, {@code METHOD}, {@code FIELD}) the constraint was + * Describes on which level ({@code TYPE}, {@code METHOD}, {@code FIELD}...) the constraint was * defined on. */ - private final ElementType elementType; + private final ConstraintLocationKind constraintLocationKind; /** * The origin of the constraint. Defined on the actual root class or somewhere in the class hierarchy @@ -164,14 +163,14 @@ public class ConstraintDescriptorImpl implements Constrain private final int hashCode; public ConstraintDescriptorImpl(ConstraintHelper constraintHelper, - Member member, + Constrainable constrainable, ConstraintAnnotationDescriptor annotationDescriptor, - ElementType type, + ConstraintLocationKind constraintLocationKind, Class implicitGroup, ConstraintOrigin definedOn, ConstraintType externalConstraintType) { this.annotationDescriptor = annotationDescriptor; - this.elementType = type; + this.constraintLocationKind = constraintLocationKind; this.definedOn = definedOn; this.isReportAsSingleInvalidConstraint = annotationDescriptor.getType().isAnnotationPresent( ReportAsSingleViolation.class @@ -182,7 +181,7 @@ public ConstraintDescriptorImpl(ConstraintHelper constraintHelper, this.groups = buildGroupSet( annotationDescriptor, implicitGroup ); this.payloads = buildPayloadSet( annotationDescriptor ); - this.valueUnwrapping = determineValueUnwrapping( this.payloads, member, annotationDescriptor.getType() ); + this.valueUnwrapping = determineValueUnwrapping( this.payloads, constrainable, annotationDescriptor.getType() ); this.validationAppliesTo = determineValidationAppliesTo( annotationDescriptor ); @@ -206,13 +205,12 @@ public ConstraintDescriptorImpl(ConstraintHelper constraintHelper, this.constraintType = determineConstraintType( annotationDescriptor.getType(), - member, - type, + constrainable, !genericValidatorDescriptors.isEmpty(), !crossParameterValidatorDescriptors.isEmpty(), externalConstraintType ); - this.composingConstraints = parseComposingConstraints( constraintHelper, member, constraintType ); + this.composingConstraints = parseComposingConstraints( constraintHelper, constrainable, constraintType ); this.compositionType = parseCompositionType( constraintHelper ); validateComposingConstraintTypes(); @@ -227,18 +225,18 @@ public ConstraintDescriptorImpl(ConstraintHelper constraintHelper, } public ConstraintDescriptorImpl(ConstraintHelper constraintHelper, - Member member, + Constrainable constrainable, ConstraintAnnotationDescriptor annotationDescriptor, - ElementType type) { - this( constraintHelper, member, annotationDescriptor, type, null, ConstraintOrigin.DEFINED_LOCALLY, null ); + ConstraintLocationKind constraintLocationKind) { + this( constraintHelper, constrainable, annotationDescriptor, constraintLocationKind, null, ConstraintOrigin.DEFINED_LOCALLY, null ); } public ConstraintDescriptorImpl(ConstraintHelper constraintHelper, - Member member, + Constrainable constrainable, ConstraintAnnotationDescriptor annotationDescriptor, - ElementType type, + ConstraintLocationKind constraintLocationKind, ConstraintType constraintType) { - this( constraintHelper, member, annotationDescriptor, type, null, ConstraintOrigin.DEFINED_LOCALLY, constraintType ); + this( constraintHelper, constrainable, annotationDescriptor, constraintLocationKind, null, ConstraintOrigin.DEFINED_LOCALLY, constraintType ); } public ConstraintAnnotationDescriptor getAnnotationDescriptor() { @@ -314,8 +312,8 @@ public boolean isReportAsSingleViolation() { return isReportAsSingleInvalidConstraint; } - public ElementType getElementType() { - return elementType; + public ConstraintLocationKind getConstraintLocationKind() { + return constraintLocationKind; } public ConstraintOrigin getDefinedOn() { @@ -362,7 +360,7 @@ public String toString() { sb.append( ", payloads=" ).append( payloads ); sb.append( ", hasComposingConstraints=" ).append( composingConstraints.isEmpty() ); sb.append( ", isReportAsSingleInvalidConstraint=" ).append( isReportAsSingleInvalidConstraint ); - sb.append( ", elementType=" ).append( elementType ); + sb.append( ", constraintLocationKind=" ).append( constraintLocationKind ); sb.append( ", definedOn=" ).append( definedOn ); sb.append( ", groups=" ).append( groups ); sb.append( ", attributes=" ).append( annotationDescriptor.getAttributes() ); @@ -389,8 +387,7 @@ public String toString() { * specify the target explicitly). * * - * @param member The annotated member - * @param elementType The type of the annotated element + * @param constrainable The annotated member * @param hasGenericValidators Whether the constraint has at least one generic validator or * not * @param hasCrossParameterValidator Whether the constraint has a cross-parameter validator @@ -400,14 +397,13 @@ public String toString() { * @return The type of this constraint */ private ConstraintType determineConstraintType(Class constraintAnnotationType, - Member member, - ElementType elementType, + Constrainable constrainable, boolean hasGenericValidators, boolean hasCrossParameterValidator, ConstraintType externalConstraintType) { ConstraintTarget constraintTarget = validationAppliesTo; ConstraintType constraintType = null; - boolean isExecutable = isExecutable( elementType ); + boolean isExecutable = constraintLocationKind.isExecutable(); //target explicitly set to RETURN_VALUE if ( constraintTarget == ConstraintTarget.RETURN_VALUE ) { @@ -453,9 +449,10 @@ else if ( constraintAnnotationType.isAnnotationPresent( SupportedValidationTarge } //try to derive from existence of parameters/return value - else { - boolean hasParameters = hasParameters( member ); - boolean hasReturnValue = hasReturnValue( member ); + //hence look only if it is a callable + else if ( constrainable instanceof Callable ) { + boolean hasParameters = constrainable.as( Callable.class ).hasParameters(); + boolean hasReturnValue = constrainable.as( Callable.class ).hasReturnValue(); if ( !hasParameters && hasReturnValue ) { constraintType = ConstraintType.GENERIC; @@ -472,16 +469,16 @@ else if ( hasParameters && !hasReturnValue ) { } if ( constraintType == ConstraintType.CROSS_PARAMETER ) { - validateCrossParameterConstraintType( member, hasCrossParameterValidator ); + validateCrossParameterConstraintType( constrainable, hasCrossParameterValidator ); } return constraintType; } - private static ValidateUnwrappedValue determineValueUnwrapping(Set> payloads, Member member, Class annotationType) { + private static ValidateUnwrappedValue determineValueUnwrapping(Set> payloads, Constrainable constrainable, Class annotationType) { if ( payloads.contains( Unwrapping.Unwrap.class ) ) { if ( payloads.contains( Unwrapping.Skip.class ) ) { - throw LOG.getInvalidUnwrappingConfigurationForConstraintException( member, annotationType ); + throw LOG.getInvalidUnwrappingConfigurationForConstraintException( constrainable, annotationType ); } return ValidateUnwrappedValue.UNWRAP; @@ -498,20 +495,20 @@ private static ConstraintTarget determineValidationAppliesTo(ConstraintAnnotatio return annotationDescriptor.getValidationAppliesTo(); } - private void validateCrossParameterConstraintType(Member member, boolean hasCrossParameterValidator) { + private void validateCrossParameterConstraintType(Constrainable constrainable, boolean hasCrossParameterValidator) { if ( !hasCrossParameterValidator ) { throw LOG.getCrossParameterConstraintHasNoValidatorException( annotationDescriptor.getType() ); } - else if ( member == null ) { + else if ( constrainable == null ) { throw LOG.getCrossParameterConstraintOnClassException( annotationDescriptor.getType() ); } - else if ( member instanceof Field ) { - throw LOG.getCrossParameterConstraintOnFieldException( annotationDescriptor.getType(), member ); + else if ( constrainable instanceof Property ) { + throw LOG.getCrossParameterConstraintOnFieldException( annotationDescriptor.getType(), constrainable ); } - else if ( !hasParameters( member ) ) { + else if ( !constrainable.as( Callable.class ).hasParameters() ) { throw LOG.getCrossParameterConstraintOnMethodWithoutParametersException( annotationDescriptor.getType(), - (Executable) member + constrainable ); } } @@ -533,39 +530,6 @@ private void validateComposingConstraintTypes() { } } - private boolean hasParameters(Member member) { - boolean hasParameters = false; - if ( member instanceof Constructor ) { - Constructor constructor = (Constructor) member; - hasParameters = constructor.getParameterTypes().length > 0; - } - else if ( member instanceof Method ) { - Method method = (Method) member; - hasParameters = method.getParameterTypes().length > 0; - } - return hasParameters; - } - - private boolean hasReturnValue(Member member) { - boolean hasReturnValue; - if ( member instanceof Constructor ) { - hasReturnValue = true; - } - else if ( member instanceof Method ) { - Method method = (Method) member; - hasReturnValue = method.getGenericReturnType() != void.class; - } - else { - // field or type - hasReturnValue = false; - } - return hasReturnValue; - } - - private boolean isExecutable(ElementType elementType) { - return elementType == ElementType.METHOD || elementType == ElementType.CONSTRUCTOR; - } - @SuppressWarnings("unchecked") private static Set> buildPayloadSet(ConstraintAnnotationDescriptor annotationDescriptor) { Set> payloadSet = newHashSet(); @@ -648,7 +612,7 @@ private void ensureAttributeIsOverridable(Method m, OverridesAttribute overrides } } - private Set> parseComposingConstraints(ConstraintHelper constraintHelper, Member member, + private Set> parseComposingConstraints(ConstraintHelper constraintHelper, Constrainable constrainable, ConstraintType constraintType) { Set> composingConstraintsSet = newHashSet(); Map> overrideParameters = parseOverrideParameters(); @@ -669,7 +633,7 @@ private Set> parseComposingConstraints(ConstraintHel ConstraintDescriptorImpl descriptor = createComposingConstraintDescriptor( constraintHelper, - member, + constrainable, overrideParameters, OVERRIDES_PARAMETER_DEFAULT_INDEX, declaredAnnotation, @@ -691,7 +655,7 @@ else if ( constraintHelper.isMultiValueConstraint( declaredAnnotationType ) ) { ConstraintDescriptorImpl descriptor = createComposingConstraintDescriptor( constraintHelper, - member, + constrainable, overrideParameters, index, constraintAnnotation, @@ -727,7 +691,7 @@ private CompositionType parseCompositionType(ConstraintHelper constraintHelper) private ConstraintDescriptorImpl createComposingConstraintDescriptor( ConstraintHelper constraintHelper, - Member member, + Constrainable constrainable, Map> overrideParameters, int index, U constraintAnnotation, @@ -773,7 +737,7 @@ annotationType, run( GetAnnotationAttributes.action( constraintAnnotation ) ) } return new ConstraintDescriptorImpl<>( - constraintHelper, member, annotationDescriptorBuilder.build(), elementType, null, definedOn, constraintType + constraintHelper, constrainable, annotationDescriptorBuilder.build(), constraintLocationKind, null, definedOn, constraintType ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/descriptor/ElementDescriptorImpl.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/descriptor/ElementDescriptorImpl.java index d80a1972f2..0c865382fa 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/descriptor/ElementDescriptorImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/descriptor/ElementDescriptorImpl.java @@ -142,7 +142,7 @@ public boolean hasConstraints() { private void addMatchingDescriptorsForGroup(Class group, Set> matchingDescriptors) { for ( ConstraintDescriptorImpl descriptor : constraintDescriptors ) { - if ( definedInSet.contains( descriptor.getDefinedOn() ) && elementTypes.contains( descriptor.getElementType() ) + if ( definedInSet.contains( descriptor.getDefinedOn() ) && elementTypes.contains( descriptor.getConstraintLocationKind().getElementType() ) && descriptor.getGroups().contains( group ) ) { matchingDescriptors.add( descriptor ); } @@ -160,7 +160,7 @@ private void findMatchingDescriptors(Set> matchingDescri } else { for ( ConstraintDescriptorImpl descriptor : constraintDescriptors ) { - if ( definedInSet.contains( descriptor.getDefinedOn() ) && elementTypes.contains( descriptor.getElementType() ) ) { + if ( definedInSet.contains( descriptor.getDefinedOn() ) && elementTypes.contains( descriptor.getConstraintLocationKind().getElementType() ) ) { matchingDescriptors.add( descriptor ); } } 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 f63ed55c9b..fd5864a3d2 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 @@ -6,12 +6,12 @@ */ package org.hibernate.validator.internal.metadata.facets; -import java.lang.annotation.ElementType; import java.lang.reflect.Type; 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.location.ConstraintLocation.ConstraintLocationKind; /** * Provides a unified view on cascadable elements of all kinds, be it properties @@ -24,11 +24,11 @@ public interface Cascadable { /** - * Returns the element type of the cascadable. + * Returns the constraint location kind of the cascadable. * - * @return Returns the element type of the cascadable. + * @return Returns the constraint location kind of the cascadable. */ - ElementType getElementType(); + ConstraintLocationKind getConstraintLocationKind(); /** * Returns the data type of this cascadable, e.g. the type of a bean property or the diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/AbstractPropertyConstraintLocation.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/AbstractPropertyConstraintLocation.java new file mode 100644 index 0000000000..950a49250c --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/AbstractPropertyConstraintLocation.java @@ -0,0 +1,92 @@ +/* + * 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 java.lang.reflect.Type; + +import org.hibernate.validator.internal.engine.path.PathImpl; +import org.hibernate.validator.internal.properties.Property; +import org.hibernate.validator.internal.properties.PropertyAccessor; +import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; + +/** + * An abstract property constraint location. + * + * @author Marko Bekhta + * @author Guillaume Smet + */ +public abstract class AbstractPropertyConstraintLocation implements ConstraintLocation { + + /** + * The property the constraint was defined on. + */ + private final T property; + + private final PropertyAccessor propertyAccessor; + + AbstractPropertyConstraintLocation(T property) { + this.property = property; + this.propertyAccessor = property.createAccessor(); + } + + @Override + public Class getDeclaringClass() { + return property.getDeclaringClass(); + } + + @Override + public T getConstrainable() { + return property; + } + + public String getPropertyName() { + return property.getPropertyName(); + } + + @Override + public Type getTypeForValidatorResolution() { + return property.getTypeForValidatorResolution(); + } + + @Override + public void appendTo(ExecutableParameterNameProvider parameterNameProvider, PathImpl path) { + path.addPropertyNode( property.getResolvedPropertyName() ); + } + + @Override + public Object getValue(Object parent) { + return propertyAccessor.getValueFrom( parent ); + } + + @Override + public String toString() { + return getClass().getSimpleName() + " [property=" + property + "]"; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + + AbstractPropertyConstraintLocation that = (AbstractPropertyConstraintLocation) o; + + if ( !property.equals( that.property ) ) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return property.hashCode(); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/BeanConstraintLocation.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/BeanConstraintLocation.java index e8a573f27b..0ff0fd218f 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/BeanConstraintLocation.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/BeanConstraintLocation.java @@ -6,10 +6,10 @@ */ package org.hibernate.validator.internal.metadata.location; -import java.lang.reflect.Member; import java.lang.reflect.Type; import org.hibernate.validator.internal.engine.path.PathImpl; +import org.hibernate.validator.internal.properties.Constrainable; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.TypeHelper; @@ -18,6 +18,7 @@ * * @author Hardy Ferentschik * @author Gunnar Morling + * @author Guillaume Smet */ class BeanConstraintLocation implements ConstraintLocation { @@ -47,7 +48,7 @@ public Class getDeclaringClass() { } @Override - public Member getMember() { + public Constrainable getConstrainable() { return null; } @@ -66,6 +67,11 @@ public Object getValue(Object parent) { return parent; } + @Override + public ConstraintLocationKind getKind() { + return ConstraintLocationKind.TYPE; + } + @Override public String toString() { return "BeanConstraintLocation [declaringClass=" + declaringClass + ", typeForValidatorResolution=" + typeForValidatorResolution + "]"; 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 429d7d711d..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 @@ -6,15 +6,18 @@ */ package org.hibernate.validator.internal.metadata.location; -import java.lang.reflect.Executable; -import java.lang.reflect.Field; -import java.lang.reflect.Member; -import java.lang.reflect.Method; +import java.lang.annotation.ElementType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import org.hibernate.validator.internal.engine.path.PathImpl; +import org.hibernate.validator.internal.metadata.raw.ConstrainedElement.ConstrainedElementKind; +import org.hibernate.validator.internal.properties.Callable; +import org.hibernate.validator.internal.properties.Constrainable; +import org.hibernate.validator.internal.properties.Field; +import org.hibernate.validator.internal.properties.Getter; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; +import org.hibernate.validator.internal.util.StringHelper; /** * Represents the location (e.g. a bean, field or method parameter) of a constraint and provides logic related to it, @@ -43,7 +46,7 @@ static ConstraintLocation forField(Field field) { return new FieldConstraintLocation( field ); } - static ConstraintLocation forGetter(Method getter) { + static ConstraintLocation forGetter(Getter getter) { return new GetterConstraintLocation( getter ); } @@ -51,16 +54,16 @@ static ConstraintLocation forTypeArgument(ConstraintLocation delegate, TypeVaria return new TypeArgumentConstraintLocation( delegate, typeParameter, typeOfAnnotatedElement ); } - static ConstraintLocation forReturnValue(Executable executable) { - return new ReturnValueConstraintLocation( executable ); + static ConstraintLocation forReturnValue(Callable callable) { + return new ReturnValueConstraintLocation( callable ); } - static ConstraintLocation forCrossParameter(Executable executable) { - return new CrossParameterConstraintLocation( executable ); + static ConstraintLocation forCrossParameter(Callable callable) { + return new CrossParameterConstraintLocation( callable ); } - static ConstraintLocation forParameter(Executable executable, int index) { - return new ParameterConstraintLocation( executable, index ); + static ConstraintLocation forParameter(Callable callable, int index) { + return new ParameterConstraintLocation( callable, index ); } /** @@ -73,7 +76,7 @@ static ConstraintLocation forParameter(Executable executable, int index) { * * @return the member represented by this location. Will be {@code null} when this location represents a type. */ - Member getMember(); + Constrainable getConstrainable(); /** * Returns the type to be used when resolving constraint validators for constraints at this location. Note that this @@ -91,8 +94,61 @@ static ConstraintLocation forParameter(Executable executable, int index) { /** * Obtains the value of this location from the parent. The type of the passed parent depends on the location type, - * e.g. a bean would be passed for a {@link FieldConstraintLocation} or {@link GetterConstraintLocation} but an + * e.g. a bean would be passed for a {@link AbstractPropertyConstraintLocation} but an * object array for a {@link ParameterConstraintLocation}. */ Object getValue(Object parent); + + /** + * Returns the nature of the constraint location. + */ + ConstraintLocationKind getKind(); + + enum ConstraintLocationKind { + TYPE( ElementType.TYPE ), + CONSTRUCTOR( ElementType.CONSTRUCTOR ), + METHOD( ElementType.METHOD ), + PARAMETER( ElementType.PARAMETER ), + FIELD( ElementType.FIELD ), + GETTER( ElementType.METHOD ), + TYPE_USE( ElementType.TYPE_USE ); + + private final ElementType elementType; + + ConstraintLocationKind(ElementType elementType) { + this.elementType = elementType; + } + + public ElementType getElementType() { + return elementType; + } + + public boolean isExecutable() { + return this == CONSTRUCTOR || isMethod(); + } + + public boolean isMethod() { + return this == METHOD || this == GETTER; + } + + public static ConstraintLocationKind of(ConstrainedElementKind constrainedElementKind) { + switch ( constrainedElementKind ) { + case CONSTRUCTOR: + return ConstraintLocationKind.CONSTRUCTOR; + case FIELD: + return ConstraintLocationKind.FIELD; + case METHOD: + return ConstraintLocationKind.METHOD; + case PARAMETER: + return ConstraintLocationKind.PARAMETER; + case TYPE: + return ConstraintLocationKind.TYPE; + case GETTER: + return ConstraintLocationKind.GETTER; + default: + throw new IllegalArgumentException( + StringHelper.format( "Constrained element kind '%1$s' not supported.", constrainedElementKind ) ); + } + } + } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/CrossParameterConstraintLocation.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/CrossParameterConstraintLocation.java index 0c127fbb0f..0f36fa5ed7 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/CrossParameterConstraintLocation.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/CrossParameterConstraintLocation.java @@ -6,11 +6,11 @@ */ package org.hibernate.validator.internal.metadata.location; -import java.lang.reflect.Executable; -import java.lang.reflect.Member; import java.lang.reflect.Type; import org.hibernate.validator.internal.engine.path.PathImpl; +import org.hibernate.validator.internal.properties.Callable; +import org.hibernate.validator.internal.properties.Constrainable; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; /** @@ -18,23 +18,27 @@ * * @author Hardy Ferentschik * @author Gunnar Morling + * @author Guillaume Smet */ class CrossParameterConstraintLocation implements ConstraintLocation { - private final Executable executable; + private final Callable callable; - CrossParameterConstraintLocation(Executable executable) { - this.executable = executable; + private final ConstraintLocationKind kind; + + CrossParameterConstraintLocation(Callable callable) { + this.callable = callable; + this.kind = ConstraintLocationKind.of( callable.getConstrainedElementKind() ); } @Override public Class getDeclaringClass() { - return executable.getDeclaringClass(); + return callable.getDeclaringClass(); } @Override - public Member getMember() { - return executable; + public Constrainable getConstrainable() { + return callable; } @Override @@ -52,17 +56,19 @@ public Object getValue(Object parent) { return parent; } + @Override + public ConstraintLocationKind getKind() { + return kind; + } + @Override public String toString() { - return "CrossParameterConstraintLocation [executable=" + executable + "]"; + return "CrossParameterConstraintLocation [callable=" + callable + "]"; } @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ( ( executable == null ) ? 0 : executable.hashCode() ); - return result; + return callable.hashCode(); } @Override @@ -77,12 +83,7 @@ public boolean equals(Object obj) { return false; } CrossParameterConstraintLocation other = (CrossParameterConstraintLocation) obj; - if ( executable == null ) { - if ( other.executable != null ) { - return false; - } - } - else if ( !executable.equals( other.executable ) ) { + if ( !callable.equals( other.callable ) ) { return false; } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/FieldConstraintLocation.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/FieldConstraintLocation.java index 5b88190ba6..d7639c0646 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/FieldConstraintLocation.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/FieldConstraintLocation.java @@ -6,141 +6,21 @@ */ package org.hibernate.validator.internal.metadata.location; -import java.lang.reflect.Field; -import java.lang.reflect.Member; -import java.lang.reflect.Type; -import java.security.AccessController; -import java.security.PrivilegedAction; - -import org.hibernate.validator.HibernateValidatorPermission; -import org.hibernate.validator.internal.engine.path.PathImpl; -import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; -import org.hibernate.validator.internal.util.ReflectionHelper; -import org.hibernate.validator.internal.util.StringHelper; -import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredField; +import org.hibernate.validator.internal.properties.Field; /** - * Field constraint location. + * Field property constraint location. * - * @author Hardy Ferentschik - * @author Gunnar Morling + * @author Marko Bekhta */ -public class FieldConstraintLocation implements ConstraintLocation { - - /** - * The member the constraint was defined on. - */ - private final Field field; - - private final Field accessibleField; - - /** - * The property name associated with the member. - */ - private final String propertyName; - - /** - * The type to be used for validator resolution for constraints at this location. - */ - private final Type typeForValidatorResolution; - +public class FieldConstraintLocation extends AbstractPropertyConstraintLocation { FieldConstraintLocation(Field field) { - this.field = field; - this.accessibleField = getAccessible( field ); - this.propertyName = ReflectionHelper.getPropertyName( field ); - this.typeForValidatorResolution = ReflectionHelper.boxedType( ReflectionHelper.typeOf( field ) ); - } - - @Override - public Class getDeclaringClass() { - return field.getDeclaringClass(); - } - - @Override - public Member getMember() { - return field; - } - - public String getPropertyName() { - return propertyName; - } - - @Override - public Type getTypeForValidatorResolution() { - return typeForValidatorResolution; - } - - @Override - public void appendTo(ExecutableParameterNameProvider parameterNameProvider, PathImpl path) { - path.addPropertyNode( propertyName ); + super( field ); } @Override - public Object getValue(Object parent) { - return ReflectionHelper.getValue( accessibleField, parent ); - } - - @Override - public String toString() { - return "FieldConstraintLocation [member=" + StringHelper.toShortString( field ) + ", typeForValidatorResolution=" - + StringHelper.toShortString( typeForValidatorResolution ) + "]"; - } - - @Override - public boolean equals(Object o) { - if ( this == o ) { - return true; - } - if ( o == null || getClass() != o.getClass() ) { - return false; - } - - FieldConstraintLocation that = (FieldConstraintLocation) o; - - if ( field != null ? !field.equals( that.field ) : that.field != null ) { - return false; - } - if ( !typeForValidatorResolution.equals( that.typeForValidatorResolution ) ) { - return false; - } - - return true; - } - - @Override - public int hashCode() { - int result = field != null ? field.hashCode() : 0; - result = 31 * result + typeForValidatorResolution.hashCode(); - return result; - } - - /** - * Returns an accessible version of the given member. Will be the given member itself in case it is accessible, - * otherwise a copy which is set accessible. - */ - private static Field getAccessible(Field original) { - if ( original.isAccessible() ) { - return original; - } - - SecurityManager sm = System.getSecurityManager(); - if ( sm != null ) { - sm.checkPermission( HibernateValidatorPermission.ACCESS_PRIVATE_MEMBERS ); - } - - Class clazz = original.getDeclaringClass(); - - return run( GetDeclaredField.andMakeAccessible( clazz, original.getName() ) ); - } - - /** - * 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 T run(PrivilegedAction action) { - return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); + public ConstraintLocationKind getKind() { + return ConstraintLocationKind.FIELD; } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/GetterConstraintLocation.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/GetterConstraintLocation.java index 0e59e27cf4..2dee31bf03 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/GetterConstraintLocation.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/GetterConstraintLocation.java @@ -6,142 +6,21 @@ */ package org.hibernate.validator.internal.metadata.location; -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.security.AccessController; -import java.security.PrivilegedAction; - -import org.hibernate.validator.HibernateValidatorPermission; -import org.hibernate.validator.internal.engine.path.PathImpl; -import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; -import org.hibernate.validator.internal.util.ReflectionHelper; -import org.hibernate.validator.internal.util.StringHelper; -import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredMethod; +import org.hibernate.validator.internal.properties.Getter; /** - * Getter method constraint location. + * Getter property constraint location. * - * @author Hardy Ferentschik - * @author Gunnar Morling + * @author Marko Bekhta */ -public class GetterConstraintLocation implements ConstraintLocation { - - /** - * The method the constraint was defined on. - */ - private final Method method; - - private final Method accessibleMethod; - - /** - * The property name associated with the method. - */ - private final String propertyName; - - /** - * The type to be used for validator resolution for constraints at this location. - */ - private final Type typeForValidatorResolution; - - - GetterConstraintLocation(Method method) { - this.method = method; - this.accessibleMethod = getAccessible( method ); - this.propertyName = ReflectionHelper.getPropertyName( method ); - this.typeForValidatorResolution = ReflectionHelper.boxedType( ReflectionHelper.typeOf( method ) ); - } - - @Override - public Class getDeclaringClass() { - return method.getDeclaringClass(); - } - - @Override - public Method getMember() { - return method; - } - - public String getPropertyName() { - return propertyName; - } +public class GetterConstraintLocation extends AbstractPropertyConstraintLocation { - @Override - public Type getTypeForValidatorResolution() { - return typeForValidatorResolution; - } - - @Override - public void appendTo(ExecutableParameterNameProvider parameterNameProvider, PathImpl path) { - path.addPropertyNode( propertyName ); + GetterConstraintLocation(Getter getter) { + super( getter ); } @Override - public Object getValue(Object parent) { - return ReflectionHelper.getValue( accessibleMethod, parent ); - } - - @Override - public String toString() { - return "GetterConstraintLocation [method=" + StringHelper.toShortString( method ) + ", typeForValidatorResolution=" - + StringHelper.toShortString( typeForValidatorResolution ) + "]"; - } - - @Override - public boolean equals(Object o) { - if ( this == o ) { - return true; - } - if ( o == null || getClass() != o.getClass() ) { - return false; - } - - GetterConstraintLocation that = (GetterConstraintLocation) o; - - if ( method != null ? !method.equals( that.method ) : that.method != null ) { - return false; - } - if ( !typeForValidatorResolution.equals( that.typeForValidatorResolution ) ) { - return false; - } - - return true; - } - - @Override - public int hashCode() { - int result = method.hashCode(); - result = 31 * result + typeForValidatorResolution.hashCode(); - return result; - } - - /** - * Returns an accessible version of the given method. Will be the given method itself in case it is accessible, - * otherwise a copy which is set accessible. - */ - private static Method getAccessible(Method original) { - if ( ( (AccessibleObject) original ).isAccessible() ) { - return original; - } - - SecurityManager sm = System.getSecurityManager(); - if ( sm != null ) { - sm.checkPermission( HibernateValidatorPermission.ACCESS_PRIVATE_MEMBERS ); - } - - Class clazz = original.getDeclaringClass(); - Method accessibleMethod = run( GetDeclaredMethod.andMakeAccessible( clazz, original.getName() ) ); - - return accessibleMethod; - } - - /** - * 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 T run(PrivilegedAction action) { - return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); + public ConstraintLocationKind getKind() { + return ConstraintLocationKind.GETTER; } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ParameterConstraintLocation.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ParameterConstraintLocation.java index 8c816b2299..29392b77ff 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ParameterConstraintLocation.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ParameterConstraintLocation.java @@ -6,11 +6,11 @@ */ package org.hibernate.validator.internal.metadata.location; -import java.lang.reflect.Executable; -import java.lang.reflect.Member; import java.lang.reflect.Type; import org.hibernate.validator.internal.engine.path.PathImpl; +import org.hibernate.validator.internal.properties.Callable; +import org.hibernate.validator.internal.properties.Constrainable; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.ReflectionHelper; @@ -19,27 +19,30 @@ * * @author Hardy Ferentschik * @author Gunnar Morling + * @author Guillaume Smet */ public class ParameterConstraintLocation implements ConstraintLocation { - private final Executable executable; + private final Callable callable; private final int index; private final Type typeForValidatorResolution; + private final ConstraintLocationKind kind; - ParameterConstraintLocation(Executable executable, int index) { - this.executable = executable; + public ParameterConstraintLocation(Callable callable, int index) { + this.callable = callable; this.index = index; - this.typeForValidatorResolution = ReflectionHelper.boxedType( ReflectionHelper.typeOf( executable, index ) ); + this.typeForValidatorResolution = ReflectionHelper.boxedType( callable.getParameterGenericType( index ) ); + this.kind = ConstraintLocationKind.of( callable.getConstrainedElementKind() ); } @Override public Class getDeclaringClass() { - return executable.getDeclaringClass(); + return callable.getDeclaringClass(); } @Override - public Member getMember() { - return executable; + public Constrainable getConstrainable() { + return callable; } @Override @@ -53,8 +56,7 @@ public int getIndex() { @Override public void appendTo(ExecutableParameterNameProvider parameterNameProvider, PathImpl path) { - String name = parameterNameProvider.getParameterNames( executable ).get( index ); - path.addParameterNode( name, index ); + path.addParameterNode( callable.getParameterName( parameterNameProvider, index ), index ); } @Override @@ -62,17 +64,21 @@ public Object getValue(Object parent) { return ( (Object[]) parent )[index]; } + @Override + public ConstraintLocationKind getKind() { + return kind; + } + @Override public String toString() { - return "ParameterConstraintLocation [executable=" + executable + ", index=" + index - + ", typeForValidatorResolution=" + typeForValidatorResolution + "]"; + return "ParameterConstraintLocation [callable=" + callable + ", index=" + index + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ( ( executable == null ) ? 0 : executable.hashCode() ); + result = prime * result + callable.hashCode(); result = prime * result + index; return result; } @@ -89,12 +95,7 @@ public boolean equals(Object obj) { return false; } ParameterConstraintLocation other = (ParameterConstraintLocation) obj; - if ( executable == null ) { - if ( other.executable != null ) { - return false; - } - } - else if ( !executable.equals( other.executable ) ) { + if ( !callable.equals( other.callable ) ) { return false; } if ( index != other.index ) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ReturnValueConstraintLocation.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ReturnValueConstraintLocation.java index b6ac18e2ca..6ee7b3f924 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ReturnValueConstraintLocation.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ReturnValueConstraintLocation.java @@ -6,43 +6,45 @@ */ package org.hibernate.validator.internal.metadata.location; -import java.lang.reflect.Executable; -import java.lang.reflect.Member; import java.lang.reflect.Type; import org.hibernate.validator.internal.engine.path.PathImpl; +import org.hibernate.validator.internal.properties.Callable; +import org.hibernate.validator.internal.properties.Constrainable; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; -import org.hibernate.validator.internal.util.ReflectionHelper; /** * Executable return value constraint location. * * @author Hardy Ferentschik * @author Gunnar Morling + * @author Marko Bekhta + * @author Guillaume Smet */ class ReturnValueConstraintLocation implements ConstraintLocation { - private final Executable executable; - private final Type typeForValidatorResolution; + private final Callable callable; - ReturnValueConstraintLocation(Executable executable) { - this.executable = executable; - this.typeForValidatorResolution = ReflectionHelper.boxedType( ReflectionHelper.typeOf( executable ) ); + private final ConstraintLocationKind kind; + + ReturnValueConstraintLocation(Callable callable) { + this.callable = callable; + this.kind = ConstraintLocationKind.of( callable.getConstrainedElementKind() ); } @Override public Class getDeclaringClass() { - return executable.getDeclaringClass(); + return callable.getDeclaringClass(); } @Override - public Member getMember() { - return executable; + public Constrainable getConstrainable() { + return callable; } @Override public Type getTypeForValidatorResolution() { - return typeForValidatorResolution; + return callable.getTypeForValidatorResolution(); } @Override @@ -55,16 +57,21 @@ public Object getValue(Object parent) { return parent; } + @Override + public ConstraintLocationKind getKind() { + return kind; + } + @Override public String toString() { - return "ReturnValueConstraintLocation [executable=" + executable + "]"; + return "ReturnValueConstraintLocation [callable=" + callable + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ( ( executable == null ) ? 0 : executable.hashCode() ); + result = prime * result + callable.hashCode(); return result; } @@ -80,12 +87,7 @@ public boolean equals(Object obj) { return false; } ReturnValueConstraintLocation other = (ReturnValueConstraintLocation) obj; - if ( executable == null ) { - if ( other.executable != null ) { - return false; - } - } - else if ( !executable.equals( other.executable ) ) { + if ( !callable.equals( other.callable ) ) { return false; } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/TypeArgumentConstraintLocation.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/TypeArgumentConstraintLocation.java index 989d8a2c76..0f82da3644 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/TypeArgumentConstraintLocation.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/TypeArgumentConstraintLocation.java @@ -6,11 +6,11 @@ */ package org.hibernate.validator.internal.metadata.location; -import java.lang.reflect.Member; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import org.hibernate.validator.internal.engine.path.PathImpl; +import org.hibernate.validator.internal.properties.Constrainable; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.ReflectionHelper; import org.hibernate.validator.internal.util.StringHelper; @@ -52,8 +52,8 @@ public Class getDeclaringClass() { } @Override - public Member getMember() { - return delegate.getMember(); + public Constrainable getConstrainable() { + return delegate.getConstrainable(); } public TypeVariable getTypeParameter() { @@ -87,6 +87,11 @@ public ConstraintLocation getOuterDelegate() { return outerDelegate; } + @Override + public ConstraintLocationKind getKind() { + return ConstraintLocationKind.TYPE_USE; + } + @Override public String toString() { return "TypeArgumentValueConstraintLocation [typeForValidatorResolution=" + StringHelper.toShortString( typeForValidatorResolution ) 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 1b387b8e13..20076d9e85 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 @@ -12,20 +12,14 @@ import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES; import java.lang.annotation.Annotation; -import java.lang.annotation.ElementType; import java.lang.invoke.MethodHandles; -import java.lang.reflect.AccessibleObject; import java.lang.reflect.AnnotatedArrayType; -import java.lang.reflect.AnnotatedElement; import java.lang.reflect.AnnotatedParameterizedType; import java.lang.reflect.AnnotatedType; -import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Field; -import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.lang.reflect.Parameter; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; @@ -45,17 +39,17 @@ import javax.validation.groups.ConvertGroup; import org.hibernate.validator.group.GroupSequenceProvider; +import org.hibernate.validator.internal.engine.ConstraintCreationContext; 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.core.AnnotationProcessingOptions; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; -import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.core.MetaConstraints; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl.ConstraintType; import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +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; @@ -63,10 +57,17 @@ import org.hibernate.validator.internal.metadata.raw.ConstrainedField; import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter; import org.hibernate.validator.internal.metadata.raw.ConstrainedType; +import org.hibernate.validator.internal.properties.Callable; +import org.hibernate.validator.internal.properties.Constrainable; +import org.hibernate.validator.internal.properties.Getter; +import org.hibernate.validator.internal.properties.javabean.JavaBeanAnnotatedConstrainable; +import org.hibernate.validator.internal.properties.javabean.JavaBeanAnnotatedElement; +import org.hibernate.validator.internal.properties.javabean.JavaBeanExecutable; +import org.hibernate.validator.internal.properties.javabean.JavaBeanField; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; +import org.hibernate.validator.internal.properties.javabean.JavaBeanParameter; import org.hibernate.validator.internal.util.CollectionHelper; -import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ReflectionHelper; -import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; @@ -88,22 +89,17 @@ public class AnnotationMetaDataProvider implements MetaDataProvider { private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - private static final Annotation[] EMPTY_PARAMETER_ANNOTATIONS = new Annotation[0]; - - private final ConstraintHelper constraintHelper; - private final TypeResolutionHelper typeResolutionHelper; + private final ConstraintCreationContext constraintCreationContext; private final AnnotationProcessingOptions annotationProcessingOptions; - private final ValueExtractorManager valueExtractorManager; + private final JavaBeanHelper javaBeanHelper; private final BeanConfiguration objectBeanConfiguration; - public AnnotationMetaDataProvider(ConstraintHelper constraintHelper, - TypeResolutionHelper typeResolutionHelper, - ValueExtractorManager valueExtractorManager, + public AnnotationMetaDataProvider(ConstraintCreationContext constraintCreationContext, + JavaBeanHelper javaBeanHelper, AnnotationProcessingOptions annotationProcessingOptions) { - this.constraintHelper = constraintHelper; - this.typeResolutionHelper = typeResolutionHelper; - this.valueExtractorManager = valueExtractorManager; + this.constraintCreationContext = constraintCreationContext; + this.javaBeanHelper = javaBeanHelper; this.annotationProcessingOptions = annotationProcessingOptions; this.objectBeanConfiguration = retrieveBeanConfiguration( Object.class ); @@ -134,8 +130,6 @@ private BeanConfiguration retrieveBeanConfiguration(Class beanClass) { constrainedElements.addAll( getMethodMetaData( beanClass ) ); constrainedElements.addAll( getConstructorMetaData( beanClass ) ); - //TODO GM: currently class level constraints are represented by a PropertyMetaData. This - //works but seems somewhat unnatural Set> classLevelConstraints = getClassLevelConstraints( beanClass ); if ( !classLevelConstraints.isEmpty() ) { ConstrainedType classLevelMetaData = @@ -196,15 +190,22 @@ private Set> getClassLevelConstraints(Class clazz) { return Collections.emptySet(); } - Set> classLevelConstraints = newHashSet(); + List> classLevelConstraintDescriptors = findConstraints( null, clazz.getDeclaredAnnotations(), + ConstraintLocationKind.TYPE ); - // HV-262 - List> classMetaData = findClassLevelConstraints( clazz ); + if ( classLevelConstraintDescriptors.isEmpty() ) { + return Collections.emptySet(); + } + Set> classLevelConstraints = newHashSet( classLevelConstraintDescriptors.size() ); ConstraintLocation location = ConstraintLocation.forClass( clazz ); - for ( ConstraintDescriptorImpl constraintDescription : classMetaData ) { - classLevelConstraints.add( MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintDescription, location ) ); + for ( ConstraintDescriptorImpl constraintDescriptor : classLevelConstraintDescriptors ) { + classLevelConstraints.add( MetaConstraints.create( constraintCreationContext.getTypeResolutionHelper(), + constraintCreationContext.getValueExtractorManager(), + constraintCreationContext.getConstraintValidatorManager(), + constraintDescriptor, + location ) ); } return classLevelConstraints; @@ -215,47 +216,53 @@ private Set getFieldMetaData(Class beanClass) { for ( Field field : run( GetDeclaredFields.action( beanClass ) ) ) { // HV-172 - if ( Modifier.isStatic( field.getModifiers() ) || - annotationProcessingOptions.areMemberConstraintsIgnoredFor( field ) || - field.isSynthetic() ) { + if ( Modifier.isStatic( field.getModifiers() ) || field.isSynthetic() ) { + continue; + } + + JavaBeanField javaBeanField = javaBeanHelper.field( field ); + if ( annotationProcessingOptions.areMemberConstraintsIgnoredFor( javaBeanField ) ) { continue; } - propertyMetaData.add( findPropertyMetaData( field ) ); + propertyMetaData.add( findPropertyMetaData( javaBeanField ) ); } return propertyMetaData; } - private ConstrainedField findPropertyMetaData(Field field) { + private ConstrainedField findPropertyMetaData(JavaBeanField javaBeanField) { Set> constraints = convertToMetaConstraints( - findConstraints( field, ElementType.FIELD ), - field + findConstraints( javaBeanField, ConstraintLocationKind.FIELD ), + javaBeanField ); - CascadingMetaDataBuilder cascadingMetaDataBuilder = findCascadingMetaData( field ); - Set> typeArgumentsConstraints = findTypeAnnotationConstraints( field ); + CascadingMetaDataBuilder cascadingMetaDataBuilder = findCascadingMetaData( javaBeanField ); + Set> typeArgumentsConstraints = findTypeAnnotationConstraints( javaBeanField ); return new ConstrainedField( ConfigurationSource.ANNOTATION, - field, + javaBeanField, constraints, typeArgumentsConstraints, cascadingMetaDataBuilder ); } - private Set> convertToMetaConstraints(List> constraintDescriptors, Field field) { + private Set> convertToMetaConstraints(List> constraintDescriptors, JavaBeanField javaBeanField) { if ( constraintDescriptors.isEmpty() ) { return Collections.emptySet(); } - Set> constraints = newHashSet(); + Set> constraints = newHashSet( constraintDescriptors.size() ); - ConstraintLocation location = ConstraintLocation.forField( field ); + ConstraintLocation location = ConstraintLocation.forField( javaBeanField ); for ( ConstraintDescriptorImpl constraintDescription : constraintDescriptors ) { - constraints.add( MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintDescription, location ) ); + constraints.add( MetaConstraints.create( constraintCreationContext.getTypeResolutionHelper(), + constraintCreationContext.getValueExtractorManager(), + constraintCreationContext.getConstraintValidatorManager(), + constraintDescription, location ) ); } return constraints; } @@ -297,20 +304,22 @@ private Set getMetaData(Executable[] executableElements) * given element. */ private ConstrainedExecutable findExecutableMetaData(Executable executable) { - List parameterConstraints = getParameterMetaData( executable ); + JavaBeanExecutable javaBeanExecutable = javaBeanHelper.executable( executable ); + List parameterConstraints = getParameterMetaData( javaBeanExecutable ); - Map>> executableConstraints = findConstraints( executable, ExecutableHelper.getElementType( executable ) ) - .stream() - .collect( Collectors.groupingBy( ConstraintDescriptorImpl::getConstraintType ) ); + Map>> executableConstraints = findConstraints( + javaBeanExecutable, + ConstraintLocationKind.of( javaBeanExecutable.getConstrainedElementKind() ) + ).stream().collect( Collectors.groupingBy( ConstraintDescriptorImpl::getConstraintType ) ); Set> crossParameterConstraints; - if ( annotationProcessingOptions.areCrossParameterConstraintsIgnoredFor( executable ) ) { + if ( annotationProcessingOptions.areCrossParameterConstraintsIgnoredFor( javaBeanExecutable ) ) { crossParameterConstraints = Collections.emptySet(); } else { crossParameterConstraints = convertToMetaConstraints( executableConstraints.get( ConstraintType.CROSS_PARAMETER ), - executable + javaBeanExecutable ); } @@ -318,25 +327,23 @@ private ConstrainedExecutable findExecutableMetaData(Executable executable) { Set> typeArgumentsConstraints; CascadingMetaDataBuilder cascadingMetaDataBuilder; - if ( annotationProcessingOptions.areReturnValueConstraintsIgnoredFor( executable ) ) { + if ( annotationProcessingOptions.areReturnValueConstraintsIgnoredFor( javaBeanExecutable ) ) { returnValueConstraints = Collections.emptySet(); typeArgumentsConstraints = Collections.emptySet(); cascadingMetaDataBuilder = CascadingMetaDataBuilder.nonCascading(); } else { - AnnotatedType annotatedReturnType = executable.getAnnotatedReturnType(); - - typeArgumentsConstraints = findTypeAnnotationConstraints( executable, annotatedReturnType ); + typeArgumentsConstraints = findTypeAnnotationConstraints( javaBeanExecutable ); returnValueConstraints = convertToMetaConstraints( executableConstraints.get( ConstraintType.GENERIC ), - executable + javaBeanExecutable ); - cascadingMetaDataBuilder = findCascadingMetaData( executable, annotatedReturnType ); + cascadingMetaDataBuilder = findCascadingMetaData( javaBeanExecutable ); } return new ConstrainedExecutable( ConfigurationSource.ANNOTATION, - executable, + javaBeanExecutable, parameterConstraints, crossParameterConstraints, returnValueConstraints, @@ -345,19 +352,23 @@ private ConstrainedExecutable findExecutableMetaData(Executable executable) { ); } - private Set> convertToMetaConstraints(List> constraintsDescriptors, Executable executable) { - if ( constraintsDescriptors == null ) { + private Set> convertToMetaConstraints(List> constraintDescriptors, Callable callable) { + if ( constraintDescriptors == null || constraintDescriptors.isEmpty() ) { return Collections.emptySet(); } - Set> constraints = newHashSet(); + Set> constraints = newHashSet( constraintDescriptors.size() ); - ConstraintLocation returnValueLocation = ConstraintLocation.forReturnValue( executable ); - ConstraintLocation crossParameterLocation = ConstraintLocation.forCrossParameter( executable ); + ConstraintLocation returnValueLocation = ConstraintLocation.forReturnValue( callable ); + ConstraintLocation crossParameterLocation = ConstraintLocation.forCrossParameter( callable ); - for ( ConstraintDescriptorImpl constraintDescriptor : constraintsDescriptors ) { - ConstraintLocation location = constraintDescriptor.getConstraintType() == ConstraintType.GENERIC ? returnValueLocation : crossParameterLocation; - constraints.add( MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintDescriptor, location ) ); + for ( ConstraintDescriptorImpl constraintDescriptor : constraintDescriptors ) { + ConstraintLocation location = constraintDescriptor.getConstraintType() == ConstraintType.GENERIC + ? returnValueLocation + : crossParameterLocation; + constraints.add( MetaConstraints.create( constraintCreationContext.getTypeResolutionHelper(), + constraintCreationContext.getValueExtractorManager(), + constraintCreationContext.getConstraintValidatorManager(), constraintDescriptor, location ) ); } return constraints; @@ -367,41 +378,29 @@ private Set> convertToMetaConstraints(List getParameterMetaData(Executable executable) { - if ( executable.getParameterCount() == 0 ) { + private List getParameterMetaData(JavaBeanExecutable javaBeanExecutable) { + if ( !javaBeanExecutable.hasParameters() ) { return Collections.emptyList(); } - Parameter[] parameters = executable.getParameters(); + List parameters = javaBeanExecutable.getParameters(); - List metaData = new ArrayList<>( parameters.length ); + List metaData = new ArrayList<>( parameters.size() ); int i = 0; - for ( Parameter parameter : parameters ) { - Annotation[] parameterAnnotations; - try { - parameterAnnotations = parameter.getAnnotations(); - } - catch (ArrayIndexOutOfBoundsException ex) { - LOG.warn( MESSAGES.constraintOnConstructorOfNonStaticInnerClass(), ex ); - parameterAnnotations = EMPTY_PARAMETER_ANNOTATIONS; - } - - Set> parameterConstraints = newHashSet(); - - if ( annotationProcessingOptions.areParameterConstraintsIgnoredFor( executable, i ) ) { - Type type = ReflectionHelper.typeOf( executable, i ); + for ( JavaBeanParameter parameter : parameters ) { + if ( annotationProcessingOptions.areParameterConstraintsIgnoredFor( javaBeanExecutable, i ) ) { metaData.add( new ConstrainedParameter( ConfigurationSource.ANNOTATION, - executable, - type, + javaBeanExecutable, + parameter.getGenericType(), i, - parameterConstraints, + Collections.emptySet(), Collections.emptySet(), CascadingMetaDataBuilder.nonCascading() ) @@ -410,30 +409,33 @@ private List getParameterMetaData(Executable executable) { continue; } - ConstraintLocation location = ConstraintLocation.forParameter( executable, i ); + List> constraintDescriptors = findConstraints( javaBeanExecutable, parameter, ConstraintLocationKind.PARAMETER ); + Set> parameterConstraints; - for ( Annotation parameterAnnotation : parameterAnnotations ) { - // collect constraints if this annotation is a constraint annotation - List> constraints = findConstraintAnnotations( - executable, parameterAnnotation, ElementType.PARAMETER - ); - for ( ConstraintDescriptorImpl constraintDescriptorImpl : constraints ) { + if ( !constraintDescriptors.isEmpty() ) { + parameterConstraints = newHashSet( constraintDescriptors.size() ); + ConstraintLocation location = ConstraintLocation.forParameter( javaBeanExecutable, i ); + + for ( ConstraintDescriptorImpl constraintDescriptorImpl : constraintDescriptors ) { parameterConstraints.add( - MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintDescriptorImpl, location ) - ); + MetaConstraints.create( constraintCreationContext.getTypeResolutionHelper(), + constraintCreationContext.getValueExtractorManager(), + constraintCreationContext.getConstraintValidatorManager(), constraintDescriptorImpl, + location ) ); } } + else { + parameterConstraints = Collections.emptySet(); + } - AnnotatedType parameterAnnotatedType = parameter.getAnnotatedType(); - - Set> typeArgumentsConstraints = findTypeAnnotationConstraintsForExecutableParameter( executable, i, parameterAnnotatedType ); - CascadingMetaDataBuilder cascadingMetaData = findCascadingMetaData( executable, parameters, i, parameterAnnotatedType ); + Set> typeArgumentsConstraints = findTypeAnnotationConstraintsForExecutableParameter( javaBeanExecutable, parameter ); + CascadingMetaDataBuilder cascadingMetaData = findCascadingMetaData( parameter ); metaData.add( new ConstrainedParameter( ConfigurationSource.ANNOTATION, - executable, - ReflectionHelper.typeOf( executable, i ), + javaBeanExecutable, + parameter.getGenericType(), i, parameterConstraints, typeArgumentsConstraints, @@ -447,43 +449,67 @@ private List getParameterMetaData(Executable executable) { } /** - * Finds all constraint annotations defined for the given member and returns them in a list of + * Finds all constraint annotations defined for the given constrainable and returns them in a list of * constraint descriptors. * - * @param member The member to check for constraints annotations. - * @param type The element type the constraint/annotation is placed on. + * @param constrainable The constrainable to check for constraint annotations. + * @param kind The constraint location kind. * * @return A list of constraint descriptors for all constraint specified for the given member. */ - private List> findConstraints(Member member, ElementType type) { + private List> findConstraints(JavaBeanAnnotatedConstrainable constrainable, ConstraintLocationKind kind) { + return findConstraints( constrainable, constrainable, kind ); + } + + /** + * Finds all constraint annotations defined for the given constrainable and returns them in a list of constraint + * descriptors. + * + * @param constrainable The constrainable element (will be the executable for a method parameter). + * @param annotatedElement The annotated element. Usually the same as the constrainable except in the case of method + * parameters constraints when it is the parameter. + * @param kind The constraint location kind. + * + * @return A list of constraint descriptors for all constraint specified for the given member. + */ + private List> findConstraints(Constrainable constrainable, JavaBeanAnnotatedElement annotatedElement, + ConstraintLocationKind kind) { List> metaData = newArrayList(); - for ( Annotation annotation : ( (AccessibleObject) member ).getDeclaredAnnotations() ) { - metaData.addAll( findConstraintAnnotations( member, annotation, type ) ); + for ( Annotation annotation : annotatedElement.getDeclaredAnnotations() ) { + metaData.addAll( findConstraintAnnotations( constrainable, annotation, kind ) ); } return metaData; } /** - * Finds all constraint annotations defined for the given class and returns them in a list of - * constraint descriptors. + * Finds all constraint annotations defined for the given constrainable and returns them in a list of constraint + * descriptors. * - * @param beanClass The class to check for constraints annotations. + * @param constrainable The constrainable element (will be the executable for a method parameter). + * @param annotations The annotations. + * @param kind The constraint location kind. * - * @return A list of constraint descriptors for all constraint specified on the given class. + * @return A list of constraint descriptors for all constraint specified for the given member. */ - private List> findClassLevelConstraints(Class beanClass) { + private List> findConstraints(Constrainable constrainable, Annotation[] annotations, + ConstraintLocationKind kind) { + if ( annotations.length == 0 ) { + return Collections.emptyList(); + } + List> metaData = newArrayList(); - for ( Annotation annotation : beanClass.getDeclaredAnnotations() ) { - metaData.addAll( findConstraintAnnotations( null, annotation, ElementType.TYPE ) ); + for ( Annotation annotation : annotations ) { + metaData.addAll( findConstraintAnnotations( constrainable, annotation, kind ) ); } + return metaData; } /** * Examines the given annotation to see whether it is a single- or multi-valued constraint annotation. * - * @param member The member to check for constraints annotations + * @param constrainable The constrainable to check for constraints annotations * @param annotation The annotation to examine * @param type the element type on which the annotation/constraint is placed on * @param the annotation type @@ -491,39 +517,44 @@ private List> findClassLevelConstraints(Class bea * @return A list of constraint descriptors or the empty list in case {@code annotation} is neither a * single nor multi-valued annotation. */ - protected List> findConstraintAnnotations(Member member, + protected List> findConstraintAnnotations( + Constrainable constrainable, A annotation, - ElementType type) { + ConstraintLocationKind type) { // HV-1049 and HV-1311 - Ignore annotations from the JDK (jdk.internal.* and java.*); They cannot be constraint // annotations so skip them right here, as for the proper check we'd need package access permission for // "jdk.internal" and "java". - if ( constraintHelper.isJdkAnnotation( annotation.annotationType() ) ) { + if ( constraintCreationContext.getConstraintHelper().isJdkAnnotation( annotation.annotationType() ) ) { return Collections.emptyList(); } List constraints = newArrayList(); Class annotationType = annotation.annotationType(); - if ( constraintHelper.isConstraintAnnotation( annotationType ) ) { + if ( constraintCreationContext.getConstraintHelper().isConstraintAnnotation( annotationType ) ) { constraints.add( annotation ); } - else if ( constraintHelper.isMultiValueConstraint( annotationType ) ) { - constraints.addAll( constraintHelper.getConstraintsFromMultiValueConstraint( annotation ) ); + else if ( constraintCreationContext.getConstraintHelper().isMultiValueConstraint( annotationType ) ) { + constraints.addAll( constraintCreationContext.getConstraintHelper().getConstraintsFromMultiValueConstraint( annotation ) ); } return constraints.stream() - .map( c -> buildConstraintDescriptor( member, c, type ) ) + .map( c -> buildConstraintDescriptor( constrainable, c, type ) ) .collect( Collectors.toList() ); } - private Map, Class> getGroupConversions(AnnotatedElement annotatedElement) { + private Map, Class> getGroupConversions(AnnotatedType annotatedType) { return getGroupConversions( - annotatedElement.getAnnotation( ConvertGroup.class ), - annotatedElement.getAnnotation( ConvertGroup.List.class ) + annotatedType.getAnnotation( ConvertGroup.class ), + annotatedType.getAnnotation( ConvertGroup.List.class ) ); } private Map, Class> getGroupConversions(ConvertGroup groupConversion, ConvertGroup.List groupConversionList) { + if ( groupConversion == null && ( groupConversionList == null || groupConversionList.value().length == 0 ) ) { + return Collections.emptyMap(); + } + Map, Class> groupConversions = newHashMap(); if ( groupConversion != null ) { @@ -549,12 +580,12 @@ private Map, Class> getGroupConversions(ConvertGroup groupConversion return groupConversions; } - private ConstraintDescriptorImpl buildConstraintDescriptor(Member member, + private ConstraintDescriptorImpl buildConstraintDescriptor(Constrainable constrainable, A annotation, - ElementType type) { + ConstraintLocationKind type) { return new ConstraintDescriptorImpl<>( - constraintHelper, - member, + constraintCreationContext.getConstraintHelper(), + constrainable, new ConstraintAnnotationDescriptor<>( annotation ), type ); @@ -573,35 +604,31 @@ private T run(PrivilegedAction action) { /** * Finds type arguments constraints for fields. */ - protected Set> findTypeAnnotationConstraints(Field field) { + protected Set> findTypeAnnotationConstraints(JavaBeanField javaBeanField) { return findTypeArgumentsConstraints( - field, - new TypeArgumentFieldLocation( field ), - field.getAnnotatedType() + javaBeanField, + new TypeArgumentFieldLocation( javaBeanField ), + javaBeanField.getAnnotatedType() ); } /** * Finds type arguments constraints for method return values. */ - protected Set> findTypeAnnotationConstraints(Executable executable, AnnotatedType annotatedReturnType) { + protected Set> findTypeAnnotationConstraints(JavaBeanExecutable javaBeanExecutable) { return findTypeArgumentsConstraints( - executable, - new TypeArgumentReturnValueLocation( executable ), - annotatedReturnType + javaBeanExecutable, + new TypeArgumentReturnValueLocation( javaBeanExecutable ), + javaBeanExecutable.getAnnotatedType() ); } - private CascadingMetaDataBuilder findCascadingMetaData(Executable executable, Parameter[] parameters, int i, AnnotatedType parameterAnnotatedType) { - Parameter parameter = parameters[i]; - TypeVariable[] typeParameters = parameter.getType().getTypeParameters(); - - Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData = getTypeParametersCascadingMetadata( parameterAnnotatedType, - typeParameters ); + private CascadingMetaDataBuilder findCascadingMetaData(JavaBeanParameter javaBeanParameter) { + Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData = getTypeParametersCascadingMetadata( javaBeanParameter.getAnnotatedType(), + javaBeanParameter.getTypeParameters() ); try { - return getCascadingMetaData( ReflectionHelper.typeOf( parameter.getDeclaringExecutable(), i ), - parameter, containerElementTypesCascadingMetaData ); + return getCascadingMetaData( javaBeanParameter, containerElementTypesCascadingMetaData ); } catch (ArrayIndexOutOfBoundsException ex) { LOG.warn( MESSAGES.constraintOnConstructorOfNonStaticInnerClass(), ex ); @@ -609,29 +636,19 @@ private CascadingMetaDataBuilder findCascadingMetaData(Executable executable, Pa } } - private CascadingMetaDataBuilder findCascadingMetaData(Field field) { - TypeVariable[] typeParameters = field.getType().getTypeParameters(); - AnnotatedType annotatedType = field.getAnnotatedType(); - - Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData = getTypeParametersCascadingMetadata( annotatedType, typeParameters ); + private CascadingMetaDataBuilder findCascadingMetaData(JavaBeanField javaBeanField) { + Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData = getTypeParametersCascadingMetadata( + javaBeanField.getAnnotatedType(), + javaBeanField.getTypeParameters() ); - return getCascadingMetaData( ReflectionHelper.typeOf( field ), field, containerElementTypesCascadingMetaData ); + return getCascadingMetaData( javaBeanField, containerElementTypesCascadingMetaData ); } - private CascadingMetaDataBuilder findCascadingMetaData(Executable executable, AnnotatedType annotatedReturnType) { - TypeVariable[] typeParameters; - - if ( executable instanceof Method ) { - typeParameters = ( (Method) executable ).getReturnType().getTypeParameters(); - } - else { - typeParameters = ( (Constructor) executable ).getDeclaringClass().getTypeParameters(); - } + private CascadingMetaDataBuilder findCascadingMetaData(JavaBeanExecutable javaBeanExecutable) { + Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData = getTypeParametersCascadingMetadata( javaBeanExecutable.getAnnotatedType(), + javaBeanExecutable.getTypeParameters() ); - Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData = getTypeParametersCascadingMetadata( annotatedReturnType, - typeParameters ); - - return getCascadingMetaData( ReflectionHelper.typeOf( executable ), executable, containerElementTypesCascadingMetaData ); + return getCascadingMetaData( javaBeanExecutable, containerElementTypesCascadingMetaData ); } private Map, CascadingMetaDataBuilder> getTypeParametersCascadingMetadata(AnnotatedType annotatedType, @@ -702,17 +719,17 @@ else if ( annotatedType instanceof AnnotatedParameterizedType ) { /** * Finds type arguments constraints for parameters. * - * @param executable the executable - * @param i the parameter index + * @param javaBeanParameter the parameter * * @return a set of type arguments constraints, or an empty set if no constrained type arguments are found */ - protected Set> findTypeAnnotationConstraintsForExecutableParameter(Executable executable, int i, AnnotatedType parameterAnnotatedType) { + protected Set> findTypeAnnotationConstraintsForExecutableParameter(JavaBeanExecutable javaBeanExecutable, + JavaBeanParameter javaBeanParameter) { try { return findTypeArgumentsConstraints( - executable, - new TypeArgumentExecutableParameterLocation( executable, i ), - parameterAnnotatedType + javaBeanExecutable, + new TypeArgumentExecutableParameterLocation( javaBeanExecutable, javaBeanParameter.getIndex() ), + javaBeanParameter.getAnnotatedType() ); } catch (ArrayIndexOutOfBoundsException ex) { @@ -721,7 +738,7 @@ protected Set> findTypeAnnotationConstraintsForExecutableParam } } - private Set> findTypeArgumentsConstraints(Member member, TypeArgumentLocation location, AnnotatedType annotatedType) { + private Set> findTypeArgumentsConstraints(Constrainable constrainable, TypeArgumentLocation location, AnnotatedType annotatedType) { // HV-1428 Container element support is disabled for arrays if ( !(annotatedType instanceof AnnotatedParameterizedType) ) { return Collections.emptySet(); @@ -735,9 +752,9 @@ private Set> findTypeArgumentsConstraints(Member member, TypeA Type validatedType = annotatedArrayType.getAnnotatedGenericComponentType().getType(); TypeVariable arrayElementTypeArgument = new ArrayElement( annotatedArrayType ); - typeArgumentConstraints.addAll( findTypeUseConstraints( member, annotatedArrayType, arrayElementTypeArgument, location, validatedType ) ); + typeArgumentConstraints.addAll( findTypeUseConstraints( constrainable, annotatedArrayType, arrayElementTypeArgument, location, validatedType ) ); - typeArgumentConstraints.addAll( findTypeArgumentsConstraints( member, + typeArgumentConstraints.addAll( findTypeArgumentsConstraints( constrainable, new NestedTypeArgumentLocation( location, arrayElementTypeArgument, validatedType ), annotatedArrayType.getAnnotatedGenericComponentType() ) ); } @@ -755,10 +772,10 @@ else if ( annotatedType instanceof AnnotatedParameterizedType ) { // In the latter case a value unwrapping has to occur Type validatedType = annotatedTypeParameter.getType(); - typeArgumentConstraints.addAll( findTypeUseConstraints( member, annotatedTypeParameter, typeVariable, location, validatedType ) ); + typeArgumentConstraints.addAll( findTypeUseConstraints( constrainable, annotatedTypeParameter, typeVariable, location, validatedType ) ); if ( validatedType instanceof ParameterizedType ) { - typeArgumentConstraints.addAll( findTypeArgumentsConstraints( member, + typeArgumentConstraints.addAll( findTypeArgumentsConstraints( constrainable, new NestedTypeArgumentLocation( location, typeVariable, validatedType ), annotatedTypeParameter ) ); } @@ -773,35 +790,39 @@ else if ( annotatedType instanceof AnnotatedParameterizedType ) { /** * Finds type use annotation constraints defined on the type argument. */ - private Set> findTypeUseConstraints(Member member, AnnotatedType typeArgument, TypeVariable typeVariable, TypeArgumentLocation location, Type type) { - Set> constraints = Arrays.stream( typeArgument.getAnnotations() ) - .flatMap( a -> findConstraintAnnotations( member, a, ElementType.TYPE_USE ).stream() ) - .map( d -> createTypeArgumentMetaConstraint( d, location, typeVariable, type ) ) - .collect( Collectors.toSet() ); + private Set> findTypeUseConstraints(Constrainable constrainable, AnnotatedType typeArgument, TypeVariable typeVariable, + TypeArgumentLocation location, Type type) { + List> constraintDescriptors = findConstraints( constrainable, typeArgument.getAnnotations(), ConstraintLocationKind.TYPE_USE ); - return constraints; - } + if ( constraintDescriptors.isEmpty() ) { + return Collections.emptySet(); + } - /** - * Creates a {@code MetaConstraint} for a type argument constraint. - */ - private MetaConstraint createTypeArgumentMetaConstraint(ConstraintDescriptorImpl descriptor, TypeArgumentLocation location, - TypeVariable typeVariable, Type type) { + Set> constraints = newHashSet( constraintDescriptors.size() ); ConstraintLocation constraintLocation = ConstraintLocation.forTypeArgument( location.toConstraintLocation(), typeVariable, type ); - return MetaConstraints.create( typeResolutionHelper, valueExtractorManager, descriptor, constraintLocation ); + + for ( ConstraintDescriptorImpl constraintDescriptor : constraintDescriptors ) { + constraints.add( MetaConstraints.create( constraintCreationContext.getTypeResolutionHelper(), + constraintCreationContext.getValueExtractorManager(), + constraintCreationContext.getConstraintValidatorManager(), constraintDescriptor, + constraintLocation ) ); + } + + return constraints; } - private CascadingMetaDataBuilder getCascadingMetaData(Type type, AnnotatedElement annotatedElement, + private CascadingMetaDataBuilder getCascadingMetaData(JavaBeanAnnotatedElement annotatedElement, Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData) { - return CascadingMetaDataBuilder.annotatedObject( type, annotatedElement.isAnnotationPresent( Valid.class ), containerElementTypesCascadingMetaData, - getGroupConversions( annotatedElement ) ); + return CascadingMetaDataBuilder.annotatedObject( annotatedElement.getType(), annotatedElement.isAnnotationPresent( Valid.class ), + containerElementTypesCascadingMetaData, getGroupConversions( annotatedElement.getAnnotatedType() ) ); } /** * The location of a type argument before it is really considered a constraint location. *

* It avoids initializing a constraint location if we did not find any constraints. This is especially useful in - * a Java 9 environment as {@link ConstraintLocation#forProperty(Member) tries to make the {@code Member} accessible + * a Java 9 environment as {@link ConstraintLocation#forField(org.hibernate.validator.internal.properties.Field)} + * or {@link ConstraintLocation#forGetter(Getter)} tries to make the {@code Member} accessible * which might not be possible (for instance for {@code java.util} classes). */ private interface TypeArgumentLocation { @@ -809,44 +830,44 @@ private interface TypeArgumentLocation { } private static class TypeArgumentExecutableParameterLocation implements TypeArgumentLocation { - private final Executable executable; + private final JavaBeanExecutable javaBeanExecutable; private final int index; - private TypeArgumentExecutableParameterLocation(Executable executable, int index) { - this.executable = executable; + private TypeArgumentExecutableParameterLocation(JavaBeanExecutable javaBeanExecutable, int index) { + this.javaBeanExecutable = javaBeanExecutable; this.index = index; } @Override public ConstraintLocation toConstraintLocation() { - return ConstraintLocation.forParameter( executable, index ); + return ConstraintLocation.forParameter( javaBeanExecutable, index ); } } private static class TypeArgumentFieldLocation implements TypeArgumentLocation { - private final Field field; + private final JavaBeanField javaBeanField; - private TypeArgumentFieldLocation(Field field) { - this.field = field; + private TypeArgumentFieldLocation(JavaBeanField javaBeanField) { + this.javaBeanField = javaBeanField; } @Override public ConstraintLocation toConstraintLocation() { - return ConstraintLocation.forField( field ); + return ConstraintLocation.forField( javaBeanField ); } } private static class TypeArgumentReturnValueLocation implements TypeArgumentLocation { - private final Executable executable; + private final JavaBeanExecutable javaBeanExecutable; - private TypeArgumentReturnValueLocation(Executable executable) { - this.executable = executable; + private TypeArgumentReturnValueLocation(JavaBeanExecutable javaBeanExecutable) { + this.javaBeanExecutable = javaBeanExecutable; } @Override public ConstraintLocation toConstraintLocation() { - return ConstraintLocation.forReturnValue( executable ); + return ConstraintLocation.forReturnValue( javaBeanExecutable ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/ProgrammaticMetaDataProvider.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/ProgrammaticMetaDataProvider.java index f9f293de31..3d3d8828e7 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/ProgrammaticMetaDataProvider.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/ProgrammaticMetaDataProvider.java @@ -14,14 +14,12 @@ import java.util.Set; import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping; -import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.engine.ConstraintCreationContext; 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.raw.BeanConfiguration; 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; @@ -40,14 +38,12 @@ public class ProgrammaticMetaDataProvider implements MetaDataProvider { private final Map> configuredBeans; private final AnnotationProcessingOptions annotationProcessingOptions; - public ProgrammaticMetaDataProvider(ConstraintHelper constraintHelper, - TypeResolutionHelper typeResolutionHelper, - ValueExtractorManager valueExtractorManager, + public ProgrammaticMetaDataProvider(ConstraintCreationContext constraintCreationContext, Set constraintMappings) { Contracts.assertNotNull( constraintMappings ); configuredBeans = CollectionHelper.toImmutableMap( - createBeanConfigurations( constraintMappings, constraintHelper, typeResolutionHelper, valueExtractorManager ) + createBeanConfigurations( constraintMappings, constraintCreationContext ) ); assertUniquenessOfConfiguredTypes( constraintMappings ); @@ -68,12 +64,11 @@ private static void assertUniquenessOfConfiguredTypes(Set> createBeanConfigurations(Set mappings, ConstraintHelper constraintHelper, - TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager) { + private static Map> createBeanConfigurations(Set mappings, + ConstraintCreationContext constraintCreationContext) { final Map> configuredBeans = new HashMap<>(); for ( DefaultConstraintMapping mapping : mappings ) { - Set> beanConfigurations = mapping.getBeanConfigurations( constraintHelper, typeResolutionHelper, - valueExtractorManager ); + Set> beanConfigurations = mapping.getBeanConfigurations( constraintCreationContext ); for ( BeanConfiguration beanConfiguration : beanConfigurations ) { configuredBeans.put( beanConfiguration.getBeanClass().getName(), beanConfiguration ); @@ -89,7 +84,7 @@ private static Map> createBeanConfigurations(Set mappingStreams, ClassLoader externalClassLoader) { - MappingXmlParser mappingParser = new MappingXmlParser( constraintHelper, typeResolutionHelper, valueExtractorManager, - externalClassLoader ); + MappingXmlParser mappingParser = new MappingXmlParser( constraintCreationContext, + javaBeanHelper, externalClassLoader ); mappingParser.parse( mappingStreams ); configuredBeans = CollectionHelper.toImmutableMap( createBeanConfigurations( mappingParser ) ); 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 fc7114ab4e..a1b1c456fd 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 @@ -40,16 +40,6 @@ */ public interface ConstrainedElement extends Iterable> { - /** - * The kind of a {@link ConstrainedElement}. Can be used to determine an - * element's type when traversing over a collection of constrained elements. - * - * @author Gunnar Morling - */ - enum ConstrainedElementKind { - TYPE, FIELD, CONSTRUCTOR, METHOD, PARAMETER - } - /** * Returns the kind of this constrained element. * @@ -91,4 +81,28 @@ enum ConstrainedElementKind { * Returns the configuration source contributing this constrained element. */ ConfigurationSource getSource(); + + /** + * The kind of a {@link ConstrainedElement}. Can be used to determine an + * element's type when traversing over a collection of constrained elements. + * + * @author Gunnar Morling + */ + enum ConstrainedElementKind { + + TYPE, + FIELD, + CONSTRUCTOR, + METHOD, + PARAMETER, + GETTER; + + public boolean isExecutable() { + return this == CONSTRUCTOR || isMethod(); + } + + public boolean isMethod() { + return this == METHOD || this == GETTER; + } + } } 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 8069e49cd3..2d9a612929 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 @@ -10,8 +10,6 @@ import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; import java.lang.invoke.MethodHandles; -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -21,9 +19,8 @@ import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.properties.Callable; import org.hibernate.validator.internal.util.CollectionHelper; -import org.hibernate.validator.internal.util.ReflectionHelper; -import org.hibernate.validator.internal.util.StringHelper; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; import org.hibernate.validator.internal.util.stereotypes.Immutable; @@ -40,7 +37,7 @@ public class ConstrainedExecutable extends AbstractConstrainedElement { private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - private final Executable executable; + private final Callable callable; /** * Constrained-related meta data for this executable's parameters. @@ -53,13 +50,11 @@ public class ConstrainedExecutable extends AbstractConstrainedElement { @Immutable private final Set> crossParameterConstraints; - private final boolean isGetterMethod; - /** * Creates a new executable meta data object for a parameter-less executable. * * @param source The source of meta data. - * @param executable The represented executable. + * @param callable The represented executable. * @param returnValueConstraints Type arguments constraints, if any. * @param typeArgumentConstraints The type argument constraints on the return value of the represented executable, * if any. @@ -67,13 +62,13 @@ public class ConstrainedExecutable extends AbstractConstrainedElement { */ public ConstrainedExecutable( ConfigurationSource source, - Executable executable, + Callable callable, Set> returnValueConstraints, Set> typeArgumentConstraints, CascadingMetaDataBuilder cascadingMetaDataBuilder) { this( source, - executable, + callable, Collections.emptyList(), Collections.>emptySet(), returnValueConstraints, @@ -86,7 +81,7 @@ public ConstrainedExecutable( * Creates a new executable meta data object. * * @param source The source of meta data. - * @param executable The represented executable. + * @param callable The represented executable. * @param parameterMetaData A list with parameter meta data. The length must correspond with the number of * parameters of the represented executable. So this list may be empty (in case of a parameterless executable), but * never {@code null}. @@ -98,7 +93,7 @@ public ConstrainedExecutable( */ public ConstrainedExecutable( ConfigurationSource source, - Executable executable, + Callable callable, List parameterMetaData, Set> crossParameterConstraints, Set> returnValueConstraints, @@ -106,18 +101,18 @@ public ConstrainedExecutable( CascadingMetaDataBuilder cascadingMetaDataBuilder) { super( source, - ( executable instanceof Constructor ) ? ConstrainedElementKind.CONSTRUCTOR : ConstrainedElementKind.METHOD, + callable.getConstrainedElementKind(), returnValueConstraints, typeArgumentConstraints, cascadingMetaDataBuilder ); - this.executable = executable; + this.callable = callable; - if ( parameterMetaData.size() != executable.getParameterTypes().length ) { + if ( parameterMetaData.size() != callable.getParameterCount() ) { throw LOG.getInvalidLengthOfParameterMetaDataListException( - executable, - executable.getParameterTypes().length, + callable, + callable.getParameterCount(), parameterMetaData.size() ); } @@ -125,7 +120,6 @@ public ConstrainedExecutable( this.crossParameterConstraints = CollectionHelper.toImmutableSet( crossParameterConstraints ); this.parameterMetaData = CollectionHelper.toImmutableList( parameterMetaData ); this.hasParameterConstraints = hasParameterConstraints( parameterMetaData ) || !crossParameterConstraints.isEmpty(); - this.isGetterMethod = ReflectionHelper.isGetterMethod( executable ); } /** @@ -142,7 +136,7 @@ public ConstrainedExecutable( public ConstrainedParameter getParameterMetaData(int parameterIndex) { if ( parameterIndex < 0 || parameterIndex > parameterMetaData.size() - 1 ) { throw LOG.getInvalidExecutableParameterIndexException( - executable, + callable, parameterIndex ); } @@ -192,23 +186,13 @@ public boolean hasParameterConstraints() { return hasParameterConstraints; } - /** - * Whether the represented executable is a JavaBeans getter executable or not. - * - * @return {@code True}, if this executable is a getter method, {@code false} - * otherwise. - */ - public boolean isGetterMethod() { - return isGetterMethod; - } - - public Executable getExecutable() { - return executable; + public Callable getCallable() { + return callable; } @Override public String toString() { - return "ConstrainedExecutable [executable=" + StringHelper.toShortString( executable ) + return "ConstrainedExecutable [executable=" + callable + ", parameterMetaData=" + parameterMetaData + ", hasParameterConstraints=" + hasParameterConstraints + "]"; } @@ -284,7 +268,7 @@ public ConstrainedExecutable merge(ConstrainedExecutable other) { return new ConstrainedExecutable( mergedSource, - executable, + callable, mergedParameterMetaData, mergedCrossParameterConstraints, mergedReturnValueConstraints, @@ -307,8 +291,7 @@ private Set> getDescriptors(Iterable> public int hashCode() { final int prime = 31; int result = super.hashCode(); - result = prime * result - + ( ( executable == null ) ? 0 : executable.hashCode() ); + result = prime * result + callable.hashCode(); return result; } @@ -324,12 +307,7 @@ public boolean equals(Object obj) { return false; } ConstrainedExecutable other = (ConstrainedExecutable) obj; - if ( executable == null ) { - if ( other.executable != null ) { - return false; - } - } - else if ( !executable.equals( other.executable ) ) { + if ( !callable.equals( other.callable ) ) { return false; } return true; 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 cc889ae089..143ad0fcc0 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 @@ -6,12 +6,11 @@ */ package org.hibernate.validator.internal.metadata.raw; -import java.lang.reflect.Field; 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.util.StringHelper; +import org.hibernate.validator.internal.properties.Field; /** * Represents a field of a Java type and all its associated meta-data relevant @@ -19,6 +18,7 @@ * * @author Gunnar Morling * @author Guillaume Smet + * @author Marko Bekhta */ public class ConstrainedField extends AbstractConstrainedElement { @@ -34,30 +34,29 @@ public class ConstrainedField extends AbstractConstrainedElement { * @param cascadingMetaDataBuilder The cascaded validation metadata for this element and its container elements. */ public ConstrainedField(ConfigurationSource source, - Field field, - Set> constraints, - Set> typeArgumentConstraints, - CascadingMetaDataBuilder cascadingMetaDataBuilder) { + Field field, + Set> constraints, + Set> typeArgumentConstraints, + CascadingMetaDataBuilder cascadingMetaDataBuilder) { super( source, ConstrainedElementKind.FIELD, constraints, typeArgumentConstraints, cascadingMetaDataBuilder ); this.field = field; + } public Field getField() { return field; } - @Override public String toString() { - return "ConstrainedField [field=" + StringHelper.toShortString( field ) + "]"; + return "ConstrainedField [field=" + field.getName() + "]"; } @Override public int hashCode() { - final int prime = 31; int result = super.hashCode(); - result = prime * result + ( ( field == null ) ? 0 : field.hashCode() ); + result = 31 * result + this.field.hashCode(); return result; } @@ -73,14 +72,6 @@ public boolean equals(Object obj) { return false; } ConstrainedField other = (ConstrainedField) obj; - if ( field == null ) { - if ( other.field != null ) { - return false; - } - } - else if ( !field.equals( other.field ) ) { - return false; - } - return true; + return this.field.equals( other.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 d8d161d764..dab9ac417c 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 @@ -8,7 +8,6 @@ import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; -import java.lang.reflect.Executable; import java.lang.reflect.Type; import java.util.Collections; import java.util.HashSet; @@ -16,6 +15,7 @@ import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.properties.Callable; /** * Contains constraint-related meta-data for one method parameter. @@ -25,17 +25,17 @@ */ public class ConstrainedParameter extends AbstractConstrainedElement { - private final Executable executable; + private final Callable callable; private final Type type; private final int index; public ConstrainedParameter(ConfigurationSource source, - Executable executable, + Callable callable, Type type, int index) { this( source, - executable, + callable, type, index, Collections.>emptySet(), @@ -48,7 +48,7 @@ public ConstrainedParameter(ConfigurationSource source, * Creates a new parameter meta data object. * * @param source The source of meta data. - * @param executable The executable of the represented method parameter. + * @param callable The executable of the represented method parameter. * @param type the parameter type * @param index the index of the parameter * @param constraints The constraints of the represented method parameter, if @@ -57,7 +57,7 @@ public ConstrainedParameter(ConfigurationSource source, * @param cascadingMetaDataBuilder The cascaded validation metadata for this element and its container elements. */ public ConstrainedParameter(ConfigurationSource source, - Executable executable, + Callable callable, Type type, int index, Set> constraints, @@ -71,7 +71,7 @@ public ConstrainedParameter(ConfigurationSource source, cascadingMetaDataBuilder ); - this.executable = executable; + this.callable = callable; this.type = type; this.index = index; } @@ -80,8 +80,8 @@ public Type getType() { return type; } - public Executable getExecutable() { - return executable; + public Callable getCallable() { + return callable; } public int getIndex() { @@ -109,7 +109,7 @@ public ConstrainedParameter merge(ConstrainedParameter other) { return new ConstrainedParameter( mergedSource, - executable, + callable, type, index, mergedConstraints, @@ -130,7 +130,7 @@ public String toString() { String constraintsAsString = sb.length() > 0 ? sb.substring( 0, sb.length() - 2 ) : sb.toString(); - return "ParameterMetaData [executable=" + executable + ", index=" + index + "], constraints=[" + return "ParameterMetaData [callable=" + callable + ", index=" + index + "], constraints=[" + constraintsAsString + "]"; } @@ -139,7 +139,7 @@ public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + index; - result = prime * result + ( ( executable == null ) ? 0 : executable.hashCode() ); + result = prime * result + callable.hashCode(); return result; } @@ -158,12 +158,7 @@ public boolean equals(Object obj) { if ( index != other.index ) { return false; } - if ( executable == null ) { - if ( other.executable != null ) { - return false; - } - } - else if ( !executable.equals( other.executable ) ) { + else if ( !callable.equals( other.callable ) ) { return false; } return true; diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/Callable.java b/engine/src/main/java/org/hibernate/validator/internal/properties/Callable.java new file mode 100644 index 0000000000..9ef259f46d --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/Callable.java @@ -0,0 +1,40 @@ +/* + * 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; + +import java.lang.reflect.Type; + +import org.hibernate.validator.internal.util.ExecutableHelper; +import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; + +/** + * @author Marko Bekhta + * @author Guillaume Smet + */ +public interface Callable extends Constrainable { + + boolean hasReturnValue(); + + boolean hasParameters(); + + int getParameterCount(); + + Type getParameterGenericType(int index); + + Class[] getParameterTypes(); + + String getParameterName(ExecutableParameterNameProvider parameterNameProvider, int parameterIndex); + + boolean isPrivate(); + + String getSignature(); + + boolean overrides(ExecutableHelper executableHelper, Callable superTypeMethod); + + boolean isResolvedToSameMethodInHierarchy(ExecutableHelper executableHelper, Class mainSubType, Callable superTypeMethod); + +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/Constrainable.java b/engine/src/main/java/org/hibernate/validator/internal/properties/Constrainable.java new file mode 100644 index 0000000000..8e2be2e24a --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/Constrainable.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.internal.properties; + +import java.lang.reflect.Type; + +import org.hibernate.validator.internal.metadata.raw.ConstrainedElement.ConstrainedElementKind; + +/** + * An element on which it is possible to define constraints (e.g. a JavaBean property, a JavaBean method, a JSON + * property). + * + * @author Marko Bekhta + */ +public interface Constrainable { + + String getName(); + + Class getDeclaringClass(); + + Type getTypeForValidatorResolution(); + + Type getType(); + + ConstrainedElementKind getConstrainedElementKind(); + + @SuppressWarnings("unchecked") + default T as(Class clazz) { + return ( (T) this ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/DefaultGetterPropertySelectionStrategy.java b/engine/src/main/java/org/hibernate/validator/internal/properties/DefaultGetterPropertySelectionStrategy.java new file mode 100644 index 0000000000..885c340537 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/DefaultGetterPropertySelectionStrategy.java @@ -0,0 +1,105 @@ +/* + * 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; + +import java.lang.invoke.MethodHandles; +import java.util.Optional; +import java.util.Set; + +import org.hibernate.validator.internal.util.CollectionHelper; +import org.hibernate.validator.internal.util.Contracts; +import org.hibernate.validator.internal.util.StringHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.spi.properties.ConstrainableExecutable; +import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy; + +/** + * @author Marko Bekhta + */ +public class DefaultGetterPropertySelectionStrategy implements GetterPropertySelectionStrategy { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final String GETTER_PREFIX_GET = "get"; + private static final String GETTER_PREFIX_IS = "is"; + private static final String GETTER_PREFIX_HAS = "has"; + private static final String[] GETTER_PREFIXES = { + GETTER_PREFIX_GET, + GETTER_PREFIX_IS, + GETTER_PREFIX_HAS + }; + + @Override + public Optional getProperty(ConstrainableExecutable executable) { + Contracts.assertNotNull( executable, "executable cannot be null" ); + + if ( !isGetter( executable ) ) { + return Optional.empty(); + } + + String methodName = executable.getName(); + + for ( String prefix : GETTER_PREFIXES ) { + if ( methodName.startsWith( prefix ) ) { + return Optional.of( StringHelper.decapitalize( methodName.substring( prefix.length() ) ) ); + } + } + + throw new AssertionError( "Method " + executable.getName() + " was considered a getter but we couldn't extract a property name." ); + } + + @Override + public Set getGetterMethodNameCandidates(String propertyName) { + Contracts.assertNotEmpty( propertyName, "Name of a property must not be empty" ); + + Set nameCandidates = CollectionHelper.newHashSet( GETTER_PREFIXES.length ); + for ( String prefix : GETTER_PREFIXES ) { + nameCandidates.add( prefix + Character.toUpperCase( propertyName.charAt( 0 ) ) + propertyName.substring( 1 ) ); + } + return nameCandidates; + } + + /** + * Checks whether the given executable is a valid JavaBean getter method, which + * is the case if + *

    + *
  • its name starts with "get" and it has a return type but no parameter or
  • + *
  • its name starts with "is", it has no parameter and is returning + * {@code boolean} or
  • + *
  • its name starts with "has", it has no parameter and is returning + * {@code boolean} (HV-specific, not mandated by the JavaBeans spec).
  • + *
+ * + * @param executable The executable of interest. + * + * @return {@code true}, if the given executable is a JavaBean getter method, + * {@code false} otherwise. + */ + private static boolean isGetter(ConstrainableExecutable executable) { + if ( executable.getParameterTypes().length != 0 ) { + return false; + } + + String methodName = executable.getName(); + + // get() + if ( methodName.startsWith( GETTER_PREFIX_GET ) && executable.getReturnType() != void.class ) { + return true; + } + //boolean is() + else if ( methodName.startsWith( GETTER_PREFIX_IS ) && executable.getReturnType() == boolean.class ) { + return true; + } + //boolean has() + else if ( methodName.startsWith( GETTER_PREFIX_HAS ) && executable.getReturnType() == boolean.class ) { + return true; + } + + return false; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/Field.java b/engine/src/main/java/org/hibernate/validator/internal/properties/Field.java new file mode 100644 index 0000000000..65d72330da --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/Field.java @@ -0,0 +1,20 @@ +/* + * 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; + +import org.hibernate.validator.internal.metadata.raw.ConstrainedElement.ConstrainedElementKind; + +/** + * @author Guillaume Smet + */ +public interface Field extends Property { + + @Override + default ConstrainedElementKind getConstrainedElementKind() { + return ConstrainedElementKind.FIELD; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/Getter.java b/engine/src/main/java/org/hibernate/validator/internal/properties/Getter.java new file mode 100644 index 0000000000..fede3102bd --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/Getter.java @@ -0,0 +1,20 @@ +/* + * 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; + +import org.hibernate.validator.internal.metadata.raw.ConstrainedElement.ConstrainedElementKind; + +/** + * @author Guillaume Smet + */ +public interface Getter extends Property { + + @Override + default ConstrainedElementKind getConstrainedElementKind() { + return ConstrainedElementKind.GETTER; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/Property.java b/engine/src/main/java/org/hibernate/validator/internal/properties/Property.java new file mode 100644 index 0000000000..63cc7fa07c --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/Property.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.properties; + +/** + * @author Marko Bekhta + */ +public interface Property extends Constrainable { + + String getPropertyName(); + + String getResolvedPropertyName(); + + PropertyAccessor createAccessor(); +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/PropertyAccessor.java b/engine/src/main/java/org/hibernate/validator/internal/properties/PropertyAccessor.java new file mode 100644 index 0000000000..b269dbb778 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/PropertyAccessor.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 . + */ +package org.hibernate.validator.internal.properties; + +/** + * @author Guillaume Smet + */ +public interface PropertyAccessor { + + Object getValueFrom(Object bean); +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanAnnotatedConstrainable.java b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanAnnotatedConstrainable.java new file mode 100644 index 0000000000..55589a6c4a --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanAnnotatedConstrainable.java @@ -0,0 +1,16 @@ +/* + * 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.javabean; + +import org.hibernate.validator.internal.properties.Constrainable; + +/** + * @author Guillaume Smet + */ +public interface JavaBeanAnnotatedConstrainable extends Constrainable, JavaBeanAnnotatedElement { + +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanAnnotatedElement.java b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanAnnotatedElement.java new file mode 100644 index 0000000000..11a99d3eab --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanAnnotatedElement.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.internal.properties.javabean; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; + +/** + * @author Guillaume Smet + */ +public interface JavaBeanAnnotatedElement { + + Type getType(); + + AnnotatedType getAnnotatedType(); + + Annotation[] getDeclaredAnnotations(); + + Type getGenericType(); + + TypeVariable[] getTypeParameters(); + +
A getAnnotation(Class annotationClass); + + default boolean isAnnotationPresent(Class annotationClass) { + return getAnnotation( annotationClass ) != null; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanConstructor.java b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanConstructor.java new file mode 100644 index 0000000000..8b4b257b36 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanConstructor.java @@ -0,0 +1,37 @@ +/* + * 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.javabean; + +import java.lang.reflect.Constructor; +import java.lang.reflect.TypeVariable; + +import org.hibernate.validator.internal.metadata.raw.ConstrainedElement.ConstrainedElementKind; + +/** + * @author Guillaume Smet + */ +public class JavaBeanConstructor extends JavaBeanExecutable> { + + public JavaBeanConstructor(Constructor executable) { + super( executable, true ); + } + + @Override + public String getName() { + return getDeclaringClass().getSimpleName(); + } + + @Override + public ConstrainedElementKind getConstrainedElementKind() { + return ConstrainedElementKind.CONSTRUCTOR; + } + + @Override + public TypeVariable[] getTypeParameters() { + return executable.getDeclaringClass().getTypeParameters(); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanExecutable.java b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanExecutable.java new file mode 100644 index 0000000000..11e84f89d6 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanExecutable.java @@ -0,0 +1,264 @@ +/* + * 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.javabean; + +import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.AnnotatedType; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Parameter; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +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.ReflectionHelper; +import org.hibernate.validator.internal.util.TypeHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; + +/** + * @author Marko Bekhta + * @author Guillaume Smet + */ +public abstract class JavaBeanExecutable implements Callable, JavaBeanAnnotatedConstrainable { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + protected final T executable; + private final Type typeForValidatorResolution; + private final boolean hasReturnValue; + private final Type type; + private final List parameters; + + JavaBeanExecutable(T executable, boolean hasReturnValue) { + this.executable = executable; + this.type = ReflectionHelper.typeOf( executable ); + this.typeForValidatorResolution = ReflectionHelper.boxedType( type ); + this.hasReturnValue = hasReturnValue; + this.parameters = getParameters( executable ); + } + + @Override + public boolean hasReturnValue() { + return hasReturnValue; + } + + @Override + public boolean hasParameters() { + return !parameters.isEmpty(); + } + + @Override + public String getName() { + return executable.getName(); + } + + @Override + public Class getDeclaringClass() { + return executable.getDeclaringClass(); + } + + @Override + public Type getTypeForValidatorResolution() { + return typeForValidatorResolution; + } + + @Override + public Type getType() { + return type; + } + + @Override + public String getParameterName(ExecutableParameterNameProvider parameterNameProvider, int parameterIndex) { + return parameterNameProvider.getParameterNames( executable ).get( parameterIndex ); + } + + @Override + public boolean isPrivate() { + return Modifier.isPrivate( executable.getModifiers() ); + } + + @Override + public String getSignature() { + return ExecutableHelper.getSignature( executable ); + } + + @Override + public Annotation[] getDeclaredAnnotations() { + return executable.getDeclaredAnnotations(); + } + + @Override + public boolean overrides(ExecutableHelper executableHelper, Callable superTypeMethod) { + return executableHelper.overrides( ( (Method) this.executable ), ( (Method) ( (JavaBeanExecutable) superTypeMethod ).executable ) ); + } + + @Override + public boolean isResolvedToSameMethodInHierarchy(ExecutableHelper executableHelper, Class mainSubType, Callable superTypeMethod) { + return executableHelper.isResolvedToSameMethodInHierarchy( mainSubType, ( (Method) this.executable ), ( (Method) ( (JavaBeanExecutable) superTypeMethod ).executable ) ); + } + + @Override + public Type getGenericType() { + return ReflectionHelper.typeOf( executable ); + } + + @Override + public AnnotatedType getAnnotatedType() { + return executable.getAnnotatedReturnType(); + } + + @Override + public A getAnnotation(Class annotationClass) { + return executable.getAnnotation( annotationClass ); + } + + public List getParameters() { + return parameters; + } + + @Override + public Type getParameterGenericType(int index) { + return parameters.get( index ).getGenericType(); + } + + @Override + public int getParameterCount() { + return parameters.size(); + } + + @Override + public Class[] getParameterTypes() { + return executable.getParameterTypes(); + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || this.getClass() != o.getClass() ) { + return false; + } + + JavaBeanExecutable that = (JavaBeanExecutable) o; + + if ( this.hasReturnValue != that.hasReturnValue ) { + return false; + } + if ( !this.executable.equals( that.executable ) ) { + return false; + } + if ( !this.typeForValidatorResolution.equals( that.typeForValidatorResolution ) ) { + return false; + } + return this.type.equals( that.type ); + } + + @Override + public int hashCode() { + int result = this.executable.hashCode(); + result = 31 * result + this.typeForValidatorResolution.hashCode(); + result = 31 * result + ( this.hasReturnValue ? 1 : 0 ); + result = 31 * result + this.type.hashCode(); + return result; + } + + @Override + public String toString() { + return ExecutableHelper.getExecutableAsString( + getDeclaringClass().getSimpleName() + "#" + executable.getName(), + executable.getParameterTypes() + ); + } + + private static List getParameters(Executable executable) { + if ( executable.getParameterCount() == 0 ) { + return Collections.emptyList(); + } + + List parameters = new ArrayList<>( executable.getParameterCount() ); + + Parameter[] parameterArray = executable.getParameters(); + Class[] parameterTypes = executable.getParameterTypes(); + // getGenericParameterTypes() does not include either the synthetic or the implicit parameters so we need to be + // extra careful + Type[] genericParameterTypes = executable.getGenericParameterTypes(); + + if ( parameterTypes.length == genericParameterTypes.length ) { + // this is the simple case where both arrays are consistent + // we could do without it but at some point, the behavior of getGenericParameterTypes() might be changed in + // Java and we'd better be ready. + for ( int i = 0; i < parameterArray.length; i++ ) { + parameters.add( new JavaBeanParameter( i, parameterArray[i], parameterTypes[i], getErasedTypeIfTypeVariable( genericParameterTypes[i] ) ) ); + } + } + else { + // in this case, we have synthetic or implicit parameters + + // do we have the information about which parameter is synthetic/implicit? + // (this metadata is only included when classes are compiled with the '-parameters' flag) + boolean hasParameterModifierInfo = isAnyParameterCarryingMetadata( parameterArray ); + + if ( ! hasParameterModifierInfo ) { + LOG.missingParameterMetadataWithSyntheticOrImplicitParameters( executable ); + } + + int explicitlyDeclaredParameterIndex = 0; + + for ( int i = 0; i < parameterArray.length; i++ ) { + if ( explicitlyDeclaredParameterIndex < genericParameterTypes.length // we might already be out of the bounds of generic params array + && isExplicit( parameterArray[i] ) + && parameterTypesMatch( parameterTypes[i], genericParameterTypes[explicitlyDeclaredParameterIndex] ) ) { + // in this case we have a parameter that is present and matches ("most likely") to the one in the generic parameter types list + parameters.add( new JavaBeanParameter( i, parameterArray[i], parameterTypes[i], + getErasedTypeIfTypeVariable( genericParameterTypes[explicitlyDeclaredParameterIndex] ) ) ); + explicitlyDeclaredParameterIndex++; + } + else { + // in this case, the parameter is not present in genericParameterTypes, or the types doesn't match + parameters.add( new JavaBeanParameter( i, parameterArray[i], parameterTypes[i], parameterTypes[i] ) ); + } + } + } + + return CollectionHelper.toImmutableList( parameters ); + } + + private static boolean isAnyParameterCarryingMetadata(Parameter[] parameterArray) { + for ( Parameter parameter : parameterArray ) { + if ( parameter.isSynthetic() || parameter.isImplicit() ) { + return true; + } + } + return false; + } + + private static boolean parameterTypesMatch(Class paramType, Type genericParamType) { + return TypeHelper.getErasedType( genericParamType ).equals( paramType ); + } + + private static boolean isExplicit(Parameter parameter) { + return !parameter.isSynthetic() && !parameter.isImplicit(); + } + + private static Type getErasedTypeIfTypeVariable(Type genericType) { + if ( genericType instanceof TypeVariable ) { + return TypeHelper.getErasedType( genericType ); + } + + return genericType; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanField.java b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanField.java new file mode 100644 index 0000000000..88bc06db46 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanField.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.properties.javabean; + +import static org.hibernate.validator.internal.util.TypeHelper.isHibernateValidatorEnhancedBean; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedType; +import java.lang.reflect.Field; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.security.AccessController; +import java.security.PrivilegedAction; + +import org.hibernate.validator.HibernateValidatorPermission; +import org.hibernate.validator.engine.HibernateValidatorEnhancedBean; +import org.hibernate.validator.internal.properties.PropertyAccessor; +import org.hibernate.validator.internal.util.ReflectionHelper; +import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredField; + +/** + * @author Marko Bekhta + */ +public class JavaBeanField implements org.hibernate.validator.internal.properties.Field, JavaBeanAnnotatedConstrainable { + + private final Field field; + private final String resolvedPropertyName; + private final Type typeForValidatorResolution; + private final Type type; + + public JavaBeanField(Field field, String resolvedPropertyName) { + this.field = field; + this.type = ReflectionHelper.typeOf( field ); + this.typeForValidatorResolution = ReflectionHelper.boxedType( this.type ); + this.resolvedPropertyName = resolvedPropertyName; + } + + @Override + public String getName() { + return field.getName(); + } + + @Override + public Class getDeclaringClass() { + return field.getDeclaringClass(); + } + + @Override + public Type getType() { + return type; + } + + @Override + public Type getTypeForValidatorResolution() { + return typeForValidatorResolution; + } + + @Override + public String getPropertyName() { + return getName(); + } + + @Override + public String getResolvedPropertyName() { + return resolvedPropertyName; + } + + @Override + public AnnotatedType getAnnotatedType() { + return field.getAnnotatedType(); + } + + @Override + public Annotation[] getDeclaredAnnotations() { + return field.getDeclaredAnnotations(); + } + + @Override + public A getAnnotation(Class annotationClass) { + return field.getAnnotation( annotationClass ); + } + + @Override + public Type getGenericType() { + return ReflectionHelper.typeOf( field ); + } + + @Override + public TypeVariable[] getTypeParameters() { + return field.getType().getTypeParameters(); + } + + @Override + public PropertyAccessor createAccessor() { + if ( isHibernateValidatorEnhancedBean( field.getDeclaringClass() ) ) { + return new EnhancedBeanFieldAccessor( field.getName() ); + } + else { + return new FieldAccessor( field ); + } + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || this.getClass() != o.getClass() ) { + return false; + } + + JavaBeanField that = (JavaBeanField) o; + + if ( !this.field.equals( that.field ) ) { + return false; + } + if ( !this.typeForValidatorResolution.equals( that.typeForValidatorResolution ) ) { + return false; + } + return this.type.equals( that.type ); + } + + @Override + public int hashCode() { + int result = this.field.hashCode(); + result = 31 * result + this.typeForValidatorResolution.hashCode(); + result = 31 * result + this.type.hashCode(); + return result; + } + + @Override + public String toString() { + return getName(); + } + + private static class EnhancedBeanFieldAccessor implements PropertyAccessor { + + private final String name; + + private EnhancedBeanFieldAccessor(final String name) { + this.name = name; + } + + @Override + public Object getValueFrom(Object bean) { + // we don't do an instanceof check here as it should already be applied when the accessor was created. + return ( (HibernateValidatorEnhancedBean) bean ).$$_hibernateValidator_getFieldValue( name ); + } + } + + private static class FieldAccessor implements PropertyAccessor { + + private Field accessibleField; + + private FieldAccessor(Field field) { + this.accessibleField = getAccessible( field ); + } + + @Override + public Object getValueFrom(Object bean) { + return ReflectionHelper.getValue( accessibleField, bean ); + } + } + + /** + * Returns an accessible copy of the given member. + */ + private static Field getAccessible(Field original) { + SecurityManager sm = System.getSecurityManager(); + if ( sm != null ) { + sm.checkPermission( HibernateValidatorPermission.ACCESS_PRIVATE_MEMBERS ); + } + + Class clazz = original.getDeclaringClass(); + + return run( GetDeclaredField.andMakeAccessible( clazz, original.getName() ) ); + } + + /** + * 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 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/javabean/JavaBeanGetter.java b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanGetter.java new file mode 100644 index 0000000000..fb83ba3ee5 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanGetter.java @@ -0,0 +1,173 @@ +/* + * 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.javabean; + +import static org.hibernate.validator.internal.util.TypeHelper.isHibernateValidatorEnhancedBean; + +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; + +import org.hibernate.validator.HibernateValidatorPermission; +import org.hibernate.validator.engine.HibernateValidatorEnhancedBean; +import org.hibernate.validator.internal.metadata.raw.ConstrainedElement.ConstrainedElementKind; +import org.hibernate.validator.internal.properties.Getter; +import org.hibernate.validator.internal.properties.PropertyAccessor; +import org.hibernate.validator.internal.util.Contracts; +import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; +import org.hibernate.validator.internal.util.ReflectionHelper; +import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredMethod; + +/** + * @author Marko Bekhta + */ +public class JavaBeanGetter extends JavaBeanMethod implements Getter { + + private final String propertyName; + private final String resolvedPropertyName; + + /** + * The class of the method for which the constraint was defined. + *

+ * It is usually the same as the declaring class of the method itself, except in the XML case when a user could + * declare a constraint for a specific subclass. + */ + private final Class declaringClass; + + public JavaBeanGetter(Class declaringClass, Method method, String propertyName, String resolvedPropertyName) { + super( method ); + Contracts.assertNotNull( propertyName, "Property name cannot be null." ); + + this.declaringClass = declaringClass; + this.propertyName = propertyName; + this.resolvedPropertyName = resolvedPropertyName; + } + + @Override + public String getPropertyName() { + return propertyName; + } + + @Override + public String getResolvedPropertyName() { + return resolvedPropertyName; + } + + @Override + public boolean hasReturnValue() { + // getters should always have a return value + return true; + } + + @Override + public boolean hasParameters() { + // getters should never have parameters + return false; + } + + @Override + public String getParameterName(ExecutableParameterNameProvider parameterNameProvider, int parameterIndex) { + throw new IllegalStateException( "Getters may not have parameters" ); + } + + @Override + public Class getDeclaringClass() { + return declaringClass; + } + + @Override + public ConstrainedElementKind getConstrainedElementKind() { + return ConstrainedElementKind.GETTER; + } + + @Override + public PropertyAccessor createAccessor() { + if ( isHibernateValidatorEnhancedBean( executable.getDeclaringClass() ) ) { + return new EnhancedBeanGetterAccessor( executable.getName() ); + } + else { + return new GetterAccessor( executable ); + } + + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || this.getClass() != o.getClass() ) { + return false; + } + if ( !super.equals( o ) ) { + return false; + } + + JavaBeanGetter that = (JavaBeanGetter) o; + + return this.propertyName.equals( that.propertyName ); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + this.propertyName.hashCode(); + return result; + } + + private static class EnhancedBeanGetterAccessor implements PropertyAccessor { + private final String getterFullName; + + private EnhancedBeanGetterAccessor(final String getterFullName) { + this.getterFullName = getterFullName; + } + + @Override + public Object getValueFrom(Object bean) { + // we don't do an instanceof check here as it should already be applied when the accessor was created. + return ( (HibernateValidatorEnhancedBean) bean ).$$_hibernateValidator_getGetterValue( getterFullName ); + } + } + private static class GetterAccessor implements PropertyAccessor { + + private Method accessibleGetter; + + private GetterAccessor(Method getter) { + this.accessibleGetter = getAccessible( getter ); + } + + @Override + public Object getValueFrom(Object bean) { + return ReflectionHelper.getValue( accessibleGetter, bean ); + } + } + + /** + * Returns an accessible copy of the given method. + */ + private static Method getAccessible(Method original) { + SecurityManager sm = System.getSecurityManager(); + if ( sm != null ) { + sm.checkPermission( HibernateValidatorPermission.ACCESS_PRIVATE_MEMBERS ); + } + + Class clazz = original.getDeclaringClass(); + Method accessibleMethod = run( GetDeclaredMethod.andMakeAccessible( clazz, original.getName() ) ); + + return accessibleMethod; + } + + /** + * 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 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/javabean/JavaBeanHelper.java b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanHelper.java new file mode 100644 index 0000000000..6afed49ecc --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanHelper.java @@ -0,0 +1,188 @@ +/* + * 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.javabean; + +import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Optional; + +import org.hibernate.validator.internal.properties.Constrainable; +import org.hibernate.validator.internal.util.Contracts; +import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredConstructor; +import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredField; +import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredMethod; +import org.hibernate.validator.internal.util.privilegedactions.GetMethodFromGetterNameCandidates; +import org.hibernate.validator.spi.nodenameprovider.JavaBeanProperty; +import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider; +import org.hibernate.validator.spi.properties.ConstrainableExecutable; +import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy; + +/** + * Helper class that gives ability to find {@link Constrainable} versions + * of JavaBean's fields, getters, constructors and methods. + * + * @author Marko Bekhta + */ +public class JavaBeanHelper { + + private final GetterPropertySelectionStrategy getterPropertySelectionStrategy; + private final PropertyNodeNameProvider propertyNodeNameProvider; + + public JavaBeanHelper(GetterPropertySelectionStrategy getterPropertySelectionStrategy, PropertyNodeNameProvider propertyNodeNameProvider) { + this.getterPropertySelectionStrategy = getterPropertySelectionStrategy; + this.propertyNodeNameProvider = propertyNodeNameProvider; + } + + public GetterPropertySelectionStrategy getGetterPropertySelectionStrategy() { + return getterPropertySelectionStrategy; + } + + public Optional findDeclaredField(Class declaringClass, String property) { + Contracts.assertNotNull( declaringClass, MESSAGES.classCannotBeNull() ); + + Field field = run( GetDeclaredField.action( declaringClass, property ) ); + + return Optional.ofNullable( field ).map( this::field ); + } + + public Optional findDeclaredGetter(Class declaringClass, String property) { + Contracts.assertNotNull( declaringClass, MESSAGES.classCannotBeNull() ); + + return findGetter( declaringClass, property, false ); + } + + public Optional findGetter(Class declaringClass, String property) { + Contracts.assertNotNull( declaringClass, MESSAGES.classCannotBeNull() ); + + return findGetter( declaringClass, property, true ); + } + + private Optional findGetter(Class declaringClass, String property, boolean lookForMethodsOnSuperClass) { + Method getter = run( + GetMethodFromGetterNameCandidates.action( + declaringClass, + getterPropertySelectionStrategy.getGetterMethodNameCandidates( property ), + lookForMethodsOnSuperClass + ) + ); + if ( getter == null ) { + return Optional.empty(); + } + else { + return Optional.of( new JavaBeanGetter( declaringClass, getter, property, propertyNodeNameProvider.getName( + new JavaBeanPropertyImpl( declaringClass, property ) ) ) ); + } + } + + public Optional findDeclaredMethod(Class declaringClass, String methodName, Class... parameterTypes) { + Method method = run( GetDeclaredMethod.action( declaringClass, methodName, parameterTypes ) ); + if ( method == null ) { + return Optional.empty(); + } + else { + return Optional.of( executable( declaringClass, method ) ); + } + } + + public Optional findDeclaredConstructor(Class declaringClass, Class... parameterTypes) { + Constructor constructor = run( GetDeclaredConstructor.action( declaringClass, parameterTypes ) ); + if ( constructor == null ) { + return Optional.empty(); + } + else { + return Optional.of( new JavaBeanConstructor( constructor ) ); + } + } + + public JavaBeanExecutable executable(Executable executable) { + return executable( executable.getDeclaringClass(), executable ); + } + + public JavaBeanExecutable executable(Class declaringClass, Executable executable) { + if ( executable instanceof Constructor ) { + return new JavaBeanConstructor( (Constructor) executable ); + } + + return executable( declaringClass, ( (Method) executable ) ); + } + + public JavaBeanMethod executable(Class declaringClass, Method method) { + JavaBeanConstrainableExecutable executable = new JavaBeanConstrainableExecutable( method ); + + Optional correspondingProperty = getterPropertySelectionStrategy.getProperty( executable ); + if ( correspondingProperty.isPresent() ) { + return new JavaBeanGetter( declaringClass, method, correspondingProperty.get(), propertyNodeNameProvider.getName( + new JavaBeanPropertyImpl( declaringClass, correspondingProperty.get() ) ) ); + } + + return new JavaBeanMethod( method ); + } + + public JavaBeanField field(Field field) { + return new JavaBeanField( field, propertyNodeNameProvider.getName( new JavaBeanPropertyImpl( field.getDeclaringClass(), field.getName() ) ) ); + } + + /** + * 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(); + } + + private static class JavaBeanConstrainableExecutable implements ConstrainableExecutable { + + private final Method method; + + private JavaBeanConstrainableExecutable(Method method) { + this.method = method; + } + + @Override + public Class getReturnType() { + return method.getReturnType(); + } + + @Override + public String getName() { + return method.getName(); + } + + @Override + public Class[] getParameterTypes() { + return method.getParameterTypes(); + } + } + + private static class JavaBeanPropertyImpl implements JavaBeanProperty { + private final Class declaringClass; + private final String name; + + private JavaBeanPropertyImpl(Class declaringClass, String name) { + this.declaringClass = declaringClass; + this.name = name; + } + + @Override + public Class getDeclaringClass() { + return declaringClass; + } + + @Override + public String getName() { + return name; + } + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanMethod.java b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanMethod.java new file mode 100644 index 0000000000..166af53e8d --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanMethod.java @@ -0,0 +1,32 @@ +/* + * 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.javabean; + +import java.lang.reflect.Method; +import java.lang.reflect.TypeVariable; + +import org.hibernate.validator.internal.metadata.raw.ConstrainedElement.ConstrainedElementKind; + +/** + * @author Guillaume Smet + */ +public class JavaBeanMethod extends JavaBeanExecutable { + + JavaBeanMethod(Method method) { + super( method, method.getGenericReturnType() != void.class ); + } + + @Override + public ConstrainedElementKind getConstrainedElementKind() { + return ConstrainedElementKind.METHOD; + } + + @Override + public TypeVariable[] getTypeParameters() { + return executable.getReturnType().getTypeParameters(); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanParameter.java b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanParameter.java new file mode 100644 index 0000000000..0e8b15b652 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanParameter.java @@ -0,0 +1,85 @@ +/* + * 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.javabean; + +import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES; + +import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.AnnotatedType; +import java.lang.reflect.Parameter; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; + +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; + +/** + * @author Guillaume Smet + */ +public class JavaBeanParameter implements JavaBeanAnnotatedElement { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final Annotation[] EMPTY_PARAMETER_ANNOTATIONS = new Annotation[0]; + + private final int index; + + private final Parameter parameter; + + private final Class type; + + private final Type genericType; + + JavaBeanParameter(int index, Parameter parameter, Class type, Type genericType) { + this.index = index; + this.parameter = parameter; + this.type = type; + this.genericType = genericType; + } + + public int getIndex() { + return index; + } + + @Override + public Class getType() { + return type; + } + + @Override + public AnnotatedType getAnnotatedType() { + return parameter.getAnnotatedType(); + } + + @Override + public Annotation[] getDeclaredAnnotations() { + try { + return parameter.getDeclaredAnnotations(); + } + catch (ArrayIndexOutOfBoundsException ex) { + // This looks like a JVM bug we are trying to work around, kept as is for now + LOG.warn( MESSAGES.constraintOnConstructorOfNonStaticInnerClass(), ex ); + return EMPTY_PARAMETER_ANNOTATIONS; + } + } + + @Override + public Type getGenericType() { + return genericType; + } + + @Override + public TypeVariable[] getTypeParameters() { + return parameter.getType().getTypeParameters(); + } + + @Override + public A getAnnotation(Class annotationClass) { + return parameter.getAnnotation( annotationClass ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/package-info.java b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/package-info.java new file mode 100644 index 0000000000..410921fdeb --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/package-info.java @@ -0,0 +1,8 @@ +/* + * 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.javabean; diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/Contracts.java b/engine/src/main/java/org/hibernate/validator/internal/util/Contracts.java index a5867276a9..35ec6fd7b2 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/Contracts.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/Contracts.java @@ -69,12 +69,12 @@ public static void assertTrue(boolean condition, String message) { public static void assertTrue(boolean condition, String message, Object... messageParameters) { if ( !condition ) { - throw LOG.getIllegalArgumentException( String.format( message, messageParameters ) ); + throw LOG.getIllegalArgumentException( StringHelper.format( message, messageParameters ) ); } } public static void assertNotEmpty(String s, String message) { - if ( s.length() == 0 ) { + if ( StringHelper.isNullOrEmptyString( s ) ) { throw LOG.getIllegalArgumentException( message ); } } @@ -87,7 +87,7 @@ public static void assertNotEmpty(Collection collection, String message) { public static void assertNotEmpty(Collection collection, String message, Object... messageParameters) { if ( collection.size() == 0 ) { - throw LOG.getIllegalArgumentException( String.format( message, messageParameters ) ); + throw LOG.getIllegalArgumentException( StringHelper.format( message, messageParameters ) ); } } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/ExecutableHelper.java b/engine/src/main/java/org/hibernate/validator/internal/util/ExecutableHelper.java index ce83775ef2..cd5e1247a6 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/ExecutableHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/ExecutableHelper.java @@ -17,6 +17,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.hibernate.validator.internal.properties.Callable; +import org.hibernate.validator.internal.util.classhierarchy.Filters; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; import org.hibernate.validator.internal.util.privilegedactions.GetResolvedMemberMethods; @@ -45,6 +47,10 @@ public ExecutableHelper(TypeResolutionHelper typeResolutionHelper) { this.typeResolver = typeResolutionHelper.getTypeResolver(); } + public boolean overrides(Callable subTypeMethod, Callable superTypeMethod) { + return subTypeMethod.overrides( this, superTypeMethod ); + } + /** * Checks, whether {@code subTypeMethod} overrides {@code superTypeMethod}. * @@ -90,14 +96,83 @@ public boolean overrides(Method subTypeMethod, Method superTypeMethod) { return false; } - if ( !Modifier.isPublic( superTypeMethod.getModifiers() ) && !Modifier.isProtected( superTypeMethod.getModifiers() ) - && !superTypeMethod.getDeclaringClass().getPackage().equals( subTypeMethod.getDeclaringClass().getPackage() ) ) { + if ( !isMethodVisibleTo( superTypeMethod, subTypeMethod ) ) { return false; } return instanceMethodParametersResolveToSameTypes( subTypeMethod, superTypeMethod ); } + /** + * Checks if a pair of given methods ({@code left} and {@code right}) are resolved to the same + * method based on the {@code mainSubType} type. + * + * @param mainSubType a type at the bottom of class hierarchy to be used to lookup the methods. + * @param left one of the methods to check + * @param right another of the methods to check + * + * @return {@code true} if a pair of methods are equal {@code left == right}, or one of the methods + * override another one in the class hierarchy with {@code mainSubType} at the bottom, + * {@code false} otherwise. + */ + public boolean isResolvedToSameMethodInHierarchy(Class mainSubType, Method left, Method right) { + Contracts.assertValueNotNull( mainSubType, "mainSubType" ); + Contracts.assertValueNotNull( left, "left" ); + Contracts.assertValueNotNull( right, "right" ); + + if ( left.equals( right ) ) { + return true; + } + + if ( !left.getName().equals( right.getName() ) ) { + return false; + } + + // methods with same name in the same class should be different + if ( left.getDeclaringClass().equals( right.getDeclaringClass() ) ) { + return false; + } + + if ( left.getParameterTypes().length != right.getParameterTypes().length ) { + return false; + } + + // if at least one method from a pair is static - they are different methods + if ( Modifier.isStatic( right.getModifiers() ) || Modifier.isStatic( left.getModifiers() ) ) { + return false; + } + + // HV-861 Bridge method should be ignored. Classmates type/member resolution will take care of proper + // override detection without considering bridge methods + if ( left.isBridge() || right.isBridge() ) { + return false; + } + + // if one of the methods is private - methods are different + if ( Modifier.isPrivate( left.getModifiers() ) || Modifier.isPrivate( right.getModifiers() ) ) { + return false; + } + + if ( !isMethodVisibleTo( right, left ) || !isMethodVisibleTo( left, right ) ) { + return false; + } + + // We need to check if the passed mainSubType is not a Weld proxy. In case of proxy we need to get + // a class that was proxied otherwise we can use the class itself. This is due to the issue that + // call to Class#getGenericInterfaces() on a Weld proxy returns raw types instead of parametrized + // generics and methods will not be resolved correctly. + return instanceMethodParametersResolveToSameTypes( + Filters.excludeProxies().accepts( mainSubType ) ? mainSubType : mainSubType.getSuperclass(), + left, + right + ); + } + + private static boolean isMethodVisibleTo(Method visibleMethod, Method otherMethod) { + return Modifier.isPublic( visibleMethod.getModifiers() ) || Modifier.isProtected( visibleMethod.getModifiers() ) + || visibleMethod.getDeclaringClass().getPackage().equals( otherMethod.getDeclaringClass().getPackage() ); + } + public static String getSimpleName(Executable executable) { return executable instanceof Constructor ? executable.getDeclaringClass().getSimpleName() : executable.getName(); } @@ -140,14 +215,18 @@ public static ElementType getElementType(Executable executable) { * @return {@code true} if the parameters of the two methods resolve to the same types, {@code false otherwise}. */ private boolean instanceMethodParametersResolveToSameTypes(Method subTypeMethod, Method superTypeMethod) { - if ( subTypeMethod.getParameterTypes().length == 0 ) { + return instanceMethodParametersResolveToSameTypes( subTypeMethod.getDeclaringClass(), subTypeMethod, superTypeMethod ); + } + + private boolean instanceMethodParametersResolveToSameTypes(Class mainSubType, Method left, Method right) { + if ( left.getParameterTypes().length == 0 ) { return true; } - ResolvedType resolvedSubType = typeResolver.resolve( subTypeMethod.getDeclaringClass() ); + ResolvedType resolvedSubType = typeResolver.resolve( mainSubType ); MemberResolver memberResolver = new MemberResolver( typeResolver ); - memberResolver.setMethodFilter( new SimpleMethodFilter( subTypeMethod, superTypeMethod ) ); + memberResolver.setMethodFilter( new SimpleMethodFilter( left, right ) ); ResolvedTypeWithMembers typeWithMembers = memberResolver.resolve( resolvedSubType, null, @@ -181,9 +260,9 @@ private boolean instanceMethodParametersResolveToSameTypes(Method subTypeMethod, catch (ArrayIndexOutOfBoundsException e) { LOG.debug( "Error in ExecutableHelper#instanceMethodParametersResolveToSameTypes comparing " - + subTypeMethod + + left + " with " - + superTypeMethod + + right ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/IdentitySet.java b/engine/src/main/java/org/hibernate/validator/internal/util/IdentitySet.java deleted file mode 100644 index 2c4e2794db..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/util/IdentitySet.java +++ /dev/null @@ -1,116 +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.util; - -import java.util.Collection; -import java.util.IdentityHashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; - -/** - * Set that compares object by identity rather than equality. Wraps around a {@code IdentityHashMap} - * - * @author Emmanuel Bernard - */ -public class IdentitySet implements Set { - private final Map map; - private final Object CONTAINS = new Object(); - - public IdentitySet() { - this( 10 ); - } - - public IdentitySet(int size) { - this.map = new IdentityHashMap( size ); - } - - @Override - public int size() { - return map.size(); - } - - @Override - public boolean isEmpty() { - return map.isEmpty(); - } - - @Override - public boolean contains(Object o) { - return map.containsKey( o ); - } - - @Override - public Iterator iterator() { - return map.keySet().iterator(); - } - - @Override - public Object[] toArray() { - return map.keySet().toArray(); - } - - @Override - public boolean add(Object o) { - return map.put( o, CONTAINS ) == null; - } - - @Override - public boolean remove(Object o) { - return map.remove( o ) == CONTAINS; - } - - @Override - public boolean addAll(Collection c) { - boolean doThing = false; - for ( Object o : c ) { - doThing = doThing || add( o ); - } - return doThing; - } - - @Override - public void clear() { - map.clear(); - } - - @Override - public boolean removeAll(Collection c) { - boolean remove = false; - for ( Object o : c ) { - remove = remove || remove( o ); - } - return remove; - } - - @Override - public boolean retainAll(Collection c) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean containsAll(Collection c) { - for ( Object o : c ) { - if ( !contains( o ) ) { - return false; - } - } - return true; - } - - @Override - public Object[] toArray(Object[] a) { - return map.keySet().toArray( a ); - } - - @Override - public String toString() { - return "IdentitySet{" + - "map=" + map + - '}'; - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/ReflectionHelper.java b/engine/src/main/java/org/hibernate/validator/internal/util/ReflectionHelper.java index f964a54d6a..c968bb0af5 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/ReflectionHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/ReflectionHelper.java @@ -10,7 +10,6 @@ import java.lang.invoke.MethodHandles; import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; import java.lang.reflect.InvocationTargetException; @@ -38,15 +37,6 @@ */ public final class ReflectionHelper { - private static final String PROPERTY_ACCESSOR_PREFIX_GET = "get"; - private static final String PROPERTY_ACCESSOR_PREFIX_IS = "is"; - private static final String PROPERTY_ACCESSOR_PREFIX_HAS = "has"; - public static final String[] PROPERTY_ACCESSOR_PREFIXES = { - PROPERTY_ACCESSOR_PREFIX_GET, - PROPERTY_ACCESSOR_PREFIX_IS, - PROPERTY_ACCESSOR_PREFIX_HAS - }; - private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); private static final Map, Class> PRIMITIVE_TO_WRAPPER_TYPES; @@ -91,91 +81,6 @@ public final class ReflectionHelper { private ReflectionHelper() { } - /** - * Returns the JavaBeans property name of the given member. - *

- * For fields, the field name will be returned. For getter methods, the - * decapitalized property name will be returned, with the "get", "is" or "has" - * prefix stripped off. Getter methods are methods - *

- *
    - *
  • whose name start with "get" and who have a return type but no parameter - * or
  • - *
  • whose name starts with "is" and who have no parameter and return - * {@code boolean} or
  • - *
  • whose name starts with "has" and who have no parameter and return - * {@code boolean} (HV-specific, not mandated by JavaBeans spec).
  • - *
- * - * @param member The member for which to get the property name. - * - * @return The property name for the given member or {@code null} if the - * member is neither a field nor a getter method according to the - * JavaBeans standard. - */ - public static String getPropertyName(Member member) { - String name = null; - - if ( member instanceof Field ) { - name = member.getName(); - } - - if ( member instanceof Method ) { - String methodName = member.getName(); - for ( String prefix : PROPERTY_ACCESSOR_PREFIXES ) { - if ( methodName.startsWith( prefix ) ) { - name = StringHelper.decapitalize( methodName.substring( prefix.length() ) ); - } - } - } - return name; - } - - /** - * Checks whether the given executable is a valid JavaBeans getter method, which - * is the case if - *
    - *
  • its name starts with "get" and it has a return type but no parameter or
  • - *
  • its name starts with "is", it has no parameter and is returning - * {@code boolean} or
  • - *
  • its name starts with "has", it has no parameter and is returning - * {@code boolean} (HV-specific, not mandated by JavaBeans spec).
  • - *
- * - * @param executable The executable of interest. - * - * @return {@code true}, if the given executable is a JavaBeans getter method, - * {@code false} otherwise. - */ - public static boolean isGetterMethod(Executable executable) { - if ( !( executable instanceof Method ) ) { - return false; - } - - Method method = (Method) executable; - - if ( method.getParameterTypes().length != 0 ) { - return false; - } - - String methodName = method.getName(); - - // get() - if ( methodName.startsWith( PROPERTY_ACCESSOR_PREFIX_GET ) && method.getReturnType() != void.class ) { - return true; - } - //boolean is() - else if ( methodName.startsWith( PROPERTY_ACCESSOR_PREFIX_IS ) && method.getReturnType() == boolean.class ) { - return true; - } - //boolean has() - else if ( methodName.startsWith( PROPERTY_ACCESSOR_PREFIX_HAS ) && method.getReturnType() == boolean.class ) { - return true; - } - - return false; - } - /** * @param member The {@code Member} instance for which to retrieve the type. * @@ -204,30 +109,6 @@ else if ( member instanceof Constructor ) { return type; } - /** - * Returns the type of the parameter of the given method with the given parameter index. - * - * @param executable The executable of interest. - * @param parameterIndex The index of the parameter for which the type should be returned. - * - * @return The erased type. - */ - public static Type typeOf(Executable executable, int parameterIndex) { - Type[] genericParameterTypes = executable.getGenericParameterTypes(); - - // getGenericParameterTypes() doesn't return synthetic parameters; in this case fall back to getParameterTypes() - if ( parameterIndex >= genericParameterTypes.length ) { - genericParameterTypes = executable.getParameterTypes(); - } - - Type type = genericParameterTypes[parameterIndex]; - - if ( type instanceof TypeVariable ) { - type = TypeHelper.getErasedType( type ); - } - return type; - } - public static Object getValue(Field field, Object object) { try { return field.get( object ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/StringHelper.java b/engine/src/main/java/org/hibernate/validator/internal/util/StringHelper.java index 875033a11b..320e70938d 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/StringHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/StringHelper.java @@ -192,4 +192,8 @@ private static boolean startsWithSeveralUpperCaseLetters(String string) { Character.isUpperCase( string.charAt( 1 ) ); } + public static String format(String format, Object... args) { + return String.format( Locale.ROOT, format, args ); + } + } diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/TypeHelper.java b/engine/src/main/java/org/hibernate/validator/internal/util/TypeHelper.java index 680100121c..0a4e0c1318 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/TypeHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/TypeHelper.java @@ -31,6 +31,7 @@ import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import java.util.Collections; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -39,6 +40,7 @@ import javax.validation.ConstraintValidator; +import org.hibernate.validator.engine.HibernateValidatorEnhancedBean; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorDescriptor; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; @@ -52,6 +54,7 @@ public final class TypeHelper { private static final Map, Set>> SUBTYPES_BY_PRIMITIVE; + private static final int CONSTRAINT_TYPE_INDEX = 0; private static final int VALIDATOR_TYPE_INDEX = 1; private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); @@ -75,6 +78,10 @@ private TypeHelper() { throw new AssertionError(); } + public static boolean isHibernateValidatorEnhancedBean(Class clazz) { + return HibernateValidatorEnhancedBean.class.isAssignableFrom( clazz ); + } + public static boolean isAssignable(Type supertype, Type type) { Contracts.assertNotNull( supertype, "supertype" ); Contracts.assertNotNull( type, "type" ); @@ -324,24 +331,32 @@ public static Map> return validatorsTypes; } - public static Type extractType(Class> validator) { - Map resolvedTypes = newHashMap(); + public static Type extractValidatedType(Class> validator) { + return extractConstraintValidatorTypeArgumentType( validator, VALIDATOR_TYPE_INDEX ); + } + + public static Type extractConstraintType(Class> validator) { + return extractConstraintValidatorTypeArgumentType( validator, CONSTRAINT_TYPE_INDEX ); + } + + public static Type extractConstraintValidatorTypeArgumentType(Class> validator, int typeArgumentIndex) { + Map resolvedTypes = new HashMap<>(); Type constraintValidatorType = resolveTypes( resolvedTypes, validator ); //we now have all bind from a type to its resolution at one level - Type validatorType = ( (ParameterizedType) constraintValidatorType ).getActualTypeArguments()[VALIDATOR_TYPE_INDEX]; - if ( validatorType == null ) { + Type type = ( (ParameterizedType) constraintValidatorType ).getActualTypeArguments()[typeArgumentIndex]; + if ( type == null ) { throw LOG.getNullIsAnInvalidTypeForAConstraintValidatorException(); } - else if ( validatorType instanceof GenericArrayType ) { - validatorType = TypeHelper.getArrayType( TypeHelper.getComponentType( validatorType ) ); + else if ( type instanceof GenericArrayType ) { + type = TypeHelper.getArrayType( TypeHelper.getComponentType( type ) ); } - while ( resolvedTypes.containsKey( validatorType ) ) { - validatorType = resolvedTypes.get( validatorType ); + while ( resolvedTypes.containsKey( type ) ) { + type = resolvedTypes.get( type ); } //FIXME raise an exception if validatorType is not a class - return validatorType; + return type; } public static boolean isUnboundWildcard(Type type) { @@ -528,6 +543,7 @@ private static boolean isSuperAssignable(Type supertype, Type type) { *
  • a parameterized type
  • *
  • a type variable
  • *
  • the null type
  • + *
  • a wildcard type
  • * * * @param type the type to check @@ -541,7 +557,8 @@ private static boolean isReferenceType(Type type) { || type instanceof Class || type instanceof ParameterizedType || type instanceof TypeVariable - || type instanceof GenericArrayType; + || type instanceof GenericArrayType + || type instanceof WildcardType; } private static boolean isArraySupertype(Class type) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/annotation/AnnotationFactory.java b/engine/src/main/java/org/hibernate/validator/internal/util/annotation/AnnotationFactory.java index 9bd6c9270e..973137a018 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/annotation/AnnotationFactory.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/annotation/AnnotationFactory.java @@ -7,16 +7,11 @@ package org.hibernate.validator.internal.util.annotation; import java.lang.annotation.Annotation; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Proxy; import java.security.AccessController; import java.security.PrivilegedAction; -import org.hibernate.validator.internal.util.privilegedactions.ConstructorInstance; import org.hibernate.validator.internal.util.privilegedactions.GetClassLoader; -import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredConstructor; +import org.hibernate.validator.internal.util.privilegedactions.NewProxyInstance; /** * Creates live annotations (actually {@link AnnotationProxy} instances) from {@code AnnotationDescriptor}s. @@ -32,31 +27,11 @@ private AnnotationFactory() { } public static T create(AnnotationDescriptor descriptor) { - @SuppressWarnings("unchecked") - Class proxyClass = (Class) Proxy.getProxyClass( + return run( NewProxyInstance.action( run( GetClassLoader.fromClass( descriptor.getType() ) ), - descriptor.getType() - ); - InvocationHandler handler = new AnnotationProxy( descriptor ); - try { - return getProxyInstance( proxyClass, handler ); - } - catch (RuntimeException e) { - throw e; - } - catch (Exception e) { - throw new RuntimeException( e ); - } - } - - private static T getProxyInstance(Class proxyClass, InvocationHandler handler) throws - SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, - IllegalAccessException, InvocationTargetException { - final Constructor constructor = run( GetDeclaredConstructor.action( - proxyClass, - InvocationHandler.class + descriptor.getType(), + new AnnotationProxy( descriptor ) ) ); - return run( ConstructorInstance.action( constructor, handler ) ); } /** 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 816e30a440..42ec9d4134 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 @@ -7,21 +7,22 @@ package org.hibernate.validator.internal.util.logging; import static org.jboss.logging.Logger.Level.DEBUG; +import static org.jboss.logging.Logger.Level.ERROR; import static org.jboss.logging.Logger.Level.INFO; import static org.jboss.logging.Logger.Level.WARN; import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.invoke.MethodHandles.Lookup; -import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Member; -import java.lang.reflect.Method; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.time.Duration; import java.util.Collection; import java.util.List; +import java.util.Locale; +import java.util.ServiceConfigurationError; import java.util.Set; import java.util.regex.PatternSyntaxException; @@ -48,6 +49,12 @@ import org.hibernate.validator.internal.engine.messageinterpolation.parser.MessageDescriptorFormatException; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl.ConstraintType; import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; +import org.hibernate.validator.internal.properties.Callable; +import org.hibernate.validator.internal.properties.Constrainable; +import org.hibernate.validator.internal.properties.javabean.JavaBeanConstructor; +import org.hibernate.validator.internal.properties.javabean.JavaBeanMethod; +import org.hibernate.validator.internal.util.logging.formatter.ArrayOfClassesObjectFormatter; import org.hibernate.validator.internal.util.logging.formatter.ClassObjectFormatter; import org.hibernate.validator.internal.util.logging.formatter.CollectionOfClassesObjectFormatter; import org.hibernate.validator.internal.util.logging.formatter.CollectionOfObjectsToStringFormatter; @@ -55,7 +62,9 @@ import org.hibernate.validator.internal.util.logging.formatter.ExecutableFormatter; import org.hibernate.validator.internal.util.logging.formatter.ObjectArrayFormatter; import org.hibernate.validator.internal.util.logging.formatter.TypeFormatter; -import org.hibernate.validator.internal.xml.ContainerElementTypePath; +import org.hibernate.validator.internal.xml.mapping.ContainerElementTypePath; +import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider; +import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy; import org.hibernate.validator.spi.scripting.ScriptEvaluationException; import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; import org.hibernate.validator.spi.scripting.ScriptEvaluatorNotFoundException; @@ -83,27 +92,27 @@ public interface Log extends BasicLogger { @Message(id = 1, value = "Hibernate Validator %s") void version(String version); - @LogMessage(level = INFO) + @LogMessage(level = DEBUG) @Message(id = 2, value = "Ignoring XML configuration.") void ignoringXmlConfiguration(); - @LogMessage(level = INFO) + @LogMessage(level = DEBUG) @Message(id = 3, value = "Using %s as constraint validator factory.") void usingConstraintValidatorFactory(@FormatWith(ClassObjectFormatter.class) Class constraintValidatorFactoryClass); - @LogMessage(level = INFO) + @LogMessage(level = DEBUG) @Message(id = 4, value = "Using %s as message interpolator.") void usingMessageInterpolator(@FormatWith(ClassObjectFormatter.class) Class messageInterpolatorClass); - @LogMessage(level = INFO) + @LogMessage(level = DEBUG) @Message(id = 5, value = "Using %s as traversable resolver.") void usingTraversableResolver(@FormatWith(ClassObjectFormatter.class) Class traversableResolverClass); - @LogMessage(level = INFO) + @LogMessage(level = DEBUG) @Message(id = 6, value = "Using %s as validation provider.") void usingValidationProvider(@FormatWith(ClassObjectFormatter.class) Class> validationProviderClass); - @LogMessage(level = INFO) + @LogMessage(level = DEBUG) @Message(id = 7, value = "%s found. Parsing XML based configuration.") void parsingXMLFile(String fileName); @@ -247,14 +256,13 @@ GroupDefinitionException getUnableToExpandDefaultGroupListException(@FormatWith( GroupDefinitionException getWrongDefaultGroupSequenceProviderTypeException(@FormatWith(ClassObjectFormatter.class) Class beanClass); @Message(id = 56, value = "Method or constructor %1$s doesn't have a parameter with index %2$d.") - IllegalArgumentException getInvalidExecutableParameterIndexException(@FormatWith(ExecutableFormatter.class) Executable executable, int index); + IllegalArgumentException getInvalidExecutableParameterIndexException(Callable callable, int index); @Message(id = 59, value = "Unable to retrieve annotation parameter value.") ValidationException getUnableToRetrieveAnnotationParameterValueException(@Cause Exception e); - @Message(id = 62, - value = "Method or constructor %1$s has %2$s parameters, but the passed list of parameter meta data has a size of %3$s.") - IllegalArgumentException getInvalidLengthOfParameterMetaDataListException(@FormatWith(ExecutableFormatter.class) Executable executable, int nbParameters, int listSize); + @Message(id = 62, value = "Method or constructor %1$s has %2$s parameters, but the passed list of parameter meta data has a size of %3$s.") + IllegalArgumentException getInvalidLengthOfParameterMetaDataListException(Callable callable, int nbParameters, int listSize); @Message(id = 63, value = "Unable to instantiate %s.") ValidationException getUnableToInstantiateException(@FormatWith(ClassObjectFormatter.class) Class clazz, @Cause Exception e); @@ -379,7 +387,7 @@ RuntimeException getTryingToInstantiateAnnotationWithUnknownAttributesException( ValidationException getIsNotAConstraintValidatorClassException(@FormatWith(ClassObjectFormatter.class) Class validatorClass); @Message(id = 103, value = "%s is configured at least twice in xml.") - ValidationException getBeanClassHasAlreadyBeConfiguredInXmlException(@FormatWith(ClassObjectFormatter.class) Class beanClass); + ValidationException getBeanClassHasAlreadyBeenConfiguredInXmlException(@FormatWith(ClassObjectFormatter.class) Class beanClass); @Message(id = 104, value = "%1$s is defined twice in mapping xml for bean %2$s.") ValidationException getIsDefinedTwiceInMappingXmlForBeanException(String name, @FormatWith(ClassObjectFormatter.class) Class beanClass); @@ -427,7 +435,7 @@ ValidationException getAnnotationDoesNotContainAParameterException(@FormatWith(C ClassCastException getUnableToNarrowNodeTypeException(@FormatWith(ClassObjectFormatter.class) Class actualDescriptorType, ElementKind kind, @FormatWith(ClassObjectFormatter.class) Class expectedDescriptorType); - @LogMessage(level = INFO) + @LogMessage(level = DEBUG) @Message(id = 119, value = "Using %s as parameter name provider.") void usingParameterNameProvider(@FormatWith(ClassObjectFormatter.class) Class parameterNameProviderClass); @@ -460,31 +468,31 @@ ConstraintDeclarationException getMultipleGroupConversionsForSameSourceException @Message(id = 131, value = "A method return value must not be marked for cascaded validation more than once in a class hierarchy, but the following two methods are marked as such: %s, %s.") - ConstraintDeclarationException getMethodReturnValueMustNotBeMarkedMoreThanOnceForCascadedValidationException(@FormatWith(ExecutableFormatter.class) Executable executable1, @FormatWith(ExecutableFormatter.class) Executable executable2); + ConstraintDeclarationException getMethodReturnValueMustNotBeMarkedMoreThanOnceForCascadedValidationException(Callable callable1, Callable callable2); @Message(id = 132, value = "Void methods must not be constrained or marked for cascaded validation, but method %s is.") - ConstraintDeclarationException getVoidMethodsMustNotBeConstrainedException(@FormatWith(ExecutableFormatter.class) Executable executable); + ConstraintDeclarationException getVoidMethodsMustNotBeConstrainedException(Callable callable); @Message(id = 133, value = "%1$s does not contain a constructor with the parameter types %2$s.") ValidationException getBeanDoesNotContainConstructorException(@FormatWith(ClassObjectFormatter.class) Class beanClass, - @FormatWith(CollectionOfClassesObjectFormatter.class) List> parameterTypes); + @FormatWith(ArrayOfClassesObjectFormatter.class) Class[] parameterTypes); @Message(id = 134, value = "Unable to load parameter of type '%1$s' in %2$s.") ValidationException getInvalidParameterTypeException(String type, @FormatWith(ClassObjectFormatter.class) Class beanClass); @Message(id = 135, value = "%1$s does not contain a method with the name '%2$s' and parameter types %3$s.") ValidationException getBeanDoesNotContainMethodException(@FormatWith(ClassObjectFormatter.class) Class beanClass, String methodName, - @FormatWith(CollectionOfClassesObjectFormatter.class) List> parameterTypes); + @FormatWith(ArrayOfClassesObjectFormatter.class) Class[] parameterTypes); @Message(id = 136, value = "The specified constraint annotation class %1$s cannot be loaded.") ValidationException getUnableToLoadConstraintAnnotationClassException(String constraintAnnotationClassName, @Cause Exception e); @Message(id = 137, value = "The method '%1$s' is defined twice in the mapping xml for bean %2$s.") - ValidationException getMethodIsDefinedTwiceInMappingXmlForBeanException(Method name, @FormatWith(ClassObjectFormatter.class) Class beanClass); + ValidationException getMethodIsDefinedTwiceInMappingXmlForBeanException(JavaBeanMethod javaBeanMethod, @FormatWith(ClassObjectFormatter.class) Class beanClass); @Message(id = 138, value = "The constructor '%1$s' is defined twice in the mapping xml for bean %2$s.") - ValidationException getConstructorIsDefinedTwiceInMappingXmlForBeanException(Constructor name, @FormatWith(ClassObjectFormatter.class) Class beanClass); + ValidationException getConstructorIsDefinedTwiceInMappingXmlForBeanException(JavaBeanConstructor javaBeanConstructor, @FormatWith(ClassObjectFormatter.class) Class beanClass); @Message(id = 139, value = "The constraint '%1$s' defines multiple cross parameter validators. Only one is allowed.") @@ -499,7 +507,7 @@ ConstraintDeclarationException getImplicitConstraintTargetInAmbiguousConfigurati @Message(id = 142, value = "Cross parameter constraint %1$s is illegally placed on a parameterless method or constructor '%2$s'.") ConstraintDeclarationException getCrossParameterConstraintOnMethodWithoutParametersException( - @FormatWith(ClassObjectFormatter.class) Class constraint, @FormatWith(ExecutableFormatter.class) Executable executable); + @FormatWith(ClassObjectFormatter.class) Class constraint, Constrainable executable); @Message(id = 143, value = "Cross parameter constraint %1$s is illegally placed on class level.") @@ -508,7 +516,7 @@ ConstraintDeclarationException getCrossParameterConstraintOnMethodWithoutParamet @Message(id = 144, value = "Cross parameter constraint %1$s is illegally placed on field '%2$s'.") ConstraintDeclarationException getCrossParameterConstraintOnFieldException(@FormatWith(ClassObjectFormatter.class) Class constraint, - Member field); + Constrainable field); @Message(id = 146, value = "No parameter nodes may be added since path %s doesn't refer to a cross-parameter constraint.") @@ -534,11 +542,11 @@ UnexpectedTypeException getMultipleValidatorsForSameTypeException(@FormatWith(Cl @Message(id = 151, value = "A method overriding another method must not redefine the parameter constraint configuration, but method %2$s redefines the configuration of %1$s.") - ConstraintDeclarationException getParameterConfigurationAlteredInSubTypeException(@FormatWith(ExecutableFormatter.class) Executable superMethod, @FormatWith(ExecutableFormatter.class) Executable subMethod); + ConstraintDeclarationException getParameterConfigurationAlteredInSubTypeException(Callable superMethod, Callable subMethod); @Message(id = 152, value = "Two methods defined in parallel types must not declare parameter constraints, if they are overridden by the same method, but methods %s and %s both define parameter constraints.") - ConstraintDeclarationException getParameterConstraintsDefinedInMethodsFromParallelTypesException(@FormatWith(ExecutableFormatter.class) Executable method1, @FormatWith(ExecutableFormatter.class) Executable method2); + ConstraintDeclarationException getParameterConstraintsDefinedInMethodsFromParallelTypesException(Callable method1, Callable method2); @Message(id = 153, value = "The constraint %1$s used ConstraintTarget#%2$s but is not specified on a method or constructor.") @@ -580,7 +588,7 @@ ConstraintDefinitionException getValidatorForCrossParameterConstraintMustEitherV @Message(id = 161, value = "Two methods defined in parallel types must not define group conversions for a cascaded method return value, if they are overridden by the same method, but methods %s and %s both define parameter constraints.") - ConstraintDeclarationException getMethodsFromParallelTypesMustNotDefineGroupConversionsForCascadedReturnValueException(@FormatWith(ExecutableFormatter.class) Executable method1, @FormatWith(ExecutableFormatter.class) Executable method2); + ConstraintDeclarationException getMethodsFromParallelTypesMustNotDefineGroupConversionsForCascadedReturnValueException(Callable method1, Callable method2); @Message(id = 162, value = "The validated type %1$s does not specify the constructor/method: %2$s") @@ -624,15 +632,15 @@ ConstraintDefinitionException getValidatorForCrossParameterConstraintMustEitherV @Message(id = 173, value = "Method %2$s of type %1$s is configured more than once via the programmatic constraint declaration API.") - ValidationException getMethodHasAlreadyBeConfiguredViaProgrammaticApiException(@FormatWith(ClassObjectFormatter.class) Class beanClass, String method); + ValidationException getMethodHasAlreadyBeenConfiguredViaProgrammaticApiException(@FormatWith(ClassObjectFormatter.class) Class beanClass, String method); @Message(id = 174, value = "Parameter %3$s of method or constructor %2$s of type %1$s is configured more than once via the programmatic constraint declaration API.") - ValidationException getParameterHasAlreadyBeConfiguredViaProgrammaticApiException(@FormatWith(ClassObjectFormatter.class) Class beanClass, @FormatWith(ExecutableFormatter.class) Executable executable, int parameterIndex); + ValidationException getParameterHasAlreadyBeConfiguredViaProgrammaticApiException(@FormatWith(ClassObjectFormatter.class) Class beanClass, Callable callable, int parameterIndex); @Message(id = 175, value = "The return value of method or constructor %2$s of type %1$s is configured more than once via the programmatic constraint declaration API.") - ValidationException getReturnValueHasAlreadyBeConfiguredViaProgrammaticApiException(@FormatWith(ClassObjectFormatter.class) Class beanClass, @FormatWith(ExecutableFormatter.class) Executable executable); + ValidationException getReturnValueHasAlreadyBeConfiguredViaProgrammaticApiException(@FormatWith(ClassObjectFormatter.class) Class beanClass, Callable callable); @Message(id = 176, value = "Constructor %2$s of type %1$s is configured more than once via the programmatic constraint declaration API.") @@ -640,7 +648,7 @@ ConstraintDefinitionException getValidatorForCrossParameterConstraintMustEitherV @Message(id = 177, value = "Cross-parameter constraints for the method or constructor %2$s of type %1$s are declared more than once via the programmatic constraint declaration API.") - ValidationException getCrossParameterElementHasAlreadyBeConfiguredViaProgrammaticApiException(@FormatWith(ClassObjectFormatter.class) Class beanClass, @FormatWith(ExecutableFormatter.class) Executable executable); + ValidationException getCrossParameterElementHasAlreadyBeConfiguredViaProgrammaticApiException(@FormatWith(ClassObjectFormatter.class) Class beanClass, Callable callable); @Message(id = 178, value = "Multiplier cannot be negative: %d.") IllegalArgumentException getMultiplierCannotBeNegativeException(int multiplier); @@ -696,7 +704,7 @@ ConstraintDeclarationException getInconsistentValueUnwrappingConfigurationBetwee @Message(id = 198, value = "No suitable value extractor found for type %1$s.") ConstraintDeclarationException getNoValueExtractorFoundForUnwrapException(Type type); - @LogMessage(level = INFO) + @LogMessage(level = DEBUG) @Message(id = 200, value = "Using %s as clock provider.") void usingClockProvider(@FormatWith(ClassObjectFormatter.class) Class clockProviderClass); @@ -713,12 +721,12 @@ ConstraintDeclarationException getInconsistentValueUnwrappingConfigurationBetwee ValueExtractorDefinitionException getValueExtractorDeclaresExtractedValueMultipleTimesException(@FormatWith(ClassObjectFormatter.class) Class extractorType); @Message(id = 205, value = "Invalid unwrapping configuration for constraint %2$s on %1$s. You can only define one of 'Unwrapping.Skip' or 'Unwrapping.Unwrap'.") - ConstraintDeclarationException getInvalidUnwrappingConfigurationForConstraintException(Member member, @FormatWith(ClassObjectFormatter.class) Class constraint); + ConstraintDeclarationException getInvalidUnwrappingConfigurationForConstraintException(Constrainable constrainable, @FormatWith(ClassObjectFormatter.class) Class constraint); @Message(id = 206, value = "Unable to instantiate value extractor class %s.") ValidationException getUnableToInstantiateValueExtractorClassException(String valueExtractorClassName, @Cause ValidationException e); - @LogMessage(level = INFO) + @LogMessage(level = DEBUG) @Message(id = 207, value = "Adding value extractor %s.") void addingValueExtractor(@FormatWith(ClassObjectFormatter.class) Class> valueExtractorClass); @@ -804,7 +812,7 @@ ConstraintDeclarationException getNoValueExtractorFoundForTypeException(@FormatW @Message(id = 229, value = "Unable to cast %1$s to %2$s.") ClassCastException getUnableToCastException(Object object, @FormatWith(ClassObjectFormatter.class) Class clazz); - @LogMessage(level = INFO) + @LogMessage(level = DEBUG) @Message(id = 230, value = "Using %s as script evaluator factory.") void usingScriptEvaluatorFactory(@FormatWith(ClassObjectFormatter.class) Class scriptEvaluatorFactoryClass); @@ -831,7 +839,7 @@ ConstraintDeclarationException getNoValueExtractorFoundForTypeException(@FormatW ValidationException getUnableToAccessMethodException(Lookup lookup, @FormatWith(ClassObjectFormatter.class) Class clazz, String methodName, @FormatWith(ObjectArrayFormatter.class) Object[] parameterTypes, @Cause Throwable e); - @LogMessage(level = INFO) + @LogMessage(level = DEBUG) @Message(id = 238, value = "Temporal validation tolerance set to %1$s.") void logTemporalValidationTolerance(@FormatWith(DurationFormatter.class) Duration tolerance); @@ -841,4 +849,55 @@ ValidationException getUnableToAccessMethodException(Lookup lookup, @FormatWith( @LogMessage(level = DEBUG) @Message(id = 240, value = "Constraint validator payload set to %1$s.") void logConstraintValidatorPayload(Object payload); + + @Message(id = 241, value = "Encountered unsupported element %1$s while parsing the XML configuration.") + ValidationException logUnknownElementInXmlConfiguration(String tag); + + @LogMessage(level = WARN) + @Message(id = 242, value = "Unable to load or instantiate JPA aware resolver %1$s. All properties will per default be traversable.") + void logUnableToLoadOrInstantiateJPAAwareResolver(String traversableResolverClassName); + + @Message(id = 243, value = "Constraint %2$s references constraint validator type %1$s, but this validator is defined for constraint type %3$s.") + ConstraintDefinitionException getConstraintValidatorDefinitionConstraintMismatchException( + @FormatWith(ClassObjectFormatter.class) Class> constraintValidatorImplementationType, + @FormatWith(ClassObjectFormatter.class) Class registeredConstraintAnnotationType, + @FormatWith(TypeFormatter.class) Type declaredConstraintAnnotationType); + + @Message(id = 244, value = "ConstrainedElement expected class was %1$s, but instead received %2$s.") + AssertionError getUnexpectedConstraintElementType(@FormatWith(ClassObjectFormatter.class) Class expecting, @FormatWith(ClassObjectFormatter.class) Class got); + + @Message(id = 245, value = "Allowed constraint element types are FIELD and GETTER, but instead received %1$s.") + AssertionError getUnsupportedConstraintElementType(ConstrainedElement.ConstrainedElementKind kind); + + @LogMessage(level = DEBUG) + @Message(id = 246, value = "Using %s as getter property selection strategy.") + void usingGetterPropertySelectionStrategy(@FormatWith(ClassObjectFormatter.class) Class getterPropertySelectionStrategyClass); + + @Message(id = 247, value = "Unable to instantiate getter property selection strategy class %s.") + ValidationException getUnableToInstantiateGetterPropertySelectionStrategyClassException(String getterPropertySelectionStrategyClassName, @Cause Exception e); + + @Message(id = 248, value = "Unable to get an XML schema named %s.") + ValidationException unableToGetXmlSchema(String schemaResourceName); + + @Message(id = 250, value = "Uninitialized locale: %s. Please register your locale as a locale to initialize when initializing your ValidatorFactory.") + ValidationException uninitializedLocale(Locale locale); + + @LogMessage(level = ERROR) + @Message(id = 251, value = "An error occurred while loading an instance of service %s.") + void unableToLoadInstanceOfService(String serviceName, @Cause ServiceConfigurationError e); + + @LogMessage(level = DEBUG) + @Message(id = 252, value = "Using %s as property node name provider.") + void usingPropertyNodeNameProvider(@FormatWith(ClassObjectFormatter.class) Class propertyNodeNameProviderClass); + + @Message(id = 253, value = "Unable to instantiate property node name provider class %s.") + ValidationException getUnableToInstantiatePropertyNodeNameProviderClassException(String propertyNodeNameProviderClassName, @Cause Exception e); + + @LogMessage(level = WARN) + @Message(id = 254, value = "Missing parameter metadata for %s, which declares implicit or synthetic parameters." + + " Automatic resolution of generic type information for method parameters" + + " may yield incorrect results if multiple parameters have the same erasure." + + " To solve this, compile your code with the '-parameters' flag." + ) + void missingParameterMetadataWithSyntheticOrImplicitParameters(@FormatWith(ExecutableFormatter.class) Executable executable); } 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..5ae9361a3d 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 @@ -86,7 +86,7 @@ public interface Messages { "This can happen most notably in a Google App Engine environment or when running Hibernate Validator as Java 9 named module. " + "A PlatformResourceBundleLocator without bundle aggregation was created. " + "This only affects you in case you are using multiple ConstraintDefinitionContributor JARs. " + - "ConstraintDefinitionContributors are a Hibernate Validator specific feature. All Bean Validation " + + "ConstraintDefinitionContributors are a Hibernate Validator specific feature. All Jakarta Bean Validation " + "features work as expected. See also https://hibernate.atlassian.net/browse/HV-1023.") String unableToUseResourceBundleAggregation(); diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/logging/formatter/ArrayOfClassesObjectFormatter.java b/engine/src/main/java/org/hibernate/validator/internal/util/logging/formatter/ArrayOfClassesObjectFormatter.java new file mode 100644 index 0000000000..9399bf630c --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/util/logging/formatter/ArrayOfClassesObjectFormatter.java @@ -0,0 +1,32 @@ +/* + * 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.util.logging.formatter; + +import java.util.Arrays; +import java.util.stream.Collectors; + +/** + * Used with JBoss Logging to display array of class names in log messages. + * + * @author Guillaume Smet + * @author Marko Bekhta + */ +public class ArrayOfClassesObjectFormatter { + + private final String stringRepresentation; + + public ArrayOfClassesObjectFormatter(Class[] classes) { + this.stringRepresentation = Arrays.stream( classes ) + .map( c -> c.getName() ) + .collect( Collectors.joining( ", " ) ); + } + + @Override + public String toString() { + return stringRepresentation; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetAnnotationAttribute.java b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetAnnotationAttribute.java index 1bbd5a7a12..f74103b7e9 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetAnnotationAttribute.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetAnnotationAttribute.java @@ -38,6 +38,7 @@ private GetAnnotationAttribute(Annotation annotation, String attributeName, Clas } @Override + @SuppressWarnings("unchecked") public T run() { try { Method m = annotation.getClass().getMethod( attributeName ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetClassLoader.java b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetClassLoader.java index 9f1a626e94..79a817d456 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetClassLoader.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetClassLoader.java @@ -16,10 +16,13 @@ * @author Emmanuel Bernard */ public final class GetClassLoader implements PrivilegedAction { + + private static final GetClassLoader CONTEXT = new GetClassLoader( null ); + private final Class clazz; public static GetClassLoader fromContext() { - return new GetClassLoader( null ); + return CONTEXT; } public static GetClassLoader fromClass(Class clazz) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetInstancesFromServiceLoader.java b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetInstancesFromServiceLoader.java index 3a61292f4b..c0f7a509ce 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetInstancesFromServiceLoader.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetInstancesFromServiceLoader.java @@ -6,6 +6,10 @@ */ package org.hibernate.validator.internal.util.privilegedactions; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; + +import java.lang.invoke.MethodHandles; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Iterator; @@ -22,6 +26,8 @@ public class GetInstancesFromServiceLoader implements PrivilegedAction clazz; + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + private GetInstancesFromServiceLoader(ClassLoader primaryClassLoader, Class clazz) { this.primaryClassLoader = primaryClassLoader; this.clazz = clazz; @@ -57,6 +63,8 @@ private List loadInstances(ClassLoader classloader) { // ignore, because it can happen when multiple // services are present and some of them are not class loader // compatible with our API. + // log an error still as it can hide a legitimate issue (see HV-1689) + LOG.unableToLoadInstanceOfService( loader.getClass().getName(), e ); } } return instances; diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetMethodFromGetterNameCandidates.java b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetMethodFromGetterNameCandidates.java new file mode 100644 index 0000000000..73fd5f8351 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetMethodFromGetterNameCandidates.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.util.privilegedactions; + +import java.lang.reflect.Method; +import java.security.PrivilegedAction; +import java.util.Set; + +/** + * Returns the method with the specified property name or {@code null} if it does not exist. This action will + * iterate through getter name candidates and return the first found method. + *

    + * {@code GetMethodFromPropertyName#lookForMethodsInHierarchy} parameter controls if we need to check for methods on + * superclasses/implemented interfaces or not, if equals to {@code false} it will use + * {@link Class#getDeclaredMethod(String, Class[])}, and {@link Class#getMethod(String, Class[])} otherwise. + * + * @author Marko Bekhta + */ +public final class GetMethodFromGetterNameCandidates implements PrivilegedAction { + + private final Class clazz; + private final Set getterNameCandidates; + private final boolean lookForMethodsInHierarchy; + + private GetMethodFromGetterNameCandidates(Class clazz, Set getterNameCandidates, boolean lookForMethodsInHierarchy) { + this.clazz = clazz; + this.getterNameCandidates = getterNameCandidates; + this.lookForMethodsInHierarchy = lookForMethodsInHierarchy; + } + + public static GetMethodFromGetterNameCandidates action(Class clazz, Set getterNameCandidates) { + return new GetMethodFromGetterNameCandidates( clazz, getterNameCandidates, false ); + } + + public static GetMethodFromGetterNameCandidates action(Class clazz, Set possibleMethodNames, boolean lookForMethodsInHierarchy) { + return new GetMethodFromGetterNameCandidates( clazz, possibleMethodNames, lookForMethodsInHierarchy ); + } + + + @Override + public Method run() { + for ( String methodName : getterNameCandidates ) { + try { + if ( lookForMethodsInHierarchy ) { + return clazz.getMethod( methodName ); + } + else { + return clazz.getDeclaredMethod( methodName ); + } + } + catch (NoSuchMethodException e) { + // just ignore the exception + } + } + + return null; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetMethodFromPropertyName.java b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetMethodFromPropertyName.java deleted file mode 100644 index 359f617837..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetMethodFromPropertyName.java +++ /dev/null @@ -1,49 +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.util.privilegedactions; - -import java.lang.reflect.Method; -import java.security.PrivilegedAction; - -/** - * Returns the method with the specified property name or {@code null} if it does not exist. This method will prepend - * 'is' and 'get' to the property name and capitalize the first letter. - * - * @author Emmanuel Bernard - * @author Hardy Ferentschik - */ -public final class GetMethodFromPropertyName implements PrivilegedAction { - private final Class clazz; - private final String property; - - public static GetMethodFromPropertyName action(Class clazz, String property) { - return new GetMethodFromPropertyName( clazz, property ); - } - - private GetMethodFromPropertyName(Class clazz, String property) { - this.clazz = clazz; - this.property = property; - } - - @Override - public Method run() { - try { - char[] string = property.toCharArray(); - string[0] = Character.toUpperCase( string[0] ); - String fullMethodName = new String( string ); - try { - return clazz.getMethod( "get" + fullMethodName ); - } - catch (NoSuchMethodException e) { - return clazz.getMethod( "is" + fullMethodName ); - } - } - catch (NoSuchMethodException e) { - return null; - } - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/LoadClass.java b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/LoadClass.java index da33182ca3..2ea36ccf30 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/LoadClass.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/LoadClass.java @@ -26,6 +26,7 @@ * @author Hardy Ferentschik * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI * @author Gunnar Morling + * @author Guillaume Smet */ public final class LoadClass implements PrivilegedAction> { @@ -37,22 +38,32 @@ public final class LoadClass implements PrivilegedAction> { private final ClassLoader classLoader; + private final ClassLoader initialThreadContextClassLoader; + /** * when true, it will check the Thread Context ClassLoader when the class is not found in the provided one */ private final boolean fallbackOnTCCL; public static LoadClass action(String className, ClassLoader classLoader) { - return new LoadClass( className, classLoader, true ); + return action( className, classLoader, true ); } public static LoadClass action(String className, ClassLoader classLoader, boolean fallbackOnTCCL) { - return new LoadClass( className, classLoader, fallbackOnTCCL ); + return new LoadClass( className, classLoader, null, fallbackOnTCCL ); + } + + /** + * in some cases, the TCCL has been overridden so we need to pass it explicitly. + */ + public static LoadClass action(String className, ClassLoader classLoader, ClassLoader initialThreadContextClassLoader) { + return new LoadClass( className, classLoader, initialThreadContextClassLoader, true ); } - private LoadClass(String className, ClassLoader classLoader, boolean fallbackOnTCCL) { + private LoadClass(String className, ClassLoader classLoader, ClassLoader initialThreadContextClassLoader, boolean fallbackOnTCCL) { this.className = className; this.classLoader = classLoader; + this.initialThreadContextClassLoader = initialThreadContextClassLoader; this.fallbackOnTCCL = fallbackOnTCCL; } @@ -80,7 +91,9 @@ private Class loadClassInValidatorNameSpace() { exception = e; } if ( fallbackOnTCCL ) { - ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + ClassLoader contextClassLoader = initialThreadContextClassLoader != null + ? initialThreadContextClassLoader + : Thread.currentThread().getContextClassLoader(); if ( contextClassLoader != null ) { try { return Class.forName( className, false, contextClassLoader ); @@ -100,20 +113,22 @@ private Class loadClassInValidatorNameSpace() { private Class loadNonValidatorClass() { Exception exception = null; - try { - if ( classLoader != null ) { + if ( classLoader != null ) { + try { return Class.forName( className, false, classLoader ); } - } - catch (ClassNotFoundException e) { - exception = e; - } - catch (RuntimeException e) { - exception = e; + catch (ClassNotFoundException e) { + exception = e; + } + catch (RuntimeException e) { + exception = e; + } } if ( fallbackOnTCCL ) { try { - ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + ClassLoader contextClassLoader = initialThreadContextClassLoader != null + ? initialThreadContextClassLoader + : Thread.currentThread().getContextClassLoader(); if ( contextClassLoader != null ) { return Class.forName( className, false, contextClassLoader ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/NewInstance.java b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/NewInstance.java index 92404467a9..8951907f44 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/NewInstance.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/NewInstance.java @@ -7,6 +7,7 @@ package org.hibernate.validator.internal.util.privilegedactions; import java.lang.invoke.MethodHandles; +import java.lang.reflect.InvocationTargetException; import java.security.PrivilegedAction; import org.hibernate.validator.internal.util.logging.Log; @@ -37,9 +38,9 @@ private NewInstance(Class clazz, String message) { @Override public T run() { try { - return clazz.newInstance(); + return clazz.getConstructor().newInstance(); } - catch (InstantiationException e) { + catch (InstantiationException | NoSuchMethodException | InvocationTargetException e) { throw LOG.getUnableToInstantiateException( message, clazz, e ); } catch (IllegalAccessException e) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/NewJaxbContext.java b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/NewJaxbContext.java deleted file mode 100644 index cb8f7e16da..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/NewJaxbContext.java +++ /dev/null @@ -1,35 +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.util.privilegedactions; - -import java.security.PrivilegedExceptionAction; - -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; - -/** - * Returns a new {@link JAXBContext} for the given class. - * - * @author Gunnar Morling - */ -public final class NewJaxbContext implements PrivilegedExceptionAction { - - private final Class clazz; - - public static NewJaxbContext action(Class clazz) { - return new NewJaxbContext( clazz ); - } - - private NewJaxbContext(Class clazz) { - this.clazz = clazz; - } - - @Override - public JAXBContext run() throws JAXBException { - return JAXBContext.newInstance( clazz ); - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/NewProxyInstance.java b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/NewProxyInstance.java new file mode 100644 index 0000000000..46dfbf2bee --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/NewProxyInstance.java @@ -0,0 +1,49 @@ +/* + * 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.util.privilegedactions; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import java.security.PrivilegedAction; + +/** + * Execute proxy creation as privileged action. + * + * @author Guillaume Smet + */ +public final class NewProxyInstance implements PrivilegedAction { + + private final ClassLoader classLoader; + private final Class[] interfaces; + private final InvocationHandler invocationHandler; + + public static NewProxyInstance action(ClassLoader classLoader, Class interfaze, InvocationHandler invocationHandler) { + return new NewProxyInstance( classLoader, interfaze, invocationHandler ); + } + + public static NewProxyInstance action(ClassLoader classLoader, Class[] interfaces, InvocationHandler invocationHandler) { + return new NewProxyInstance( classLoader, interfaces, invocationHandler ); + } + + private NewProxyInstance(ClassLoader classLoader, Class[] interfaces, InvocationHandler invocationHandler) { + this.classLoader = classLoader; + this.interfaces = interfaces; + this.invocationHandler = invocationHandler; + } + + private NewProxyInstance(ClassLoader classLoader, Class interfaze, InvocationHandler invocationHandler) { + this.classLoader = classLoader; + this.interfaces = new Class[] { interfaze }; + this.invocationHandler = invocationHandler; + } + + @SuppressWarnings("unchecked") + @Override + public T run() { + return (T) Proxy.newProxyInstance( classLoader, interfaces, invocationHandler ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/Unmarshal.java b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/Unmarshal.java deleted file mode 100644 index 73073081f3..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/Unmarshal.java +++ /dev/null @@ -1,40 +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.util.privilegedactions; - -import java.security.PrivilegedExceptionAction; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Unmarshaller; -import javax.xml.stream.XMLEventReader; - -/** - * Unmarshals the given source. - * - * @author Gunnar Morling - */ -public final class Unmarshal implements PrivilegedExceptionAction> { - - private final Unmarshaller unmarshaller; - private final XMLEventReader xmlEventReader; - private final Class clazz; - - public static Unmarshal action(Unmarshaller unmarshaller, XMLEventReader xmlEventReader, Class clazz) { - return new Unmarshal( unmarshaller, xmlEventReader, clazz ); - } - - private Unmarshal(Unmarshaller unmarshaller, XMLEventReader xmlEventReader, Class clazz) { - this.unmarshaller = unmarshaller; - this.xmlEventReader = xmlEventReader; - this.clazz = clazz; - } - - @Override - public JAXBElement run() throws JAXBException { - return unmarshaller.unmarshal( xmlEventReader, clazz ); - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/stereotypes/Lazy.java b/engine/src/main/java/org/hibernate/validator/internal/util/stereotypes/Lazy.java new file mode 100644 index 0000000000..63f90de397 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/util/stereotypes/Lazy.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.internal.util.stereotypes; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Documents that the value assigned to the annotated field is lazily initialized and shouldn't be accessed directly but + * via a getter. + * + * @author Guillaume Smet + */ +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.FIELD) +public @interface Lazy { +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/AbstractStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/AbstractStaxBuilder.java new file mode 100644 index 0000000000..bbe5f22108 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/AbstractStaxBuilder.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.xml; + +import java.util.Optional; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.Attribute; +import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; + +/** + * Other Stax xml builders should extend from this one. + * Provides some common functionality like reading an attribute value + * or value of a simple tag. + * + * @author Marko Bekhta + */ +public abstract class AbstractStaxBuilder { + + protected abstract String getAcceptableQName(); + + /** + * Checks if the given {@link XMLEvent} is a {@link StartElement} and if the + * corresponding xml tag can be processed based on a tag name. + * + * @param xmlEvent an event to check + * + * @return {@code true} if corresponding event can be processed by current builder, + * {@code false} otherwise + */ + protected boolean accept(XMLEvent xmlEvent) { + return xmlEvent.isStartElement() && xmlEvent.asStartElement().getName().getLocalPart().equals( getAcceptableQName() ); + } + + public boolean process(XMLEventReader xmlEventReader, XMLEvent xmlEvent) { + if ( accept( xmlEvent ) ) { + try { + add( xmlEventReader, xmlEvent ); + } + catch (XMLStreamException e) { + throw new IllegalStateException( e ); + } + return true; + } + return false; + } + + protected abstract void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException; + + /** + * Reads a value between a simple tag element. In case of a {@code some-value} will + * return {@code some-value} as a string. + * + * @param xmlEventReader a current {@link XMLEventReader} + * + * @return a value of a current xml tag as a string + */ + protected String readSingleElement(XMLEventReader xmlEventReader) throws XMLStreamException { + // trimming the string value as it might contain leading/trailing spaces or \n + XMLEvent xmlEvent = xmlEventReader.nextEvent(); + StringBuilder stringBuilder = new StringBuilder( xmlEvent.asCharacters().getData() ); + while ( xmlEventReader.peek().isCharacters() ) { + xmlEvent = xmlEventReader.nextEvent(); + stringBuilder.append( xmlEvent.asCharacters().getData() ); + } + return stringBuilder.toString().trim(); + } + + /** + * Reads a value of an attribute of a given element. + * + * @param startElement an element to get an attribute from + * @param qName a {@link QName} of an attribute to read + * + * @return a value of an attribute if it is present, {@link Optional#empty()} otherwise + */ + protected Optional readAttribute(StartElement startElement, QName qName) { + Attribute attribute = startElement.getAttributeByName( qName ); + return Optional.ofNullable( attribute ).map( Attribute::getValue ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/CloseIgnoringInputStream.java b/engine/src/main/java/org/hibernate/validator/internal/xml/CloseIgnoringInputStream.java index 7db4d6fc7b..e5def26adc 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/CloseIgnoringInputStream.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/CloseIgnoringInputStream.java @@ -17,7 +17,7 @@ * * @author Guillaume Smet */ -class CloseIgnoringInputStream extends FilterInputStream { +public class CloseIgnoringInputStream extends FilterInputStream { public CloseIgnoringInputStream(InputStream in) { super( in ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedExecutableBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedExecutableBuilder.java deleted file mode 100644 index 382abe3206..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedExecutableBuilder.java +++ /dev/null @@ -1,345 +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.xml; - -import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; -import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; - -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.validation.ValidationException; - -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; -import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; -import org.hibernate.validator.internal.metadata.core.MetaConstraint; -import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; -import org.hibernate.validator.internal.metadata.location.ConstraintLocation; -import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; -import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable; -import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter; -import org.hibernate.validator.internal.util.ExecutableHelper; -import org.hibernate.validator.internal.util.ReflectionHelper; -import org.hibernate.validator.internal.util.logging.Log; -import org.hibernate.validator.internal.util.logging.LoggerFactory; -import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredConstructor; -import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredMethod; -import org.hibernate.validator.internal.xml.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration; -import org.hibernate.validator.internal.xml.binding.ConstraintType; -import org.hibernate.validator.internal.xml.binding.ConstructorType; -import org.hibernate.validator.internal.xml.binding.CrossParameterType; -import org.hibernate.validator.internal.xml.binding.MethodType; -import org.hibernate.validator.internal.xml.binding.ParameterType; -import org.hibernate.validator.internal.xml.binding.ReturnValueType; - -/** - * Builder for constrained methods and constructors. - * - * @author Hardy Ferentschik - * @author Guillaume Smet - */ -class ConstrainedExecutableBuilder { - - private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - - private final ClassLoadingHelper classLoadingHelper; - private final MetaConstraintBuilder metaConstraintBuilder; - private final GroupConversionBuilder groupConversionBuilder; - private final ConstrainedParameterBuilder constrainedParameterBuilder; - private final AnnotationProcessingOptionsImpl annotationProcessingOptions; - - ConstrainedExecutableBuilder(ClassLoadingHelper classLoadingHelper, MetaConstraintBuilder metaConstraintBuilder, - GroupConversionBuilder groupConversionBuilder, AnnotationProcessingOptionsImpl annotationProcessingOptions) { - this.classLoadingHelper = classLoadingHelper; - this.metaConstraintBuilder = metaConstraintBuilder; - this.groupConversionBuilder = groupConversionBuilder; - this.constrainedParameterBuilder = new ConstrainedParameterBuilder( - metaConstraintBuilder, - groupConversionBuilder, - annotationProcessingOptions - ); - this.annotationProcessingOptions = annotationProcessingOptions; - } - - Set buildMethodConstrainedExecutable(List methods, - Class beanClass, - String defaultPackage) { - Set constrainedExecutables = newHashSet(); - List alreadyProcessedMethods = newArrayList(); - for ( MethodType methodType : methods ) { - // parse the parameters - List> parameterTypes = createParameterTypes( - methodType.getParameter(), - beanClass, - defaultPackage - ); - - String methodName = methodType.getName(); - - final Method method = run( - GetDeclaredMethod.action( - beanClass, - methodName, - parameterTypes.toArray( new Class[parameterTypes.size()] ) - ) - ); - - if ( method == null ) { - throw LOG.getBeanDoesNotContainMethodException( - beanClass, - methodName, - parameterTypes - ); - } - - if ( alreadyProcessedMethods.contains( method ) ) { - throw LOG.getMethodIsDefinedTwiceInMappingXmlForBeanException( method, beanClass ); - } - else { - alreadyProcessedMethods.add( method ); - } - - // ignore annotations - if ( methodType.getIgnoreAnnotations() != null ) { - annotationProcessingOptions.ignoreConstraintAnnotationsOnMember( - method, - methodType.getIgnoreAnnotations() - ); - } - - ConstrainedExecutable constrainedExecutable = parseExecutableType( - defaultPackage, - methodType.getParameter(), - methodType.getCrossParameter(), - methodType.getReturnValue(), - method - ); - - constrainedExecutables.add( constrainedExecutable ); - } - return constrainedExecutables; - } - - Set buildConstructorConstrainedExecutable(List constructors, - Class beanClass, - String defaultPackage) { - Set constrainedExecutables = newHashSet(); - List> alreadyProcessedConstructors = newArrayList(); - for ( ConstructorType constructorType : constructors ) { - // parse the parameters - List> constructorParameterTypes = createParameterTypes( - constructorType.getParameter(), - beanClass, - defaultPackage - ); - - final Constructor constructor = run( - GetDeclaredConstructor.action( - beanClass, - constructorParameterTypes.toArray( new Class[constructorParameterTypes.size()] ) - ) - ); - - if ( constructor == null ) { - throw LOG.getBeanDoesNotContainConstructorException( - beanClass, - constructorParameterTypes - ); - } - if ( alreadyProcessedConstructors.contains( constructor ) ) { - throw LOG.getConstructorIsDefinedTwiceInMappingXmlForBeanException( - constructor, - beanClass - ); - } - else { - alreadyProcessedConstructors.add( constructor ); - } - - // ignore annotations - if ( constructorType.getIgnoreAnnotations() != null ) { - annotationProcessingOptions.ignoreConstraintAnnotationsOnMember( - constructor, - constructorType.getIgnoreAnnotations() - ); - } - - ConstrainedExecutable constrainedExecutable = parseExecutableType( - defaultPackage, - constructorType.getParameter(), - constructorType.getCrossParameter(), - constructorType.getReturnValue(), - constructor - ); - constrainedExecutables.add( constrainedExecutable ); - } - return constrainedExecutables; - } - - private ConstrainedExecutable parseExecutableType(String defaultPackage, - List parameterTypeList, - CrossParameterType crossParameterType, - ReturnValueType returnValueType, - Executable executable) { - List parameterMetaData = constrainedParameterBuilder.buildConstrainedParameters( - parameterTypeList, - executable, - defaultPackage - ); - - Set> crossParameterConstraints = parseCrossParameterConstraints( - defaultPackage, - crossParameterType, - executable - ); - - // parse the return value - Set> returnValueConstraints = new HashSet<>(); - Set> returnValueTypeArgumentConstraints = new HashSet<>(); - CascadingMetaDataBuilder cascadingMetaDataBuilder = parseReturnValueType( - returnValueType, - executable, - returnValueConstraints, - returnValueTypeArgumentConstraints, - defaultPackage - ); - - return new ConstrainedExecutable( - ConfigurationSource.XML, - executable, - parameterMetaData, - crossParameterConstraints, - returnValueConstraints, - returnValueTypeArgumentConstraints, - cascadingMetaDataBuilder - ); - } - - private Set> parseCrossParameterConstraints(String defaultPackage, - CrossParameterType crossParameterType, - Executable executable) { - - Set> crossParameterConstraints = newHashSet(); - if ( crossParameterType == null ) { - return crossParameterConstraints; - } - - ConstraintLocation constraintLocation = ConstraintLocation.forCrossParameter( executable ); - - for ( ConstraintType constraintType : crossParameterType.getConstraint() ) { - MetaConstraint metaConstraint = metaConstraintBuilder.buildMetaConstraint( - constraintLocation, - constraintType, - ExecutableHelper.getElementType( executable ), - defaultPackage, - ConstraintDescriptorImpl.ConstraintType.CROSS_PARAMETER - ); - crossParameterConstraints.add( metaConstraint ); - } - - // ignore annotations - if ( crossParameterType.getIgnoreAnnotations() != null ) { - annotationProcessingOptions.ignoreConstraintAnnotationsForCrossParameterConstraint( - executable, - crossParameterType.getIgnoreAnnotations() - ); - } - - return crossParameterConstraints; - } - - private CascadingMetaDataBuilder parseReturnValueType(ReturnValueType returnValueType, - Executable executable, - Set> returnValueConstraints, - Set> returnValueTypeArgumentConstraints, - String defaultPackage) { - if ( returnValueType == null ) { - return CascadingMetaDataBuilder.nonCascading(); - } - - ConstraintLocation constraintLocation = ConstraintLocation.forReturnValue( executable ); - for ( ConstraintType constraint : returnValueType.getConstraint() ) { - MetaConstraint metaConstraint = metaConstraintBuilder.buildMetaConstraint( - constraintLocation, - constraint, - ExecutableHelper.getElementType( executable ), - defaultPackage, - ConstraintDescriptorImpl.ConstraintType.GENERIC - ); - returnValueConstraints.add( metaConstraint ); - } - - ContainerElementTypeConfigurationBuilder containerElementTypeConfigurationBuilder = new ContainerElementTypeConfigurationBuilder( - metaConstraintBuilder, groupConversionBuilder, constraintLocation, defaultPackage ); - ContainerElementTypeConfiguration containerElementTypeConfiguration = containerElementTypeConfigurationBuilder - .build( returnValueType.getContainerElementType(), ReflectionHelper.typeOf( executable ) ); - - returnValueTypeArgumentConstraints.addAll( containerElementTypeConfiguration.getMetaConstraints() ); - - // ignore annotations - if ( returnValueType.getIgnoreAnnotations() != null ) { - annotationProcessingOptions.ignoreConstraintAnnotationsForReturnValue( - executable, - returnValueType.getIgnoreAnnotations() - ); - } - - return getCascadingMetaDataForReturnValue( containerElementTypeConfiguration.getTypeParametersCascadingMetaData(), executable, returnValueType, - defaultPackage ); - } - - private List> createParameterTypes(List parameterList, - Class beanClass, - String defaultPackage) { - List> parameterTypes = newArrayList(); - for ( ParameterType parameterType : parameterList ) { - String type = null; - try { - type = parameterType.getType(); - Class parameterClass = classLoadingHelper.loadClass( type, defaultPackage ); - parameterTypes.add( parameterClass ); - } - catch (ValidationException e) { - throw LOG.getInvalidParameterTypeException( type, beanClass ); - } - } - - return parameterTypes; - } - - private CascadingMetaDataBuilder getCascadingMetaDataForReturnValue(Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, Executable executable, - ReturnValueType returnValueType, String defaultPackage) { - Type type = ReflectionHelper.typeOf( executable ); - boolean isCascaded = returnValueType.getValid() != null; - Map, Class> groupConversions = groupConversionBuilder.buildGroupConversionMap( - returnValueType.getConvertGroup(), - defaultPackage - ); - - return CascadingMetaDataBuilder.annotatedObject( type, isCascaded, containerElementTypesCascadingMetaData, groupConversions ); - } - - /** - * 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 T run(PrivilegedAction action) { - return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedFieldBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedFieldBuilder.java deleted file mode 100644 index f36ed6e054..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedFieldBuilder.java +++ /dev/null @@ -1,138 +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.xml; - -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Field; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; -import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; -import org.hibernate.validator.internal.metadata.core.MetaConstraint; -import org.hibernate.validator.internal.metadata.location.ConstraintLocation; -import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; -import org.hibernate.validator.internal.metadata.raw.ConstrainedField; -import org.hibernate.validator.internal.util.ReflectionHelper; -import org.hibernate.validator.internal.util.logging.Log; -import org.hibernate.validator.internal.util.logging.LoggerFactory; -import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredField; -import org.hibernate.validator.internal.xml.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration; -import org.hibernate.validator.internal.xml.binding.ConstraintType; -import org.hibernate.validator.internal.xml.binding.FieldType; - -/** - * Builder for constraint fields. - * - * @author Hardy Ferentschik - * @author Guillaume Smet - */ -class ConstrainedFieldBuilder { - private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - - private final GroupConversionBuilder groupConversionBuilder; - private final MetaConstraintBuilder metaConstraintBuilder; - private final AnnotationProcessingOptionsImpl annotationProcessingOptions; - - ConstrainedFieldBuilder(MetaConstraintBuilder metaConstraintBuilder, GroupConversionBuilder groupConversionBuilder, - AnnotationProcessingOptionsImpl annotationProcessingOptions) { - this.metaConstraintBuilder = metaConstraintBuilder; - this.groupConversionBuilder = groupConversionBuilder; - this.annotationProcessingOptions = annotationProcessingOptions; - } - - Set buildConstrainedFields(List fields, - Class beanClass, - String defaultPackage) { - Set constrainedFields = new HashSet<>(); - List alreadyProcessedFieldNames = new ArrayList<>(); - for ( FieldType fieldType : fields ) { - Field field = findField( beanClass, fieldType.getName(), alreadyProcessedFieldNames ); - ConstraintLocation constraintLocation = ConstraintLocation.forField( field ); - Set> metaConstraints = new HashSet<>(); - - for ( ConstraintType constraint : fieldType.getConstraint() ) { - MetaConstraint metaConstraint = metaConstraintBuilder.buildMetaConstraint( - constraintLocation, - constraint, - java.lang.annotation.ElementType.FIELD, - defaultPackage, - null - ); - metaConstraints.add( metaConstraint ); - } - - ContainerElementTypeConfigurationBuilder containerElementTypeConfigurationBuilder = new ContainerElementTypeConfigurationBuilder( - metaConstraintBuilder, groupConversionBuilder, constraintLocation, defaultPackage ); - ContainerElementTypeConfiguration containerElementTypeConfiguration = containerElementTypeConfigurationBuilder - .build( fieldType.getContainerElementType(), ReflectionHelper.typeOf( field ) ); - - ConstrainedField constrainedField = new ConstrainedField( - ConfigurationSource.XML, - field, - metaConstraints, - containerElementTypeConfiguration.getMetaConstraints(), - getCascadingMetaDataForField( containerElementTypeConfiguration.getTypeParametersCascadingMetaData(), field, fieldType, defaultPackage ) - ); - constrainedFields.add( constrainedField ); - - // ignore annotations - if ( fieldType.getIgnoreAnnotations() != null ) { - annotationProcessingOptions.ignoreConstraintAnnotationsOnMember( - field, - fieldType.getIgnoreAnnotations() - ); - } - } - - return constrainedFields; - } - - private CascadingMetaDataBuilder getCascadingMetaDataForField(Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, Field field, - FieldType fieldType, String defaultPackage) { - Type type = ReflectionHelper.typeOf( field ); - boolean isCascaded = fieldType.getValid() != null; - Map, Class> groupConversions = groupConversionBuilder.buildGroupConversionMap( - fieldType.getConvertGroup(), - defaultPackage - ); - - return CascadingMetaDataBuilder.annotatedObject( type, isCascaded, containerElementTypesCascadingMetaData, groupConversions ); - } - - private static Field findField(Class beanClass, String fieldName, List alreadyProcessedFieldNames) { - if ( alreadyProcessedFieldNames.contains( fieldName ) ) { - throw LOG.getIsDefinedTwiceInMappingXmlForBeanException( fieldName, beanClass ); - } - else { - alreadyProcessedFieldNames.add( fieldName ); - } - - final Field field = run( GetDeclaredField.action( beanClass, fieldName ) ); - if ( field == null ) { - throw LOG.getBeanDoesNotContainTheFieldException( beanClass, fieldName ); - } - return field; - } - - /** - * 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 T run(PrivilegedAction action) { - return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedGetterBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedGetterBuilder.java deleted file mode 100644 index f78c94f4e2..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedGetterBuilder.java +++ /dev/null @@ -1,146 +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.xml; - -import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; -import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; - -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; -import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; -import org.hibernate.validator.internal.metadata.core.MetaConstraint; -import org.hibernate.validator.internal.metadata.location.ConstraintLocation; -import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; -import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable; -import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter; -import org.hibernate.validator.internal.util.ReflectionHelper; -import org.hibernate.validator.internal.util.logging.Log; -import org.hibernate.validator.internal.util.logging.LoggerFactory; -import org.hibernate.validator.internal.util.privilegedactions.GetMethodFromPropertyName; -import org.hibernate.validator.internal.xml.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration; -import org.hibernate.validator.internal.xml.binding.ConstraintType; -import org.hibernate.validator.internal.xml.binding.GetterType; - -/** - * Builder for constraint getters. - * - * @author Hardy Ferentschik - * @author Guillaume Smet - */ -class ConstrainedGetterBuilder { - - private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - - private final GroupConversionBuilder groupConversionBuilder; - private final MetaConstraintBuilder metaConstraintBuilder; - private final AnnotationProcessingOptionsImpl annotationProcessingOptions; - - ConstrainedGetterBuilder(MetaConstraintBuilder metaConstraintBuilder, GroupConversionBuilder groupConversionBuilder, - AnnotationProcessingOptionsImpl annotationProcessingOptions) { - this.metaConstraintBuilder = metaConstraintBuilder; - this.groupConversionBuilder = groupConversionBuilder; - this.annotationProcessingOptions = annotationProcessingOptions; - } - - Set buildConstrainedGetters(List getterList, - Class beanClass, - String defaultPackage) { - Set constrainedExecutables = newHashSet(); - List alreadyProcessedGetterNames = newArrayList(); - for ( GetterType getterType : getterList ) { - String getterName = getterType.getName(); - Method getter = findGetter( beanClass, getterName, alreadyProcessedGetterNames ); - ConstraintLocation constraintLocation = ConstraintLocation.forGetter( getter ); - - Set> metaConstraints = newHashSet(); - for ( ConstraintType constraint : getterType.getConstraint() ) { - MetaConstraint metaConstraint = metaConstraintBuilder.buildMetaConstraint( - constraintLocation, - constraint, - java.lang.annotation.ElementType.METHOD, - defaultPackage, - null - ); - metaConstraints.add( metaConstraint ); - } - - ContainerElementTypeConfigurationBuilder containerElementTypeConfigurationBuilder = new ContainerElementTypeConfigurationBuilder( - metaConstraintBuilder, groupConversionBuilder, constraintLocation, defaultPackage ); - ContainerElementTypeConfiguration containerElementTypeConfiguration = containerElementTypeConfigurationBuilder - .build( getterType.getContainerElementType(), ReflectionHelper.typeOf( getter ) ); - - ConstrainedExecutable constrainedGetter = new ConstrainedExecutable( - ConfigurationSource.XML, - getter, - Collections.emptyList(), - Collections.>emptySet(), - metaConstraints, - containerElementTypeConfiguration.getMetaConstraints(), - getCascadingMetaDataForGetter( containerElementTypeConfiguration.getTypeParametersCascadingMetaData(), getter, getterType, defaultPackage ) - ); - constrainedExecutables.add( constrainedGetter ); - - // ignore annotations - if ( getterType.getIgnoreAnnotations() != null ) { - annotationProcessingOptions.ignoreConstraintAnnotationsOnMember( - getter, - getterType.getIgnoreAnnotations() - ); - } - } - - return constrainedExecutables; - } - - private CascadingMetaDataBuilder getCascadingMetaDataForGetter(Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, Method method, - GetterType getterType, String defaultPackage) { - Type type = ReflectionHelper.typeOf( method ); - boolean isCascaded = getterType.getValid() != null; - Map, Class> groupConversions = groupConversionBuilder.buildGroupConversionMap( - getterType.getConvertGroup(), - defaultPackage - ); - - return CascadingMetaDataBuilder.annotatedObject( type, isCascaded, containerElementTypesCascadingMetaData, groupConversions ); - } - - private static Method findGetter(Class beanClass, String getterName, List alreadyProcessedGetterNames) { - if ( alreadyProcessedGetterNames.contains( getterName ) ) { - throw LOG.getIsDefinedTwiceInMappingXmlForBeanException( getterName, beanClass ); - } - else { - alreadyProcessedGetterNames.add( getterName ); - } - - final Method method = run( GetMethodFromPropertyName.action( beanClass, getterName ) ); - if ( method == null ) { - throw LOG.getBeanDoesNotContainThePropertyException( beanClass, getterName ); - } - - return method; - } - - /** - * 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 T run(PrivilegedAction action) { - return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedParameterBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedParameterBuilder.java deleted file mode 100644 index 576d6de700..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedParameterBuilder.java +++ /dev/null @@ -1,113 +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.xml; - -import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; - -import java.lang.annotation.ElementType; -import java.lang.reflect.Executable; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; -import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; -import org.hibernate.validator.internal.metadata.core.MetaConstraint; -import org.hibernate.validator.internal.metadata.location.ConstraintLocation; -import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; -import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter; -import org.hibernate.validator.internal.util.ReflectionHelper; -import org.hibernate.validator.internal.xml.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration; -import org.hibernate.validator.internal.xml.binding.ConstraintType; -import org.hibernate.validator.internal.xml.binding.ParameterType; - -/** - * Builder for constraint parameters. - * - * @author Hardy Ferentschik - * @author Guillaume Smet - */ -class ConstrainedParameterBuilder { - - private final GroupConversionBuilder groupConversionBuilder; - private final MetaConstraintBuilder metaConstraintBuilder; - private final AnnotationProcessingOptionsImpl annotationProcessingOptions; - - ConstrainedParameterBuilder(MetaConstraintBuilder metaConstraintBuilder, - GroupConversionBuilder groupConversionBuilder, - AnnotationProcessingOptionsImpl annotationProcessingOptions) { - this.metaConstraintBuilder = metaConstraintBuilder; - this.groupConversionBuilder = groupConversionBuilder; - this.annotationProcessingOptions = annotationProcessingOptions; - } - - List buildConstrainedParameters(List parameterList, - Executable executable, - String defaultPackage) { - List constrainedParameters = newArrayList(); - int i = 0; - for ( ParameterType parameterType : parameterList ) { - ConstraintLocation constraintLocation = ConstraintLocation.forParameter( executable, i ); - Type type = ReflectionHelper.typeOf( executable, i ); - - Set> metaConstraints = new HashSet<>(); - for ( ConstraintType constraint : parameterType.getConstraint() ) { - MetaConstraint metaConstraint = metaConstraintBuilder.buildMetaConstraint( - constraintLocation, - constraint, - ElementType.PARAMETER, - defaultPackage, - null - ); - metaConstraints.add( metaConstraint ); - } - - ContainerElementTypeConfigurationBuilder containerElementTypeConfigurationBuilder = new ContainerElementTypeConfigurationBuilder( - metaConstraintBuilder, groupConversionBuilder, constraintLocation, defaultPackage ); - ContainerElementTypeConfiguration containerElementTypeConfiguration = containerElementTypeConfigurationBuilder - .build( parameterType.getContainerElementType(), type ); - - // ignore annotations - if ( parameterType.getIgnoreAnnotations() != null ) { - annotationProcessingOptions.ignoreConstraintAnnotationsOnParameter( - executable, - i, - parameterType.getIgnoreAnnotations() - ); - } - - ConstrainedParameter constrainedParameter = new ConstrainedParameter( - ConfigurationSource.XML, - executable, - type, - i, - metaConstraints, - containerElementTypeConfiguration.getMetaConstraints(), - getCascadingMetaDataForParameter( containerElementTypeConfiguration.getTypeParametersCascadingMetaData(), type, parameterType, defaultPackage ) - ); - constrainedParameters.add( constrainedParameter ); - i++; - } - - return constrainedParameters; - } - - - private CascadingMetaDataBuilder getCascadingMetaDataForParameter(Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, Type type, - ParameterType parameterType, String defaultPackage) { - boolean isCascaded = parameterType.getValid() != null; - Map, Class> groupConversions = groupConversionBuilder.buildGroupConversionMap( - parameterType.getConvertGroup(), - defaultPackage - ); - - return CascadingMetaDataBuilder.annotatedObject( type, isCascaded, containerElementTypesCascadingMetaData, groupConversions ); - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedTypeBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedTypeBuilder.java deleted file mode 100644 index 4c54ae39ad..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedTypeBuilder.java +++ /dev/null @@ -1,99 +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.xml; - -import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; -import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; -import org.hibernate.validator.internal.metadata.core.MetaConstraint; -import org.hibernate.validator.internal.metadata.location.ConstraintLocation; -import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; -import org.hibernate.validator.internal.metadata.raw.ConstrainedType; -import org.hibernate.validator.internal.xml.binding.ClassType; -import org.hibernate.validator.internal.xml.binding.ConstraintType; -import org.hibernate.validator.internal.xml.binding.GroupSequenceType; - -/** - * Builder for constraint types. - * - * @author Hardy Ferentschik - */ -class ConstrainedTypeBuilder { - - private final ClassLoadingHelper classLoadingHelper; - private final MetaConstraintBuilder metaConstraintBuilder; - private final AnnotationProcessingOptionsImpl annotationProcessingOptions; - private final Map, List>> defaultSequences; - - public ConstrainedTypeBuilder(ClassLoadingHelper classLoadingHelper, - MetaConstraintBuilder metaConstraintBuilder, - AnnotationProcessingOptionsImpl annotationProcessingOptions, - Map, List>> defaultSequences) { - this.classLoadingHelper = classLoadingHelper; - this.metaConstraintBuilder = metaConstraintBuilder; - this.annotationProcessingOptions = annotationProcessingOptions; - this.defaultSequences = defaultSequences; - } - - ConstrainedType buildConstrainedType(ClassType classType, Class beanClass, String defaultPackage) { - if ( classType == null ) { - return null; - } - - // group sequence - List> groupSequence = createGroupSequence( classType.getGroupSequence(), defaultPackage ); - if ( !groupSequence.isEmpty() ) { - defaultSequences.put( beanClass, groupSequence ); - } - - // constraints - ConstraintLocation constraintLocation = ConstraintLocation.forClass( beanClass ); - Set> metaConstraints = newHashSet(); - for ( ConstraintType constraint : classType.getConstraint() ) { - MetaConstraint metaConstraint = metaConstraintBuilder.buildMetaConstraint( - constraintLocation, - constraint, - java.lang.annotation.ElementType.TYPE, - defaultPackage, - null - ); - metaConstraints.add( metaConstraint ); - } - - // ignore annotation - if ( classType.getIgnoreAnnotations() != null ) { - annotationProcessingOptions.ignoreClassLevelConstraintAnnotations( - beanClass, - classType.getIgnoreAnnotations() - ); - } - - return new ConstrainedType( - ConfigurationSource.XML, - beanClass, - metaConstraints - ); - } - - private List> createGroupSequence(GroupSequenceType groupSequenceType, String defaultPackage) { - List> groupSequence = newArrayList(); - if ( groupSequenceType != null ) { - for ( String groupName : groupSequenceType.getValue() ) { - Class group = classLoadingHelper.loadClass( groupName, defaultPackage ); - groupSequence.add( group ); - } - } - return groupSequence; - } -} - - diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/ContainerElementTypeConfigurationBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/ContainerElementTypeConfigurationBuilder.java deleted file mode 100644 index 521fbfd3c7..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/ContainerElementTypeConfigurationBuilder.java +++ /dev/null @@ -1,186 +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.xml; - -import java.lang.invoke.MethodHandles; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.hibernate.validator.internal.engine.valueextraction.ArrayElement; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; -import org.hibernate.validator.internal.metadata.core.MetaConstraint; -import org.hibernate.validator.internal.metadata.location.ConstraintLocation; -import org.hibernate.validator.internal.util.CollectionHelper; -import org.hibernate.validator.internal.util.ReflectionHelper; -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.xml.binding.ConstraintType; -import org.hibernate.validator.internal.xml.binding.ContainerElementTypeType; - -/** - * Builds the cascading and type argument constraints configuration from the {@link ContainerElementType} elements. - * - * @author Guillaume Smet - */ -class ContainerElementTypeConfigurationBuilder { - - private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - - private final ConstraintLocation rootConstraintLocation; - - private final MetaConstraintBuilder metaConstraintBuilder; - - private final GroupConversionBuilder groupConversionBuilder; - - private final String defaultPackage; - - private final Set configuredPaths = new HashSet<>(); - - ContainerElementTypeConfigurationBuilder(MetaConstraintBuilder metaConstraintBuilder, GroupConversionBuilder groupConversionBuilder, - ConstraintLocation rootConstraintLocation, String defaultPackage) { - this.metaConstraintBuilder = metaConstraintBuilder; - this.groupConversionBuilder = groupConversionBuilder; - this.rootConstraintLocation = rootConstraintLocation; - this.defaultPackage = defaultPackage; - } - - ContainerElementTypeConfiguration build(List xmlContainerElementTypes, Type enclosingType) { - return add( ContainerElementTypePath.root(), xmlContainerElementTypes, rootConstraintLocation, enclosingType ); - } - - private ContainerElementTypeConfiguration add(ContainerElementTypePath parentConstraintElementTypePath, List xmlContainerElementTypes, - ConstraintLocation parentConstraintLocation, Type enclosingType) { - if ( xmlContainerElementTypes.isEmpty() ) { - return new ContainerElementTypeConfiguration( Collections.emptySet(), Collections.emptyMap() ); - } - - // HV-1428 Container element support is disabled for arrays - if ( TypeHelper.isArray( enclosingType ) ) { - throw LOG.getContainerElementConstraintsAndCascadedValidationNotSupportedOnArraysException( enclosingType ); - } - - if ( !( enclosingType instanceof ParameterizedType ) && !TypeHelper.isArray( enclosingType ) ) { - throw LOG.getTypeIsNotAParameterizedNorArrayTypeException( enclosingType ); - } - - Set> metaConstraints = new HashSet<>(); - Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaDataBuilder = - CollectionHelper.newHashMap( xmlContainerElementTypes.size() ); - - boolean isArray = TypeHelper.isArray( enclosingType ); - TypeVariable[] typeParameters = isArray ? new TypeVariable[0] : ReflectionHelper.getClassFromType( enclosingType ).getTypeParameters(); - - for ( ContainerElementTypeType xmlContainerElementType : xmlContainerElementTypes ) { - Integer typeArgumentIndex = getTypeArgumentIndex( xmlContainerElementType, typeParameters, isArray, enclosingType ); - - ContainerElementTypePath constraintElementTypePath = ContainerElementTypePath.of( parentConstraintElementTypePath, typeArgumentIndex ); - boolean configuredBefore = !configuredPaths.add( constraintElementTypePath ); - if ( configuredBefore ) { - throw LOG.getContainerElementTypeHasAlreadyBeenConfiguredViaXmlMappingConfigurationException( rootConstraintLocation, constraintElementTypePath ); - } - - TypeVariable typeParameter = getTypeParameter( typeParameters, typeArgumentIndex, isArray, enclosingType ); - Type containerElementType = getContainerElementType( enclosingType, typeArgumentIndex, isArray ); - ConstraintLocation containerElementTypeConstraintLocation = ConstraintLocation.forTypeArgument( parentConstraintLocation, typeParameter, - containerElementType ); - - for ( ConstraintType constraint : xmlContainerElementType.getConstraint() ) { - MetaConstraint metaConstraint = metaConstraintBuilder.buildMetaConstraint( - containerElementTypeConstraintLocation, - constraint, - java.lang.annotation.ElementType.TYPE_USE, - defaultPackage, - null - ); - metaConstraints.add( metaConstraint ); - } - - ContainerElementTypeConfiguration nestedContainerElementTypeConfiguration = add( constraintElementTypePath, xmlContainerElementType.getContainerElementType(), - containerElementTypeConstraintLocation, containerElementType ); - - metaConstraints.addAll( nestedContainerElementTypeConfiguration.getMetaConstraints() ); - - boolean isCascaded = xmlContainerElementType.getValid() != null; - - containerElementTypesCascadingMetaDataBuilder.put( typeParameter, new CascadingMetaDataBuilder( enclosingType, typeParameter, isCascaded, - nestedContainerElementTypeConfiguration.getTypeParametersCascadingMetaData(), - groupConversionBuilder.buildGroupConversionMap( xmlContainerElementType.getConvertGroup(), defaultPackage ) ) - ); - } - - return new ContainerElementTypeConfiguration( metaConstraints, containerElementTypesCascadingMetaDataBuilder ); - } - - private Integer getTypeArgumentIndex(ContainerElementTypeType xmlContainerElementType, TypeVariable[] typeParameters, boolean isArray, Type enclosingType) { - if ( isArray ) { - return null; - } - - Integer typeArgumentIndex = xmlContainerElementType.getTypeArgumentIndex(); - if ( typeArgumentIndex == null ) { - if ( typeParameters.length > 1 ) { - throw LOG.getNoTypeArgumentIndexIsGivenForTypeWithMultipleTypeArgumentsException( enclosingType ); - } - typeArgumentIndex = 0; - } - - return typeArgumentIndex; - } - - private TypeVariable getTypeParameter(TypeVariable[] typeParameters, Integer typeArgumentIndex, boolean isArray, Type enclosingType) { - TypeVariable typeParameter; - if ( !isArray ) { - if ( typeArgumentIndex > typeParameters.length - 1 ) { - throw LOG.getInvalidTypeArgumentIndexException( enclosingType, typeArgumentIndex ); - } - - typeParameter = typeParameters[typeArgumentIndex]; - } - else { - typeParameter = new ArrayElement( enclosingType ); - } - return typeParameter; - } - - private Type getContainerElementType(Type enclosingType, Integer typeArgumentIndex, boolean isArray) { - Type containerElementType; - if ( !isArray ) { - containerElementType = ( (ParameterizedType) enclosingType ).getActualTypeArguments()[typeArgumentIndex]; - } - else { - containerElementType = TypeHelper.getComponentType( enclosingType ); - } - return containerElementType; - } - - static class ContainerElementTypeConfiguration { - - private final Set> metaConstraints; - - private final Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaDataBuilder; - - private ContainerElementTypeConfiguration(Set> metaConstraints, Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData) { - this.metaConstraints = metaConstraints; - this.containerElementTypesCascadingMetaDataBuilder = containerElementTypesCascadingMetaData; - } - - public Set> getMetaConstraints() { - return metaConstraints; - } - - public Map, CascadingMetaDataBuilder> getTypeParametersCascadingMetaData() { - return containerElementTypesCascadingMetaDataBuilder; - } - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/GroupConversionBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/GroupConversionBuilder.java deleted file mode 100644 index 6077edc763..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/GroupConversionBuilder.java +++ /dev/null @@ -1,57 +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.xml; - -import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; - -import java.lang.invoke.MethodHandles; -import java.util.List; -import java.util.Map; - -import javax.validation.groups.Default; - -import org.hibernate.validator.internal.util.CollectionHelper; -import org.hibernate.validator.internal.util.logging.Log; -import org.hibernate.validator.internal.util.logging.LoggerFactory; -import org.hibernate.validator.internal.xml.binding.GroupConversionType; - -/** - * Builder for group conversions. - * - * @author Hardy Ferentschik - */ -class GroupConversionBuilder { - - private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - - private final ClassLoadingHelper classLoadingHelper; - - GroupConversionBuilder(ClassLoadingHelper classLoadingHelper) { - this.classLoadingHelper = classLoadingHelper; - } - - Map, Class> buildGroupConversionMap(List groupConversionTypes, - String defaultPackage) { - Map, Class> groupConversionMap = newHashMap(); - for ( GroupConversionType groupConversionType : groupConversionTypes ) { - Class fromClass = groupConversionType.getFrom() == null ? - Default.class : - classLoadingHelper.loadClass( groupConversionType.getFrom(), defaultPackage ); - Class toClass = classLoadingHelper.loadClass( groupConversionType.getTo(), defaultPackage ); - - if ( groupConversionMap.containsKey( fromClass ) ) { - throw LOG.getMultipleGroupConversionsForSameSourceException( - fromClass, - CollectionHelper.>asSet( groupConversionMap.get( fromClass ), toClass ) ); - } - - groupConversionMap.put( fromClass, toClass ); - } - - return groupConversionMap; - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/LocalNamespace.java b/engine/src/main/java/org/hibernate/validator/internal/xml/LocalNamespace.java deleted file mode 100644 index ffca5e1f1e..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/LocalNamespace.java +++ /dev/null @@ -1,31 +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.xml; - -/** - * Bean Validation namespaces reference. - * - * @author Guillaume Smet - */ -public enum LocalNamespace { - VALIDATION_1_CONFIGURATION("http://jboss.org/xml/ns/javax/validation/configuration"), - VALIDATION_1_MAPPING("http://jboss.org/xml/ns/javax/validation/mapping"), - - VALIDATION_2_CONFIGURATION("http://xmlns.jcp.org/xml/ns/validation/configuration"), - VALIDATION_2_MAPPING("http://xmlns.jcp.org/xml/ns/validation/mapping"); - - private String namespaceURI; - - private LocalNamespace(String namespaceURI) { - this.namespaceURI = namespaceURI; - } - - public String getNamespaceURI() { - return namespaceURI; - } - -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/MappingXmlParser.java b/engine/src/main/java/org/hibernate/validator/internal/xml/MappingXmlParser.java deleted file mode 100644 index 271b879b53..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/MappingXmlParser.java +++ /dev/null @@ -1,428 +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.xml; - -import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; -import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; - -import java.io.IOException; -import java.io.InputStream; -import java.lang.annotation.Annotation; -import java.lang.invoke.MethodHandles; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.security.PrivilegedExceptionAction; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.validation.ConstraintValidator; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Unmarshaller; -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.XMLStreamException; -import javax.xml.transform.stream.StreamSource; -import javax.xml.validation.Schema; -import javax.xml.validation.Validator; - -import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorDescriptor; -import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -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.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.util.TypeResolutionHelper; -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.NewJaxbContext; -import org.hibernate.validator.internal.util.privilegedactions.SetContextClassLoader; -import org.hibernate.validator.internal.util.privilegedactions.Unmarshal; -import org.hibernate.validator.internal.xml.binding.BeanType; -import org.hibernate.validator.internal.xml.binding.ConstraintDefinitionType; -import org.hibernate.validator.internal.xml.binding.ConstraintMappingsType; -import org.hibernate.validator.internal.xml.binding.ValidatedByType; -import org.xml.sax.SAXException; - -/** - * XML parser for validation-mapping files. - * - * @author Hardy Ferentschik - */ -public class MappingXmlParser { - - private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - - private final Set> processedClasses = newHashSet(); - private final ConstraintHelper constraintHelper; - private final TypeResolutionHelper typeResolutionHelper; - private final ValueExtractorManager valueExtractorManager; - private final AnnotationProcessingOptionsImpl annotationProcessingOptions; - private final Map, List>> defaultSequences; - private final Map, Set> constrainedElements; - - private final XmlParserHelper xmlParserHelper; - - private final ClassLoadingHelper classLoadingHelper; - - private static final Map SCHEMAS_BY_VERSION = Collections.unmodifiableMap( getSchemasByVersion() ); - - private static Map getSchemasByVersion() { - Map schemasByVersion = new HashMap<>(); - - schemasByVersion.put( "1.0", "META-INF/validation-mapping-1.0.xsd" ); - schemasByVersion.put( "1.1", "META-INF/validation-mapping-1.1.xsd" ); - schemasByVersion.put( "2.0", "META-INF/validation-mapping-2.0.xsd" ); - - return schemasByVersion; - } - - public MappingXmlParser(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, - ClassLoader externalClassLoader) { - this.constraintHelper = constraintHelper; - this.typeResolutionHelper = typeResolutionHelper; - this.valueExtractorManager = valueExtractorManager; - this.annotationProcessingOptions = new AnnotationProcessingOptionsImpl(); - this.defaultSequences = newHashMap(); - this.constrainedElements = newHashMap(); - this.xmlParserHelper = new XmlParserHelper(); - this.classLoadingHelper = new ClassLoadingHelper( externalClassLoader ); - } - - /** - * Parses the given set of input stream representing XML constraint - * mappings. - * - * @param mappingStreams The streams to parse. Must support the mark/reset contract. - */ - public final void parse(Set mappingStreams) { - try { - // JAXBContext#newInstance() requires several permissions internally and doesn't use any privileged blocks - // itself; Wrapping it here avoids that all calling code bases need to have these permissions as well - JAXBContext jc = run( NewJaxbContext.action( ConstraintMappingsType.class ) ); - - MetaConstraintBuilder metaConstraintBuilder = new MetaConstraintBuilder( - classLoadingHelper, - constraintHelper, - typeResolutionHelper, - valueExtractorManager - ); - GroupConversionBuilder groupConversionBuilder = new GroupConversionBuilder( classLoadingHelper ); - - ConstrainedTypeBuilder constrainedTypeBuilder = new ConstrainedTypeBuilder( - classLoadingHelper, - metaConstraintBuilder, - annotationProcessingOptions, - defaultSequences - ); - ConstrainedFieldBuilder constrainedFieldBuilder = new ConstrainedFieldBuilder( - metaConstraintBuilder, - groupConversionBuilder, - annotationProcessingOptions - ); - ConstrainedExecutableBuilder constrainedExecutableBuilder = new ConstrainedExecutableBuilder( - classLoadingHelper, - metaConstraintBuilder, - groupConversionBuilder, - annotationProcessingOptions - ); - ConstrainedGetterBuilder constrainedGetterBuilder = new ConstrainedGetterBuilder( - metaConstraintBuilder, - groupConversionBuilder, - annotationProcessingOptions - ); - - Set alreadyProcessedConstraintDefinitions = newHashSet(); - for ( InputStream in : mappingStreams ) { - ConstraintMappingsType mapping = unmarshal( jc, in ); - String defaultPackage = mapping.getDefaultPackage(); - - parseConstraintDefinitions( - mapping.getConstraintDefinition(), - defaultPackage, - alreadyProcessedConstraintDefinitions - ); - - for ( BeanType bean : mapping.getBean() ) { - processBeanType( - constrainedTypeBuilder, - constrainedFieldBuilder, - constrainedExecutableBuilder, - constrainedGetterBuilder, - defaultPackage, - bean - ); - } - - in.reset(); - } - } - catch (JAXBException | SAXException | IOException | XMLStreamException e) { - throw LOG.getErrorParsingMappingFileException( e ); - } - } - - private ConstraintMappingsType unmarshal(JAXBContext jc, InputStream in) throws JAXBException, XMLStreamException, IOException, SAXException { - ClassLoader previousTccl = run( GetClassLoader.fromContext() ); - - try { - run( SetContextClassLoader.action( MappingXmlParser.class.getClassLoader() ) ); - - // the InputStreams passed in parameters support mark and reset - in.mark( Integer.MAX_VALUE ); - - XMLEventReader xmlEventReader = xmlParserHelper.createXmlEventReader( "constraint mapping file", new CloseIgnoringInputStream( in ) ); - String schemaVersion = xmlParserHelper.getSchemaVersion( "constraint mapping file", xmlEventReader ); - xmlEventReader.close(); - - in.reset(); - - // The validation is done first as we manipulate the XML document before pushing it to the unmarshaller - // and it might not be valid anymore as we might have switched the namespace to the latest namespace - // supported. - String schemaResourceName = getSchemaResourceName( schemaVersion ); - Schema schema = xmlParserHelper.getSchema( schemaResourceName ); - Validator validator = schema.newValidator(); - validator.validate( new StreamSource( new CloseIgnoringInputStream( in ) ) ); - - in.reset(); - - xmlEventReader = xmlParserHelper.createXmlEventReader( "constraint mapping file", new CloseIgnoringInputStream( in ) ); - Unmarshaller unmarshaller = jc.createUnmarshaller(); - ConstraintMappingsType mapping = getValidationConfig( xmlEventReader, unmarshaller ); - xmlEventReader.close(); - - return mapping; - } - finally { - run( SetContextClassLoader.action( previousTccl ) ); - } - } - - public final Set> getXmlConfiguredClasses() { - return processedClasses; - } - - public final AnnotationProcessingOptions getAnnotationProcessingOptions() { - return annotationProcessingOptions; - } - - public final Set getConstrainedElementsForClass(Class beanClass) { - if ( constrainedElements.containsKey( beanClass ) ) { - return constrainedElements.get( beanClass ); - } - else { - return Collections.emptySet(); - } - } - - public final List> getDefaultSequenceForClass(Class beanClass) { - return defaultSequences.get( beanClass ); - } - - private void processBeanType(ConstrainedTypeBuilder constrainedTypeBuilder, ConstrainedFieldBuilder constrainedFieldBuilder, ConstrainedExecutableBuilder constrainedExecutableBuilder, ConstrainedGetterBuilder constrainedGetterBuilder, String defaultPackage, BeanType bean) { - Class beanClass = classLoadingHelper.loadClass( bean.getClazz(), defaultPackage ); - checkClassHasNotBeenProcessed( processedClasses, beanClass ); - - // update annotation ignores - annotationProcessingOptions.ignoreAnnotationConstraintForClass( - beanClass, - bean.getIgnoreAnnotations() - ); - - ConstrainedType constrainedType = constrainedTypeBuilder.buildConstrainedType( - bean.getClassType(), - beanClass, - defaultPackage - ); - if ( constrainedType != null ) { - addConstrainedElement( beanClass, constrainedType ); - } - - Set constrainedFields = constrainedFieldBuilder.buildConstrainedFields( - bean.getField(), - beanClass, - defaultPackage - ); - addConstrainedElements( beanClass, constrainedFields ); - - Set constrainedGetters = constrainedGetterBuilder.buildConstrainedGetters( - bean.getGetter(), - beanClass, - defaultPackage - - ); - addConstrainedElements( beanClass, constrainedGetters ); - - Set constrainedConstructors = constrainedExecutableBuilder.buildConstructorConstrainedExecutable( - bean.getConstructor(), - beanClass, - defaultPackage - ); - addConstrainedElements( beanClass, constrainedConstructors ); - - Set constrainedMethods = constrainedExecutableBuilder.buildMethodConstrainedExecutable( - bean.getMethod(), - beanClass, - defaultPackage - ); - addConstrainedElements( beanClass, constrainedMethods ); - - processedClasses.add( beanClass ); - } - - @SuppressWarnings("unchecked") - private void parseConstraintDefinitions(List constraintDefinitionList, - String defaultPackage, - Set alreadyProcessedConstraintDefinitions) { - for ( ConstraintDefinitionType constraintDefinition : constraintDefinitionList ) { - String annotationClassName = constraintDefinition.getAnnotation(); - if ( alreadyProcessedConstraintDefinitions.contains( annotationClassName ) ) { - throw LOG.getOverridingConstraintDefinitionsInMultipleMappingFilesException( annotationClassName ); - } - else { - alreadyProcessedConstraintDefinitions.add( annotationClassName ); - } - - Class clazz = classLoadingHelper.loadClass( annotationClassName, defaultPackage ); - if ( !clazz.isAnnotation() ) { - throw LOG.getIsNotAnAnnotationException( clazz ); - } - Class annotationClass = (Class) clazz; - - addValidatorDefinitions( annotationClass, defaultPackage, constraintDefinition.getValidatedBy() ); - } - } - - private void addValidatorDefinitions(Class annotationClass, String defaultPackage, - ValidatedByType validatedByType) { - List> constraintValidatorDescriptors = new ArrayList<>( validatedByType.getValue().size() ); - - for ( String validatorClassName : validatedByType.getValue() ) { - @SuppressWarnings("unchecked") - Class> validatorClass = (Class>) classLoadingHelper - .loadClass( validatorClassName, defaultPackage ); - - if ( !ConstraintValidator.class.isAssignableFrom( validatorClass ) ) { - throw LOG.getIsNotAConstraintValidatorClassException( validatorClass ); - } - - constraintValidatorDescriptors.add( ConstraintValidatorDescriptor.forClass( validatorClass ) ); - } - constraintHelper.putValidatorDescriptors( - annotationClass, - constraintValidatorDescriptors, - Boolean.TRUE.equals( validatedByType.getIncludeExistingValidators() ) - ); - } - - private void checkClassHasNotBeenProcessed(Set> processedClasses, Class beanClass) { - if ( processedClasses.contains( beanClass ) ) { - throw LOG.getBeanClassHasAlreadyBeConfiguredInXmlException( beanClass ); - } - } - - private void addConstrainedElement(Class beanClass, ConstrainedElement constrainedElement) { - if ( constrainedElements.containsKey( beanClass ) ) { - constrainedElements.get( beanClass ).add( constrainedElement ); - } - else { - Set tmpList = newHashSet(); - tmpList.add( constrainedElement ); - constrainedElements.put( beanClass, tmpList ); - } - } - - private void addConstrainedElements(Class beanClass, Set newConstrainedElements) { - if ( constrainedElements.containsKey( beanClass ) ) { - - Set existingConstrainedElements = constrainedElements.get( beanClass ); - - for ( ConstrainedElement constrainedElement : newConstrainedElements ) { - if ( existingConstrainedElements.contains( constrainedElement ) ) { - throw LOG.getConstrainedElementConfiguredMultipleTimesException( - constrainedElement.toString() - ); - } - } - - existingConstrainedElements.addAll( newConstrainedElements ); - } - else { - Set tmpSet = newHashSet(); - tmpSet.addAll( newConstrainedElements ); - constrainedElements.put( beanClass, tmpSet ); - } - } - - private ConstraintMappingsType getValidationConfig(XMLEventReader xmlEventReader, Unmarshaller unmarshaller) { - ConstraintMappingsType constraintMappings; - try { - // Unmashaller#unmarshal() requires several permissions internally and doesn't use any privileged blocks - // itself; Wrapping it here avoids that all calling code bases need to have these permissions as well - JAXBElement root = run( - Unmarshal.action( - unmarshaller, - xmlEventReader, - ConstraintMappingsType.class - ) - ); - constraintMappings = root.getValue(); - } - catch (Exception e) { - throw LOG.getErrorParsingMappingFileException( e ); - } - return constraintMappings; - } - - private String getSchemaResourceName(String schemaVersion) { - String schemaResource = SCHEMAS_BY_VERSION.get( schemaVersion ); - - if ( schemaResource == null ) { - throw LOG.getUnsupportedSchemaVersionException( "constraint mapping file", schemaVersion ); - } - - return schemaResource; - } - - /** - * 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 T run(PrivilegedAction action) { - return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); - } - - /** - * 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(PrivilegedExceptionAction action) throws JAXBException { - try { - return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); - } - catch (JAXBException e) { - throw e; - } - catch (Exception e) { - throw LOG.getErrorParsingMappingFileException( e ); - } - } - -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/MetaConstraintBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/MetaConstraintBuilder.java deleted file mode 100644 index 7851214f3b..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/MetaConstraintBuilder.java +++ /dev/null @@ -1,327 +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.xml; - -import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; - -import java.io.Serializable; -import java.lang.annotation.Annotation; -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Array; -import java.lang.reflect.Method; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.Iterator; -import java.util.List; -import java.util.regex.Pattern; - -import javax.validation.Payload; -import javax.validation.ValidationException; -import javax.xml.bind.JAXBElement; - -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.core.MetaConstraints; -import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; -import org.hibernate.validator.internal.metadata.location.ConstraintLocation; -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.GetMethod; -import org.hibernate.validator.internal.xml.binding.AnnotationType; -import org.hibernate.validator.internal.xml.binding.ConstraintType; -import org.hibernate.validator.internal.xml.binding.ElementType; -import org.hibernate.validator.internal.xml.binding.GroupsType; -import org.hibernate.validator.internal.xml.binding.PayloadType; - -/** - * Build meta constraint from XML - * - * @author Hardy Ferentschik - */ -class MetaConstraintBuilder { - - private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - - private static final Pattern IS_ONLY_WHITESPACE = Pattern.compile( "\\s*" ); - private static final Class[] EMPTY_CLASSES_ARRAY = new Class[0]; - - private final ClassLoadingHelper classLoadingHelper; - private final ConstraintHelper constraintHelper; - private final TypeResolutionHelper typeResolutionHelper; - private final ValueExtractorManager valueExtractorManager; - - MetaConstraintBuilder(ClassLoadingHelper classLoadingHelper, ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, - ValueExtractorManager valueExtractorManager) { - this.classLoadingHelper = classLoadingHelper; - this.constraintHelper = constraintHelper; - this.typeResolutionHelper = typeResolutionHelper; - this.valueExtractorManager = valueExtractorManager; - } - - @SuppressWarnings("unchecked") - MetaConstraint buildMetaConstraint(ConstraintLocation constraintLocation, - ConstraintType constraint, - java.lang.annotation.ElementType type, - String defaultPackage, - ConstraintDescriptorImpl.ConstraintType constraintType) { - Class annotationClass; - try { - annotationClass = (Class) classLoadingHelper.loadClass( constraint.getAnnotation(), defaultPackage ); - } - catch (ValidationException e) { - throw LOG.getUnableToLoadConstraintAnnotationClassException( constraint.getAnnotation(), e ); - } - ConstraintAnnotationDescriptor.Builder annotationDescriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( annotationClass ); - - if ( constraint.getMessage() != null ) { - annotationDescriptorBuilder.setMessage( constraint.getMessage() ); - } - annotationDescriptorBuilder.setGroups( getGroups( constraint.getGroups(), defaultPackage ) ) - .setPayload( getPayload( constraint.getPayload(), defaultPackage ) ); - - for ( ElementType elementType : constraint.getElement() ) { - String name = elementType.getName(); - checkNameIsValid( name ); - Class returnType = getAnnotationParameterType( annotationClass, name ); - Object elementValue = getElementValue( elementType, returnType, defaultPackage ); - annotationDescriptorBuilder.setAttribute( name, elementValue ); - } - - ConstraintAnnotationDescriptor annotationDescriptor; - try { - annotationDescriptor = annotationDescriptorBuilder.build(); - } - catch (RuntimeException e) { - throw LOG.getUnableToCreateAnnotationForConfiguredConstraintException( e ); - } - - // we set initially ConstraintOrigin.DEFINED_LOCALLY for all xml configured constraints - // later we will make copies of this constraint descriptor when needed and adjust the ConstraintOrigin - ConstraintDescriptorImpl constraintDescriptor = new ConstraintDescriptorImpl( - constraintHelper, constraintLocation.getMember(), annotationDescriptor, type, constraintType - ); - - return MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintDescriptor, constraintLocation ); - } - - private Annotation buildAnnotation(AnnotationType annotationType, Class returnType, String defaultPackage) { - AnnotationDescriptor.Builder annotationDescriptorBuilder = new AnnotationDescriptor.Builder<>( returnType ); - for ( ElementType elementType : annotationType.getElement() ) { - String name = elementType.getName(); - Class parameterType = getAnnotationParameterType( returnType, name ); - Object elementValue = getElementValue( elementType, parameterType, defaultPackage ); - annotationDescriptorBuilder.setAttribute( name, elementValue ); - } - return annotationDescriptorBuilder.build().getAnnotation(); - } - - private static void checkNameIsValid(String name) { - if ( ConstraintHelper.MESSAGE.equals( name ) || ConstraintHelper.GROUPS.equals( name ) ) { - throw LOG.getReservedParameterNamesException( ConstraintHelper.MESSAGE, ConstraintHelper.GROUPS, ConstraintHelper.PAYLOAD ); - } - } - - private static Class getAnnotationParameterType(Class annotationClass, String name) { - Method m = run( GetMethod.action( annotationClass, name ) ); - if ( m == null ) { - throw LOG.getAnnotationDoesNotContainAParameterException( annotationClass, name ); - } - return m.getReturnType(); - } - - private Object getElementValue(ElementType elementType, Class returnType, String defaultPackage) { - removeEmptyContentElements( elementType ); - - boolean isArray = returnType.isArray(); - if ( !isArray ) { - if ( elementType.getContent().size() == 0 ) { - if ( returnType == String.class ) { - return ""; - } - else { - throw LOG.getEmptyElementOnlySupportedWhenCharSequenceIsExpectedExpection(); - } - } - else if ( elementType.getContent().size() > 1 ) { - throw LOG.getAttemptToSpecifyAnArrayWhereSingleValueIsExpectedException(); - } - return getSingleValue( elementType.getContent().get( 0 ), returnType, defaultPackage ); - } - else { - List values = newArrayList(); - for ( Serializable s : elementType.getContent() ) { - values.add( getSingleValue( s, returnType.getComponentType(), defaultPackage ) ); - } - return values.toArray( (Object[]) Array.newInstance( returnType.getComponentType(), values.size() ) ); - } - } - - private static void removeEmptyContentElements(ElementType elementType) { - for ( Iterator contentIterator = elementType.getContent().iterator(); contentIterator.hasNext(); ) { - Serializable content = contentIterator.next(); - if ( content instanceof String && IS_ONLY_WHITESPACE.matcher( (String) content ).matches() ) { - contentIterator.remove(); - } - } - } - - private Object getSingleValue(Serializable serializable, Class returnType, String defaultPackage) { - - Object returnValue; - if ( serializable instanceof String ) { - String value = (String) serializable; - returnValue = convertStringToReturnType( returnType, value, defaultPackage ); - } - else if ( serializable instanceof JAXBElement && ( (JAXBElement) serializable ).getDeclaredType() - .equals( String.class ) ) { - JAXBElement elem = (JAXBElement) serializable; - String value = (String) elem.getValue(); - returnValue = convertStringToReturnType( returnType, value, defaultPackage ); - } - else if ( serializable instanceof JAXBElement && ( (JAXBElement) serializable ).getDeclaredType() - .equals( AnnotationType.class ) ) { - JAXBElement elem = (JAXBElement) serializable; - AnnotationType annotationType = (AnnotationType) elem.getValue(); - try { - @SuppressWarnings("unchecked") - Class annotationClass = (Class) returnType; - returnValue = buildAnnotation( annotationType, annotationClass, defaultPackage ); - } - catch (ClassCastException e) { - throw LOG.getUnexpectedParameterValueException( e ); - } - } - else { - throw LOG.getUnexpectedParameterValueException(); - } - return returnValue; - - } - - private Object convertStringToReturnType(Class returnType, String value, String defaultPackage) { - Object returnValue; - if ( returnType == byte.class ) { - try { - returnValue = Byte.parseByte( value ); - } - catch (NumberFormatException e) { - throw LOG.getInvalidNumberFormatException( "byte", e ); - } - } - else if ( returnType == short.class ) { - try { - returnValue = Short.parseShort( value ); - } - catch (NumberFormatException e) { - throw LOG.getInvalidNumberFormatException( "short", e ); - } - } - else if ( returnType == int.class ) { - try { - returnValue = Integer.parseInt( value ); - } - catch (NumberFormatException e) { - throw LOG.getInvalidNumberFormatException( "int", e ); - } - } - else if ( returnType == long.class ) { - try { - returnValue = Long.parseLong( value ); - } - catch (NumberFormatException e) { - throw LOG.getInvalidNumberFormatException( "long", e ); - } - } - else if ( returnType == float.class ) { - try { - returnValue = Float.parseFloat( value ); - } - catch (NumberFormatException e) { - throw LOG.getInvalidNumberFormatException( "float", e ); - } - } - else if ( returnType == double.class ) { - try { - returnValue = Double.parseDouble( value ); - } - catch (NumberFormatException e) { - throw LOG.getInvalidNumberFormatException( "double", e ); - } - } - else if ( returnType == boolean.class ) { - returnValue = Boolean.parseBoolean( value ); - } - else if ( returnType == char.class ) { - if ( value.length() != 1 ) { - throw LOG.getInvalidCharValueException( value ); - } - returnValue = value.charAt( 0 ); - } - else if ( returnType == String.class ) { - returnValue = value; - } - else if ( returnType == Class.class ) { - returnValue = classLoadingHelper.loadClass( value, defaultPackage ); - } - else { - try { - @SuppressWarnings("unchecked") - Class enumClass = (Class) returnType; - returnValue = Enum.valueOf( enumClass, value ); - } - catch (ClassCastException e) { - throw LOG.getInvalidReturnTypeException( returnType, e ); - } - } - return returnValue; - } - - private Class[] getGroups(GroupsType groupsType, String defaultPackage) { - if ( groupsType == null ) { - return EMPTY_CLASSES_ARRAY; - } - - List> groupList = newArrayList(); - for ( String groupClass : groupsType.getValue() ) { - groupList.add( classLoadingHelper.loadClass( groupClass, defaultPackage ) ); - } - return groupList.toArray( new Class[groupList.size()] ); - } - - @SuppressWarnings("unchecked") - private Class[] getPayload(PayloadType payloadType, String defaultPackage) { - if ( payloadType == null ) { - return EMPTY_CLASSES_ARRAY; - } - - List> payloadList = newArrayList(); - for ( String groupClass : payloadType.getValue() ) { - Class payload = classLoadingHelper.loadClass( groupClass, defaultPackage ); - if ( !Payload.class.isAssignableFrom( payload ) ) { - throw LOG.getWrongPayloadClassException( payload ); - } - else { - payloadList.add( (Class) payload ); - } - } - return payloadList.toArray( new Class[payloadList.size()] ); - } - - /** - * 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 T run(PrivilegedAction action) { - return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/NamespaceNormalizingXMLEventReaderDelegate.java b/engine/src/main/java/org/hibernate/validator/internal/xml/NamespaceNormalizingXMLEventReaderDelegate.java deleted file mode 100644 index 99215486e6..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/NamespaceNormalizingXMLEventReaderDelegate.java +++ /dev/null @@ -1,101 +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.xml; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import javax.xml.namespace.QName; -import javax.xml.stream.XMLEventFactory; -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.events.EndElement; -import javax.xml.stream.events.Namespace; -import javax.xml.stream.events.StartElement; -import javax.xml.stream.events.XMLEvent; -import javax.xml.stream.util.EventReaderDelegate; - -/** - * An XML {@link EventReaderDelegate} designed to normalize the XML namespaces. - *

    - * From BV 1.x to BV 2, we changed the namespaces and we need to normalize the namespaces to the ones of BV 2 so that - * the unmarshaller can do its job. - *

    - * Note: it used to work in JDK 1.8 before the 102 release but the JDK is now stricter: - * https://bugs.openjdk.java.net/browse/JDK-8134111. - * - * @author Guillaume Smet - */ -public class NamespaceNormalizingXMLEventReaderDelegate extends EventReaderDelegate { - - private final XMLEventFactory eventFactory; - - private final Map namespaceMapping; - - public NamespaceNormalizingXMLEventReaderDelegate(XMLEventReader eventReader, XMLEventFactory eventFactory, Map namespaceMapping) { - super( eventReader ); - this.eventFactory = eventFactory; - this.namespaceMapping = namespaceMapping; - } - - @Override - public XMLEvent peek() throws XMLStreamException { - return normalizeXMLEvent( super.peek() ); - } - - @Override - public XMLEvent nextEvent() throws XMLStreamException { - return normalizeXMLEvent( super.nextEvent() ); - } - - private XMLEvent normalizeXMLEvent(XMLEvent xmlEvent) { - if ( xmlEvent.isStartElement() ) { - return normalizeNamespace( xmlEvent.asStartElement() ); - } - else if ( xmlEvent.isEndElement() ) { - return normalizeNamespace( xmlEvent.asEndElement() ); - } - else { - return xmlEvent; - } - } - - @SuppressWarnings("unchecked") - private StartElement normalizeNamespace(StartElement element) { - eventFactory.setLocation( element.getLocation() ); - return eventFactory.createStartElement( normalizeQName( element.getName() ), element.getAttributes(), normalizeNamespaces( element.getNamespaces() ) ); - } - - @SuppressWarnings("unchecked") - private EndElement normalizeNamespace(EndElement element) { - eventFactory.setLocation( element.getLocation() ); - return eventFactory.createEndElement( normalizeQName( element.getName() ), normalizeNamespaces( element.getNamespaces() ) ); - } - - private QName normalizeQName(QName qName) { - return new QName( normalizeNamespaceURI( qName.getNamespaceURI() ), qName.getLocalPart() ); - } - - private Iterator normalizeNamespaces(Iterator namespaces) { - List newNamespaces = new ArrayList<>(); - while ( namespaces.hasNext() ) { - newNamespaces.add( normalizeNamespace( namespaces.next() ) ); - } - return newNamespaces.iterator(); - } - - private Namespace normalizeNamespace(Namespace namespace) { - return eventFactory.createNamespace( namespace.getPrefix(), normalizeNamespaceURI( namespace.getNamespaceURI() ) ); - } - - private String normalizeNamespaceURI(String namespaceURI) { - return namespaceMapping.getOrDefault( namespaceURI, namespaceURI ); - } - -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/ValidationXmlParser.java b/engine/src/main/java/org/hibernate/validator/internal/xml/ValidationXmlParser.java deleted file mode 100644 index 8d7dd550b0..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/ValidationXmlParser.java +++ /dev/null @@ -1,274 +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.xml; - -import java.io.IOException; -import java.io.InputStream; -import java.lang.invoke.MethodHandles; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.security.PrivilegedExceptionAction; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.validation.BootstrapConfiguration; -import javax.validation.executable.ExecutableType; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.Unmarshaller; -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.XMLStreamException; -import javax.xml.transform.stream.StreamSource; -import javax.xml.validation.Schema; -import javax.xml.validation.Validator; - -import org.hibernate.validator.HibernateValidatorConfiguration; -import org.hibernate.validator.internal.util.CollectionHelper; -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.NewJaxbContext; -import org.hibernate.validator.internal.util.privilegedactions.SetContextClassLoader; -import org.hibernate.validator.internal.util.privilegedactions.Unmarshal; -import org.hibernate.validator.internal.xml.binding.DefaultValidatedExecutableTypesType; -import org.hibernate.validator.internal.xml.binding.ExecutableValidationType; -import org.hibernate.validator.internal.xml.binding.PropertyType; -import org.hibernate.validator.internal.xml.binding.ValidationConfigType; - -import org.xml.sax.SAXException; - -/** - * Parser for validation.xml using JAXB. - * - * @author Hardy Ferentschik - * @author Gunnar Morling - */ -public class ValidationXmlParser { - - private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - - private static final String VALIDATION_XML_FILE = "META-INF/validation.xml"; - private static final Map SCHEMAS_BY_VERSION = Collections.unmodifiableMap( getSchemasByVersion() ); - - private final ClassLoader externalClassLoader; - - private static Map getSchemasByVersion() { - Map schemasByVersion = CollectionHelper.newHashMap( 3 ); - - schemasByVersion.put( "1.0", "META-INF/validation-configuration-1.0.xsd" ); - schemasByVersion.put( "1.1", "META-INF/validation-configuration-1.1.xsd" ); - schemasByVersion.put( "2.0", "META-INF/validation-configuration-2.0.xsd" ); - - return schemasByVersion; - } - - public ValidationXmlParser(ClassLoader externalClassLoader) { - this.externalClassLoader = externalClassLoader; - } - - /** - * Tries to check whether a validation.xml file exists and parses it. - * - * @return The parameters parsed out of validation.xml wrapped in an instance of {@code ConfigurationImpl.ValidationBootstrapParameters}. - */ - public final BootstrapConfiguration parseValidationXml() { - InputStream in = getValidationXmlInputStream(); - if ( in == null ) { - return BootstrapConfigurationImpl.getDefaultBootstrapConfiguration(); - } - - ClassLoader previousTccl = run( GetClassLoader.fromContext() ); - - try { - run( SetContextClassLoader.action( ValidationXmlParser.class.getClassLoader() ) ); - - // HV-970 The parser helper is only loaded if there actually is a validation.xml file; - // this avoids accessing javax.xml.stream.* (which does not exist on Android) when not actually - // working with the XML configuration - XmlParserHelper xmlParserHelper = new XmlParserHelper(); - - // the InputStream supports mark and reset - in.mark( Integer.MAX_VALUE ); - - XMLEventReader xmlEventReader = xmlParserHelper.createXmlEventReader( VALIDATION_XML_FILE, new CloseIgnoringInputStream( in ) ); - String schemaVersion = xmlParserHelper.getSchemaVersion( VALIDATION_XML_FILE, xmlEventReader ); - xmlEventReader.close(); - - in.reset(); - - // The validation is done first as we manipulate the XML document before pushing it to the unmarshaller - // and it might not be valid anymore as we might have switched the namespace to the latest namespace - // supported. - Schema schema = getSchema( xmlParserHelper, schemaVersion ); - Validator validator = schema.newValidator(); - validator.validate( new StreamSource( new CloseIgnoringInputStream( in ) ) ); - - in.reset(); - - xmlEventReader = xmlParserHelper.createXmlEventReader( VALIDATION_XML_FILE, new CloseIgnoringInputStream( in ) ); - ValidationConfigType validationConfig = unmarshal( xmlEventReader ); - xmlEventReader.close(); - - return createBootstrapConfiguration( validationConfig ); - } - catch (XMLStreamException | IOException | SAXException e) { - throw LOG.getUnableToParseValidationXmlFileException( VALIDATION_XML_FILE, e ); - } - finally { - run( SetContextClassLoader.action( previousTccl ) ); - closeStream( in ); - } - } - - private InputStream getValidationXmlInputStream() { - LOG.debugf( "Trying to load %s for XML based Validator configuration.", VALIDATION_XML_FILE ); - InputStream inputStream = ResourceLoaderHelper.getResettableInputStreamForPath( VALIDATION_XML_FILE, externalClassLoader ); - - if ( inputStream != null ) { - return inputStream; - } - else { - LOG.debugf( "No %s found. Using annotation based configuration only.", VALIDATION_XML_FILE ); - return null; - } - } - - private Schema getSchema(XmlParserHelper xmlParserHelper, String schemaVersion) { - String schemaResource = SCHEMAS_BY_VERSION.get( schemaVersion ); - - if ( schemaResource == null ) { - throw LOG.getUnsupportedSchemaVersionException( VALIDATION_XML_FILE, schemaVersion ); - } - - return xmlParserHelper.getSchema( schemaResource ); - } - - private ValidationConfigType unmarshal(XMLEventReader xmlEventReader) { - LOG.parsingXMLFile( VALIDATION_XML_FILE ); - - try { - // JAXBContext#newInstance() requires several permissions internally and doesn't use any privileged blocks - // itself; Wrapping it here avoids that all calling code bases need to have these permissions as well - JAXBContext jc = run( NewJaxbContext.action( ValidationConfigType.class ) ); - Unmarshaller unmarshaller = jc.createUnmarshaller(); - - // Unmashaller#unmarshal() requires several permissions internally and doesn't use any privileged blocks - // itself; Wrapping it here avoids that all calling code bases need to have these permissions as well - JAXBElement root = run( Unmarshal.action( unmarshaller, xmlEventReader, ValidationConfigType.class ) ); - return root.getValue(); - } - catch (Exception e) { - throw LOG.getUnableToParseValidationXmlFileException( VALIDATION_XML_FILE, e ); - } - } - - private void closeStream(InputStream inputStream) { - try { - inputStream.close(); - } - catch (IOException io) { - LOG.unableToCloseXMLFileInputStream( VALIDATION_XML_FILE ); - } - } - - private BootstrapConfiguration createBootstrapConfiguration(ValidationConfigType config) { - Map properties = new HashMap<>(); - for ( PropertyType property : config.getProperty() ) { - if ( LOG.isDebugEnabled() ) { - LOG.debugf( - "Found property '%s' with value '%s' in validation.xml.", - property.getName(), - property.getValue() - ); - } - properties.put( property.getName(), property.getValue() ); - } - - ExecutableValidationType executableValidationType = config.getExecutableValidation(); - EnumSet defaultValidatedExecutableTypes = executableValidationType == null - ? getValidatedExecutableTypes( null ) - : getValidatedExecutableTypes( executableValidationType.getDefaultValidatedExecutableTypes() ); - boolean executableValidationEnabled = executableValidationType == null || executableValidationType.getEnabled(); - - return new BootstrapConfigurationImpl( - config.getDefaultProvider(), - config.getConstraintValidatorFactory(), - config.getMessageInterpolator(), - config.getTraversableResolver(), - config.getParameterNameProvider(), - config.getClockProvider(), - getScriptEvaluatorFactoryClassProperty( config.getProperty() ), - getValueExtractorClassNames( config ), - defaultValidatedExecutableTypes, - executableValidationEnabled, - new HashSet<>( config.getConstraintMapping() ), - properties - ); - } - - private String getScriptEvaluatorFactoryClassProperty(List properties) { - return properties.stream() - .filter( property -> HibernateValidatorConfiguration.SCRIPT_EVALUATOR_FACTORY_CLASSNAME.equals( property.getName() ) ) - .map( PropertyType::getValue ) - .findFirst().orElse( null ); - } - - private Set getValueExtractorClassNames(ValidationConfigType config) { - Set valueExtractorClassNames = CollectionHelper.newHashSet( config.getValueExtractor().size() ); - for ( String className : config.getValueExtractor() ) { - if ( !valueExtractorClassNames.add( className ) ) { - throw LOG.getDuplicateDefinitionsOfValueExtractorException( className ); - } - } - return valueExtractorClassNames; - } - - /** - * Returns an enum set with the executable types corresponding to the given - * XML configuration, considering the special elements - * {@link ExecutableType#ALL} and {@link ExecutableType#NONE}. - * - * @param validatedExecutables Schema type with executable types. - * - * @return An enum set representing the given executable types. - */ - private EnumSet getValidatedExecutableTypes(DefaultValidatedExecutableTypesType validatedExecutables) { - if ( validatedExecutables == null ) { - return null; - } - - EnumSet executableTypes = EnumSet.noneOf( ExecutableType.class ); - executableTypes.addAll( validatedExecutables.getExecutableType() ); - - return executableTypes; - } - - /** - * 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 T run(PrivilegedAction action) { - return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); - } - - /** - * 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(PrivilegedExceptionAction action) throws Exception { - return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/XmlParserHelper.java b/engine/src/main/java/org/hibernate/validator/internal/xml/XmlParserHelper.java index a2001c4c03..511da201f6 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/XmlParserHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/XmlParserHelper.java @@ -14,14 +14,10 @@ import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import javax.xml.namespace.QName; -import javax.xml.stream.XMLEventFactory; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; @@ -54,29 +50,16 @@ public class XmlParserHelper { */ private static final int NUMBER_OF_SCHEMAS = 4; private static final String DEFAULT_VERSION = "1.0"; - - private static final Map NAMESPACE_NORMALIZATION_MAPPING; + private static final QName VERSION_QNAME = new QName( "version" ); // xmlInputFactory used to be static in order to cache the factory, but that introduced a leakage of // class loader in WildFly. See HV-842 private final XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); - // xmlEventFactory should not be static either. - private final XMLEventFactory xmlEventFactory = XMLEventFactory.newInstance(); - private static final ConcurrentMap schemaCache = new ConcurrentHashMap( NUMBER_OF_SCHEMAS ); - static { - Map namespaceNormalizationMapping = new HashMap<>(); - namespaceNormalizationMapping.put( LocalNamespace.VALIDATION_1_CONFIGURATION.getNamespaceURI(), - LocalNamespace.VALIDATION_2_CONFIGURATION.getNamespaceURI() ); - namespaceNormalizationMapping.put( LocalNamespace.VALIDATION_1_MAPPING.getNamespaceURI(), - LocalNamespace.VALIDATION_2_MAPPING.getNamespaceURI() ); - NAMESPACE_NORMALIZATION_MAPPING = Collections.unmodifiableMap( namespaceNormalizationMapping ); - } - /** * Retrieves the schema version applying for the given XML input stream as * represented by the "version" attribute of the root element of the stream. @@ -105,11 +88,7 @@ public String getSchemaVersion(String resourceName, XMLEventReader xmlEventReade public synchronized XMLEventReader createXmlEventReader(String resourceName, InputStream xmlStream) { try { - return new NamespaceNormalizingXMLEventReaderDelegate( - xmlInputFactory.createXMLEventReader( xmlStream ), - xmlEventFactory, - NAMESPACE_NORMALIZATION_MAPPING - ); + return xmlInputFactory.createXMLEventReader( xmlStream ); } catch (Exception e) { throw LOG.getUnableToCreateXMLEventReader( resourceName, e ); @@ -121,7 +100,7 @@ private String getVersionValue(StartElement startElement) { return null; } - Attribute versionAttribute = startElement.getAttributeByName( new QName( "version" ) ); + Attribute versionAttribute = startElement.getAttributeByName( VERSION_QNAME ); return versionAttribute != null ? versionAttribute.getValue() : DEFAULT_VERSION; } @@ -143,7 +122,7 @@ private StartElement getRootElement(XMLEventReader xmlEventReader) throws XMLStr * @return the schema identified by the given resource name or {@code null} if the resource was not found or could * not be loaded. */ - Schema getSchema(String schemaResource) { + public Schema getSchema(String schemaResource) { Schema schema = schemaCache.get( schemaResource ); if ( schema != null ) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/BootstrapConfigurationImpl.java b/engine/src/main/java/org/hibernate/validator/internal/xml/config/BootstrapConfigurationImpl.java similarity index 90% rename from engine/src/main/java/org/hibernate/validator/internal/xml/BootstrapConfigurationImpl.java rename to engine/src/main/java/org/hibernate/validator/internal/xml/config/BootstrapConfigurationImpl.java index 0c6f03a914..f29c16f3b5 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/BootstrapConfigurationImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/config/BootstrapConfigurationImpl.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.xml; +package org.hibernate.validator.internal.xml.config; import java.util.Collections; import java.util.EnumSet; @@ -16,6 +16,7 @@ import javax.validation.BootstrapConfiguration; import javax.validation.executable.ExecutableType; +import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.internal.util.stereotypes.Immutable; /** @@ -23,7 +24,7 @@ * * @author Hardy Ferentschik */ -public class BootstrapConfigurationImpl implements BootstrapConfiguration { +class BootstrapConfigurationImpl implements BootstrapConfiguration { /** * The executable types validated by default. @@ -57,7 +58,6 @@ public class BootstrapConfigurationImpl implements BootstrapConfiguration { private final String traversableResolverClassName; private final String parameterNameProviderClassName; private final String clockProviderClassName; - private final String scriptEvaluatorFactoryClassName; private final Set valueExtractorClassNames; private final Set constraintMappingResourcePaths; private final Map properties; @@ -71,7 +71,6 @@ private BootstrapConfigurationImpl() { this.traversableResolverClassName = null; this.parameterNameProviderClassName = null; this.clockProviderClassName = null; - this.scriptEvaluatorFactoryClassName = null; this.valueExtractorClassNames = new HashSet<>(); this.validatedExecutableTypes = DEFAULT_VALIDATED_EXECUTABLE_TYPES; this.isExecutableValidationEnabled = true; @@ -85,7 +84,6 @@ public BootstrapConfigurationImpl(String defaultProviderClassName, String traversableResolverClassName, String parameterNameProviderClassName, String clockProviderClassName, - String scriptEvaluatorFactoryClassName, Set valueExtractorClassNames, EnumSet validatedExecutableTypes, boolean isExecutableValidationEnabled, @@ -97,7 +95,6 @@ public BootstrapConfigurationImpl(String defaultProviderClassName, this.traversableResolverClassName = traversableResolverClassName; this.parameterNameProviderClassName = parameterNameProviderClassName; this.clockProviderClassName = clockProviderClassName; - this.scriptEvaluatorFactoryClassName = scriptEvaluatorFactoryClassName; this.valueExtractorClassNames = valueExtractorClassNames; this.validatedExecutableTypes = prepareValidatedExecutableTypes( validatedExecutableTypes ); this.isExecutableValidationEnabled = isExecutableValidationEnabled; @@ -113,15 +110,23 @@ private Set prepareValidatedExecutableTypes(EnumSet preparedValidatedExecutableTypes = EnumSet.copyOf( validatedExecutableTypes ); + preparedValidatedExecutableTypes.remove( ExecutableType.NONE ); + return CollectionHelper.toImmutableSet( preparedValidatedExecutableTypes ); + } } + + return CollectionHelper.toImmutableSet( validatedExecutableTypes ); } @Override @@ -154,10 +159,6 @@ public String getClockProviderClassName() { return clockProviderClassName; } - public String getScriptEvaluatorFactoryClassName() { - return scriptEvaluatorFactoryClassName; - } - @Override public Set getValueExtractorClassNames() { return new HashSet<>( valueExtractorClassNames ); @@ -195,7 +196,6 @@ public String toString() { sb.append( ", traversableResolverClassName='" ).append( traversableResolverClassName ).append( '\'' ); sb.append( ", parameterNameProviderClassName='" ).append( parameterNameProviderClassName ).append( '\'' ); sb.append( ", clockProviderClassName='" ).append( clockProviderClassName ).append( '\'' ); - sb.append( ", scriptEvaluatorFactoryClassName='" ).append( scriptEvaluatorFactoryClassName ).append( '\'' ); sb.append( ", validatedExecutableTypes='" ).append( validatedExecutableTypes ).append( '\'' ); sb.append( ", constraintMappingResourcePaths=" ).append( constraintMappingResourcePaths ).append( '\'' ); sb.append( ", properties=" ).append( properties ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/ResourceLoaderHelper.java b/engine/src/main/java/org/hibernate/validator/internal/xml/config/ResourceLoaderHelper.java similarity index 98% rename from engine/src/main/java/org/hibernate/validator/internal/xml/ResourceLoaderHelper.java rename to engine/src/main/java/org/hibernate/validator/internal/xml/config/ResourceLoaderHelper.java index db36ca736a..442113cc00 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/ResourceLoaderHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/config/ResourceLoaderHelper.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.xml; +package org.hibernate.validator.internal.xml.config; import java.io.BufferedInputStream; import java.io.InputStream; diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/ValidationBootstrapParameters.java b/engine/src/main/java/org/hibernate/validator/internal/xml/config/ValidationBootstrapParameters.java similarity index 96% rename from engine/src/main/java/org/hibernate/validator/internal/xml/ValidationBootstrapParameters.java rename to engine/src/main/java/org/hibernate/validator/internal/xml/config/ValidationBootstrapParameters.java index 4460711659..0ccaa9cec4 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/ValidationBootstrapParameters.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/config/ValidationBootstrapParameters.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.xml; +package org.hibernate.validator.internal.xml.config; import java.io.InputStream; import java.lang.invoke.MethodHandles; @@ -32,6 +32,7 @@ import org.hibernate.validator.internal.util.logging.LoggerFactory; import org.hibernate.validator.internal.util.privilegedactions.LoadClass; import org.hibernate.validator.internal.util.privilegedactions.NewInstance; +import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider; /** * @author Hardy Ferentschik @@ -50,6 +51,7 @@ public class ValidationBootstrapParameters { private final Map configProperties = new HashMap<>(); private final Set mappings = new HashSet<>(); private final Map valueExtractorDescriptors = new HashMap<>(); + private PropertyNodeNameProvider propertyNodeNameProvider; public ValidationBootstrapParameters() { } @@ -150,6 +152,14 @@ public void addValueExtractorDescriptor(ValueExtractorDescriptor descriptor) { valueExtractorDescriptors.put( descriptor.getKey(), descriptor ); } + public PropertyNodeNameProvider getPropertyNodeNameProvider() { + return propertyNodeNameProvider; + } + + public void setPropertyNodeNameProvider(PropertyNodeNameProvider propertyNodeNameProvider) { + this.propertyNodeNameProvider = propertyNodeNameProvider; + } + @SuppressWarnings("unchecked") private void setProviderClass(String providerFqcn, ClassLoader externalClassLoader) { if ( providerFqcn != null ) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/config/ValidationConfigStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/config/ValidationConfigStaxBuilder.java new file mode 100644 index 0000000000..8ea287b9ee --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/config/ValidationConfigStaxBuilder.java @@ -0,0 +1,305 @@ +/* + * 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.xml.config; + +import java.lang.invoke.MethodHandles; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import javax.validation.BootstrapConfiguration; +import javax.validation.executable.ExecutableType; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.util.CollectionHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; + +/** + * Stax parser/builder for {@code validation.xml} that reads {@code } + * information and creates {@link BootstrapConfiguration}. + * + * @author Marko Bekhta + */ +class ValidationConfigStaxBuilder extends AbstractStaxBuilder { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final String VALIDATION_CONFIG_QNAME = "validation-config"; + + private final SimpleConfigurationsStaxBuilder simpleConfigurationsStaxBuilder = new SimpleConfigurationsStaxBuilder(); + private final PropertyStaxBuilder propertyStaxBuilder = new PropertyStaxBuilder(); + private final ValueExtractorsStaxBuilder valueExtractorsStaxBuilder = new ValueExtractorsStaxBuilder(); + private final ConstraintMappingsStaxBuilder constraintMappingsStaxBuilder = new ConstraintMappingsStaxBuilder(); + private final ExecutableValidationStaxBuilder executableValidationStaxBuilder = new ExecutableValidationStaxBuilder(); + + private final Map builders; + + public ValidationConfigStaxBuilder(XMLEventReader xmlEventReader) throws XMLStreamException { + builders = new HashMap<>(); + builders.put( propertyStaxBuilder.getAcceptableQName(), propertyStaxBuilder ); + builders.put( valueExtractorsStaxBuilder.getAcceptableQName(), valueExtractorsStaxBuilder ); + builders.put( constraintMappingsStaxBuilder.getAcceptableQName(), constraintMappingsStaxBuilder ); + builders.put( executableValidationStaxBuilder.getAcceptableQName(), executableValidationStaxBuilder ); + for ( String name : SimpleConfigurationsStaxBuilder.getProcessedElementNames() ) { + builders.put( name, simpleConfigurationsStaxBuilder ); + } + + while ( xmlEventReader.hasNext() ) { + process( xmlEventReader, xmlEventReader.nextEvent() ); + } + } + + @Override + protected String getAcceptableQName() { + return VALIDATION_CONFIG_QNAME; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( VALIDATION_CONFIG_QNAME ) ) ) { + XMLEvent currentEvent = xmlEventReader.nextEvent(); + xmlEvent = currentEvent; + if ( currentEvent.isStartElement() ) { + StartElement startElement = currentEvent.asStartElement(); + String localPart = startElement.getName().getLocalPart(); + AbstractStaxBuilder builder = builders.get( localPart ); + if ( builder != null ) { + builder.process( xmlEventReader, xmlEvent ); + } + else { + LOG.logUnknownElementInXmlConfiguration( localPart ); + } + } + } + } + + public BootstrapConfiguration build() { + Map properties = propertyStaxBuilder.build(); + return new BootstrapConfigurationImpl( + simpleConfigurationsStaxBuilder.getDefaultProvider(), + simpleConfigurationsStaxBuilder.getConstraintValidatorFactory(), + simpleConfigurationsStaxBuilder.getMessageInterpolator(), + simpleConfigurationsStaxBuilder.getTraversableResolver(), + simpleConfigurationsStaxBuilder.getParameterNameProvider(), + simpleConfigurationsStaxBuilder.getClockProvider(), + valueExtractorsStaxBuilder.build(), + executableValidationStaxBuilder.build(), + executableValidationStaxBuilder.isEnabled(), + constraintMappingsStaxBuilder.build(), + properties + ); + } + + private static class SimpleConfigurationsStaxBuilder extends AbstractStaxBuilder { + + /** + * Single occurrence elements: + */ + private static final String DEFAULT_PROVIDER = "default-provider"; + private static final String MESSAGE_INTERPOLATOR = "message-interpolator"; + private static final String TRAVERSABLE_RESOLVER = "traversable-resolver"; + private static final String CONSTRAINT_VALIDATOR_FACTORY = "constraint-validator-factory"; + private static final String PARAMETER_NAME_PROVIDER = "parameter-name-provider"; + private static final String CLOCK_PROVIDER = "clock-provider"; + + private static final Set SINGLE_ELEMENTS = CollectionHelper.toImmutableSet( + CollectionHelper.asSet( + DEFAULT_PROVIDER, MESSAGE_INTERPOLATOR, TRAVERSABLE_RESOLVER, + CONSTRAINT_VALIDATOR_FACTORY, PARAMETER_NAME_PROVIDER, CLOCK_PROVIDER + ) + ); + + /** + * Map that contains any of the {@link this#SINGLE_ELEMENTS} elements. + */ + private final Map singleValuedElements = new HashMap<>(); + + @Override + protected String getAcceptableQName() { + throw new UnsupportedOperationException( "this method shouldn't be called" ); + } + + @Override + protected boolean accept(XMLEvent xmlEvent) { + return xmlEvent.isStartElement() + && SINGLE_ELEMENTS.contains( xmlEvent.asStartElement().getName().getLocalPart() ); + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + String localPart = xmlEvent.asStartElement().getName().getLocalPart(); + singleValuedElements.put( localPart, readSingleElement( xmlEventReader ) ); + } + + public String getDefaultProvider() { + return singleValuedElements.get( DEFAULT_PROVIDER ); + } + + public String getMessageInterpolator() { + return singleValuedElements.get( MESSAGE_INTERPOLATOR ); + } + + public String getTraversableResolver() { + return singleValuedElements.get( TRAVERSABLE_RESOLVER ); + } + + public String getClockProvider() { + return singleValuedElements.get( CLOCK_PROVIDER ); + } + + public String getConstraintValidatorFactory() { + return singleValuedElements.get( CONSTRAINT_VALIDATOR_FACTORY ); + } + + public String getParameterNameProvider() { + return singleValuedElements.get( PARAMETER_NAME_PROVIDER ); + } + + public static Set getProcessedElementNames() { + return SINGLE_ELEMENTS; + } + } + + private static class PropertyStaxBuilder extends AbstractStaxBuilder { + + private static final String PROPERTY_QNAME_LOCAL_PART = "property"; + private static final QName NAME_QNAME = new QName( "name" ); + + private final Map properties; + + private PropertyStaxBuilder() { + properties = new HashMap<>(); + } + + @Override + protected String getAcceptableQName() { + return PROPERTY_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + StartElement startElement = xmlEvent.asStartElement(); + String name = readAttribute( startElement, NAME_QNAME ).get(); + String value = readSingleElement( xmlEventReader ); + if ( LOG.isDebugEnabled() ) { + LOG.debugf( + "Found property '%s' with value '%s' in validation.xml.", + name, + value + ); + } + properties.put( name, value ); + } + + public Map build() { + return properties; + } + } + + private static class ValueExtractorsStaxBuilder extends AbstractStaxBuilder { + + private static final String VALUE_EXTRACTOR_QNAME_LOCAL_PART = "value-extractor"; + + private final Set valueExtractors = new HashSet<>(); + + @Override + protected String getAcceptableQName() { + return VALUE_EXTRACTOR_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + String value = readSingleElement( xmlEventReader ); + if ( !valueExtractors.add( value ) ) { + throw LOG.getDuplicateDefinitionsOfValueExtractorException( value ); + } + } + + public Set build() { + return valueExtractors; + } + } + + private static class ConstraintMappingsStaxBuilder extends AbstractStaxBuilder { + + private static final String CONSTRAINT_MAPPING_QNAME_LOCAL_PART = "constraint-mapping"; + + private final Set constraintMappings = new HashSet<>(); + + @Override + protected String getAcceptableQName() { + return CONSTRAINT_MAPPING_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + String value = readSingleElement( xmlEventReader ); + constraintMappings.add( value ); + } + + public Set build() { + return constraintMappings; + } + } + + private static class ExecutableValidationStaxBuilder extends AbstractStaxBuilder { + + private static final String EXECUTABLE_VALIDATION_QNAME_LOCAL_PART = "executable-validation"; + private static final String EXECUTABLE_TYPE_QNAME_LOCAL_PART = "executable-type"; + + private static final QName ENABLED_QNAME = new QName( "enabled" ); + + private Boolean enabled; + + private EnumSet executableTypes = EnumSet.noneOf( ExecutableType.class ); + + @Override + protected String getAcceptableQName() { + return EXECUTABLE_VALIDATION_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + Optional enabledAttribute = readAttribute( xmlEvent.asStartElement(), ENABLED_QNAME ); + if ( enabledAttribute.isPresent() ) { + enabled = Boolean.parseBoolean( enabledAttribute.get() ); + } + + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( EXECUTABLE_VALIDATION_QNAME_LOCAL_PART ) ) ) { + XMLEvent currentEvent = xmlEventReader.nextEvent(); + xmlEvent = currentEvent; + if ( currentEvent.isStartElement() && currentEvent.asStartElement().getName().getLocalPart().equals( EXECUTABLE_TYPE_QNAME_LOCAL_PART ) ) { + executableTypes.add( ExecutableType.valueOf( readSingleElement( xmlEventReader ) ) ); + } + } + } + + public boolean isEnabled() { + return enabled == null ? true : enabled; + } + + /** + * Returns an enum set with the executable types corresponding to the given + * XML configuration, considering the special elements + * {@link ExecutableType#ALL} and {@link ExecutableType#NONE}. + * + * @return An enum set representing the given executable types. + */ + public EnumSet build() { + return executableTypes.isEmpty() ? null : executableTypes; + } + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/config/ValidationXmlParser.java b/engine/src/main/java/org/hibernate/validator/internal/xml/config/ValidationXmlParser.java new file mode 100644 index 0000000000..45d15c333a --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/config/ValidationXmlParser.java @@ -0,0 +1,167 @@ +/* + * 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.xml.config; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.invoke.MethodHandles; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Collections; +import java.util.Map; + +import javax.validation.BootstrapConfiguration; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.Validator; + +import org.hibernate.validator.internal.util.CollectionHelper; +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.SetContextClassLoader; +import org.hibernate.validator.internal.xml.CloseIgnoringInputStream; +import org.hibernate.validator.internal.xml.XmlParserHelper; + +import org.xml.sax.SAXException; + +/** + * Parser for validation.xml. + * + * @author Hardy Ferentschik + * @author Gunnar Morling + */ +public class ValidationXmlParser { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final String VALIDATION_XML_FILE = "META-INF/validation.xml"; + private static final Map SCHEMAS_BY_VERSION = Collections.unmodifiableMap( getSchemasByVersion() ); + + private final ClassLoader externalClassLoader; + + private static Map getSchemasByVersion() { + Map schemasByVersion = CollectionHelper.newHashMap( 3 ); + + schemasByVersion.put( "1.0", "META-INF/validation-configuration-1.0.xsd" ); + schemasByVersion.put( "1.1", "META-INF/validation-configuration-1.1.xsd" ); + schemasByVersion.put( "2.0", "META-INF/validation-configuration-2.0.xsd" ); + + return schemasByVersion; + } + + public ValidationXmlParser(ClassLoader externalClassLoader) { + this.externalClassLoader = externalClassLoader; + } + + /** + * Tries to check whether a validation.xml file exists and parses it. + * + * @return The parameters parsed out of validation.xml wrapped in an instance of {@code ConfigurationImpl.ValidationBootstrapParameters}. + */ + public final BootstrapConfiguration parseValidationXml() { + InputStream in = getValidationXmlInputStream(); + if ( in == null ) { + return BootstrapConfigurationImpl.getDefaultBootstrapConfiguration(); + } + + ClassLoader previousTccl = run( GetClassLoader.fromContext() ); + + try { + run( SetContextClassLoader.action( ValidationXmlParser.class.getClassLoader() ) ); + + // HV-970 The parser helper is only loaded if there actually is a validation.xml file; + // this avoids accessing javax.xml.stream.* (which does not exist on Android) when not actually + // working with the XML configuration + XmlParserHelper xmlParserHelper = new XmlParserHelper(); + + // the InputStream supports mark and reset + in.mark( Integer.MAX_VALUE ); + + XMLEventReader xmlEventReader = xmlParserHelper.createXmlEventReader( VALIDATION_XML_FILE, new CloseIgnoringInputStream( in ) ); + String schemaVersion = xmlParserHelper.getSchemaVersion( VALIDATION_XML_FILE, xmlEventReader ); + xmlEventReader.close(); + + in.reset(); + + // The validation is done first as StAX builders used below are assuming that the XML file is correct and don't + // do any validation of the input. + Schema schema = getSchema( xmlParserHelper, schemaVersion ); + Validator validator = schema.newValidator(); + validator.validate( new StreamSource( new CloseIgnoringInputStream( in ) ) ); + + in.reset(); + + xmlEventReader = xmlParserHelper.createXmlEventReader( VALIDATION_XML_FILE, new CloseIgnoringInputStream( in ) ); + + ValidationConfigStaxBuilder validationConfigStaxBuilder = new ValidationConfigStaxBuilder( xmlEventReader ); + + xmlEventReader.close(); + in.reset(); + + return validationConfigStaxBuilder.build(); + } + catch (XMLStreamException | IOException | SAXException e) { + throw LOG.getUnableToParseValidationXmlFileException( VALIDATION_XML_FILE, e ); + } + finally { + run( SetContextClassLoader.action( previousTccl ) ); + closeStream( in ); + } + } + + private InputStream getValidationXmlInputStream() { + LOG.debugf( "Trying to load %s for XML based Validator configuration.", VALIDATION_XML_FILE ); + InputStream inputStream = ResourceLoaderHelper.getResettableInputStreamForPath( VALIDATION_XML_FILE, externalClassLoader ); + + if ( inputStream != null ) { + return inputStream; + } + else { + LOG.debugf( "No %s found. Using annotation based configuration only.", VALIDATION_XML_FILE ); + return null; + } + } + + private Schema getSchema(XmlParserHelper xmlParserHelper, String schemaVersion) { + String schemaResource = SCHEMAS_BY_VERSION.get( schemaVersion ); + + if ( schemaResource == null ) { + throw LOG.getUnsupportedSchemaVersionException( VALIDATION_XML_FILE, schemaVersion ); + } + + Schema schema = xmlParserHelper.getSchema( schemaResource ); + + if ( schema == null ) { + throw LOG.unableToGetXmlSchema( schemaResource ); + } + + return schema; + } + + private void closeStream(InputStream inputStream) { + try { + inputStream.close(); + } + catch (IOException io) { + LOG.unableToCloseXMLFileInputStream( VALIDATION_XML_FILE ); + } + } + + /** + * 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 T run(PrivilegedAction action) { + return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); + } + +} 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 new file mode 100644 index 0000000000..c1b33da4f7 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractConstrainedElementStaxBuilder.java @@ -0,0 +1,106 @@ +/* + * 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.xml.mapping; + +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.engine.ConstraintCreationContext; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; +import org.hibernate.validator.internal.xml.mapping.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration; + +/** + * Base builder for all constrained element builders that can have constraints or type argument constraints. + * + * @author Marko Bekhta + */ +abstract class AbstractConstrainedElementStaxBuilder extends AbstractStaxBuilder { + + private static final QName IGNORE_ANNOTATIONS_QNAME = new QName( "ignore-annotations" ); + + protected final ClassLoadingHelper classLoadingHelper; + protected final ConstraintCreationContext constraintCreationContext; + protected final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + protected final AnnotationProcessingOptionsImpl annotationProcessingOptions; + + protected String mainAttributeValue; + protected Optional ignoreAnnotations; + protected final GroupConversionStaxBuilder groupConversionBuilder; + protected final ValidStaxBuilder validStaxBuilder; + protected final List constraintTypeStaxBuilders; + protected final ContainerElementTypeConfigurationBuilder containerElementTypeConfigurationBuilder; + + AbstractConstrainedElementStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintCreationContext constraintCreationContext, + DefaultPackageStaxBuilder defaultPackageStaxBuilder, AnnotationProcessingOptionsImpl annotationProcessingOptions) { + this.classLoadingHelper = classLoadingHelper; + this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; + this.constraintCreationContext = constraintCreationContext; + + this.groupConversionBuilder = new GroupConversionStaxBuilder( classLoadingHelper, defaultPackageStaxBuilder ); + this.validStaxBuilder = new ValidStaxBuilder(); + this.containerElementTypeConfigurationBuilder = new ContainerElementTypeConfigurationBuilder(); + this.annotationProcessingOptions = annotationProcessingOptions; + + this.constraintTypeStaxBuilders = new ArrayList<>(); + } + + abstract Optional getMainAttributeValueQname(); + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + Optional mainAttributeValueQname = getMainAttributeValueQname(); + if ( mainAttributeValueQname.isPresent() ) { + mainAttributeValue = readAttribute( xmlEvent.asStartElement(), mainAttributeValueQname.get() ).get(); + } + ignoreAnnotations = readAttribute( xmlEvent.asStartElement(), IGNORE_ANNOTATIONS_QNAME ).map( Boolean::parseBoolean ); + ConstraintTypeStaxBuilder constraintTypeStaxBuilder = getNewConstraintTypeStaxBuilder(); + ContainerElementTypeStaxBuilder containerElementTypeStaxBuilder = getNewContainerElementTypeStaxBuilder(); + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( getAcceptableQName() ) ) ) { + xmlEvent = xmlEventReader.nextEvent(); + validStaxBuilder.process( xmlEventReader, xmlEvent ); + groupConversionBuilder.process( xmlEventReader, xmlEvent ); + if ( constraintTypeStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + constraintTypeStaxBuilders.add( constraintTypeStaxBuilder ); + constraintTypeStaxBuilder = getNewConstraintTypeStaxBuilder(); + } + if ( containerElementTypeStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + containerElementTypeConfigurationBuilder.add( containerElementTypeStaxBuilder ); + containerElementTypeStaxBuilder = getNewContainerElementTypeStaxBuilder(); + } + } + } + + private ConstraintTypeStaxBuilder getNewConstraintTypeStaxBuilder() { + return new ConstraintTypeStaxBuilder( classLoadingHelper, constraintCreationContext, + defaultPackageStaxBuilder ); + } + + private ContainerElementTypeStaxBuilder getNewContainerElementTypeStaxBuilder() { + return new ContainerElementTypeStaxBuilder( classLoadingHelper, constraintCreationContext, + defaultPackageStaxBuilder ); + } + + protected ContainerElementTypeConfiguration getContainerElementTypeConfiguration(Type type, ConstraintLocation constraintLocation) { + return containerElementTypeConfigurationBuilder.build( constraintLocation, type ); + } + + protected CascadingMetaDataBuilder getCascadingMetaData(Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, Type type) { + return CascadingMetaDataBuilder.annotatedObject( type, validStaxBuilder.build(), containerElementTypesCascadingMetaData, groupConversionBuilder.build() ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractConstrainedExecutableElementStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractConstrainedExecutableElementStaxBuilder.java new file mode 100644 index 0000000000..91c87f4d6c --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractConstrainedExecutableElementStaxBuilder.java @@ -0,0 +1,103 @@ +/* + * 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.xml.mapping; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.engine.ConstraintCreationContext; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; + +/** + * A base builder for constraint executables. Provides read logic only. Build logic should be added in implementations. + * + * @author Marko Bekhta + */ +abstract class AbstractConstrainedExecutableElementStaxBuilder extends AbstractStaxBuilder { + + private static final QName IGNORE_ANNOTATIONS_QNAME = new QName( "ignore-annotations" ); + + protected final ClassLoadingHelper classLoadingHelper; + protected final ConstraintCreationContext constraintCreationContext; + protected final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + protected final AnnotationProcessingOptionsImpl annotationProcessingOptions; + + protected String mainAttributeValue; + protected Optional ignoreAnnotations; + protected final List constrainedParameterStaxBuilders; + private CrossParameterStaxBuilder crossParameterStaxBuilder; + private ReturnValueStaxBuilder returnValueStaxBuilder; + + AbstractConstrainedExecutableElementStaxBuilder( + ClassLoadingHelper classLoadingHelper, ConstraintCreationContext constraintCreationContext, + DefaultPackageStaxBuilder defaultPackageStaxBuilder, AnnotationProcessingOptionsImpl annotationProcessingOptions) { + this.classLoadingHelper = classLoadingHelper; + this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; + this.constraintCreationContext = constraintCreationContext; + + this.annotationProcessingOptions = annotationProcessingOptions; + + this.constrainedParameterStaxBuilders = new ArrayList<>(); + } + + abstract Optional getMainAttributeValueQname(); + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + Optional mainAttributeValueQname = getMainAttributeValueQname(); + if ( mainAttributeValueQname.isPresent() ) { + mainAttributeValue = readAttribute( xmlEvent.asStartElement(), mainAttributeValueQname.get() ).get(); + } + ignoreAnnotations = readAttribute( xmlEvent.asStartElement(), IGNORE_ANNOTATIONS_QNAME ).map( Boolean::parseBoolean ); + ConstrainedParameterStaxBuilder constrainedParameterStaxBuilder = getNewConstrainedParameterStaxBuilder(); + ReturnValueStaxBuilder localReturnValueStaxBuilder = getNewReturnValueStaxBuilder(); + CrossParameterStaxBuilder localCrossParameterStaxBuilder = getNewCrossParameterStaxBuilder(); + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( getAcceptableQName() ) ) ) { + xmlEvent = xmlEventReader.nextEvent(); + if ( constrainedParameterStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + constrainedParameterStaxBuilders.add( constrainedParameterStaxBuilder ); + constrainedParameterStaxBuilder = getNewConstrainedParameterStaxBuilder(); + } + else if ( localReturnValueStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + this.returnValueStaxBuilder = localReturnValueStaxBuilder; + } + else if ( localCrossParameterStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + this.crossParameterStaxBuilder = localCrossParameterStaxBuilder; + } + } + } + + private ConstrainedParameterStaxBuilder getNewConstrainedParameterStaxBuilder() { + return new ConstrainedParameterStaxBuilder( classLoadingHelper, constraintCreationContext, + defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + private CrossParameterStaxBuilder getNewCrossParameterStaxBuilder() { + return new CrossParameterStaxBuilder( classLoadingHelper, constraintCreationContext, + defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + private ReturnValueStaxBuilder getNewReturnValueStaxBuilder() { + return new ReturnValueStaxBuilder( classLoadingHelper, constraintCreationContext, + defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + public Optional getReturnValueStaxBuilder() { + return Optional.ofNullable( returnValueStaxBuilder ); + } + + public Optional getCrossParameterStaxBuilder() { + return Optional.ofNullable( crossParameterStaxBuilder ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractMultiValuedElementStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractMultiValuedElementStaxBuilder.java new file mode 100644 index 0000000000..f284261417 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractMultiValuedElementStaxBuilder.java @@ -0,0 +1,64 @@ +/* + * 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.xml.mapping; + +import java.util.ArrayList; +import java.util.List; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; + +/** + * An abstract builder for an element that could have multiple {@code ... } entries. + * + * @author Marko Bekhta + */ +abstract class AbstractMultiValuedElementStaxBuilder extends AbstractStaxBuilder { + + private static final String VALUE_QNAME_LOCAL_PART = "value"; + + private static final Class[] EMPTY_CLASSES_ARRAY = new Class[0]; + + private final ClassLoadingHelper classLoadingHelper; + private final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + + private final List values; + + protected AbstractMultiValuedElementStaxBuilder(ClassLoadingHelper classLoadingHelper, DefaultPackageStaxBuilder defaultPackageStaxBuilder) { + this.classLoadingHelper = classLoadingHelper; + this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; + + this.values = new ArrayList<>(); + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( getAcceptableQName() ) ) ) { + xmlEvent = xmlEventReader.nextEvent(); + if ( xmlEvent.isStartElement() && xmlEvent.asStartElement().getName().getLocalPart().equals( VALUE_QNAME_LOCAL_PART ) ) { + values.add( readSingleElement( xmlEventReader ) ); + } + } + } + + public Class[] build() { + String defaultPackage = defaultPackageStaxBuilder.build().orElse( "" ); + if ( values.isEmpty() ) { + return EMPTY_CLASSES_ARRAY; + } + + return values.stream() + .map( valueClass -> classLoadingHelper.loadClass( valueClass, defaultPackage ) ) + .peek( this::verifyClass ) + .toArray( Class[]::new ); + } + + public abstract void verifyClass(Class clazz); +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractOneLineStringStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractOneLineStringStaxBuilder.java new file mode 100644 index 0000000000..762872b093 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractOneLineStringStaxBuilder.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.internal.xml.mapping; + +import java.util.Optional; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; + +/** + * An abstract builder to be used for reading simple string element that can + * occur only once in a given block, e.g. {@code } or {@code }. + * + * @author Marko Bekhta + */ +abstract class AbstractOneLineStringStaxBuilder extends AbstractStaxBuilder { + + private String value; + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + this.value = readSingleElement( xmlEventReader ); + } + + public Optional build() { + return Optional.ofNullable( value ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/BeanStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/BeanStaxBuilder.java new file mode 100644 index 0000000000..35b8cd20c8 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/BeanStaxBuilder.java @@ -0,0 +1,226 @@ +/* + * 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.xml.mapping; + +import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; + +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.engine.ConstraintCreationContext; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; +import org.hibernate.validator.internal.properties.javabean.JavaBeanConstructor; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; +import org.hibernate.validator.internal.properties.javabean.JavaBeanMethod; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; + +/** + * Builder for definition of all bean constraints. + * + * @author Marko Bekhta + */ +class BeanStaxBuilder extends AbstractStaxBuilder { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final QName IGNORE_ANNOTATIONS_QNAME = new QName( "ignore-annotations" ); + private static final QName CLASS_QNAME = new QName( "class" ); + private static final String BEAN_QNAME_LOCAL_PART = "bean"; + + private final ClassLoadingHelper classLoadingHelper; + private final ConstraintCreationContext constraintCreationContext; + private final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + private final AnnotationProcessingOptionsImpl annotationProcessingOptions; + private final Map, List>> defaultSequences; + + protected String className; + protected Optional ignoreAnnotations; + private ClassConstraintTypeStaxBuilder classConstraintTypeStaxBuilder; + private final List constrainedFieldStaxBuilders; + private final List constrainedGetterStaxBuilders; + private final List constrainedMethodStaxBuilders; + private final List constrainedConstructorStaxBuilders; + + BeanStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintCreationContext constraintCreationContext, + DefaultPackageStaxBuilder defaultPackageStaxBuilder, AnnotationProcessingOptionsImpl annotationProcessingOptions, + Map, List>> defaultSequences) { + this.classLoadingHelper = classLoadingHelper; + this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; + this.constraintCreationContext = constraintCreationContext; + + this.annotationProcessingOptions = annotationProcessingOptions; + this.defaultSequences = defaultSequences; + + this.constrainedFieldStaxBuilders = new ArrayList<>(); + this.constrainedGetterStaxBuilders = new ArrayList<>(); + this.constrainedMethodStaxBuilders = new ArrayList<>(); + this.constrainedConstructorStaxBuilders = new ArrayList<>(); + } + + @Override + protected String getAcceptableQName() { + return BEAN_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + this.className = readAttribute( xmlEvent.asStartElement(), CLASS_QNAME ).get(); + this.ignoreAnnotations = readAttribute( xmlEvent.asStartElement(), IGNORE_ANNOTATIONS_QNAME ).map( Boolean::parseBoolean ); + ConstrainedFieldStaxBuilder fieldStaxBuilder = getNewConstrainedFieldStaxBuilder(); + ConstrainedGetterStaxBuilder getterStaxBuilder = getNewConstrainedGetterStaxBuilder(); + ConstrainedMethodStaxBuilder methodStaxBuilder = getNewConstrainedMethodStaxBuilder(); + ConstrainedConstructorStaxBuilder constructorStaxBuilder = getNewConstrainedConstructorStaxBuilder(); + + ClassConstraintTypeStaxBuilder localClassConstraintTypeStaxBuilder = new ClassConstraintTypeStaxBuilder( + classLoadingHelper, constraintCreationContext, + defaultPackageStaxBuilder, annotationProcessingOptions, defaultSequences + ); + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( getAcceptableQName() ) ) ) { + xmlEvent = xmlEventReader.nextEvent(); + if ( fieldStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + constrainedFieldStaxBuilders.add( fieldStaxBuilder ); + fieldStaxBuilder = getNewConstrainedFieldStaxBuilder(); + } + else if ( getterStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + constrainedGetterStaxBuilders.add( getterStaxBuilder ); + getterStaxBuilder = getNewConstrainedGetterStaxBuilder(); + } + else if ( methodStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + constrainedMethodStaxBuilders.add( methodStaxBuilder ); + methodStaxBuilder = getNewConstrainedMethodStaxBuilder(); + } + else if ( constructorStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + constrainedConstructorStaxBuilders.add( constructorStaxBuilder ); + constructorStaxBuilder = getNewConstrainedConstructorStaxBuilder(); + } + else if ( localClassConstraintTypeStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + classConstraintTypeStaxBuilder = localClassConstraintTypeStaxBuilder; + } + } + } + + private ConstrainedFieldStaxBuilder getNewConstrainedFieldStaxBuilder() { + return new ConstrainedFieldStaxBuilder( classLoadingHelper, constraintCreationContext, + defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + private ConstrainedGetterStaxBuilder getNewConstrainedGetterStaxBuilder() { + return new ConstrainedGetterStaxBuilder( classLoadingHelper, constraintCreationContext, + defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + private ConstrainedMethodStaxBuilder getNewConstrainedMethodStaxBuilder() { + return new ConstrainedMethodStaxBuilder( classLoadingHelper, constraintCreationContext, + defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + private ConstrainedConstructorStaxBuilder getNewConstrainedConstructorStaxBuilder() { + return new ConstrainedConstructorStaxBuilder( classLoadingHelper, constraintCreationContext, + defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + void build(JavaBeanHelper javaBeanHelper, Set> processedClasses, Map, Set> constrainedElementsByType) { + Class beanClass = classLoadingHelper.loadClass( className, defaultPackageStaxBuilder.build().orElse( "" ) ); + + checkClassHasNotBeenProcessed( processedClasses, beanClass ); + + // update annotation ignores + // NOTE: if there was no ignoreAnnotations attribute specified on a bean + // we use `true` as a default + annotationProcessingOptions.ignoreAnnotationConstraintForClass( + beanClass, + ignoreAnnotations.orElse( true ) + ); + + if ( classConstraintTypeStaxBuilder != null ) { + addConstrainedElements( + constrainedElementsByType, + beanClass, + Collections.singleton( classConstraintTypeStaxBuilder.build( beanClass ) ) + ); + } + + List alreadyProcessedFieldNames = new ArrayList<>( constrainedFieldStaxBuilders.size() ); + addConstrainedElements( + constrainedElementsByType, + beanClass, constrainedFieldStaxBuilders.stream() + .map( builder -> builder.build( javaBeanHelper, beanClass, alreadyProcessedFieldNames ) ) + .collect( Collectors.toList() ) + ); + + List alreadyProcessedGetterNames = new ArrayList<>( constrainedGetterStaxBuilders.size() ); + addConstrainedElements( + constrainedElementsByType, + beanClass, + constrainedGetterStaxBuilders.stream() + .map( builder -> builder.build( javaBeanHelper, beanClass, alreadyProcessedGetterNames ) ) + .collect( Collectors.toList() ) + ); + + List alreadyProcessedMethods = new ArrayList<>( constrainedMethodStaxBuilders.size() ); + addConstrainedElements( + constrainedElementsByType, + beanClass, + constrainedMethodStaxBuilders.stream() + .map( builder -> builder.build( javaBeanHelper, beanClass, alreadyProcessedMethods ) ) + .collect( Collectors.toList() ) + ); + + List alreadyProcessedConstructors = new ArrayList<>( constrainedConstructorStaxBuilders.size() ); + addConstrainedElements( + constrainedElementsByType, + beanClass, + constrainedConstructorStaxBuilders.stream() + .map( builder -> builder.build( javaBeanHelper, beanClass, alreadyProcessedConstructors ) ) + .collect( Collectors.toList() ) + ); + } + + private void addConstrainedElements(Map, Set> constrainedElementsbyType, Class beanClass, Collection newConstrainedElements) { + if ( constrainedElementsbyType.containsKey( beanClass ) ) { + + Set existingConstrainedElements = constrainedElementsbyType.get( beanClass ); + + for ( ConstrainedElement constrainedElement : newConstrainedElements ) { + if ( existingConstrainedElements.contains( constrainedElement ) ) { + throw LOG.getConstrainedElementConfiguredMultipleTimesException( + constrainedElement.toString() + ); + } + } + + existingConstrainedElements.addAll( newConstrainedElements ); + } + else { + Set tmpSet = newHashSet(); + tmpSet.addAll( newConstrainedElements ); + constrainedElementsbyType.put( beanClass, tmpSet ); + } + } + + private void checkClassHasNotBeenProcessed(Set> processedClasses, Class beanClass) { + if ( processedClasses.contains( beanClass ) ) { + throw LOG.getBeanClassHasAlreadyBeenConfiguredInXmlException( beanClass ); + } + processedClasses.add( beanClass ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ClassConstraintTypeStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ClassConstraintTypeStaxBuilder.java new file mode 100644 index 0000000000..0a6907f039 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ClassConstraintTypeStaxBuilder.java @@ -0,0 +1,137 @@ +/* + * 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.xml.mapping; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.engine.ConstraintCreationContext; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; +import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; +import org.hibernate.validator.internal.metadata.raw.ConstrainedType; +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; + +/** + * Builder for class level constraints. + * + * @author Marko Bekhta + */ +class ClassConstraintTypeStaxBuilder extends AbstractStaxBuilder { + + private static final String CLASS_QNAME_LOCAL_PART = "class"; + private static final QName IGNORE_ANNOTATIONS_QNAME = new QName( "ignore-annotations" ); + + private final ClassLoadingHelper classLoadingHelper; + private final ConstraintCreationContext constraintCreationContext; + private final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + private final AnnotationProcessingOptionsImpl annotationProcessingOptions; + private final Map, List>> defaultSequences; + + private Optional ignoreAnnotations; + private final List constraintTypeStaxBuilders; + private final GroupSequenceStaxBuilder groupSequenceStaxBuilder; + + ClassConstraintTypeStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintCreationContext constraintCreationContext, + DefaultPackageStaxBuilder defaultPackageStaxBuilder, AnnotationProcessingOptionsImpl annotationProcessingOptions, + Map, List>> defaultSequences) { + this.classLoadingHelper = classLoadingHelper; + this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; + this.constraintCreationContext = constraintCreationContext; + + this.annotationProcessingOptions = annotationProcessingOptions; + this.defaultSequences = defaultSequences; + + this.constraintTypeStaxBuilders = new ArrayList<>(); + this.groupSequenceStaxBuilder = new GroupSequenceStaxBuilder( classLoadingHelper, defaultPackageStaxBuilder ); + } + + @Override + protected String getAcceptableQName() { + return CLASS_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + ignoreAnnotations = readAttribute( xmlEvent.asStartElement(), IGNORE_ANNOTATIONS_QNAME ).map( Boolean::parseBoolean ); + + ConstraintTypeStaxBuilder constraintTypeStaxBuilder = getNewConstraintTypeStaxBuilder(); + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( getAcceptableQName() ) ) ) { + xmlEvent = xmlEventReader.nextEvent(); + groupSequenceStaxBuilder.process( xmlEventReader, xmlEvent ); + if ( constraintTypeStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + constraintTypeStaxBuilders.add( constraintTypeStaxBuilder ); + constraintTypeStaxBuilder = getNewConstraintTypeStaxBuilder(); + } + } + } + + private ConstraintTypeStaxBuilder getNewConstraintTypeStaxBuilder() { + return new ConstraintTypeStaxBuilder( classLoadingHelper, constraintCreationContext, + defaultPackageStaxBuilder ); + } + + ConstrainedType build(Class beanClass) { + // group sequence + List> groupSequence = Arrays.asList( groupSequenceStaxBuilder.build() ); + if ( !groupSequence.isEmpty() ) { + defaultSequences.put( beanClass, groupSequence ); + } + + // constraints + ConstraintLocation constraintLocation = ConstraintLocation.forClass( beanClass ); + + Set> metaConstraints = constraintTypeStaxBuilders.stream() + .map( builder -> builder.build( constraintLocation, ConstraintLocationKind.TYPE, null ) ) + .collect( Collectors.toSet() ); + + // ignore annotation + if ( ignoreAnnotations.isPresent() ) { + annotationProcessingOptions.ignoreClassLevelConstraintAnnotations( + beanClass, + ignoreAnnotations.get() + ); + } + + return new ConstrainedType( + ConfigurationSource.XML, + beanClass, + metaConstraints + ); + } + + private static class GroupSequenceStaxBuilder extends AbstractMultiValuedElementStaxBuilder { + + private static final String GROUP_SEQUENCE_QNAME_LOCAL_PART = "group-sequence"; + + private GroupSequenceStaxBuilder(ClassLoadingHelper classLoadingHelper, DefaultPackageStaxBuilder defaultPackageStaxBuilder) { + super( classLoadingHelper, defaultPackageStaxBuilder ); + } + + @Override + public void verifyClass(Class clazz) { + // do nothing + } + + @Override + protected String getAcceptableQName() { + return GROUP_SEQUENCE_QNAME_LOCAL_PART; + } + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/ClassLoadingHelper.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ClassLoadingHelper.java similarity index 87% rename from engine/src/main/java/org/hibernate/validator/internal/xml/ClassLoadingHelper.java rename to engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ClassLoadingHelper.java index 7d5392df18..959e3e65b5 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/ClassLoadingHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ClassLoadingHelper.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.xml; +package org.hibernate.validator.internal.xml.mapping; import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; @@ -20,8 +20,9 @@ * types, qualified names or unqualified names (in which case a given default package will be assumed). * * @author Gunnar Morling + * @author Guillaume Smet */ -/*package*/ class ClassLoadingHelper { +class ClassLoadingHelper { private static final String PACKAGE_SEPARATOR = "."; private static final String ARRAY_CLASS_NAME_PREFIX = "[L"; @@ -47,11 +48,14 @@ private final ClassLoader externalClassLoader; - ClassLoadingHelper(ClassLoader externalClassLoader) { + private final ClassLoader threadContextClassLoader; + + ClassLoadingHelper(ClassLoader externalClassLoader, ClassLoader threadContextClassLoader) { this.externalClassLoader = externalClassLoader; + this.threadContextClassLoader = threadContextClassLoader; } - /*package*/ Class loadClass(String className, String defaultPackage) { + Class loadClass(String className, String defaultPackage) { if ( PRIMITIVE_NAME_TO_PRIMITIVE.containsKey( className ) ) { return PRIMITIVE_NAME_TO_PRIMITIVE.get( className ); } @@ -80,7 +84,7 @@ } private Class loadClass(String className) { - return run( LoadClass.action( className, externalClassLoader ) ); + return run( LoadClass.action( className, externalClassLoader, threadContextClassLoader ) ); } private static boolean isArrayClassName(String className) { 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 new file mode 100644 index 0000000000..a65a246a3d --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedConstructorStaxBuilder.java @@ -0,0 +1,117 @@ +/* + * 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.xml.mapping; + +import java.lang.invoke.MethodHandles; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import javax.xml.namespace.QName; + +import org.hibernate.validator.internal.engine.ConstraintCreationContext; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; +import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable; +import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter; +import org.hibernate.validator.internal.properties.javabean.JavaBeanConstructor; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; +import org.hibernate.validator.internal.util.CollectionHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; + +/** + * Builder for constrained constructors. + * + * @author Hardy Ferentschik + * @author Guillaume Smet + * @author Marko Bekhta + */ +class ConstrainedConstructorStaxBuilder extends AbstractConstrainedExecutableElementStaxBuilder { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final String METHOD_QNAME_LOCAL_PART = "constructor"; + + ConstrainedConstructorStaxBuilder( + ClassLoadingHelper classLoadingHelper, ConstraintCreationContext constraintCreationContext, + DefaultPackageStaxBuilder defaultPackageStaxBuilder, AnnotationProcessingOptionsImpl annotationProcessingOptions) { + super( classLoadingHelper, constraintCreationContext, defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + @Override + Optional getMainAttributeValueQname() { + return Optional.empty(); + } + + @Override + protected String getAcceptableQName() { + return METHOD_QNAME_LOCAL_PART; + } + + public String getMethodName() { + return mainAttributeValue; + } + + ConstrainedExecutable build(JavaBeanHelper javaBeanHelper, Class beanClass, List alreadyProcessedConstructors) { + Class[] parameterTypes = constrainedParameterStaxBuilders.stream() + .map( builder -> builder.getParameterType( beanClass ) ) + .toArray( Class[]::new ); + + final JavaBeanConstructor javaBeanConstructor = findConstructor( javaBeanHelper, beanClass, parameterTypes ); + + if ( alreadyProcessedConstructors.contains( javaBeanConstructor ) ) { + throw LOG.getConstructorIsDefinedTwiceInMappingXmlForBeanException( javaBeanConstructor, beanClass ); + } + else { + alreadyProcessedConstructors.add( javaBeanConstructor ); + } + + // ignore annotations + if ( ignoreAnnotations.isPresent() ) { + annotationProcessingOptions.ignoreConstraintAnnotationsOnMember( + javaBeanConstructor, + ignoreAnnotations.get() + ); + } + + List constrainedParameters = CollectionHelper.newArrayList( constrainedParameterStaxBuilders.size() ); + for ( int index = 0; index < constrainedParameterStaxBuilders.size(); index++ ) { + ConstrainedParameterStaxBuilder builder = constrainedParameterStaxBuilders.get( index ); + constrainedParameters.add( builder.build( javaBeanConstructor, index ) ); + } + + Set> crossParameterConstraints = getCrossParameterStaxBuilder() + .map( builder -> builder.build( javaBeanConstructor ) ).orElse( Collections.emptySet() ); + + // parse the return value + Set> returnValueConstraints = new HashSet<>(); + Set> returnValueTypeArgumentConstraints = new HashSet<>(); + CascadingMetaDataBuilder cascadingMetaDataBuilder = getReturnValueStaxBuilder() + .map( builder -> builder.build( javaBeanConstructor, returnValueConstraints, returnValueTypeArgumentConstraints ) ) + .orElse( CascadingMetaDataBuilder.nonCascading() ); + + return new ConstrainedExecutable( + ConfigurationSource.XML, + javaBeanConstructor, + constrainedParameters, + crossParameterConstraints, + returnValueConstraints, + returnValueTypeArgumentConstraints, + cascadingMetaDataBuilder + ); + } + + private JavaBeanConstructor findConstructor(JavaBeanHelper javaBeanHelper, Class beanClass, Class[] parameterTypes) { + return javaBeanHelper.findDeclaredConstructor( beanClass, parameterTypes ) + .orElseThrow( () -> LOG.getBeanDoesNotContainConstructorException( beanClass, parameterTypes ) ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedFieldStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedFieldStaxBuilder.java new file mode 100644 index 0000000000..0277d0d37d --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedFieldStaxBuilder.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.xml.mapping; + +import java.lang.invoke.MethodHandles; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.xml.namespace.QName; + +import org.hibernate.validator.internal.engine.ConstraintCreationContext; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; +import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; +import org.hibernate.validator.internal.metadata.raw.ConstrainedField; +import org.hibernate.validator.internal.properties.javabean.JavaBeanField; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.xml.mapping.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration; + +/** + * Builder for constrained fields. + * + * @author Hardy Ferentschik + * @author Guillaume Smet + * @author Marko Bekhta + */ +class ConstrainedFieldStaxBuilder extends AbstractConstrainedElementStaxBuilder { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final String FIELD_QNAME_LOCAL_PART = "field"; + private static final QName NAME_QNAME = new QName( "name" ); + + ConstrainedFieldStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintCreationContext constraintCreationContext, + DefaultPackageStaxBuilder defaultPackageStaxBuilder, AnnotationProcessingOptionsImpl annotationProcessingOptions) { + super( classLoadingHelper, constraintCreationContext, defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + @Override + Optional getMainAttributeValueQname() { + return Optional.of( NAME_QNAME ); + } + + @Override + protected String getAcceptableQName() { + return FIELD_QNAME_LOCAL_PART; + } + + ConstrainedField build(JavaBeanHelper javaBeanHelper, Class beanClass, List alreadyProcessedFieldNames) { + if ( alreadyProcessedFieldNames.contains( mainAttributeValue ) ) { + throw LOG.getIsDefinedTwiceInMappingXmlForBeanException( mainAttributeValue, beanClass ); + } + else { + alreadyProcessedFieldNames.add( mainAttributeValue ); + } + JavaBeanField javaBeanField = findField( javaBeanHelper, beanClass, mainAttributeValue ); + ConstraintLocation constraintLocation = ConstraintLocation.forField( javaBeanField ); + + Set> metaConstraints = constraintTypeStaxBuilders.stream() + .map( builder -> builder.build( constraintLocation, ConstraintLocationKind.FIELD, null ) ) + .collect( Collectors.toSet() ); + + ContainerElementTypeConfiguration containerElementTypeConfiguration = getContainerElementTypeConfiguration( + javaBeanField.getType(), constraintLocation ); + + ConstrainedField constrainedField = new ConstrainedField( + ConfigurationSource.XML, + javaBeanField, + metaConstraints, + containerElementTypeConfiguration.getMetaConstraints(), + getCascadingMetaData( containerElementTypeConfiguration.getTypeParametersCascadingMetaData(), javaBeanField.getType() ) + ); + + // ignore annotations + if ( ignoreAnnotations.isPresent() ) { + annotationProcessingOptions.ignoreConstraintAnnotationsOnMember( + javaBeanField, + ignoreAnnotations.get() + ); + } + + return constrainedField; + } + + private static JavaBeanField findField(JavaBeanHelper javaBeanHelper, Class beanClass, String fieldName) { + return javaBeanHelper.findDeclaredField( beanClass, fieldName ) + .orElseThrow( () -> LOG.getBeanDoesNotContainTheFieldException( beanClass, fieldName ) ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedGetterStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedGetterStaxBuilder.java new file mode 100644 index 0000000000..df8dabaf6f --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedGetterStaxBuilder.java @@ -0,0 +1,104 @@ +/* + * 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.xml.mapping; + +import java.lang.invoke.MethodHandles; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.xml.namespace.QName; + +import org.hibernate.validator.internal.engine.ConstraintCreationContext; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; +import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; +import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable; +import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter; +import org.hibernate.validator.internal.properties.javabean.JavaBeanGetter; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.xml.mapping.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration; + +/** + * Builder for constrained getters. + * + * @author Hardy Ferentschik + * @author Guillaume Smet + * @author Marko Bekhta + */ +class ConstrainedGetterStaxBuilder extends AbstractConstrainedElementStaxBuilder { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + private static final QName NAME_QNAME = new QName( "name" ); + + private static final String GETTER_QNAME_LOCAL_PART = "getter"; + + ConstrainedGetterStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintCreationContext constraintCreationContext, + DefaultPackageStaxBuilder defaultPackageStaxBuilder, AnnotationProcessingOptionsImpl annotationProcessingOptions) { + super( classLoadingHelper, constraintCreationContext, defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + @Override + Optional getMainAttributeValueQname() { + return Optional.of( NAME_QNAME ); + } + + @Override + protected String getAcceptableQName() { + return GETTER_QNAME_LOCAL_PART; + } + + ConstrainedExecutable build(JavaBeanHelper javaBeanHelper, Class beanClass, List alreadyProcessedGetterNames) { + if ( alreadyProcessedGetterNames.contains( mainAttributeValue ) ) { + throw LOG.getIsDefinedTwiceInMappingXmlForBeanException( mainAttributeValue, beanClass ); + } + else { + alreadyProcessedGetterNames.add( mainAttributeValue ); + } + JavaBeanGetter javaBeanGetter = findGetter( javaBeanHelper, beanClass, mainAttributeValue ); + ConstraintLocation constraintLocation = ConstraintLocation.forGetter( javaBeanGetter ); + + Set> metaConstraints = constraintTypeStaxBuilders.stream() + .map( builder -> builder.build( constraintLocation, ConstraintLocationKind.GETTER, null ) ) + .collect( Collectors.toSet() ); + + ContainerElementTypeConfiguration containerElementTypeConfiguration = getContainerElementTypeConfiguration( + javaBeanGetter.getType(), constraintLocation ); + + ConstrainedExecutable constrainedGetter = new ConstrainedExecutable( + ConfigurationSource.XML, + javaBeanGetter, + Collections.emptyList(), + Collections.>emptySet(), + metaConstraints, + containerElementTypeConfiguration.getMetaConstraints(), + getCascadingMetaData( containerElementTypeConfiguration.getTypeParametersCascadingMetaData(), javaBeanGetter.getType() ) + ); + + // ignore annotations + if ( ignoreAnnotations.isPresent() ) { + annotationProcessingOptions.ignoreConstraintAnnotationsOnMember( + javaBeanGetter, + ignoreAnnotations.get() + ); + } + + return constrainedGetter; + } + + private static JavaBeanGetter findGetter(JavaBeanHelper javaBeanHelper, Class beanClass, String getterName) { + Optional property = javaBeanHelper.findGetter( beanClass, getterName ); + + return property.orElseThrow( () -> LOG.getBeanDoesNotContainThePropertyException( beanClass, getterName ) ); + } +} 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 new file mode 100644 index 0000000000..c6cafd1ebf --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedMethodStaxBuilder.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.xml.mapping; + +import java.lang.invoke.MethodHandles; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import javax.xml.namespace.QName; + +import org.hibernate.validator.internal.engine.ConstraintCreationContext; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; +import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable; +import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; +import org.hibernate.validator.internal.properties.javabean.JavaBeanMethod; +import org.hibernate.validator.internal.util.CollectionHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; + +/** + * Builder for constrained methods. + * + * @author Hardy Ferentschik + * @author Guillaume Smet + * @author Marko Bekhta + */ +class ConstrainedMethodStaxBuilder extends AbstractConstrainedExecutableElementStaxBuilder { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final String METHOD_QNAME_LOCAL_PART = "method"; + private static final QName NAME_QNAME = new QName( "name" ); + + ConstrainedMethodStaxBuilder( + ClassLoadingHelper classLoadingHelper, ConstraintCreationContext constraintCreationContext, + DefaultPackageStaxBuilder defaultPackageStaxBuilder, AnnotationProcessingOptionsImpl annotationProcessingOptions) { + super( classLoadingHelper, constraintCreationContext, defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + @Override + Optional getMainAttributeValueQname() { + return Optional.of( NAME_QNAME ); + } + + @Override + protected String getAcceptableQName() { + return METHOD_QNAME_LOCAL_PART; + } + + public String getMethodName() { + return mainAttributeValue; + } + + ConstrainedExecutable build(JavaBeanHelper javaBeanHelper, Class beanClass, List alreadyProcessedMethods) { + Class[] parameterTypes = constrainedParameterStaxBuilders.stream() + .map( builder -> builder.getParameterType( beanClass ) ) + .toArray( Class[]::new ); + + String methodName = getMethodName(); + + JavaBeanMethod javaBeanMethod = findMethod( javaBeanHelper, beanClass, methodName, parameterTypes ); + + if ( alreadyProcessedMethods.contains( javaBeanMethod ) ) { + throw LOG.getMethodIsDefinedTwiceInMappingXmlForBeanException( javaBeanMethod, beanClass ); + } + else { + alreadyProcessedMethods.add( javaBeanMethod ); + } + + // ignore annotations + if ( ignoreAnnotations.isPresent() ) { + annotationProcessingOptions.ignoreConstraintAnnotationsOnMember( + javaBeanMethod, + ignoreAnnotations.get() + ); + } + + List constrainedParameters = CollectionHelper.newArrayList( constrainedParameterStaxBuilders.size() ); + for ( int index = 0; index < constrainedParameterStaxBuilders.size(); index++ ) { + ConstrainedParameterStaxBuilder builder = constrainedParameterStaxBuilders.get( index ); + constrainedParameters.add( builder.build( javaBeanMethod, index ) ); + } + + Set> crossParameterConstraints = getCrossParameterStaxBuilder() + .map( builder -> builder.build( javaBeanMethod ) ).orElse( Collections.emptySet() ); + + // parse the return value + Set> returnValueConstraints = new HashSet<>(); + Set> returnValueTypeArgumentConstraints = new HashSet<>(); + CascadingMetaDataBuilder cascadingMetaDataBuilder = getReturnValueStaxBuilder().map( builder -> builder.build( javaBeanMethod, returnValueConstraints, returnValueTypeArgumentConstraints ) ) + .orElse( CascadingMetaDataBuilder.nonCascading() ); + + return new ConstrainedExecutable( + ConfigurationSource.XML, + javaBeanMethod, + constrainedParameters, + crossParameterConstraints, + returnValueConstraints, + returnValueTypeArgumentConstraints, + cascadingMetaDataBuilder + ); + } + + private JavaBeanMethod findMethod(JavaBeanHelper javaBeanHelper, Class beanClass, String methodName, Class[] parameterTypes) { + return javaBeanHelper.findDeclaredMethod( beanClass, methodName, parameterTypes ) + .orElseThrow( () -> LOG.getBeanDoesNotContainMethodException( beanClass, methodName, parameterTypes ) ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedParameterStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedParameterStaxBuilder.java new file mode 100644 index 0000000000..549ba144db --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedParameterStaxBuilder.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.xml.mapping; + +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Type; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.validation.ValidationException; +import javax.xml.namespace.QName; + +import org.hibernate.validator.internal.engine.ConstraintCreationContext; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; +import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; +import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter; +import org.hibernate.validator.internal.properties.Callable; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.xml.mapping.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration; + +/** + * Builder for constrained parameters. + * + * @author Hardy Ferentschik + * @author Guillaume Smet + * @author Marko Bekhta + */ +class ConstrainedParameterStaxBuilder extends AbstractConstrainedElementStaxBuilder { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final String PARAMETER_QNAME_LOCAL_PART = "parameter"; + private static final QName TYPE_QNAME = new QName( "type" ); + + ConstrainedParameterStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintCreationContext constraintCreationContext, + DefaultPackageStaxBuilder defaultPackageStaxBuilder, AnnotationProcessingOptionsImpl annotationProcessingOptions) { + super( classLoadingHelper, constraintCreationContext, defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + @Override + Optional getMainAttributeValueQname() { + return Optional.of( TYPE_QNAME ); + } + + @Override + protected String getAcceptableQName() { + return PARAMETER_QNAME_LOCAL_PART; + } + + public Class getParameterType(Class beanClass) { + try { + return classLoadingHelper.loadClass( mainAttributeValue, defaultPackageStaxBuilder.build().orElse( "" ) ); + } + catch (ValidationException e) { + throw LOG.getInvalidParameterTypeException( mainAttributeValue, beanClass ); + } + } + + ConstrainedParameter build(Callable callable, int index) { + + ConstraintLocation constraintLocation = ConstraintLocation.forParameter( callable, index ); + Type type = callable.getParameterGenericType( index ); + + Set> metaConstraints = constraintTypeStaxBuilders.stream() + .map( builder -> builder.build( constraintLocation, ConstraintLocationKind.PARAMETER, null ) ) + .collect( Collectors.toSet() ); + + ContainerElementTypeConfiguration containerElementTypeConfiguration = getContainerElementTypeConfiguration( type, constraintLocation ); + + // ignore annotations + if ( ignoreAnnotations.isPresent() ) { + annotationProcessingOptions.ignoreConstraintAnnotationsOnParameter( + callable, + index, + ignoreAnnotations.get() + ); + } + + ConstrainedParameter constrainedParameter = new ConstrainedParameter( + ConfigurationSource.XML, + callable, + type, + index, + metaConstraints, + containerElementTypeConfiguration.getMetaConstraints(), + getCascadingMetaData( containerElementTypeConfiguration.getTypeParametersCascadingMetaData(), type ) + ); + return constrainedParameter; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstraintDefinitionStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstraintDefinitionStaxBuilder.java new file mode 100644 index 0000000000..a600d370be --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstraintDefinitionStaxBuilder.java @@ -0,0 +1,158 @@ +/* + * 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.xml.mapping; + +import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.validation.ConstraintValidator; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorDescriptor; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; + +/** + * Builder for a constraint definition. + * + * @author Marko Bekhta + */ +class ConstraintDefinitionStaxBuilder extends AbstractStaxBuilder { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final String CONSTRAINT_DEFINITION_QNAME_LOCAL_PART = "constraint-definition"; + private static final QName ANNOTATION_QNAME = new QName( "annotation" ); + + private final ClassLoadingHelper classLoadingHelper; + private final ConstraintHelper constraintHelper; + private final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + + private String annotation; + private ValidatedByStaxBuilder validatedByStaxBuilder; + + ConstraintDefinitionStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintHelper constraintHelper, DefaultPackageStaxBuilder defaultPackageStaxBuilder) { + this.classLoadingHelper = classLoadingHelper; + this.constraintHelper = constraintHelper; + this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; + + this.validatedByStaxBuilder = new ValidatedByStaxBuilder( classLoadingHelper, defaultPackageStaxBuilder ); + } + + @Override + protected String getAcceptableQName() { + return CONSTRAINT_DEFINITION_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + annotation = readAttribute( xmlEvent.asStartElement(), ANNOTATION_QNAME ).get(); + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( getAcceptableQName() ) ) ) { + validatedByStaxBuilder.process( xmlEventReader, xmlEvent ); + xmlEvent = xmlEventReader.nextEvent(); + } + } + + @SuppressWarnings("unchecked") + void build(Set alreadyProcessedConstraintDefinitions) { + checkProcessedAnnotations( alreadyProcessedConstraintDefinitions ); + String defaultPackage = defaultPackageStaxBuilder.build().orElse( "" ); + Class clazz = classLoadingHelper.loadClass( annotation, defaultPackage ); + if ( !clazz.isAnnotation() ) { + throw LOG.getIsNotAnAnnotationException( clazz ); + } + Class annotationClass = (Class) clazz; + addValidatorDefinitions( annotationClass ); + } + + private void checkProcessedAnnotations(Set alreadyProcessedConstraintDefinitions) { + if ( alreadyProcessedConstraintDefinitions.contains( annotation ) ) { + throw LOG.getOverridingConstraintDefinitionsInMultipleMappingFilesException( annotation ); + } + else { + alreadyProcessedConstraintDefinitions.add( annotation ); + } + } + + private void addValidatorDefinitions(Class annotationClass) { + constraintHelper.putValidatorDescriptors( + annotationClass, + validatedByStaxBuilder.build( annotationClass ), + validatedByStaxBuilder.isIncludeExistingValidators() + ); + } + + private static class ValidatedByStaxBuilder extends AbstractStaxBuilder { + + private static final String VALIDATED_BY_QNAME_LOCAL_PART = "validated-by"; + private static final String VALUE_QNAME_LOCAL_PART = "value"; + private static final QName INCLUDE_EXISTING_VALIDATORS_QNAME = new QName( "include-existing-validators" ); + + private final ClassLoadingHelper classLoadingHelper; + private final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + + private boolean includeExistingValidators; + private final List values; + + protected ValidatedByStaxBuilder(ClassLoadingHelper classLoadingHelper, DefaultPackageStaxBuilder defaultPackageStaxBuilder) { + this.classLoadingHelper = classLoadingHelper; + this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; + + this.values = new ArrayList<>(); + } + + @Override + protected String getAcceptableQName() { + return VALIDATED_BY_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + includeExistingValidators = readAttribute( xmlEvent.asStartElement(), INCLUDE_EXISTING_VALIDATORS_QNAME ) + .map( Boolean::parseBoolean ).orElse( true ); + + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( getAcceptableQName() ) ) ) { + if ( xmlEvent.isStartElement() && xmlEvent.asStartElement().getName().getLocalPart().equals( VALUE_QNAME_LOCAL_PART ) ) { + values.add( readSingleElement( xmlEventReader ) ); + } + xmlEvent = xmlEventReader.nextEvent(); + } + } + + @SuppressWarnings("unchecked") + List> build(Class annotation) { + String defaultPackage = defaultPackageStaxBuilder.build().orElse( "" ); + + return values.stream() + .map( value -> classLoadingHelper.loadClass( value, defaultPackage ) ) + .peek( this::checkValidatorAssignability ) + .map( clazz -> (Class>) clazz ) + .map( validatorClass -> ConstraintValidatorDescriptor.forClass( validatorClass, annotation ) ) + .collect( Collectors.toList() ); + } + + public boolean isIncludeExistingValidators() { + return includeExistingValidators; + } + + private void checkValidatorAssignability(Class validatorClass) { + if ( !ConstraintValidator.class.isAssignableFrom( validatorClass ) ) { + throw LOG.getIsNotAConstraintValidatorClassException( validatorClass ); + } + } + + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstraintMappingsStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstraintMappingsStaxBuilder.java new file mode 100644 index 0000000000..5717243a0c --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstraintMappingsStaxBuilder.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.xml.mapping; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.engine.ConstraintCreationContext; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; + +/** + * Top level builder for constraint mappings. Reads the whole mapping file and builds the constraint definitions defined + * in it as well as the list of constrained elements per bean. + * + * @author Marko Bekhta + */ +class ConstraintMappingsStaxBuilder extends AbstractStaxBuilder { + + private static final String CONSTRAINT_MAPPINGS_QNAME = "constraint-mappings"; + + private final ClassLoadingHelper classLoadingHelper; + private final ConstraintCreationContext constraintCreationContext; + private final AnnotationProcessingOptionsImpl annotationProcessingOptions; + private final JavaBeanHelper javaBeanHelper; + private final Map, List>> defaultSequences; + + private final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + private final List beanStaxBuilders; + private final List constraintDefinitionStaxBuilders; + + public ConstraintMappingsStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintCreationContext constraintCreationContext, + AnnotationProcessingOptionsImpl annotationProcessingOptions, JavaBeanHelper javaBeanHelper, Map, List>> defaultSequences) { + this.classLoadingHelper = classLoadingHelper; + this.constraintCreationContext = constraintCreationContext; + this.annotationProcessingOptions = annotationProcessingOptions; + this.javaBeanHelper = javaBeanHelper; + this.defaultSequences = defaultSequences; + + this.defaultPackageStaxBuilder = new DefaultPackageStaxBuilder(); + this.beanStaxBuilders = new ArrayList<>(); + this.constraintDefinitionStaxBuilders = new ArrayList<>(); + } + + @Override + protected String getAcceptableQName() { + return CONSTRAINT_MAPPINGS_QNAME; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + BeanStaxBuilder beanStaxBuilder = getNewBeanStaxBuilder(); + ConstraintDefinitionStaxBuilder constraintDefinitionStaxBuilder = getNewConstraintDefinitionStaxBuilder(); + + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( getAcceptableQName() ) ) ) { + xmlEvent = xmlEventReader.nextEvent(); + if ( beanStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + beanStaxBuilders.add( beanStaxBuilder ); + beanStaxBuilder = getNewBeanStaxBuilder(); + } + else if ( constraintDefinitionStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + constraintDefinitionStaxBuilders.add( constraintDefinitionStaxBuilder ); + constraintDefinitionStaxBuilder = getNewConstraintDefinitionStaxBuilder(); + } + defaultPackageStaxBuilder.process( xmlEventReader, xmlEvent ); + } + } + + private BeanStaxBuilder getNewBeanStaxBuilder() { + return new BeanStaxBuilder( classLoadingHelper, constraintCreationContext, + defaultPackageStaxBuilder, annotationProcessingOptions, defaultSequences ); + } + + private ConstraintDefinitionStaxBuilder getNewConstraintDefinitionStaxBuilder() { + return new ConstraintDefinitionStaxBuilder( classLoadingHelper, constraintCreationContext.getConstraintHelper(), defaultPackageStaxBuilder ); + } + + public void build(Set> processedClasses, Map, Set> constrainedElementsByType, Set alreadyProcessedConstraintDefinitions) { + constraintDefinitionStaxBuilders.forEach( builder -> builder.build( alreadyProcessedConstraintDefinitions ) ); + beanStaxBuilders.forEach( builder -> builder.build( javaBeanHelper, processedClasses, constrainedElementsByType ) ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstraintTypeStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstraintTypeStaxBuilder.java new file mode 100644 index 0000000000..183645419b --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstraintTypeStaxBuilder.java @@ -0,0 +1,511 @@ +/* + * 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.xml.mapping; + +import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.validation.Payload; +import javax.validation.ValidationException; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.engine.ConstraintCreationContext; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.core.MetaConstraints; +import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; +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.GetMethod; +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; + +/** + * Builder for constraint information. Creates a constraint based on a set of given values. + * + * @author Hardy Ferentschik + * @author Marko Bekhta + */ +class ConstraintTypeStaxBuilder extends AbstractStaxBuilder { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final Pattern IS_ONLY_WHITESPACE = Pattern.compile( "\\s*" ); + + private static final String CONSTRAINT_QNAME_LOCAL_PART = "constraint"; + + private static final QName CONSTRAINT_ANNOTATION_QNAME = new QName( "annotation" ); + + private final ClassLoadingHelper classLoadingHelper; + private final ConstraintCreationContext constraintCreationContext; + private final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + + // Builders: + private final GroupsStaxBuilder groupsStaxBuilder; + private final PayloadStaxBuilder payloadStaxBuilder; + private final ConstraintParameterStaxBuilder constrainParameterStaxBuilder; + private final MessageStaxBuilder messageStaxBuilder; + + private final List builders; + + private String constraintAnnotation; + + ConstraintTypeStaxBuilder( + ClassLoadingHelper classLoadingHelper, ConstraintCreationContext constraintCreationContext, + DefaultPackageStaxBuilder defaultPackageStaxBuilder) { + this.classLoadingHelper = classLoadingHelper; + this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; + this.constraintCreationContext = constraintCreationContext; + + this.groupsStaxBuilder = new GroupsStaxBuilder( classLoadingHelper, defaultPackageStaxBuilder ); + this.payloadStaxBuilder = new PayloadStaxBuilder( classLoadingHelper, defaultPackageStaxBuilder ); + this.constrainParameterStaxBuilder = new ConstraintParameterStaxBuilder( classLoadingHelper, defaultPackageStaxBuilder ); + this.messageStaxBuilder = new MessageStaxBuilder(); + + this.builders = Stream.of( groupsStaxBuilder, payloadStaxBuilder, constrainParameterStaxBuilder, messageStaxBuilder ) + .collect( Collectors.collectingAndThen( Collectors.toList(), Collections::unmodifiableList ) ); + + } + + @Override + protected String getAcceptableQName() { + return CONSTRAINT_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + StartElement startElement = xmlEvent.asStartElement(); + constraintAnnotation = readAttribute( startElement, CONSTRAINT_ANNOTATION_QNAME ).get(); + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( CONSTRAINT_QNAME_LOCAL_PART ) ) ) { + XMLEvent currentEvent = xmlEvent; + builders.forEach( builder -> builder.process( xmlEventReader, currentEvent ) ); + xmlEvent = xmlEventReader.nextEvent(); + } + } + + @SuppressWarnings("unchecked") + MetaConstraint build(ConstraintLocation constraintLocation, ConstraintLocationKind kind, ConstraintDescriptorImpl.ConstraintType constraintType) { + String defaultPackage = defaultPackageStaxBuilder.build().orElse( "" ); + + Class annotationClass; + try { + annotationClass = (Class) classLoadingHelper.loadClass( constraintAnnotation, defaultPackage ); + } + catch (ValidationException e) { + throw LOG.getUnableToLoadConstraintAnnotationClassException( constraintAnnotation, e ); + } + ConstraintAnnotationDescriptor.Builder annotationDescriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( annotationClass ); + + // set common things to all constraints: + Optional message = messageStaxBuilder.build(); + if ( message.isPresent() ) { + annotationDescriptorBuilder.setMessage( message.get() ); + } + annotationDescriptorBuilder.setGroups( groupsStaxBuilder.build() ) + .setPayload( payloadStaxBuilder.build() ); + + // set constraint specific attributes: + Map parameters = constrainParameterStaxBuilder.build( annotationClass ); + for ( Map.Entry parameter : parameters.entrySet() ) { + annotationDescriptorBuilder.setAttribute( parameter.getKey(), parameter.getValue() ); + } + + ConstraintAnnotationDescriptor annotationDescriptor; + try { + annotationDescriptor = annotationDescriptorBuilder.build(); + } + catch (RuntimeException e) { + throw LOG.getUnableToCreateAnnotationForConfiguredConstraintException( e ); + } + + // we set initially ConstraintOrigin.DEFINED_LOCALLY for all xml configured constraints + // later we will make copies of this constraint descriptor when needed and adjust the ConstraintOrigin + ConstraintDescriptorImpl constraintDescriptor = new ConstraintDescriptorImpl<>( + constraintCreationContext.getConstraintHelper(), constraintLocation.getConstrainable(), annotationDescriptor, kind, constraintType + ); + + return MetaConstraints.create( constraintCreationContext.getTypeResolutionHelper(), + constraintCreationContext.getValueExtractorManager(), + constraintCreationContext.getConstraintValidatorManager(), constraintDescriptor, constraintLocation ); + } + + private static class MessageStaxBuilder extends AbstractOneLineStringStaxBuilder { + + private static final String MESSAGE_PACKAGE_QNAME = "message"; + + @Override + protected String getAcceptableQName() { + return MESSAGE_PACKAGE_QNAME; + } + } + + private static class ConstraintParameterStaxBuilder extends AnnotationParameterStaxBuilder { + + private static final String ELEMENT_QNAME_LOCAL_PART = "element"; + private static final QName NAME_QNAME = new QName( "name" ); + + public ConstraintParameterStaxBuilder(ClassLoadingHelper classLoadingHelper, DefaultPackageStaxBuilder defaultPackageStaxBuilder) { + super( classLoadingHelper, defaultPackageStaxBuilder ); + } + + @Override + protected String getAcceptableQName() { + return ELEMENT_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + String name = readAttribute( xmlEvent.asStartElement(), NAME_QNAME ).get(); + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( ELEMENT_QNAME_LOCAL_PART ) ) ) { + xmlEvent = xmlEventReader.nextEvent(); + readElement( xmlEventReader, xmlEvent, name ); + } + } + + @Override + protected void checkNameIsValid(String name) { + if ( ConstraintHelper.MESSAGE.equals( name ) || ConstraintHelper.GROUPS.equals( name ) || ConstraintHelper.PAYLOAD.equals( name ) ) { + throw LOG.getReservedParameterNamesException( ConstraintHelper.MESSAGE, ConstraintHelper.GROUPS, ConstraintHelper.PAYLOAD ); + } + } + + public Map build(Class annotationClass) { + String defaultPackage = defaultPackageStaxBuilder.build().orElse( "" ); + Map builtParameters = new HashMap<>(); + for ( Map.Entry> parameter : parameters.entrySet() ) { + builtParameters.put( + parameter.getKey(), + getElementValue( parameter.getValue(), annotationClass, parameter.getKey(), defaultPackage ) + ); + } + for ( Map.Entry> parameter : annotationParameters.entrySet() ) { + builtParameters.put( + parameter.getKey(), + getAnnotationElementValue( parameter.getValue(), annotationClass, parameter.getKey(), defaultPackage ) + ); + } + + return builtParameters; + } + } + + private static class AnnotationParameterStaxBuilder extends AbstractStaxBuilder { + + private static final String ANNOTATION_QNAME_LOCAL_PART = "annotation"; + private static final String ELEMENT_QNAME_LOCAL_PART = "element"; + private static final String VALUE_QNAME_LOCAL_PART = "value"; + private static final QName NAME_QNAME = new QName( "name" ); + + private final ClassLoadingHelper classLoadingHelper; + protected final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + + protected Map> parameters; + protected Map> annotationParameters; + + public AnnotationParameterStaxBuilder(ClassLoadingHelper classLoadingHelper, DefaultPackageStaxBuilder defaultPackageStaxBuilder) { + this.classLoadingHelper = classLoadingHelper; + this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; + + this.parameters = new HashMap<>(); + this.annotationParameters = new HashMap<>(); + } + + @Override + protected String getAcceptableQName() { + return ANNOTATION_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( ANNOTATION_QNAME_LOCAL_PART ) ) ) { + xmlEvent = xmlEventReader.nextEvent(); + if ( xmlEvent.isStartElement() ) { + StartElement startElement = xmlEvent.asStartElement(); + if ( startElement.getName().getLocalPart().equals( ELEMENT_QNAME_LOCAL_PART ) ) { + String name = readAttribute( xmlEvent.asStartElement(), NAME_QNAME ).get(); + + // we put empty collection here in case the corresponding string element in xml is empty + // if there will be a value it will get merged in this#addParameterValue() + parameters.put( name, Collections.emptyList() ); + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( ELEMENT_QNAME_LOCAL_PART ) ) ) { + readElement( xmlEventReader, xmlEvent, name ); + xmlEvent = xmlEventReader.nextEvent(); + } + } + } + } + } + + protected void readElement(XMLEventReader xmlEventReader, XMLEvent xmlEvent, String name) throws XMLStreamException { + // need to check the next element + if ( xmlEvent.isCharacters() && !xmlEvent.asCharacters().getData().trim().isEmpty() ) { + // in case it's a value - read it + StringBuilder stringBuilder = new StringBuilder( xmlEvent.asCharacters().getData() ); + while ( xmlEventReader.peek().isCharacters() ) { + xmlEvent = xmlEventReader.nextEvent(); + stringBuilder.append( xmlEvent.asCharacters().getData() ); + } + addParameterValue( name, stringBuilder.toString().trim() ); + } + else if ( xmlEvent.isStartElement() ) { + StartElement startElement = xmlEvent.asStartElement(); + // in case of multi-valued parameter read value + if ( startElement.getName().getLocalPart().equals( VALUE_QNAME_LOCAL_PART ) ) { + addParameterValue( name, readSingleElement( xmlEventReader ) ); + } + else if ( startElement.getName().getLocalPart().equals( ANNOTATION_QNAME_LOCAL_PART ) ) { + addAnnotationParameterValue( name, xmlEventReader, xmlEvent ); + } + } + } + + protected void addAnnotationParameterValue(String name, XMLEventReader xmlEventReader, XMLEvent xmlEvent) { + checkNameIsValid( name ); + + AnnotationParameterStaxBuilder annotationParameterStaxBuilder = new AnnotationParameterStaxBuilder( classLoadingHelper, defaultPackageStaxBuilder ); + annotationParameterStaxBuilder.process( xmlEventReader, xmlEvent ); + + annotationParameters.merge( + name, + Collections.singletonList( annotationParameterStaxBuilder ), + (v1, v2) -> Stream.concat( v1.stream(), v2.stream() ).collect( Collectors.toList() ) + ); + } + + protected void addParameterValue(String name, String value) { + checkNameIsValid( name ); + parameters.merge( + name, + Collections.singletonList( value ), + (v1, v2) -> Stream.concat( v1.stream(), v2.stream() ).collect( Collectors.toList() ) + ); + } + + protected void checkNameIsValid(String name) { + // in case of simple annotation - any name is acceptable + } + + public Annotation build(Class annotationClass, String defaultPackage) { + AnnotationDescriptor.Builder annotationDescriptorBuilder = new AnnotationDescriptor.Builder<>( annotationClass ); + + for ( Map.Entry> parameter : parameters.entrySet() ) { + annotationDescriptorBuilder.setAttribute( + parameter.getKey(), + getElementValue( parameter.getValue(), annotationClass, parameter.getKey(), defaultPackage ) + ); + } + for ( Map.Entry> parameter : annotationParameters.entrySet() ) { + annotationDescriptorBuilder.setAttribute( + parameter.getKey(), + getAnnotationElementValue( parameter.getValue(), annotationClass, parameter.getKey(), defaultPackage ) + ); + } + + return annotationDescriptorBuilder.build().getAnnotation(); + } + + protected Object getElementValue(List parsedParameters, Class annotationClass, String name, String defaultPackage) { + List parameters = removeEmptyContentElements( parsedParameters ); + + Class returnType = getAnnotationParameterType( annotationClass, name ); + boolean isArray = returnType.isArray(); + if ( !isArray ) { + if ( parameters.size() == 0 ) { + return ""; + } + else if ( parameters.size() > 1 ) { + throw LOG.getAttemptToSpecifyAnArrayWhereSingleValueIsExpectedException(); + } + return convertStringToReturnType( parameters.get( 0 ), returnType, defaultPackage ); + } + else { + return parameters.stream().map( value -> convertStringToReturnType( value, returnType.getComponentType(), defaultPackage ) ) + .toArray( size -> (Object[]) Array.newInstance( returnType.getComponentType(), size ) ); + } + } + + @SuppressWarnings("unchecked") + protected Object getAnnotationElementValue(List parameters, Class annotationClass, String name, String defaultPackage) { + Class returnType = getAnnotationParameterType( annotationClass, name ); + boolean isArray = returnType.isArray(); + if ( !isArray ) { + if ( parameters.size() == 0 ) { + throw LOG.getEmptyElementOnlySupportedWhenCharSequenceIsExpectedExpection(); + } + else if ( parameters.size() > 1 ) { + throw LOG.getAttemptToSpecifyAnArrayWhereSingleValueIsExpectedException(); + } + return parameters.get( 0 ).build( (Class) returnType, defaultPackage ); + } + else { + return parameters.stream().map( value -> value.build( (Class) returnType.getComponentType(), defaultPackage ) ) + .toArray( size -> (Object[]) Array.newInstance( returnType.getComponentType(), size ) ); + } + } + + private static List removeEmptyContentElements(List params) { + return params.stream().filter( content -> !IS_ONLY_WHITESPACE.matcher( content ).matches() ) + .collect( Collectors.toList() ); + } + + private static Class getAnnotationParameterType(Class annotationClass, String name) { + Method m = run( GetMethod.action( annotationClass, name ) ); + if ( m == null ) { + throw LOG.getAnnotationDoesNotContainAParameterException( annotationClass, name ); + } + return m.getReturnType(); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private Object convertStringToReturnType(String value, Class returnType, String defaultPackage) { + Object returnValue; + if ( returnType == byte.class ) { + try { + returnValue = Byte.parseByte( value ); + } + catch (NumberFormatException e) { + throw LOG.getInvalidNumberFormatException( "byte", e ); + } + } + else if ( returnType == short.class ) { + try { + returnValue = Short.parseShort( value ); + } + catch (NumberFormatException e) { + throw LOG.getInvalidNumberFormatException( "short", e ); + } + } + else if ( returnType == int.class ) { + try { + returnValue = Integer.parseInt( value ); + } + catch (NumberFormatException e) { + throw LOG.getInvalidNumberFormatException( "int", e ); + } + } + else if ( returnType == long.class ) { + try { + returnValue = Long.parseLong( value ); + } + catch (NumberFormatException e) { + throw LOG.getInvalidNumberFormatException( "long", e ); + } + } + else if ( returnType == float.class ) { + try { + returnValue = Float.parseFloat( value ); + } + catch (NumberFormatException e) { + throw LOG.getInvalidNumberFormatException( "float", e ); + } + } + else if ( returnType == double.class ) { + try { + returnValue = Double.parseDouble( value ); + } + catch (NumberFormatException e) { + throw LOG.getInvalidNumberFormatException( "double", e ); + } + } + else if ( returnType == boolean.class ) { + returnValue = Boolean.parseBoolean( value ); + } + else if ( returnType == char.class ) { + if ( value.length() != 1 ) { + throw LOG.getInvalidCharValueException( value ); + } + returnValue = value.charAt( 0 ); + } + else if ( returnType == String.class ) { + returnValue = value; + } + else if ( returnType == Class.class ) { + returnValue = classLoadingHelper.loadClass( value, defaultPackage ); + } + else { + try { + Class enumClass = (Class) returnType; + returnValue = Enum.valueOf( enumClass, value ); + } + catch (ClassCastException e) { + throw LOG.getInvalidReturnTypeException( returnType, e ); + } + } + return returnValue; + } + + /** + * 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 T run(PrivilegedAction action) { + return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); + } + } + + private static class GroupsStaxBuilder extends AbstractMultiValuedElementStaxBuilder { + + private static final String GROUPS_QNAME_LOCAL_PART = "groups"; + + private GroupsStaxBuilder(ClassLoadingHelper classLoadingHelper, DefaultPackageStaxBuilder defaultPackageStaxBuilder) { + super( classLoadingHelper, defaultPackageStaxBuilder ); + } + + @Override + public void verifyClass(Class clazz) { + // do nothing + } + + @Override + protected String getAcceptableQName() { + return GROUPS_QNAME_LOCAL_PART; + } + } + + private static class PayloadStaxBuilder extends AbstractMultiValuedElementStaxBuilder { + + private static final String PAYLOAD_QNAME_LOCAL_PART = "payload"; + + private PayloadStaxBuilder(ClassLoadingHelper classLoadingHelper, DefaultPackageStaxBuilder defaultPackageStaxBuilder) { + super( classLoadingHelper, defaultPackageStaxBuilder ); + } + + @Override + public void verifyClass(Class payload) { + if ( !Payload.class.isAssignableFrom( payload ) ) { + throw LOG.getWrongPayloadClassException( payload ); + } + } + + @Override + protected String getAcceptableQName() { + return PAYLOAD_QNAME_LOCAL_PART; + } + } + +} 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 new file mode 100644 index 0000000000..fd424cd664 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ContainerElementTypeConfigurationBuilder.java @@ -0,0 +1,84 @@ +/* + * 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.xml.mapping; + +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; + +/** + * Builds the aggregated cascading and type argument constraints configuration from the {@link ContainerElementTypeStaxBuilder} elements. + * + * @author Guillaume Smet + * @author Marko Bekhta + */ +class ContainerElementTypeConfigurationBuilder { + + private final List containerElementTypeStaxBuilders; + private final Set configuredPaths; + + ContainerElementTypeConfigurationBuilder() { + this.containerElementTypeStaxBuilders = new ArrayList<>(); + this.configuredPaths = new HashSet<>(); + } + + public void add(ContainerElementTypeStaxBuilder containerElementTypeStaxBuilder) { + containerElementTypeStaxBuilders.add( containerElementTypeStaxBuilder ); + } + + ContainerElementTypeConfiguration build(ConstraintLocation parentConstraintLocation, Type enclosingType) { + return build( ContainerElementTypePath.root(), parentConstraintLocation, enclosingType ); + } + + private ContainerElementTypeConfiguration build(ContainerElementTypePath parentConstraintElementTypePath, + ConstraintLocation parentConstraintLocation, Type enclosingType) { + return containerElementTypeStaxBuilders.stream() + .map( builder -> builder.build( configuredPaths, parentConstraintElementTypePath, parentConstraintLocation, enclosingType ) ) + .reduce( ContainerElementTypeConfiguration.EMPTY_CONFIGURATION, ContainerElementTypeConfiguration::merge ); + } + + static class ContainerElementTypeConfiguration { + + public static final ContainerElementTypeConfiguration EMPTY_CONFIGURATION = new ContainerElementTypeConfiguration( Collections.emptySet(), Collections.emptyMap() ); + + private final Set> metaConstraints; + + private final Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaDataBuilder; + + ContainerElementTypeConfiguration(Set> metaConstraints, Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData) { + this.metaConstraints = metaConstraints; + this.containerElementTypesCascadingMetaDataBuilder = containerElementTypesCascadingMetaData; + } + + public Set> getMetaConstraints() { + return metaConstraints; + } + + public Map, CascadingMetaDataBuilder> getTypeParametersCascadingMetaData() { + return containerElementTypesCascadingMetaDataBuilder; + } + + public static ContainerElementTypeConfiguration merge(ContainerElementTypeConfiguration l, ContainerElementTypeConfiguration r) { + return new ContainerElementTypeConfiguration( + Stream.concat( l.metaConstraints.stream(), r.metaConstraints.stream() ).collect( Collectors.toSet() ), + Stream.concat( l.containerElementTypesCascadingMetaDataBuilder.entrySet().stream(), r.containerElementTypesCascadingMetaDataBuilder.entrySet().stream() ) + .collect( Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue ) ) + ); + } + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/ContainerElementTypePath.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ContainerElementTypePath.java similarity index 96% rename from engine/src/main/java/org/hibernate/validator/internal/xml/ContainerElementTypePath.java rename to engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ContainerElementTypePath.java index 61333a6017..ce03e3d911 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/ContainerElementTypePath.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ContainerElementTypePath.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.xml; +package org.hibernate.validator.internal.xml.mapping; import java.util.ArrayList; import java.util.List; 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 new file mode 100644 index 0000000000..456dacc83b --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ContainerElementTypeStaxBuilder.java @@ -0,0 +1,216 @@ +/* + * 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.xml.mapping; + +import java.lang.invoke.MethodHandles; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.engine.ConstraintCreationContext; +import org.hibernate.validator.internal.engine.valueextraction.ArrayElement; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.util.CollectionHelper; +import org.hibernate.validator.internal.util.ReflectionHelper; +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.xml.AbstractStaxBuilder; +import org.hibernate.validator.internal.xml.mapping.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration; + +/** + * Builds the cascading and type argument constraints configuration from the {@code } elements. + * + * @author Guillaume Smet + * @author Marko Bekhta + */ +class ContainerElementTypeStaxBuilder extends AbstractStaxBuilder { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final String CONTAINER_ELEMENT_TYPE_QNAME_LOCAL_PART = "container-element-type"; + private static final QName TYPE_ARGUMENT_INDEX_QNAME = new QName( "type-argument-index" ); + + private final ClassLoadingHelper classLoadingHelper; + private final ConstraintCreationContext constraintCreationContext; + private final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + + private Integer typeArgumentIndex; + private final ValidStaxBuilder validStaxBuilder; + private final List constraintTypeStaxBuilders; + private final GroupConversionStaxBuilder groupConversionBuilder; + private final List containerElementTypeConfigurationStaxBuilders; + + ContainerElementTypeStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintCreationContext constraintCreationContext, + DefaultPackageStaxBuilder defaultPackageStaxBuilder) { + this.classLoadingHelper = classLoadingHelper; + this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; + this.constraintCreationContext = constraintCreationContext; + + this.groupConversionBuilder = new GroupConversionStaxBuilder( classLoadingHelper, defaultPackageStaxBuilder ); + this.validStaxBuilder = new ValidStaxBuilder(); + this.constraintTypeStaxBuilders = new ArrayList<>(); + this.containerElementTypeConfigurationStaxBuilders = new ArrayList<>(); + + } + + @Override + protected String getAcceptableQName() { + return CONTAINER_ELEMENT_TYPE_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + Optional typeArgumentIndex = readAttribute( xmlEvent.asStartElement(), TYPE_ARGUMENT_INDEX_QNAME ); + if ( typeArgumentIndex.isPresent() ) { + this.typeArgumentIndex = Integer.parseInt( typeArgumentIndex.get() ); + } + ConstraintTypeStaxBuilder constraintTypeStaxBuilder = getNewConstraintTypeStaxBuilder(); + ContainerElementTypeStaxBuilder containerElementTypeConfigurationStaxBuilder = getNewContainerElementTypeConfigurationStaxBuilder(); + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( getAcceptableQName() ) ) ) { + xmlEvent = xmlEventReader.nextEvent(); + validStaxBuilder.process( xmlEventReader, xmlEvent ); + groupConversionBuilder.process( xmlEventReader, xmlEvent ); + if ( constraintTypeStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + constraintTypeStaxBuilders.add( constraintTypeStaxBuilder ); + constraintTypeStaxBuilder = getNewConstraintTypeStaxBuilder(); + } + if ( containerElementTypeConfigurationStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + containerElementTypeConfigurationStaxBuilders.add( containerElementTypeConfigurationStaxBuilder ); + containerElementTypeConfigurationStaxBuilder = getNewContainerElementTypeConfigurationStaxBuilder(); + } + } + } + + private ConstraintTypeStaxBuilder getNewConstraintTypeStaxBuilder() { + return new ConstraintTypeStaxBuilder( classLoadingHelper, constraintCreationContext, + defaultPackageStaxBuilder ); + } + + private ContainerElementTypeStaxBuilder getNewContainerElementTypeConfigurationStaxBuilder() { + return new ContainerElementTypeStaxBuilder( classLoadingHelper, constraintCreationContext, + defaultPackageStaxBuilder ); + } + + public ContainerElementTypeConfiguration build(Set configuredPaths, + ContainerElementTypePath parentConstraintElementTypePath, + ConstraintLocation parentConstraintLocation, Type enclosingType) { + // HV-1428 Container element support is disabled for arrays + if ( TypeHelper.isArray( enclosingType ) ) { + throw LOG.getContainerElementConstraintsAndCascadedValidationNotSupportedOnArraysException( enclosingType ); + } + + if ( !( enclosingType instanceof ParameterizedType ) && !TypeHelper.isArray( enclosingType ) ) { + throw LOG.getTypeIsNotAParameterizedNorArrayTypeException( enclosingType ); + } + + Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaDataBuilder = + CollectionHelper.newHashMap( containerElementTypeConfigurationStaxBuilders.size() ); + + boolean isArray = TypeHelper.isArray( enclosingType ); + TypeVariable[] typeParameters = isArray ? new TypeVariable[0] : ReflectionHelper.getClassFromType( enclosingType ).getTypeParameters(); + + Integer typeArgumentIndex = getTypeArgumentIndex( typeParameters, isArray, enclosingType ); + + ContainerElementTypePath constraintElementTypePath = ContainerElementTypePath.of( parentConstraintElementTypePath, typeArgumentIndex ); + boolean configuredBefore = !configuredPaths.add( constraintElementTypePath ); + if ( configuredBefore ) { + throw LOG.getContainerElementTypeHasAlreadyBeenConfiguredViaXmlMappingConfigurationException( parentConstraintLocation, constraintElementTypePath ); + } + + TypeVariable typeParameter = getTypeParameter( typeParameters, typeArgumentIndex, isArray, enclosingType ); + Type containerElementType = getContainerElementType( enclosingType, typeArgumentIndex, isArray ); + ConstraintLocation containerElementTypeConstraintLocation = ConstraintLocation.forTypeArgument( parentConstraintLocation, typeParameter, + containerElementType + ); + + ContainerElementTypeConfiguration nestedContainerElementTypeConfiguration = containerElementTypeConfigurationStaxBuilders.stream() + .map( nested -> nested.build( configuredPaths, constraintElementTypePath, containerElementTypeConstraintLocation, containerElementType ) ) + .reduce( ContainerElementTypeConfiguration.EMPTY_CONFIGURATION, ContainerElementTypeConfiguration::merge ); + + boolean isCascaded = validStaxBuilder.build(); + + containerElementTypesCascadingMetaDataBuilder.put( typeParameter, new CascadingMetaDataBuilder( enclosingType, typeParameter, isCascaded, + nestedContainerElementTypeConfiguration.getTypeParametersCascadingMetaData(), + groupConversionBuilder.build() + ) + ); + + return new ContainerElementTypeConfiguration( + Stream.concat( + constraintTypeStaxBuilders.stream() + .map( + builder -> builder.build( + containerElementTypeConstraintLocation, + ConstraintLocation.ConstraintLocationKind.TYPE_USE, + null + ) + ), + nestedContainerElementTypeConfiguration.getMetaConstraints().stream() + ).collect( Collectors.toSet() ), + containerElementTypesCascadingMetaDataBuilder + ); + } + + private Integer getTypeArgumentIndex(TypeVariable[] typeParameters, boolean isArray, Type enclosingType) { + if ( isArray ) { + return null; + } + + if ( typeArgumentIndex == null ) { + if ( typeParameters.length > 1 ) { + throw LOG.getNoTypeArgumentIndexIsGivenForTypeWithMultipleTypeArgumentsException( enclosingType ); + } + return 0; + } + + return typeArgumentIndex; + } + + private TypeVariable getTypeParameter(TypeVariable[] typeParameters, Integer typeArgumentIndex, boolean isArray, Type enclosingType) { + TypeVariable typeParameter; + if ( !isArray ) { + if ( typeArgumentIndex > typeParameters.length - 1 ) { + throw LOG.getInvalidTypeArgumentIndexException( enclosingType, typeArgumentIndex ); + } + + typeParameter = typeParameters[typeArgumentIndex]; + } + else { + typeParameter = new ArrayElement( enclosingType ); + } + return typeParameter; + } + + private Type getContainerElementType(Type enclosingType, Integer typeArgumentIndex, boolean isArray) { + Type containerElementType; + if ( !isArray ) { + containerElementType = ( (ParameterizedType) enclosingType ).getActualTypeArguments()[typeArgumentIndex]; + } + else { + containerElementType = TypeHelper.getComponentType( enclosingType ); + } + return containerElementType; + } + + public Integer getTypeArgumentIndex() { + return null; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/CrossParameterStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/CrossParameterStaxBuilder.java new file mode 100644 index 0000000000..5719dedbac --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/CrossParameterStaxBuilder.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.xml.mapping; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.engine.ConstraintCreationContext; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl.ConstraintType; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; +import org.hibernate.validator.internal.properties.Callable; +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; + +/** + * Builder for cross parameters. + * + * @author Marko Bekhta + */ +class CrossParameterStaxBuilder extends AbstractStaxBuilder { + + private static final String CROSS_PARAMETER_QNAME_LOCAL_PART = "cross-parameter"; + private static final QName IGNORE_ANNOTATIONS_QNAME = new QName( "ignore-annotations" ); + + protected final ClassLoadingHelper classLoadingHelper; + protected final ConstraintCreationContext constraintCreationContext; + protected final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + protected final AnnotationProcessingOptionsImpl annotationProcessingOptions; + + protected Optional ignoreAnnotations; + protected final List constraintTypeStaxBuilders; + + CrossParameterStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintCreationContext constraintCreationContext, + DefaultPackageStaxBuilder defaultPackageStaxBuilder, AnnotationProcessingOptionsImpl annotationProcessingOptions) { + this.classLoadingHelper = classLoadingHelper; + this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; + this.constraintCreationContext = constraintCreationContext; + + this.annotationProcessingOptions = annotationProcessingOptions; + + this.constraintTypeStaxBuilders = new ArrayList<>(); + } + + @Override + protected String getAcceptableQName() { + return CROSS_PARAMETER_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + ignoreAnnotations = readAttribute( xmlEvent.asStartElement(), IGNORE_ANNOTATIONS_QNAME ).map( Boolean::parseBoolean ); + ConstraintTypeStaxBuilder constraintTypeStaxBuilder = getNewConstraintTypeStaxBuilder(); + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( getAcceptableQName() ) ) ) { + xmlEvent = xmlEventReader.nextEvent(); + if ( constraintTypeStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + constraintTypeStaxBuilders.add( constraintTypeStaxBuilder ); + constraintTypeStaxBuilder = getNewConstraintTypeStaxBuilder(); + } + } + } + + private ConstraintTypeStaxBuilder getNewConstraintTypeStaxBuilder() { + return new ConstraintTypeStaxBuilder( classLoadingHelper, constraintCreationContext, defaultPackageStaxBuilder ); + } + + Set> build(Callable callable) { + + ConstraintLocation constraintLocation = ConstraintLocation.forCrossParameter( callable ); + + Set> crossParameterConstraints = constraintTypeStaxBuilders.stream() + .map( builder -> builder.build( constraintLocation, ConstraintLocationKind.of( callable.getConstrainedElementKind() ), + ConstraintType.CROSS_PARAMETER ) ) + .collect( Collectors.toSet() ); + + // ignore annotations + if ( ignoreAnnotations.isPresent() ) { + annotationProcessingOptions.ignoreConstraintAnnotationsForCrossParameterConstraint( + callable, + ignoreAnnotations.get() + ); + } + + return crossParameterConstraints; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/DefaultPackageStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/DefaultPackageStaxBuilder.java new file mode 100644 index 0000000000..a676c0e5c4 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/DefaultPackageStaxBuilder.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.xml.mapping; + +import java.util.Optional; + +/** + * Builder for default package. Provides a default package name as defined in the xml + * or {@link Optional#empty()} if none was provided. + * + * @author Marko Bekhta + */ +class DefaultPackageStaxBuilder extends AbstractOneLineStringStaxBuilder { + + private static final String DEFAULT_PACKAGE_QNAME = "default-package"; + + @Override + protected String getAcceptableQName() { + return DEFAULT_PACKAGE_QNAME; + } + +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/GroupConversionStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/GroupConversionStaxBuilder.java new file mode 100644 index 0000000000..f3ca903f8a --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/GroupConversionStaxBuilder.java @@ -0,0 +1,106 @@ +/* + * 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.xml.mapping; + +import java.lang.invoke.MethodHandles; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.validation.groups.Default; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; + +/** + * Builder for group conversions. + * + * @author Marko Bekhta + */ +class GroupConversionStaxBuilder extends AbstractStaxBuilder { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final String GROUP_CONVERSION_TYPE_QNAME_LOCAL_PART = "convert-group"; + private static final QName FROM_QNAME = new QName( "from" ); + private static final QName TO_QNAME = new QName( "to" ); + + private static final String DEFAULT_GROUP_NAME = Default.class.getName(); + + private final ClassLoadingHelper classLoadingHelper; + private final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + + private final Map> groupConversionRules; + + GroupConversionStaxBuilder(ClassLoadingHelper classLoadingHelper, DefaultPackageStaxBuilder defaultPackageStaxBuilder) { + this.classLoadingHelper = classLoadingHelper; + this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; + this.groupConversionRules = new HashMap<>(); + } + + @Override + protected String getAcceptableQName() { + return GROUP_CONVERSION_TYPE_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) { + StartElement startElement = xmlEvent.asStartElement(); + String from = readAttribute( startElement, FROM_QNAME ).orElse( DEFAULT_GROUP_NAME ); + String to = readAttribute( startElement, TO_QNAME ).get(); + groupConversionRules.merge( + from, + Collections.singletonList( to ), + (v1, v2) -> Stream.concat( v1.stream(), v2.stream() ).collect( Collectors.toList() ) + ); + } + + Map, Class> build() { + String defaultPackage = defaultPackageStaxBuilder.build().orElse( "" ); + + Map, List>> resultingMapping = groupConversionRules.entrySet().stream() + .collect( + // Using groupingBy collector to prevent possible loss of information + // as a string value in `from` could possibly contain both qualified and non qualified + // version of a same class from the default package. + Collectors.groupingBy( + entry -> classLoadingHelper.loadClass( entry.getKey(), defaultPackage ), + Collectors.collectingAndThen( + Collectors.toList(), + entries -> entries.stream() + .flatMap( entry -> entry.getValue().stream() ) + .map( className -> classLoadingHelper.loadClass( className, defaultPackage ) ) + .collect( Collectors.toList() ) + ) + + ) + ); + // in case of any duplicates in conversion rules we need to throw an exception: + for ( Map.Entry, List>> entry : resultingMapping.entrySet() ) { + if ( entry.getValue().size() > 1 ) { + throw LOG.getMultipleGroupConversionsForSameSourceException( + entry.getKey(), + entry.getValue() + ); + } + } + + return resultingMapping.entrySet().stream() + .collect( Collectors.toMap( + Map.Entry::getKey, + entry -> entry.getValue().get( 0 ) + ) ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/MappingXmlParser.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/MappingXmlParser.java new file mode 100644 index 0000000000..22a1b1b864 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/MappingXmlParser.java @@ -0,0 +1,184 @@ +/* + * 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.xml.mapping; + +import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; +import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.invoke.MethodHandles; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.Validator; + +import org.hibernate.validator.internal.engine.ConstraintCreationContext; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; +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.SetContextClassLoader; +import org.hibernate.validator.internal.xml.CloseIgnoringInputStream; +import org.hibernate.validator.internal.xml.XmlParserHelper; +import org.xml.sax.SAXException; + +/** + * XML parser for validation-mapping files. + * + * @author Hardy Ferentschik + * @author Marko Bekhta + */ +public class MappingXmlParser { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private final Set> processedClasses = newHashSet(); + private final ConstraintCreationContext constraintCreationContext; + private final AnnotationProcessingOptionsImpl annotationProcessingOptions; + private final JavaBeanHelper javaBeanHelper; + private final Map, List>> defaultSequences; + private final Map, Set> constrainedElements; + + private final XmlParserHelper xmlParserHelper; + + private final ClassLoadingHelper classLoadingHelper; + + private static final Map SCHEMAS_BY_VERSION = Collections.unmodifiableMap( getSchemasByVersion() ); + + private static Map getSchemasByVersion() { + Map schemasByVersion = new HashMap<>(); + + schemasByVersion.put( "1.0", "META-INF/validation-mapping-1.0.xsd" ); + schemasByVersion.put( "1.1", "META-INF/validation-mapping-1.1.xsd" ); + schemasByVersion.put( "2.0", "META-INF/validation-mapping-2.0.xsd" ); + + return schemasByVersion; + } + + public MappingXmlParser(ConstraintCreationContext constraintCreationContext, JavaBeanHelper javaBeanHelper, ClassLoader externalClassLoader) { + this.constraintCreationContext = constraintCreationContext; + this.annotationProcessingOptions = new AnnotationProcessingOptionsImpl(); + this.javaBeanHelper = javaBeanHelper; + this.defaultSequences = newHashMap(); + this.constrainedElements = newHashMap(); + this.xmlParserHelper = new XmlParserHelper(); + this.classLoadingHelper = new ClassLoadingHelper( externalClassLoader, run( GetClassLoader.fromContext() ) ); + } + + /** + * Parses the given set of input stream representing XML constraint + * mappings. + * + * @param mappingStreams The streams to parse. Must support the mark/reset contract. + */ + public final void parse(Set mappingStreams) { + ClassLoader previousTccl = run( GetClassLoader.fromContext() ); + + try { + run( SetContextClassLoader.action( MappingXmlParser.class.getClassLoader() ) ); + + Set alreadyProcessedConstraintDefinitions = newHashSet(); + for ( InputStream in : mappingStreams ) { + // the InputStreams passed in parameters support mark and reset + in.mark( Integer.MAX_VALUE ); + + XMLEventReader xmlEventReader = xmlParserHelper.createXmlEventReader( "constraint mapping file", new CloseIgnoringInputStream( in ) ); + String schemaVersion = xmlParserHelper.getSchemaVersion( "constraint mapping file", xmlEventReader ); + xmlEventReader.close(); + + in.reset(); + + // The validation is done first as StAX builders used below are assuming that the XML file is correct and don't + // do any validation of the input. + String schemaResourceName = getSchemaResourceName( schemaVersion ); + Schema schema = xmlParserHelper.getSchema( schemaResourceName ); + if ( schema == null ) { + throw LOG.unableToGetXmlSchema( schemaResourceName ); + } + + Validator validator = schema.newValidator(); + validator.validate( new StreamSource( new CloseIgnoringInputStream( in ) ) ); + + in.reset(); + + ConstraintMappingsStaxBuilder constraintMappingsStaxBuilder = new ConstraintMappingsStaxBuilder( + classLoadingHelper, constraintCreationContext, + annotationProcessingOptions, javaBeanHelper, defaultSequences + ); + + xmlEventReader = xmlParserHelper.createXmlEventReader( "constraint mapping file", new CloseIgnoringInputStream( in ) ); + + while ( xmlEventReader.hasNext() ) { + constraintMappingsStaxBuilder.process( xmlEventReader, xmlEventReader.nextEvent() ); + } + constraintMappingsStaxBuilder.build( processedClasses, constrainedElements, alreadyProcessedConstraintDefinitions ); + xmlEventReader.close(); + in.reset(); + } + } + catch (IOException | XMLStreamException | SAXException e) { + throw LOG.getErrorParsingMappingFileException( e ); + } + finally { + run( SetContextClassLoader.action( previousTccl ) ); + } + } + + public final Set> getXmlConfiguredClasses() { + return processedClasses; + } + + public final AnnotationProcessingOptions getAnnotationProcessingOptions() { + return annotationProcessingOptions; + } + + public final Set getConstrainedElementsForClass(Class beanClass) { + if ( constrainedElements.containsKey( beanClass ) ) { + return constrainedElements.get( beanClass ); + } + else { + return Collections.emptySet(); + } + } + + public final List> getDefaultSequenceForClass(Class beanClass) { + return defaultSequences.get( beanClass ); + } + + private String getSchemaResourceName(String schemaVersion) { + String schemaResource = SCHEMAS_BY_VERSION.get( schemaVersion ); + + if ( schemaResource == null ) { + throw LOG.getUnsupportedSchemaVersionException( "constraint mapping file", schemaVersion ); + } + + return schemaResource; + } + + /** + * 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 T run(PrivilegedAction action) { + return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); + } +} 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 new file mode 100644 index 0000000000..71520c4f5a --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ReturnValueStaxBuilder.java @@ -0,0 +1,74 @@ +/* + * 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.xml.mapping; + +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.xml.namespace.QName; + +import org.hibernate.validator.internal.engine.ConstraintCreationContext; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; +import org.hibernate.validator.internal.properties.Callable; +import org.hibernate.validator.internal.xml.mapping.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration; + +/** + * Builder for constraints on return value. + * + * @author Marko Bekhta + */ +class ReturnValueStaxBuilder extends AbstractConstrainedElementStaxBuilder { + + private static final String RETURN_VALUE_QNAME_LOCAL_PART = "return-value"; + + ReturnValueStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintCreationContext constraintCreationContext, + DefaultPackageStaxBuilder defaultPackageStaxBuilder, AnnotationProcessingOptionsImpl annotationProcessingOptions) { + super( classLoadingHelper, constraintCreationContext, defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + @Override + Optional getMainAttributeValueQname() { + return Optional.empty(); + } + + @Override + protected String getAcceptableQName() { + return RETURN_VALUE_QNAME_LOCAL_PART; + } + + CascadingMetaDataBuilder build( + Callable callable, + Set> returnValueConstraints, + Set> returnValueTypeArgumentConstraints) { + + ConstraintLocation constraintLocation = ConstraintLocation.forReturnValue( callable ); + returnValueConstraints.addAll( constraintTypeStaxBuilders.stream() + .map( builder -> builder.build( constraintLocation, ConstraintLocationKind.of( callable.getConstrainedElementKind() ), + ConstraintDescriptorImpl.ConstraintType.GENERIC ) ) + .collect( Collectors.toSet() ) ); + + ContainerElementTypeConfiguration containerElementTypeConfiguration = getContainerElementTypeConfiguration( callable.getType(), constraintLocation ); + + returnValueTypeArgumentConstraints.addAll( containerElementTypeConfiguration.getMetaConstraints() ); + + // ignore annotations + if ( ignoreAnnotations.isPresent() ) { + annotationProcessingOptions.ignoreConstraintAnnotationsForReturnValue( + callable, + ignoreAnnotations.get() + ); + } + + return getCascadingMetaData( containerElementTypeConfiguration.getTypeParametersCascadingMetaData(), callable.getType() ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ValidStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ValidStaxBuilder.java new file mode 100644 index 0000000000..7310645fdd --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ValidStaxBuilder.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.internal.xml.mapping; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; + +/** + * @author Marko Bekhta + */ +class ValidStaxBuilder extends AbstractStaxBuilder { + + private static final String VALID_QNAME_LOCAL_PART = "valid"; + private Boolean cascading; + + @Override + protected String getAcceptableQName() { + return VALID_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) { + cascading = true; + } + + public boolean build() { + return cascading == null ? false : true; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/messageinterpolation/AbstractMessageInterpolator.java b/engine/src/main/java/org/hibernate/validator/messageinterpolation/AbstractMessageInterpolator.java index 2143a4adfc..39aa6a0f71 100644 --- a/engine/src/main/java/org/hibernate/validator/messageinterpolation/AbstractMessageInterpolator.java +++ b/engine/src/main/java/org/hibernate/validator/messageinterpolation/AbstractMessageInterpolator.java @@ -9,11 +9,13 @@ import static org.hibernate.validator.internal.util.ConcurrentReferenceHashMap.ReferenceType.SOFT; import java.lang.invoke.MethodHandles; +import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -40,6 +42,7 @@ * @author Gunnar Morling * @author Adam Stawicki * @author Marko Bekhta + * @author Guillaume Smet * * @since 5.2 */ @@ -65,7 +68,7 @@ public abstract class AbstractMessageInterpolator implements MessageInterpolator /** * The name of the default message bundle. */ - private static final String DEFAULT_VALIDATION_MESSAGES = "org.hibernate.validator.ValidationMessages"; + public static final String DEFAULT_VALIDATION_MESSAGES = "org.hibernate.validator.ValidationMessages"; /** * The name of the user-provided message bundle as defined in the specification. @@ -124,12 +127,20 @@ public abstract class AbstractMessageInterpolator implements MessageInterpolator private static final Pattern SLASH = Pattern.compile( "\\\\", Pattern.LITERAL ); private static final Pattern DOLLAR = Pattern.compile( "\\$", Pattern.LITERAL ); + /** + * {@code MessageInterpolator} using the default resource bundle locators. + */ public AbstractMessageInterpolator() { - this( null ); + this( Collections.emptySet() ); } + /** + * {@code MessageInterpolator} taking a resource bundle locator. + * + * @param userResourceBundleLocator {@code ResourceBundleLocator} used to load user provided resource bundle + */ public AbstractMessageInterpolator(ResourceBundleLocator userResourceBundleLocator) { - this( userResourceBundleLocator, null ); + this( userResourceBundleLocator, Collections.emptySet() ); } /** @@ -141,7 +152,7 @@ public AbstractMessageInterpolator(ResourceBundleLocator userResourceBundleLocat */ public AbstractMessageInterpolator(ResourceBundleLocator userResourceBundleLocator, ResourceBundleLocator contributorResourceBundleLocator) { - this( userResourceBundleLocator, contributorResourceBundleLocator, true ); + this( userResourceBundleLocator, contributorResourceBundleLocator, Collections.emptySet() ); } /** @@ -155,10 +166,64 @@ public AbstractMessageInterpolator(ResourceBundleLocator userResourceBundleLocat public AbstractMessageInterpolator(ResourceBundleLocator userResourceBundleLocator, ResourceBundleLocator contributorResourceBundleLocator, boolean cacheMessages) { + this( userResourceBundleLocator, contributorResourceBundleLocator, Collections.emptySet(), cacheMessages ); + } + + /** + * {@code MessageInterpolator} using the default resource bundle locators. + * + * @param localesToInitialize The set of locales to initialize at bootstrap. + * + * @since 6.1 + */ + public AbstractMessageInterpolator(Set localesToInitialize) { + this( null, localesToInitialize ); + } + + /** + * {@code MessageInterpolator} taking a resource bundle locator. + * + * @param userResourceBundleLocator {@code ResourceBundleLocator} used to load user provided resource bundle + * @param localesToInitialize The set of locales to initialize at bootstrap. + * + * @since 6.1 + */ + public AbstractMessageInterpolator(ResourceBundleLocator userResourceBundleLocator, Set localesToInitialize) { + this( userResourceBundleLocator, null, localesToInitialize ); + } + + /** + * {@code MessageInterpolator} taking two resource bundle locators. + * + * @param userResourceBundleLocator {@code ResourceBundleLocator} used to load user provided resource bundle + * @param contributorResourceBundleLocator {@code ResourceBundleLocator} used to load resource bundle of constraint contributor + * @param localesToInitialize The set of locales to initialize at bootstrap. + * + * @since 6.1 + */ + public AbstractMessageInterpolator(ResourceBundleLocator userResourceBundleLocator, + ResourceBundleLocator contributorResourceBundleLocator, Set localesToInitialize) { + this( userResourceBundleLocator, contributorResourceBundleLocator, localesToInitialize, true ); + } + + /** + * {@code MessageInterpolator} taking two resource bundle locators. + * + * @param userResourceBundleLocator {@code ResourceBundleLocator} used to load user provided resource bundle + * @param contributorResourceBundleLocator {@code ResourceBundleLocator} used to load resource bundle of constraint contributor + * @param localesToInitialize The set of locales to initialize at bootstrap. + * @param cacheMessages Whether resolved messages should be cached or not. + * + * @since 6.1 + */ + public AbstractMessageInterpolator(ResourceBundleLocator userResourceBundleLocator, + ResourceBundleLocator contributorResourceBundleLocator, + Set localesToInitialize, + boolean cacheMessages) { defaultLocale = Locale.getDefault(); if ( userResourceBundleLocator == null ) { - this.userResourceBundleLocator = new PlatformResourceBundleLocator( USER_VALIDATION_MESSAGES ); + this.userResourceBundleLocator = new PlatformResourceBundleLocator( USER_VALIDATION_MESSAGES, localesToInitialize ); } else { this.userResourceBundleLocator = userResourceBundleLocator; @@ -167,6 +232,7 @@ public AbstractMessageInterpolator(ResourceBundleLocator userResourceBundleLocat if ( contributorResourceBundleLocator == null ) { this.contributorResourceBundleLocator = new PlatformResourceBundleLocator( CONTRIBUTOR_VALIDATION_MESSAGES, + localesToInitialize, null, true ); @@ -175,7 +241,7 @@ public AbstractMessageInterpolator(ResourceBundleLocator userResourceBundleLocat this.contributorResourceBundleLocator = contributorResourceBundleLocator; } - this.defaultResourceBundleLocator = new PlatformResourceBundleLocator( DEFAULT_VALIDATION_MESSAGES ); + this.defaultResourceBundleLocator = new PlatformResourceBundleLocator( DEFAULT_VALIDATION_MESSAGES, localesToInitialize ); this.cachingEnabled = cacheMessages; if ( cachingEnabled ) { diff --git a/engine/src/main/java/org/hibernate/validator/messageinterpolation/HibernateMessageInterpolatorContext.java b/engine/src/main/java/org/hibernate/validator/messageinterpolation/HibernateMessageInterpolatorContext.java index b216ff5161..c1640cc24c 100644 --- a/engine/src/main/java/org/hibernate/validator/messageinterpolation/HibernateMessageInterpolatorContext.java +++ b/engine/src/main/java/org/hibernate/validator/messageinterpolation/HibernateMessageInterpolatorContext.java @@ -9,6 +9,7 @@ import java.util.Map; import javax.validation.MessageInterpolator; +import javax.validation.Path; /** * Extension to {@code MessageInterpolator.Context} which provides functionality @@ -40,4 +41,11 @@ public interface HibernateMessageInterpolatorContext extends MessageInterpolator * @since 5.4.1 */ Map getExpressionVariables(); + + /** + * @return the path to the validated constraint starting from the root bean + * + * @since 6.1 + */ + Path getPropertyPath(); } diff --git a/engine/src/main/java/org/hibernate/validator/messageinterpolation/ParameterMessageInterpolator.java b/engine/src/main/java/org/hibernate/validator/messageinterpolation/ParameterMessageInterpolator.java index dad9705907..ba260bab17 100644 --- a/engine/src/main/java/org/hibernate/validator/messageinterpolation/ParameterMessageInterpolator.java +++ b/engine/src/main/java/org/hibernate/validator/messageinterpolation/ParameterMessageInterpolator.java @@ -7,7 +7,9 @@ package org.hibernate.validator.messageinterpolation; import java.lang.invoke.MethodHandles; +import java.util.Collections; import java.util.Locale; +import java.util.Set; import org.hibernate.validator.internal.engine.messageinterpolation.InterpolationTerm; import org.hibernate.validator.internal.engine.messageinterpolation.ParameterTermResolver; @@ -25,6 +27,14 @@ public class ParameterMessageInterpolator extends AbstractMessageInterpolator { private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + public ParameterMessageInterpolator() { + this( Collections.emptySet() ); + } + + public ParameterMessageInterpolator(Set localesToInitialize) { + super( localesToInitialize ); + } + @Override public String interpolate(Context context, Locale locale, String term) { if ( InterpolationTerm.isElExpression( term ) ) { diff --git a/engine/src/main/java/org/hibernate/validator/messageinterpolation/ResourceBundleMessageInterpolator.java b/engine/src/main/java/org/hibernate/validator/messageinterpolation/ResourceBundleMessageInterpolator.java index 856f3f12bf..d69c88eb2f 100644 --- a/engine/src/main/java/org/hibernate/validator/messageinterpolation/ResourceBundleMessageInterpolator.java +++ b/engine/src/main/java/org/hibernate/validator/messageinterpolation/ResourceBundleMessageInterpolator.java @@ -9,7 +9,9 @@ import java.lang.invoke.MethodHandles; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.Collections; import java.util.Locale; +import java.util.Set; import javax.el.ELManager; import javax.el.ExpressionFactory; @@ -38,35 +40,69 @@ public class ResourceBundleMessageInterpolator extends AbstractMessageInterpolat private final ExpressionFactory expressionFactory; public ResourceBundleMessageInterpolator() { - super(); - this.expressionFactory = buildExpressionFactory(); + this( Collections.emptySet() ); } public ResourceBundleMessageInterpolator(ResourceBundleLocator userResourceBundleLocator) { - super( userResourceBundleLocator ); - this.expressionFactory = buildExpressionFactory(); + this( userResourceBundleLocator, Collections.emptySet() ); } public ResourceBundleMessageInterpolator(ResourceBundleLocator userResourceBundleLocator, ResourceBundleLocator contributorResourceBundleLocator) { - super( userResourceBundleLocator, contributorResourceBundleLocator ); + this( userResourceBundleLocator, contributorResourceBundleLocator, Collections.emptySet() ); + } + + public ResourceBundleMessageInterpolator(ResourceBundleLocator userResourceBundleLocator, + ResourceBundleLocator contributorResourceBundleLocator, + boolean cachingEnabled) { + this( userResourceBundleLocator, contributorResourceBundleLocator, Collections.emptySet(), cachingEnabled ); + } + + public ResourceBundleMessageInterpolator(ResourceBundleLocator userResourceBundleLocator, boolean cachingEnabled) { + this( userResourceBundleLocator, null, Collections.emptySet(), cachingEnabled ); + } + + public ResourceBundleMessageInterpolator(ResourceBundleLocator userResourceBundleLocator, + boolean cachingEnabled, + ExpressionFactory expressionFactory) { + this( userResourceBundleLocator, null, Collections.emptySet(), cachingEnabled ); + } + + public ResourceBundleMessageInterpolator(Set localesToInitialize) { + super( localesToInitialize ); + this.expressionFactory = buildExpressionFactory(); + } + + public ResourceBundleMessageInterpolator(ResourceBundleLocator userResourceBundleLocator, Set localesToInitialize) { + super( userResourceBundleLocator, localesToInitialize ); + this.expressionFactory = buildExpressionFactory(); + } + + public ResourceBundleMessageInterpolator(ResourceBundleLocator userResourceBundleLocator, + ResourceBundleLocator contributorResourceBundleLocator, + Set localesToInitialize) { + super( userResourceBundleLocator, contributorResourceBundleLocator, localesToInitialize ); this.expressionFactory = buildExpressionFactory(); } public ResourceBundleMessageInterpolator(ResourceBundleLocator userResourceBundleLocator, ResourceBundleLocator contributorResourceBundleLocator, + Set localesToInitialize, boolean cachingEnabled) { - super( userResourceBundleLocator, contributorResourceBundleLocator, cachingEnabled ); + super( userResourceBundleLocator, contributorResourceBundleLocator, localesToInitialize, cachingEnabled ); this.expressionFactory = buildExpressionFactory(); } - public ResourceBundleMessageInterpolator(ResourceBundleLocator userResourceBundleLocator, boolean cachingEnabled) { - super( userResourceBundleLocator, null, cachingEnabled ); + public ResourceBundleMessageInterpolator(ResourceBundleLocator userResourceBundleLocator, Set localesToInitialize, boolean cachingEnabled) { + super( userResourceBundleLocator, null, localesToInitialize, cachingEnabled ); this.expressionFactory = buildExpressionFactory(); } - public ResourceBundleMessageInterpolator(ResourceBundleLocator userResourceBundleLocator, boolean cachingEnabled, ExpressionFactory expressionFactory) { - super( userResourceBundleLocator, null, cachingEnabled ); + public ResourceBundleMessageInterpolator(ResourceBundleLocator userResourceBundleLocator, + Set localesToInitialize, + boolean cachingEnabled, + ExpressionFactory expressionFactory) { + super( userResourceBundleLocator, null, localesToInitialize, cachingEnabled ); this.expressionFactory = expressionFactory; } diff --git a/engine/src/main/java/org/hibernate/validator/metadata/BeanMetaDataClassNormalizer.java b/engine/src/main/java/org/hibernate/validator/metadata/BeanMetaDataClassNormalizer.java new file mode 100644 index 0000000000..e5d50b005f --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/metadata/BeanMetaDataClassNormalizer.java @@ -0,0 +1,39 @@ +/* + * 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.metadata; + +import org.hibernate.validator.Incubating; + +/** + * Define how the validated class is normalized before being used as the key to get the bean metadata. + *

    + * In the case of the predefined scope validator factory, we have to register all the classes that will ever be + * validated. To validate method calls, frameworks usually generate proxies to intercept the calls. Such proxies might + * be hard to register in the predefined scope validator factory as they are generated code. + *

    + * This contract allows to normalize the class before obtaining the metadata from the + * {@code PredefinedScopeBeanMetaDataManager} so that we only have to register the original bean class and not the proxy + * class. + *

    + * Apart from avoiding the need to register the class, it also avoids generating unnecessary metadata for the proxy + * classes. + * + * @author Guillaume Smet + * @since 6.1 + */ +@Incubating +public interface BeanMetaDataClassNormalizer { + + /** + * Normalizes the provided class as the key used to get the bean metadata from the + * {@code PredefinedScopeBeanMetaDataManager}. + * + * @param beanClass the original bean class + * @return the normalized class + */ + Class normalize(Class beanClass); +} diff --git a/engine/src/main/java/org/hibernate/validator/overview.html b/engine/src/main/java/org/hibernate/validator/overview.html index c5a8036ebd..af6ef29a56 100644 --- a/engine/src/main/java/org/hibernate/validator/overview.html +++ b/engine/src/main/java/org/hibernate/validator/overview.html @@ -10,7 +10,7 @@

    This is the Hibernate Validator API documentation. Hibernate Validator is the reference implementation of - Bean Validation 2.0 - JSR-380 + https://projects.eclipse.org/projects/ee4j.bean-validation[Jakarta Bean Validation 2.0].

    All classes fall into three categories: diff --git a/engine/src/main/java/org/hibernate/validator/resourceloading/AggregateResourceBundleLocator.java b/engine/src/main/java/org/hibernate/validator/resourceloading/AggregateResourceBundleLocator.java index f9360c1caf..daae9fa6fb 100644 --- a/engine/src/main/java/org/hibernate/validator/resourceloading/AggregateResourceBundleLocator.java +++ b/engine/src/main/java/org/hibernate/validator/resourceloading/AggregateResourceBundleLocator.java @@ -7,6 +7,7 @@ package org.hibernate.validator.resourceloading; import java.util.ArrayList; +import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; @@ -14,6 +15,7 @@ import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; +import java.util.Set; import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.internal.util.Contracts; @@ -27,8 +29,8 @@ * @author Gunnar Morling */ public class AggregateResourceBundleLocator extends DelegatingResourceBundleLocator { - private final List bundleNames; - private final ClassLoader classLoader; + + private final List resourceBundleLocators; /** * Creates a locator that delivers a resource bundle merged from the given @@ -40,7 +42,7 @@ public class AggregateResourceBundleLocator extends DelegatingResourceBundleLoca * first bundle containing the key. */ public AggregateResourceBundleLocator(List bundleNames) { - this( bundleNames, null ); + this( bundleNames, Collections.emptySet(), null ); } /** @@ -56,7 +58,7 @@ public AggregateResourceBundleLocator(List bundleNames) { * source bundles. */ public AggregateResourceBundleLocator(List bundleNames, ResourceBundleLocator delegate) { - this( bundleNames, delegate, null ); + this( bundleNames, Collections.emptySet(), delegate, null ); } /** @@ -75,21 +77,77 @@ public AggregateResourceBundleLocator(List bundleNames, ResourceBundleLo */ public AggregateResourceBundleLocator(List bundleNames, ResourceBundleLocator delegate, ClassLoader classLoader) { + this( bundleNames, Collections.emptySet(), delegate, classLoader ); + } + + /** + * Creates a locator that delivers a resource bundle merged from the given + * list of source bundles. + * + * @param bundleNames A list with source bundle names. The returned bundle will + * contain all entries from all source bundles. In case a key occurs + * in multiple source bundles, the value will be taken from the + * first bundle containing the key. + * @param localesToInitialize The set of locales to initialize at bootstrap. + * + * @since 6.1 + */ + public AggregateResourceBundleLocator(List bundleNames, Set localesToInitialize) { + this( bundleNames, localesToInitialize, null ); + } + + /** + * Creates a locator that delivers a resource bundle merged from the given + * list of source bundles. + * + * @param bundleNames A list with source bundle names. The returned bundle will + * contain all keys from all source bundles. In case a key occurs + * in multiple source bundles, the value will be taken from the + * first bundle containing the key. + * @param localesToInitialize The set of locales to initialize at bootstrap. + * @param delegate A delegate resource bundle locator. The bundle returned by + * this locator will be added to the aggregate bundle after all + * source bundles. + * + * @since 6.1 + */ + public AggregateResourceBundleLocator(List bundleNames, Set localesToInitialize, ResourceBundleLocator delegate) { + this( bundleNames, localesToInitialize, delegate, null ); + } + + /** + * Creates a locator that delivers a resource bundle merged from the given + * list of source bundles. + * + * @param bundleNames A list with source bundle names. The returned bundle will + * contain all keys from all source bundles. In case a key occurs + * in multiple source bundles, the value will be taken from the + * first bundle containing the key. + * @param localesToInitialize The set of locales to initialize at bootstrap. + * @param delegate A delegate resource bundle locator. The bundle returned by + * this locator will be added to the aggregate bundle after all + * source bundles. + * @param classLoader The classloader to use for loading the bundle. + * + * @since 6.1 + */ + public AggregateResourceBundleLocator(List bundleNames, Set localesToInitialize, ResourceBundleLocator delegate, + ClassLoader classLoader) { super( delegate ); Contracts.assertValueNotNull( bundleNames, "bundleNames" ); - this.bundleNames = CollectionHelper.toImmutableList( bundleNames ); - this.classLoader = classLoader; + List tmpBundleLocators = new ArrayList<>( bundleNames.size() ); + for ( String bundleName : bundleNames ) { + tmpBundleLocators.add( new PlatformResourceBundleLocator( bundleName, localesToInitialize, classLoader ) ); + } + this.resourceBundleLocators = CollectionHelper.toImmutableList( tmpBundleLocators ); } @Override public ResourceBundle getResourceBundle(Locale locale) { List sourceBundles = new ArrayList(); - for ( String bundleName : bundleNames ) { - ResourceBundleLocator resourceBundleLocator = - new PlatformResourceBundleLocator( bundleName, classLoader ); - + for ( PlatformResourceBundleLocator resourceBundleLocator : resourceBundleLocators ) { ResourceBundle resourceBundle = resourceBundleLocator.getResourceBundle( locale ); if ( resourceBundle != null ) { diff --git a/engine/src/main/java/org/hibernate/validator/resourceloading/PlatformResourceBundleLocator.java b/engine/src/main/java/org/hibernate/validator/resourceloading/PlatformResourceBundleLocator.java index 2ba6139324..93ecb09465 100644 --- a/engine/src/main/java/org/hibernate/validator/resourceloading/PlatformResourceBundleLocator.java +++ b/engine/src/main/java/org/hibernate/validator/resourceloading/PlatformResourceBundleLocator.java @@ -10,6 +10,7 @@ import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES; import java.io.IOException; +import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; import java.net.URL; import java.security.AccessController; @@ -17,17 +18,21 @@ import java.util.Collections; import java.util.Enumeration; import java.util.Locale; +import java.util.Map; import java.util.MissingResourceException; import java.util.Properties; import java.util.ResourceBundle; import java.util.Set; +import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.internal.util.Contracts; +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.GetMethod; import org.hibernate.validator.internal.util.privilegedactions.GetResources; +import org.hibernate.validator.internal.util.stereotypes.Immutable; import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; -import org.jboss.logging.Logger; /** * A resource bundle locator, that loads resource bundles by invoking {@code ResourceBundle.loadBundle(String, Local, ClassLoader)}. @@ -40,15 +45,23 @@ */ public class PlatformResourceBundleLocator implements ResourceBundleLocator { - private static final Logger log = Logger.getLogger( PlatformResourceBundleLocator.class.getName() ); + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); private static final boolean RESOURCE_BUNDLE_CONTROL_INSTANTIABLE = determineAvailabilityOfResourceBundleControl(); private final String bundleName; private final ClassLoader classLoader; private final boolean aggregate; + @Immutable + private final Map preloadedResourceBundles; + + /** + * Creates a new {@link PlatformResourceBundleLocator}. + * + * @param bundleName the name of the bundle to load + */ public PlatformResourceBundleLocator(String bundleName) { - this( bundleName, null ); + this( bundleName, Collections.emptySet() ); } /** @@ -62,7 +75,7 @@ public PlatformResourceBundleLocator(String bundleName) { * @since 5.2 */ public PlatformResourceBundleLocator(String bundleName, ClassLoader classLoader) { - this( bundleName, classLoader, false ); + this( bundleName, Collections.emptySet(), classLoader ); } /** @@ -77,12 +90,66 @@ public PlatformResourceBundleLocator(String bundleName, ClassLoader classLoader) * @since 5.2 */ public PlatformResourceBundleLocator(String bundleName, ClassLoader classLoader, boolean aggregate) { + this( bundleName, Collections.emptySet(), classLoader, aggregate ); + } + + /** + * Creates a new {@link PlatformResourceBundleLocator}. + * + * @param bundleName the name of the bundle to load + * @param localesToInitialize the set of locales to initialize at bootstrap. + * + * @since 6.1 + */ + public PlatformResourceBundleLocator(String bundleName, Set localesToInitialize) { + this( bundleName, localesToInitialize, null ); + } + + /** + * Creates a new {@link PlatformResourceBundleLocator}. + * + * @param bundleName the name of the bundle to load + * @param localesToInitialize the set of locales to initialize at bootstrap. + * @param classLoader the classloader to be used for loading the bundle. If {@code null}, the current thread context + * classloader and finally Hibernate Validator's own classloader will be used for loading the specified + * bundle. + * + * @since 6.1 + */ + public PlatformResourceBundleLocator(String bundleName, Set localesToInitialize, ClassLoader classLoader) { + this( bundleName, localesToInitialize, classLoader, false ); + } + + /** + * Creates a new {@link PlatformResourceBundleLocator}. + * + * @param bundleName the name of the bundle to load + * @param localesToInitialize the set of locales to initialize at bootstrap. + * @param classLoader the classloader to be used for loading the bundle. If {@code null}, the current thread context + * classloader and finally Hibernate Validator's own classloader will be used for loading the specified + * bundle. + * @param aggregate Whether or not all resource bundles of a given name should be loaded and potentially merged. + * + * @since 6.1 + */ + public PlatformResourceBundleLocator(String bundleName, Set localesToInitialize, ClassLoader classLoader, boolean aggregate) { Contracts.assertNotNull( bundleName, "bundleName" ); this.bundleName = bundleName; this.classLoader = classLoader; this.aggregate = aggregate && RESOURCE_BUNDLE_CONTROL_INSTANTIABLE; + + if ( !localesToInitialize.isEmpty() ) { + Map tmpPreloadedResourceBundles = CollectionHelper.newHashMap( localesToInitialize.size() ); + for ( Locale localeToPreload : localesToInitialize ) { + tmpPreloadedResourceBundles.put( localeToPreload, doGetResourceBundle( localeToPreload ) ); + } + this.preloadedResourceBundles = CollectionHelper.toImmutableMap( tmpPreloadedResourceBundles ); + } + else { + this.preloadedResourceBundles = Collections.emptyMap(); + } } /** @@ -95,6 +162,21 @@ public PlatformResourceBundleLocator(String bundleName, ClassLoader classLoader, */ @Override public ResourceBundle getResourceBundle(Locale locale) { + // we are in the preloading case + if ( !preloadedResourceBundles.isEmpty() ) { + // we need to use containsKey() as the cached resource bundle can be null + if ( preloadedResourceBundles.containsKey( locale ) ) { + return preloadedResourceBundles.get( locale ); + } + else { + throw LOG.uninitializedLocale( locale ); + } + } + + return doGetResourceBundle( locale ); + } + + private ResourceBundle doGetResourceBundle(Locale locale) { ResourceBundle rb = null; if ( classLoader != null ) { @@ -122,10 +204,10 @@ public ResourceBundle getResourceBundle(Locale locale) { ); } if ( rb != null ) { - log.debugf( "%s found.", bundleName ); + LOG.debugf( "%s found.", bundleName ); } else { - log.debugf( "%s not found.", bundleName ); + LOG.debugf( "%s not found.", bundleName ); } return rb; } @@ -150,7 +232,7 @@ private ResourceBundle loadBundle(ClassLoader classLoader, Locale locale, String } } catch (MissingResourceException e) { - log.trace( message ); + LOG.trace( message ); } return rb; } @@ -178,7 +260,7 @@ private static T run(PrivilegedAction action) { * * @see GAE JRE whitelist * @see HV-1023 - * @see ResourceBundle.Control */ private static boolean determineAvailabilityOfResourceBundleControl() { try { @@ -202,7 +284,7 @@ private static boolean determineAvailabilityOfResourceBundleControl() { return !isNamed; } catch (Throwable e) { - log.info( MESSAGES.unableToUseResourceBundleAggregation() ); + LOG.info( MESSAGES.unableToUseResourceBundleAggregation() ); return false; } } diff --git a/engine/src/main/java/org/hibernate/validator/spi/nodenameprovider/JavaBeanProperty.java b/engine/src/main/java/org/hibernate/validator/spi/nodenameprovider/JavaBeanProperty.java new file mode 100644 index 0000000000..ebc845b6d5 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/spi/nodenameprovider/JavaBeanProperty.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.spi.nodenameprovider; + +import org.hibernate.validator.Incubating; + +/** + * Contains metadata for a JavaBean property. + * + * @author Damir Alibegovic + * @since 6.1.0 + */ +@Incubating +public interface JavaBeanProperty extends Property { + /** + * Owner class of the property. + * + * @return {@link Class} owning class of the property + */ + Class getDeclaringClass(); +} diff --git a/engine/src/main/java/org/hibernate/validator/spi/nodenameprovider/Property.java b/engine/src/main/java/org/hibernate/validator/spi/nodenameprovider/Property.java new file mode 100644 index 0000000000..ac9bc4d98e --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/spi/nodenameprovider/Property.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.spi.nodenameprovider; + + +import org.hibernate.validator.Incubating; + +/** + * Base interface for property metadata. + * + * @author Damir Alibegovic + * @since 6.1.0 + */ +@Incubating +public interface Property { + /** + * Returns the property name. + * + * @return {@link String} representing the property name + */ + String getName(); +} diff --git a/engine/src/main/java/org/hibernate/validator/spi/nodenameprovider/PropertyNodeNameProvider.java b/engine/src/main/java/org/hibernate/validator/spi/nodenameprovider/PropertyNodeNameProvider.java new file mode 100644 index 0000000000..751668cfc8 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/spi/nodenameprovider/PropertyNodeNameProvider.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.spi.nodenameprovider; + +import org.hibernate.validator.Incubating; + +/** + * This interface is used to resolve the name of a property node when creating the property path. + * + * @author Damir Alibegovic + * @since 6.1.0 + */ +@Incubating +public interface PropertyNodeNameProvider { + /** + * Returns the resolved name of a property. + *

    + * Depending on the subtype of the {@link Property}, + * a different strategy for name resolution could be applied, defaulting to {@link Property#getName()}. For example: + * + *

    +	 * if (property instanceof {@link JavaBeanProperty}) {
    +	 *     // for instance, generate a property name based on the annotations of the property
    +	 * } else {
    +	 *     return property.getName();
    +	 * }
    +	 * 
    + * + * @param property who's name needs to be resolved + * + * @return String representing the resolved name + */ + String getName(Property property); +} diff --git a/engine/src/main/java/org/hibernate/validator/spi/properties/ConstrainableExecutable.java b/engine/src/main/java/org/hibernate/validator/spi/properties/ConstrainableExecutable.java new file mode 100644 index 0000000000..3c51e30156 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/spi/properties/ConstrainableExecutable.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.spi.properties; + +import org.hibernate.validator.Incubating; + +/** + * Descriptor for a method of a Java class. + * + * @author Marko Bekhta + * @since 6.1.0 + */ +@Incubating +public interface ConstrainableExecutable { + + /** + * @return the return type for the method this object represents + */ + Class getReturnType(); + + /** + * @return the name of the method represented by this {@code ConstrainableExecutable} object + */ + String getName(); + + /** + * @return the parameter types for the executable this object represents + */ + Class[] getParameterTypes(); +} diff --git a/engine/src/main/java/org/hibernate/validator/spi/properties/GetterPropertySelectionStrategy.java b/engine/src/main/java/org/hibernate/validator/spi/properties/GetterPropertySelectionStrategy.java new file mode 100644 index 0000000000..0833f6ff64 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/spi/properties/GetterPropertySelectionStrategy.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.spi.properties; + +import java.util.Optional; +import java.util.Set; + +import org.hibernate.validator.Incubating; + +/** + * Used to define the strategy to detect the getters of a bean. + *

    + * A getter is considered as being a property of the bean and thus validated when validating the bean. + * + * @author Marko Bekhta + * @since 6.1.0 + */ +@Incubating +public interface GetterPropertySelectionStrategy { + + /** + * Returns the property corresponding to the getter if the method is considered a getter. + * + * @param executable a {@link ConstrainableExecutable} + * + * @return an optional containing the property corresponding to the given executable if it is considered a getter, + * or an empty optional otherwise + * + * @throws IllegalArgumentException if a property name cannot be constructed + */ + Optional getProperty(ConstrainableExecutable executable); + + /** + * Gives a set of possible method names based on a property name. Usually, it means + * a property name prefixed with something like "get", "is", "has" etc. + * + * @param propertyName a property name + * + * @return the {@link Set} of possible getter names + */ + Set getGetterMethodNameCandidates(String propertyName); + +} diff --git a/engine/src/main/java/org/hibernate/validator/spi/properties/package-info.java b/engine/src/main/java/org/hibernate/validator/spi/properties/package-info.java new file mode 100644 index 0000000000..cc2ba5b5e5 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/spi/properties/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 . + */ + +/** + *

    This package provides support for customizing the getter detection logic.

    + *

    This package is part of the public Hibernate Validator SPI.

    + */ +package org.hibernate.validator.spi.properties; diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages.properties index c9d3ac8536..2cb1354faa 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages.properties @@ -28,10 +28,10 @@ org.hibernate.validator.constraints.Email.message = not a well org.hibernate.validator.constraints.ISBN.message = invalid ISBN org.hibernate.validator.constraints.Length.message = length must be between {min} and {max} org.hibernate.validator.constraints.CodePointLength.message = length must be between {min} and {max} -org.hibernate.validator.constraints.LuhnCheck.message = The check digit for ${validatedValue} is invalid, Luhn Modulo 10 checksum failed -org.hibernate.validator.constraints.Mod10Check.message = The check digit for ${validatedValue} is invalid, Modulo 10 checksum failed -org.hibernate.validator.constraints.Mod11Check.message = The check digit for ${validatedValue} is invalid, Modulo 11 checksum failed -org.hibernate.validator.constraints.ModCheck.message = The check digit for ${validatedValue} is invalid, ${modType} checksum failed +org.hibernate.validator.constraints.LuhnCheck.message = the check digit for ${validatedValue} is invalid, Luhn Modulo 10 checksum failed +org.hibernate.validator.constraints.Mod10Check.message = the check digit for ${validatedValue} is invalid, Modulo 10 checksum failed +org.hibernate.validator.constraints.Mod11Check.message = the check digit for ${validatedValue} is invalid, Modulo 11 checksum failed +org.hibernate.validator.constraints.ModCheck.message = the check digit for ${validatedValue} is invalid, ${modType} checksum failed org.hibernate.validator.constraints.NotBlank.message = may not be empty org.hibernate.validator.constraints.NotEmpty.message = may not be empty org.hibernate.validator.constraints.ParametersScriptAssert.message = script expression "{script}" didn't evaluate to true @@ -45,9 +45,9 @@ org.hibernate.validator.constraints.br.CNPJ.message = invalid Br org.hibernate.validator.constraints.br.CPF.message = invalid Brazilian individual taxpayer registry number (CPF) org.hibernate.validator.constraints.br.TituloEleitoral.message = invalid Brazilian Voter ID card number -org.hibernate.validator.constraints.pl.REGON.message = Invalid Polish Taxpayer Identification Number (REGON) -org.hibernate.validator.constraints.pl.NIP.message = Invalid VAT Identification Number (NIP) -org.hibernate.validator.constraints.pl.PESEL.message = Invalid Polish National Identification Number (PESEL) +org.hibernate.validator.constraints.pl.REGON.message = invalid Polish Taxpayer Identification Number (REGON) +org.hibernate.validator.constraints.pl.NIP.message = invalid VAT Identification Number (NIP) +org.hibernate.validator.constraints.pl.PESEL.message = invalid Polish National Identification Number (PESEL) org.hibernate.validator.constraints.time.DurationMax.message = must be shorter than${inclusive == true ? ' or equal to' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} org.hibernate.validator.constraints.time.DurationMin.message = must be longer than${inclusive == true ? ' or equal to' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_cs.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_cs.properties index 19014e8c9d..c8f282a168 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_cs.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_cs.properties @@ -1,27 +1,53 @@ -javax.validation.constraints.AssertFalse.message = mus\u00ed b\u00fdt ne -javax.validation.constraints.AssertTrue.message = mus\u00ed b\u00fdt ano -javax.validation.constraints.DecimalMax.message = mus\u00ed b\u00fdt men\u0161\u00ed ne\u017e nebo rovno {value} -javax.validation.constraints.DecimalMin.message = mus\u00ed b\u00fdt v\u011bt\u0161\u00ed ne\u017e nebo rovno {value} -javax.validation.constraints.Digits.message = \u010d\u00edseln\u00e1 hodnota mimo rozsah (o\u010dek\u00e1v\u00e1no <{integer} \u010d\u00edslic>.<{fraction} \u010d\u00edslic>) -javax.validation.constraints.Email.message = chybn\u00e1 emailov\u00e1 adresa -javax.validation.constraints.Future.message = mus\u00ed b\u00fdt v budoucnosti -javax.validation.constraints.Max.message = mus\u00ed b\u00fdt men\u0161\u00ed ne\u017e nebo rovno {value} -javax.validation.constraints.Min.message = mus\u00ed b\u00fdt v\u011bt\u0161\u00ed ne\u017e nebo rovno {value} -javax.validation.constraints.NotBlank.message = nem\u016f\u017ee b\u00fdt pr\u00e1zdn\u00fd/\u00e1/\u00e9 -javax.validation.constraints.NotEmpty.message = nem\u016f\u017ee b\u00fdt pr\u00e1zdn\u00fd/\u00e1/\u00e9 -javax.validation.constraints.NotNull.message = nesm\u00ed b\u00fdt null -javax.validation.constraints.Null.message = mus\u00ed b\u00fdt null -javax.validation.constraints.Past.message = mus\u00ed b\u00fdt v minulosti -javax.validation.constraints.Pattern.message = se mus\u00ed shodovat s "{regexp}" -javax.validation.constraints.Size.message = velikost mus\u00ed b\u00fdt mezi {min} a {max} +javax.validation.constraints.AssertFalse.message = mus\u00ed m\u00edt hodnotu ne +javax.validation.constraints.AssertTrue.message = mus\u00ed m\u00edt hodnotu ano +javax.validation.constraints.DecimalMax.message = mus\u00ed b\u00fdt men\u0161\u00ed ne\u017e ${inclusive == true ? 'nebo rovna hodnot\u011b ' : ''}{hodnota} +javax.validation.constraints.DecimalMin.message = mus\u00ed b\u00fdt v\u011bt\u0161\u00ed ne\u017e ${inclusive == true ? 'nebo rovna hodnot\u011b ' : ''}{hodnota} +javax.validation.constraints.Digits.message = \u010d\u00edseln\u00e1 hodnota mimo rozsah (o\u010dek\u00e1v\u00e1no: <{integer} \u010d\u00edslic>.<{fraction} \u010d\u00edslic>) +javax.validation.constraints.Email.message = mus\u00ed m\u00edt spr\u00e1vn\u011b utvo\u0159enou e-mailovou adresu +javax.validation.constraints.Future.message = mus\u00ed se jednat o datum v budoucnu +javax.validation.constraints.FutureOrPresent.message = mus\u00ed m\u00edt aktu\u00e1ln\u00ed nebo budouc\u00ed datum +javax.validation.constraints.Max.message = mus\u00ed b\u00fdt men\u0161\u00ed nebo rovna hodnot\u011b {value} +javax.validation.constraints.Min.message = mus\u00ed b\u00fdt v\u011bt\u0161\u00ed nebo rovna hodnot\u011b {value} +javax.validation.constraints.Negative.message = mus\u00ed b\u00fdt men\u0161\u00ed ne\u017e 0 +javax.validation.constraints.NegativeOrZero.message = mus\u00ed b\u00fdt men\u0161\u00ed ne\u017e nebo rovna hodnot\u011b 0 +javax.validation.constraints.NotBlank.message = nesm\u00ed b\u00fdt pr\u00e1zdn\u00e1 +javax.validation.constraints.NotEmpty.message = nesm\u00ed b\u00fdt pr\u00e1zdn\u00e1 +javax.validation.constraints.NotNull.message = nesm\u00ed m\u00edt hodnotu Null +javax.validation.constraints.Null.message = mus\u00ed m\u00edt hodnotu Null +javax.validation.constraints.Past.message = mus\u00ed se jednat o datum v minulosti +javax.validation.constraints.PastOrPresent.message = mus\u00ed b\u00fdt datum v minulosti nebo aktu\u00e1ln\u00ed +javax.validation.constraints.Pattern.message = mus\u00ed odpov\u00eddat "{regexp}" +javax.validation.constraints.Positive.message = mus\u00ed b\u00fdt v\u011bt\u0161\u00ed ne\u017e 0 +javax.validation.constraints.PositiveOrZero.message = mus\u00ed b\u00fdt v\u011bt\u0161\u00ed ne\u017e nebo rovna hodnot\u011b 0 +javax.validation.constraints.Size.message = velikost mus\u00ed le\u017eet v rozsahu {min} a\u017e {max} -org.hibernate.validator.constraints.CreditCardNumber.message = neplatn\u00e9 \u010d\u00edslo kreditn\u00ed karty -org.hibernate.validator.constraints.Email.message = chybn\u00e1 emailov\u00e1 adresa -org.hibernate.validator.constraints.Length.message = d\u00e9lka mus\u00ed b\u00fdt mezi {min} a {max} -org.hibernate.validator.constraints.CodePointLength.message = d\u00e9lka mus\u00ed b\u00fdt mezi {min} a {max} -org.hibernate.validator.constraints.NotBlank.message = nem\u016f\u017ee b\u00fdt pr\u00e1zdn\u00fd/\u00e1/\u00e9 -org.hibernate.validator.constraints.NotEmpty.message = nem\u016f\u017ee b\u00fdt pr\u00e1zdn\u00fd/\u00e1/\u00e9 -org.hibernate.validator.constraints.Range.message = mus\u00ed b\u00fdt mezi {min} a {max} -org.hibernate.validator.constraints.SafeHtml.message = m\u016f\u017ee obsahovat nebezpe\u010dn\u00fd html obsah -org.hibernate.validator.constraints.ScriptAssert.message = skriptovac\u00ed v\u00fdraz "{script}" nen\u00ed vyhodnocen na ano -org.hibernate.validator.constraints.URL.message = mus\u00ed b\u00fdt platn\u00e9 URL +org.hibernate.validator.constraints.CreditCardNumber.message = neplatn\u00e9 \u010d\u00edslo kreditn\u00ed karty +org.hibernate.validator.constraints.Currency.message = neplatn\u00e1 m\u011bna (mus\u00ed m\u00edt jednu z hodnot {hodnota}) +org.hibernate.validator.constraints.EAN.message = neplatn\u00fd \u010d\u00e1rov\u00fd k\u00f3d {typ} +org.hibernate.validator.constraints.Email.message = mus\u00ed m\u00edt spr\u00e1vn\u011b utvo\u0159enou e-mailovou adresu +org.hibernate.validator.constraints.ISBN.message = neplatn\u00e9 ISBN +org.hibernate.validator.constraints.Length.message = d\u00e9lka mus\u00ed le\u017eet v rozsahu {min} a\u017e {max} +org.hibernate.validator.constraints.CodePointLength.message = d\u00e9lka mus\u00ed le\u017eet v rozsahu {min} a\u017e {max} +org.hibernate.validator.constraints.LuhnCheck.message = kontroln\u00ed \u010d\u00edslice pro ${validatedValue} je neplatn\u00e1, kontroln\u00ed sou\u010det Luhn Modulo 10 se nezda\u0159il +org.hibernate.validator.constraints.Mod10Check.message = kontroln\u00ed \u010d\u00edslice pro ${validatedValue} je neplatn\u00e1, kontroln\u00ed sou\u010det Modulo 10 se nezda\u0159il +org.hibernate.validator.constraints.Mod11Check.message = kontroln\u00ed \u010d\u00edslice pro ${validatedValue} je neplatn\u00e1, kontroln\u00ed sou\u010det Modulo 11 se nezda\u0159il +org.hibernate.validator.constraints.ModCheck.message = kontroln\u00ed \u010d\u00edslice pro ${validatedValue} je neplatn\u00e1, kontroln\u00ed sou\u010det ${modType} se nezda\u0159il +org.hibernate.validator.constraints.NotBlank.message = nesm\u00ed b\u00fdt pr\u00e1zdn\u00e1 +org.hibernate.validator.constraints.NotEmpty.message = nesm\u00ed b\u00fdt pr\u00e1zdn\u00e1 +org.hibernate.validator.constraints.ParametersScriptAssert.message = v\u00fdraz skriptu "{script}" se nevyhodnotil na true +org.hibernate.validator.constraints.Range.message = mus\u00ed le\u017eet v rozsahu {min} a\u017e {max} +org.hibernate.validator.constraints.SafeHtml.message = je mo\u017en\u00e9, \u017ee obsah html nen\u00ed bezpe\u010dn\u00fd +org.hibernate.validator.constraints.ScriptAssert.message = v\u00fdraz skriptu "{script}" se nevyhodnotil na true +org.hibernate.validator.constraints.UniqueElements.message = mus\u00ed obsahovat pouze jedine\u010dn\u00e9 prvky +org.hibernate.validator.constraints.URL.message = mus\u00ed b\u00fdt platnou adresou URL + +org.hibernate.validator.constraints.br.CNPJ.message = neplatn\u00e9 brazilsk\u00e9 registra\u010dn\u00ed \u010d\u00edslo pl\u00e1tce korpor\u00e1tn\u00ed dan\u011b (CNPJ) +org.hibernate.validator.constraints.br.CPF.message = neplatn\u00e9 brazilsk\u00e9 registra\u010dn\u00ed \u010d\u00edslo individu\u00e1ln\u00edho pl\u00e1tce dan\u011b (CPF) +org.hibernate.validator.constraints.br.TituloEleitoral.message = neplatn\u00e9 brazilsk\u00e9 \u010d\u00edslo karty Voter ID + +org.hibernate.validator.constraints.pl.REGON.message = neplatn\u00e9 Polsk\u00e9 Identifika\u010dn\u00ed \u010d\u00edslo pl\u00e1tn\u011b dan\u011b (REGON) +org.hibernate.validator.constraints.pl.NIP.message = neplatn\u00e9 da\u0148ov\u00e9 identifika\u010dn\u00ed \u010d\u00edslo (NIP) +org.hibernate.validator.constraints.pl.PESEL.message = neplatn\u00e9 Polsk\u00e9 n\u00e1rodn\u00ed Identifika\u010dn\u00ed \u010d\u00edslo (PESEL) + +org.hibernate.validator.constraints.time.DurationMax.message = mus\u00ed b\u00fdt krat\u0161\u00ed ne\u017e${inclusive == true ? ' nebo rovno hodnot\u011b' : ''}${days == 0 ? '' : days == 1 ? ' 1 den' : ' ' += days += ' dny/\u016f'}${hours == 0 ? '' : hours == 1 ? ' 1 hod' : ' ' += hours += ' hod'}${minutes == 0 ? '' : minutes == 1 ? ' 1 min' : ' ' += minutes += ' min'}${seconds == 0 ? '' : seconds == 1 ? ' 1 s' : ' ' += seconds += ' s'}${millis == 0 ? '' : millis == 1 ? ' 1 ms' : ' ' += millis += ' ms'}${nanos == 0 ? '' : nanos == 1 ? ' 1 ns' : ' ' += nanos += ' ns'} +org.hibernate.validator.constraints.time.DurationMin.message = mus\u00ed b\u00fdt del\u0161\u00ed ne\u017e${inclusive == true ? ' nebo rovno hodnot\u011b' : ''}${days == 0 ? '' : days == 1 ? ' 1 den' : ' ' += days += ' dny/\u016f'}${hours == 0 ? '' : hours == 1 ? ' 1 hod' : ' ' += hours += ' hod'}${minutes == 0 ? '' : minutes == 1 ? ' 1 min' : ' ' += minutes += ' min'}${seconds == 0 ? '' : seconds == 1 ? ' 1 s' : ' ' += seconds += ' s'}${millis == 0 ? '' : millis == 1 ? ' 1 ms' : ' ' += millis += ' ms'}${nanos == 0 ? '' : nanos == 1 ? ' 1 ns' : ' ' += nanos += ' ns'} diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_da.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_da.properties new file mode 100644 index 0000000000..c2eb592ead --- /dev/null +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_da.properties @@ -0,0 +1,53 @@ +javax.validation.constraints.AssertFalse.message = skal v\u00e6re falsk +javax.validation.constraints.AssertTrue.message = skal v\u00e6re sand +javax.validation.constraints.DecimalMax.message = skal v\u00e6re mindre end ${inclusive == true ? 'eller det samme som ' : ''}{value} +javax.validation.constraints.DecimalMin.message = skal v\u00e6re st\u00f8rre end ${inclusive == true ? 'eller det samme som ' : ''}{value} +javax.validation.constraints.Digits.message = tal v\u00e6rdierne er udenfor det gyldige omr\u00e5de (<{integer} digits>.<{fraction} digits> forventet) +javax.validation.constraints.Email.message = skal v\u00e6re en korrekt formateret e-mail-adresse +javax.validation.constraints.Future.message = skal v\u00e6re en dato i fremtiden +javax.validation.constraints.FutureOrPresent.message = skal v\u00e6re en dato i nutiden eller i fremtiden +javax.validation.constraints.Max.message = skal v\u00e6re mindre end eller lig med {value} +javax.validation.constraints.Min.message = skal v\u00e6re st\u00f8rre end eller lig med {value} +javax.validation.constraints.Negative.message = skal v\u00e6re mindre end 0 +javax.validation.constraints.NegativeOrZero.message = skal v\u00e6re mindre end eller lig med 0 +javax.validation.constraints.NotBlank.message = m\u00e5 ikke v\u00e6re blank +javax.validation.constraints.NotEmpty.message = m\u00e5 ikke v\u00e6re tom +javax.validation.constraints.NotNull.message = m\u00e5 ikke v\u00e6re null +javax.validation.constraints.Null.message = skal v\u00e6re null +javax.validation.constraints.Past.message = skal v\u00e6re en dato fortiden +javax.validation.constraints.PastOrPresent.message = skal v\u00e6re en dato i fortiden eller i nutiden +javax.validation.constraints.Pattern.message = skal matche "{regexp}" +javax.validation.constraints.Positive.message = skal v\u00e6re st\u00f8rre end 0 +javax.validation.constraints.PositiveOrZero.message = skal v\u00e6re st\u00f8rre end eller lig med 0 +javax.validation.constraints.Size.message = st\u00f8rrelse skal v\u00e6re mellem {min} og {max} + +org.hibernate.validator.constraints.CreditCardNumber.message = ugyldigt kreditkortnummer +org.hibernate.validator.constraints.Currency.message = ugyldig valuta (skal v\u00e6re en af {v\u00e6rdi}) +org.hibernate.validator.constraints.EAN.message = ugyldig {type} stregkode +org.hibernate.validator.constraints.Email.message = ikke en velformuleret email adresse +org.hibernate.validator.constraints.ISBN.message = ugyldigt ISBN +org.hibernate.validator.constraints.Length.message = l\u00e6ngden skal v\u00e6re mellem {min} og {max} +org.hibernate.validator.constraints.CodePointLength.message = l\u00e6ngden skal v\u00e6re mellem {min} og {max} +org.hibernate.validator.constraints.LuhnCheck.message = kontrolcifferet for $ {validatedValue} er ugyldigt, Luhn Modulo 10 checksum mislykkedes +org.hibernate.validator.constraints.Mod10Check.message = kontrolcifferet for $ {validatedValue} er ugyldigt, Modulo 10 checksum mislykkedes +org.hibernate.validator.constraints.Mod11Check.message = kontrolcifferet for $ {validatedValue} er ugyldigt, Modulo 11 checksum mislykkedes +org.hibernate.validator.constraints.ModCheck.message = kontrolcifferet for $ {validatedValue} er ugyldigt, $ {modType} checksummet mislykkedes +org.hibernate.validator.constraints.NotBlank.message = m\u00e5 ikke v\u00e6re tomt +org.hibernate.validator.constraints.NotEmpty.message = m\u00e5 ikke v\u00e6re tomt +org.hibernate.validator.constraints.ParametersScriptAssert.message = script udtryk "{script}" evaluerede ikke til true +org.hibernate.validator.constraints.Range.message = skal v\u00e6re mellem {min} og {max} +org.hibernate.validator.constraints.SafeHtml.message = kan have usikkert HTML-indhold +org.hibernate.validator.constraints.ScriptAssert.message = script udtryk "{script}" evaluerede ikke til true +org.hibernate.validator.constraints.UniqueElements.message = m\u00e5 kun indeholde unikke elementer +org.hibernate.validator.constraints.URL.message = skal v\u00e6re en gyldig webadresse + +org.hibernate.validator.constraints.br.CNPJ.message = ugyldigt brasiliansk selskabsskattem\u00e6ssige registreringsnummer (CNPJ) +org.hibernate.validator.constraints.br.CPF.message = ugyldigt brasiliansk enkeltregistratornummer (CPF) +org.hibernate.validator.constraints.br.TituloEleitoral.message = Ugyldigt brasiliansk Voter ID-kortnummer + +org.hibernate.validator.constraints.pl.REGON.message = ugyldigt polsk skattebetalers identifikationsnummer (REGON) +org.hibernate.validator.constraints.pl.NIP.message = ugyldigt momsidentifikationsnummer (NIP) +org.hibernate.validator.constraints.pl.PESEL.message = ugyldigt polsk nationalt identifikationsnummer (PESEL) + +org.hibernate.validator.constraints.time.DurationMax.message = skal v\u00e6re kortere end${inclusive == true ? ' eller lig med' : ''}${days == 0 ? '' : days == 1 ? ' 1 dag' : ' ' += days += ' dage'}${hours == 0 ? '' : hours == 1 ? ' 1 time' : ' ' += hours += ' timer'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minut' : ' ' += minutes += ' minutter'}${seconds == 0 ? '' : seconds == 1 ? ' 1 sekund' : ' ' += seconds += ' sekunder'}${millis == 0 ? '' : millis == 1 ? ' 1 millisekund' : ' ' += millis += ' millisekunder'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nanosekund' : ' ' += nanos += ' nanosekunder'} +org.hibernate.validator.constraints.time.DurationMin.message = skal v\u00e6re l\u00e6ngere end${inclusive == true ? ' eller lig med' : ''}${days == 0 ? '' : days == 1 ? ' 1 dag' : ' ' += days += ' dage'}${hours == 0 ? '' : hours == 1 ? ' 1 time' : ' ' += hours += ' timer'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minut' : ' ' += minutes += ' minutter'}${seconds == 0 ? '' : seconds == 1 ? ' 1 sekund : ' ' += seconds += ' sekunder'}${millis == 0 ? '' : millis == 1 ? ' 1 millisekund' : ' ' += millis += ' millisekunder'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nanosekund' : ' ' += nanos += ' nanosekunder'} diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_de.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_de.properties index c802fec1a1..413e0943b5 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_de.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_de.properties @@ -1,40 +1,53 @@ -javax.validation.constraints.AssertFalse.message = muss falsch sein -javax.validation.constraints.AssertTrue.message = muss wahr sein -javax.validation.constraints.DecimalMax.message = muss kleiner ${strict == false ? 'oder gleich ' : ''}{value} sein -javax.validation.constraints.DecimalMin.message = muss gr\u00F6\u00DFer ${inclusive == true ? 'oder gleich ' : ''}{value} sein -javax.validation.constraints.Digits.message = numerischer Wert au\u00DFerhalb erlaubten Wertebereichs (<{integer} Ziffern>,<{fraction} Ziffern> erwartet) -javax.validation.constraints.Email.message = keine g\u00FCltige E-Mail-Adresse -javax.validation.constraints.Future.message = muss in der Zukunft liegen -javax.validation.constraints.FutureOrPresent.message = muss in der Gegenwart oder Zukunft liegen -javax.validation.constraints.Max.message = muss kleiner oder gleich {value} sein -javax.validation.constraints.Min.message = muss gr\u00F6\u00DFer oder gleich {value} sein -javax.validation.constraints.Negative.message = muss kleiner 0 sein -javax.validation.constraints.NegativeOrZero.message = muss kleiner oder gleich 0 sein -javax.validation.constraints.NotBlank.message = darf nicht leer sein -javax.validation.constraints.NotEmpty.message = darf nicht leer sein -javax.validation.constraints.NotNull.message = darf nicht null sein -javax.validation.constraints.Null.message = muss null sein -javax.validation.constraints.Past.message = muss in der Vergangenheit liegen -javax.validation.constraints.PastOrPresent.message = muss in der Gegenwart oder Vergangenheit liegen -javax.validation.constraints.Pattern.message = muss auf Ausdruck "{regexp}" passen -javax.validation.constraints.Positive.message = muss gr\u00F6\u00DFer 0 sein -javax.validation.constraints.PositiveOrZero.message = muss gr\u00F6\u00DFer oder gleich 0 sein -javax.validation.constraints.Size.message = muss zwischen {min} und {max} liegen +javax.validation.constraints.AssertFalse.message = muss falsch sein +javax.validation.constraints.AssertTrue.message = muss wahr sein +javax.validation.constraints.DecimalMax.message = muss kleiner ${inclusive == true ? 'oder gleich ' : ''}{value} sein +javax.validation.constraints.DecimalMin.message = muss gr\u00f6\u00dfer ${inclusive == true ? 'oder gleich sein ' : ''}{value} +javax.validation.constraints.Digits.message = numerischer Wert au\u00dferhalb des g\u00fcltigen Bereichs (<{integer} digits>.<{fraction} digits> erwartet) +javax.validation.constraints.Email.message = muss eine korrekt formatierte E-Mail-Adresse sein +javax.validation.constraints.Future.message = muss ein Datum in der Zukunft sein +javax.validation.constraints.FutureOrPresent.message = muss ein Datum in der Gegenwart oder in der Zukunft sein +javax.validation.constraints.Max.message = muss kleiner-gleich {value} sein. +javax.validation.constraints.Min.message = muss gr\u00f6\u00dfer-gleich {value} sein +javax.validation.constraints.Negative.message = muss kleiner als 0 sein +javax.validation.constraints.NegativeOrZero.message = muss kleiner-gleich 0 sein +javax.validation.constraints.NotBlank.message = darf nicht leer sein +javax.validation.constraints.NotEmpty.message = darf nicht leer sein +javax.validation.constraints.NotNull.message = darf nicht null sein +javax.validation.constraints.Null.message = muss null sein +javax.validation.constraints.Past.message = muss ein Datum in der Vergangenheit sein +javax.validation.constraints.PastOrPresent.message = muss ein Datum in der Vergangenheit oder in der Gegenwart sein +javax.validation.constraints.Pattern.message = muss mit "{regexp}" \u00fcbereinstimmen +javax.validation.constraints.Positive.message = muss gr\u00f6\u00dfer als 0 sein +javax.validation.constraints.PositiveOrZero.message = muss gr\u00f6\u00dfer-gleich 0 sein +javax.validation.constraints.Size.message = Gr\u00f6\u00dfe muss zwischen {min} und {max} sein -org.hibernate.validator.constraints.CreditCardNumber.message = ung\u00FCltige Kreditkartennummer -org.hibernate.validator.constraints.Currency.message = ung\u00FCltige W\u00E4hrung (erlaubte Werte: {value}) -org.hibernate.validator.constraints.EAN.message = ung\u00FCltiger {type} Barcode -org.hibernate.validator.constraints.Email.message = keine g\u00FCltige E-Mail-Adresse -org.hibernate.validator.constraints.Length.message = muss zwischen {min} und {max} Zeichen lang sein -org.hibernate.validator.constraints.CodePointLength.message = muss zwischen {min} und {max} Zeichen lang sein -org.hibernate.validator.constraints.NotBlank.message = darf nicht leer sein -org.hibernate.validator.constraints.NotEmpty.message = darf nicht leer sein -org.hibernate.validator.constraints.ParametersScriptAssert.message = Skriptausdruck "{script}" gab nicht true zur\u00FCck -org.hibernate.validator.constraints.Range.message = muss zwischen {min} und {max} liegen -org.hibernate.validator.constraints.SafeHtml.message = k\u00F6nnte unsicheren HTML-Inhalt haben -org.hibernate.validator.constraints.ScriptAssert.message = Skriptausdruck "{script}" gab nicht true zur\u00FCck -org.hibernate.validator.constraints.UniqueElements.message = darf keine Duplikate enthalten -org.hibernate.validator.constraints.URL.message = muss eine g\u00FCltige URL sein +org.hibernate.validator.constraints.CreditCardNumber.message = ung\u00fcltige Kreditkartennummer +org.hibernate.validator.constraints.Currency.message = ung\u00fcltige W\u00e4hrung (muss eine der folgenden sein: {value}) +org.hibernate.validator.constraints.EAN.message = ung\u00fcltiger {type}-Barcode +org.hibernate.validator.constraints.Email.message = muss eine korrekt formatierte E-Mail-Adresse sein +org.hibernate.validator.constraints.ISBN.message = ung\u00fcltige ISBN +org.hibernate.validator.constraints.Length.message = L\u00e4nge muss zwischen {min} und {max} sein +org.hibernate.validator.constraints.CodePointLength.message = L\u00e4nge muss zwischen {min} und {max} sein +org.hibernate.validator.constraints.LuhnCheck.message = die Pr\u00fcfziffer f\u00fcr ${validatedValue} ist ung\u00fcltig, Luhn Modulo 10-Kontrollsumme ist fehlgeschlagen +org.hibernate.validator.constraints.Mod10Check.message = die Pr\u00fcfziffer f\u00fcr ${validatedValue} ist ung\u00fcltig, Modulo 10-Kontrollsumme ist fehlgeschlagen +org.hibernate.validator.constraints.Mod11Check.message = die Pr\u00fcfziffer f\u00fcr ${validatedValue} ist ung\u00fcltig, Modulo 11-Kontrollsumme ist fehlgeschlagen +org.hibernate.validator.constraints.ModCheck.message = die Pr\u00fcfziffer f\u00fcr ${validatedValue} ist ung\u00fcltig, ${modType}-Kontrollsumme ist fehlgeschlagen +org.hibernate.validator.constraints.NotBlank.message = darf nicht leer sein +org.hibernate.validator.constraints.NotEmpty.message = darf nicht leer sein +org.hibernate.validator.constraints.ParametersScriptAssert.message = die Evaluierung des Scriptausdrucks "{script}" ergab nicht true +org.hibernate.validator.constraints.Range.message = muss zwischen {min} und {max} sein +org.hibernate.validator.constraints.SafeHtml.message = hat m\u00f6glicherweise keinen gesch\u00fctzten HTML-Inhalt +org.hibernate.validator.constraints.ScriptAssert.message = die Evaluierung des Scriptausdrucks "{script}" ergab nicht true +org.hibernate.validator.constraints.UniqueElements.message = darf nur eindeutige Elemente enthalten +org.hibernate.validator.constraints.URL.message = muss eine g\u00fcltige URL sein -org.hibernate.validator.constraints.time.DurationMax.message = muss k\u00FCrzer${inclusive == true ? ' oder gleich' : ' als'}${days == 0 ? '' : days == 1 ? ' 1 Tag' : ' ' += days += ' Tage'}${hours == 0 ? '' : hours == 1 ? ' 1 Stunde' : ' ' += hours += ' Stunden'}${minutes == 0 ? '' : minutes == 1 ? ' 1 Minute' : ' ' += minutes += ' Minuten'}${seconds == 0 ? '' : seconds == 1 ? ' 1 Sekunde' : ' ' += seconds += ' Sekunden'}${millis == 0 ? '' : millis == 1 ? ' 1 Millisekunde' : ' ' += millis += ' Millisekunden'}${nanos == 0 ? '' : nanos == 1 ? ' 1 Nanosekunde' : ' ' += nanos += ' Nanosekunden'} sein -org.hibernate.validator.constraints.time.DurationMin.message = muss l\u00E4nger${inclusive == true ? ' oder gleich' : ' als'}${days == 0 ? '' : days == 1 ? ' 1 Tag' : ' ' += days += ' Tage'}${hours == 0 ? '' : hours == 1 ? ' 1 Stunde' : ' ' += hours += ' Stunden'}${minutes == 0 ? '' : minutes == 1 ? ' 1 Minute' : ' ' += minutes += ' Minuten'}${seconds == 0 ? '' : seconds == 1 ? ' 1 Sekunde' : ' ' += seconds += ' Sekunden'}${millis == 0 ? '' : millis == 1 ? ' 1 Millisekunde' : ' ' += millis += ' Millisekunden'}${nanos == 0 ? '' : nanos == 1 ? ' 1 Nanosekunde' : ' ' += nanos += ' Nanosekunden'} sein +org.hibernate.validator.constraints.br.CNPJ.message = ung\u00fcltige brasilianische Registriernummer f\u00fcr K\u00f6rperschaftssteuerzahlungen (CNPJ) +org.hibernate.validator.constraints.br.CPF.message = ung\u00fcltige brasilianische Registriernummer f\u00fcr Steuerzahlungen nat\u00fcrlicher Personen (CPF) +org.hibernate.validator.constraints.br.TituloEleitoral.message = ung\u00fcltige brasilianische ID-Karte f\u00fcr Entscheidungsberechtigte + +org.hibernate.validator.constraints.pl.REGON.message = ung\u00fcltige polnische Steuerzahleridentifikationsnummer (REGON) +org.hibernate.validator.constraints.pl.NIP.message = ung\u00fcltige Mehrwertsteueridentifikationsnummer (NIP) +org.hibernate.validator.constraints.pl.PESEL.message = ung\u00fcltige polnische nationale Identifikationsnummer (PESEL) + +org.hibernate.validator.constraints.time.DurationMax.message = muss k\u00fcrzer sein als${inclusive == true ? ' oder gleich' : ''}${days == 0 ? '' : days == 1 ? ' 1 Tag' : ' ' += days += ' Tage'}${hours == 0 ? '' : hours == 1 ? ' 1 Stunde' : ' ' += hours += ' Stunden'}${minutes == 0 ? '' : minutes == 1 ? ' 1 Minute' : ' ' += minutes += ' Minuten'}${seconds == 0 ? '' : seconds == 1 ? ' 1 Sekunde' : ' ' += seconds += ' Sekunden'}${millis == 0 ? '' : millis == 1 ? ' 1 Millisekunde' : ' ' += millis += ' Millisekunden'}${nanos == 0 ? '' : nanos == 1 ? ' 1 Nanosekunde' : ' ' += nanos += ' Nanosekunden'} +org.hibernate.validator.constraints.time.DurationMin.message = muss gr\u00f6\u00dfer sein als${inclusive == true ? ' oder gleich' : ''}${days == 0 ? '' : days == 1 ? ' 1 Tag' : ' ' += days += ' Tage'}${hours == 0 ? '' : hours == 1 ? ' 1 Stunde' : ' ' += hours += ' Stunden'}${minutes == 0 ? '' : minutes == 1 ? ' 1 Minute' : ' ' += minutes += ' Minuten'}${seconds == 0 ? '' : seconds == 1 ? ' 1 Sekunde' : ' ' += seconds += ' Sekunden'}${millis == 0 ? '' : millis == 1 ? ' 1 Millisekunde' : ' ' += millis += ' Millisekunden'}${nanos == 0 ? '' : nanos == 1 ? ' 1 Nanosekunde' : ' ' += nanos += ' Nanosekunden'} diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_es.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_es.properties index cea34f85f2..89bc7106c2 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_es.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_es.properties @@ -1,53 +1,53 @@ -javax.validation.constraints.AssertFalse.message = tiene que ser falso -javax.validation.constraints.AssertTrue.message = tiene que ser verdadero -javax.validation.constraints.DecimalMax.message = tiene que ser menor ${inclusive == true ? 'o igual que ' : ''}{value} -javax.validation.constraints.DecimalMin.message = tiene que ser mayor ${inclusive == true ? 'o igual que ' : ''}{value} -javax.validation.constraints.Digits.message = valor num\u00E9rico fuera de los l\u00EDmites (se esperaba <{integer} d\u00EDgitos>.<{fraction} d\u00EDgitos) -javax.validation.constraints.Email.message = no es una direcci\u00F3n de correo bien formada -javax.validation.constraints.Future.message = tiene que ser una fecha en el futuro -javax.validation.constraints.FutureOrPresent.message = tiene que ser una fecha en el presente o en el futuro -javax.validation.constraints.Max.message = tiene que ser menor o igual que {value} -javax.validation.constraints.Min.message = tiene que ser mayor o igual que {value} -javax.validation.constraints.Negative.message = tiene que ser menor que 0 -javax.validation.constraints.NegativeOrZero.message = tiene que ser menor o igual a 0 -javax.validation.constraints.NotBlank.message = no puede estar vac\u00EDo -javax.validation.constraints.NotEmpty.message = no puede estar vac\u00EDo -javax.validation.constraints.NotNull.message = no puede ser null -javax.validation.constraints.Null.message = tiene que ser null -javax.validation.constraints.Past.message = tiene que ser una fecha en el pasado -javax.validation.constraints.PastOrPresent.message = tiene que ser una fecha en el pasado o en el presente -javax.validation.constraints.Pattern.message = tiene que corresponder a la expresi\u00F3n regular "{regexp}" -javax.validation.constraints.Positive.message = tiene que ser mayor que 0 -javax.validation.constraints.PositiveOrZero.message = tiene que ser mayor o igual a 0 -javax.validation.constraints.Size.message = el tama\u00F1o tiene que estar entre {min} y {max} +javax.validation.constraints.AssertFalse.message = debe ser falso +javax.validation.constraints.AssertTrue.message = debe ser verdadero +javax.validation.constraints.DecimalMax.message = debe ser menor que ${inclusive == true ? 'o igual a ' : ''}{value} +javax.validation.constraints.DecimalMin.message = debe ser mayor que ${inclusive == true ? 'o igual a ' : ''}{value} +javax.validation.constraints.Digits.message = valor num\u00e9rico fuera de l\u00edmites (se esperaba <{integer} d\u00edgitos>.<{fraction} d\u00edgitos>) +javax.validation.constraints.Email.message = debe ser una direcci\u00f3n de correo electr\u00f3nico con formato correcto +javax.validation.constraints.Future.message = debe ser una fecha futura +javax.validation.constraints.FutureOrPresent.message = debe ser una fecha en el presente o en el futuro +javax.validation.constraints.Max.message = debe ser menor que o igual a {value} +javax.validation.constraints.Min.message = debe ser mayor que o igual a {value} +javax.validation.constraints.Negative.message = debe ser menor que 0 +javax.validation.constraints.NegativeOrZero.message = debe ser menor que o igual a 0 +javax.validation.constraints.NotBlank.message = no debe estar vac\u00edo +javax.validation.constraints.NotEmpty.message = no debe estar vac\u00edo +javax.validation.constraints.NotNull.message = no debe ser nulo +javax.validation.constraints.Null.message = debe ser nulo +javax.validation.constraints.Past.message = debe ser una fecha pasada +javax.validation.constraints.PastOrPresent.message = debe ser una fecha en el pasado o en el presente +javax.validation.constraints.Pattern.message = debe coincidir con "{regexp}" +javax.validation.constraints.Positive.message = debe ser mayor que 0 +javax.validation.constraints.PositiveOrZero.message = debe ser mayor que o igual a 0 +javax.validation.constraints.Size.message = el tama\u00f1o debe estar entre {min} y {max} -org.hibernate.validator.constraints.CreditCardNumber.message = n\u00FAmero de tarjeta inv\u00E1lido -org.hibernate.validator.constraints.Currency.message = mon\u00E9da inv\u00E1lida (tiene que ser una de {value}) -org.hibernate.validator.constraints.EAN.message = c\u00F3digo de barras {type} inv\u00E1lido -org.hibernate.validator.constraints.Email.message = no es una direcci\u00F3n de correo bien formada -org.hibernate.validator.constraints.ISBN.message = ISBN inv\u00E1lido -org.hibernate.validator.constraints.Length.message = la longitud tiene que estar entre {min} y {max} -org.hibernate.validator.constraints.CodePointLength.message = la longitud tiene que estar entre {min} y {max} -org.hibernate.validator.constraints.LuhnCheck.message = el d\u00EDgito de verificaci\u00F3n para ${validatedValue} es inv\u00E1lido, la suma de verificaci\u00F3n Luhn M\u00F3dulo 10 fall\u00F3 -org.hibernate.validator.constraints.Mod10Check.message = el d\u00EDgito de verificaci\u00F3n para ${validatedValue} es inv\u00E1lido, la suma de verificaci\u00F3n M\u00F3dulo 10 fall\u00F3 -org.hibernate.validator.constraints.Mod11Check.message = el d\u00EDgito de verificaci\u00F3n para ${validatedValue} es inv\u00E1lido, la suma de verificaci\u00F3n M\u00F3dulo 11 fall\u00F3 -org.hibernate.validator.constraints.ModCheck.message = el d\u00EDgito de verificaci\u00F3n para ${validatedValue} es inv\u00E1lido, la suma de verificaci\u00F3n ${modType} fall\u00F3 -org.hibernate.validator.constraints.NotBlank.message = no puede estar vac\u00EDo -org.hibernate.validator.constraints.NotEmpty.message = no puede estar vac\u00EDo -org.hibernate.validator.constraints.ParametersScriptAssert.message = la expresi\u00F3n "{script}" no se ha evaluado como verdadera -org.hibernate.validator.constraints.Range.message = tiene que estar entre {min} y {max} -org.hibernate.validator.constraints.SafeHtml.message = puede tener un contenido html inseguro -org.hibernate.validator.constraints.ScriptAssert.message = la expresi\u00F3n "{script}" no se ha evaluado como verdadera -org.hibernate.validator.constraints.UniqueElements.message = tiene que contener elementos \u00FAnicos -org.hibernate.validator.constraints.URL.message = tiene que ser una URL v\u00E1lida +org.hibernate.validator.constraints.CreditCardNumber.message = n\u00famero de tarjeta de cr\u00e9dito no v\u00e1lido +org.hibernate.validator.constraints.Currency.message = moneda no v\u00e1lida (debe ser una de {value}) +org.hibernate.validator.constraints.EAN.message = c\u00f3digo de barras {type} no v\u00e1lido +org.hibernate.validator.constraints.Email.message = debe ser una direcci\u00f3n de correo electr\u00f3nico con formato correcto +org.hibernate.validator.constraints.ISBN.message = ISBN no v\u00e1lido +org.hibernate.validator.constraints.Length.message = la longitud debe estar entre {min} y {max} +org.hibernate.validator.constraints.CodePointLength.message = la longitud debe estar entre {min} y {max} +org.hibernate.validator.constraints.LuhnCheck.message = el d\u00edgito de comprobaci\u00f3n para ${validatedValue} no es v\u00e1lido, ha fallado la suma de comprobaci\u00f3n de Luhn Modulo 10 +org.hibernate.validator.constraints.Mod10Check.message = el d\u00edgito de comprobaci\u00f3n para ${validatedValue} no es v\u00e1lido, ha fallado la suma de comprobaci\u00f3n de Modulo 10 +org.hibernate.validator.constraints.Mod11Check.message = el d\u00edgito de comprobaci\u00f3n para ${validatedValue} no es v\u00e1lido, ha fallado la suma de comprobaci\u00f3n de Modulo 11 +org.hibernate.validator.constraints.ModCheck.message = el d\u00edgito de comprobaci\u00f3n para ${validatedValue} no es v\u00e1lido, ha fallado la suma de comprobaci\u00f3n ${modType} +org.hibernate.validator.constraints.NotBlank.message = no debe estar vac\u00edo +org.hibernate.validator.constraints.NotEmpty.message = no debe estar vac\u00edo +org.hibernate.validator.constraints.ParametersScriptAssert.message = la expresi\u00f3n de script "{script}" no se ha evaluado en verdadera +org.hibernate.validator.constraints.Range.message = debe estar entre {min} y {max} +org.hibernate.validator.constraints.SafeHtml.message = puede tener contenido HTML no seguro +org.hibernate.validator.constraints.ScriptAssert.message = la expresi\u00f3n de script "{script}" no se ha evaluado en verdadera +org.hibernate.validator.constraints.UniqueElements.message = solo debe contener elementos exclusivos +org.hibernate.validator.constraints.URL.message = debe ser un URL v\u00e1lido -org.hibernate.validator.constraints.br.CNPJ.message = n\u00FAmero de identificaci\u00F3n tributario corporativo Brazile\u00F1o (CNPJ) inv\u00E1lido -org.hibernate.validator.constraints.br.CPF.message = n\u00FAmero de identificaci\u00F3n tributario individual Brazile\u00F1o (CPF) inv\u00E1lido -org.hibernate.validator.constraints.br.TituloEleitoral.message = n\u00FAmero de identificaci\u00F3n electoral Brazile\u00F1o inv\u00E1lido +org.hibernate.validator.constraints.br.CNPJ.message = n\u00famero de registro de contribuyente corporativo de Brasil no v\u00e1lido (CNPJ) +org.hibernate.validator.constraints.br.CPF.message = n\u00famero de registro de contribuyente individual de Brasil no v\u00e1lido (CPF) +org.hibernate.validator.constraints.br.TituloEleitoral.message = n\u00famero de tarjeta de ID de votante de Brasil no v\u00e1lido -org.hibernate.validator.constraints.pl.REGON.message = n\u00FAmero de identificaci\u00F3n tributario Polaco (REGON) inv\u00E1lido -org.hibernate.validator.constraints.pl.NIP.message = n\u00FAmero de identificaci\u00F3n VAT (NIP) inv\u00E1lido -org.hibernate.validator.constraints.pl.PESEL.message = n\u00FAmero de identificaci\u00F3n nacional Polaco (PESEL) inv\u00E1lido +org.hibernate.validator.constraints.pl.REGON.message = n\u00famero de Identificaci\u00f3n de Contribuyente de Polonia no v\u00e1lido (REGON) +org.hibernate.validator.constraints.pl.NIP.message = n\u00famero de Identificaci\u00f3n de VAT no v\u00e1lido (NIP) +org.hibernate.validator.constraints.pl.PESEL.message = n\u00famero de Identificaci\u00f3n Nacional de Polonia no v\u00e1lido (PESEL) -org.hibernate.validator.constraints.time.DurationMax.message = tiene que durar menos {inclusive == true ? ' o igual que' : ''}${days == 0 ? '' : days == 1 ? ' 1 d\u00EDa' : ' ' += days += ' d\u00EDs'}${hours == 0 ? '' : hours == 1 ? ' 1 hora' : ' ' += hours += ' horas'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minuto' : ' ' += minutes += ' minutos'}${seconds == 0 ? '' : seconds == 1 ? ' 1 segundo' : ' ' += seconds += ' segundos'}${millis == 0 ? '' : millis == 1 ? ' 1 milisegundo' : ' ' += millis += ' milisegundos'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nanosegundo' : ' ' += nanos += ' nanosegundos'} -org.hibernate.validator.constraints.time.DurationMin.message = tiene que durar mas {inclusive == true ? ' o igual que' : ''}${days == 0 ? '' : days == 1 ? ' 1 d\u00EDa' : ' ' += days += ' d\u00EDs'}${hours == 0 ? '' : hours == 1 ? ' 1 hora' : ' ' += hours += ' horas'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minuto' : ' ' += minutes += ' minutos'}${seconds == 0 ? '' : seconds == 1 ? ' 1 segundo' : ' ' += seconds += ' segundos'}${millis == 0 ? '' : millis == 1 ? ' 1 milisegundo' : ' ' += millis += ' milisegundos'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nanosegundo' : ' ' += nanos += ' nanosegundos'} +org.hibernate.validator.constraints.time.DurationMax.message = debe tener una longitud menor que ${inclusive == true ? ' o igual a' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} +org.hibernate.validator.constraints.time.DurationMin.message = debe tener una longitud mayor que ${inclusive == true ? ' o igual a' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_fr.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_fr.properties index 5f9715ecd4..f12655d8ac 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_fr.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_fr.properties @@ -1,53 +1,53 @@ -javax.validation.constraints.AssertFalse.message = doit \u00EAtre faux -javax.validation.constraints.AssertTrue.message = doit \u00EAtre vrai -javax.validation.constraints.DecimalMax.message = doit \u00EAtre ${inclusive == false ? 'strictement ' : ''}inf\u00E9rieur ${inclusive == true ? 'ou \u00E9gal ' : ''}\u00E0 {value} -javax.validation.constraints.DecimalMin.message = doit \u00EAtre ${inclusive == false ? 'strictement ' : ''}sup\u00E9rieur ${inclusive == true ? 'ou \u00E9gal ' : ''}\u00E0 {value} -javax.validation.constraints.Digits.message = valeur num\u00E9rique hors limite (<{integer} chiffres>.<{fraction} chiffres> attendu) -javax.validation.constraints.Email.message = doit \u00EAtre une adresse email bien form\u00E9e -javax.validation.constraints.Future.message = doit \u00EAtre dans le futur -javax.validation.constraints.FutureOrPresent.message = doit \u00EAtre dans le pr\u00E9sent ou dans le futur -javax.validation.constraints.Max.message = doit \u00EAtre au maximum \u00E9gal \u00E0 {value} -javax.validation.constraints.Min.message = doit \u00EAtre au minimum \u00E9gal \u00E0 {value} -javax.validation.constraints.Negative.message = doit \u00EAtre strictement n\u00E9gatif -javax.validation.constraints.NegativeOrZero.message = doit \u00EAtre n\u00E9gatif ou \u00E9gal à 0 -javax.validation.constraints.NotBlank.message = ne peut pas \u00EAtre vide -javax.validation.constraints.NotEmpty.message = ne peut pas \u00EAtre vide -javax.validation.constraints.NotNull.message = ne peut pas \u00EAtre nul +javax.validation.constraints.AssertFalse.message = doit avoir la valeur faux +javax.validation.constraints.AssertTrue.message = doit avoir la valeur vrai +javax.validation.constraints.DecimalMax.message = doit \u00eatre inf\u00e9rieur \u00e0 ${inclusive == true ? ' ou \u00e9gal \u00e0 ' : ''}{value} +javax.validation.constraints.DecimalMin.message = doit \u00eatre sup\u00e9rieur \u00e0 ${inclusive == true ? ' ou \u00e9gal \u00e0 ' : ''}{value} +javax.validation.constraints.Digits.message = valeur num\u00e9rique hors limites (<{integer} chiffres>.<{fraction} chiffres> attendu) +javax.validation.constraints.Email.message = doit \u00eatre une adresse \u00e9lectronique syntaxiquement correcte +javax.validation.constraints.Future.message = doit \u00eatre une date dans le futur +javax.validation.constraints.FutureOrPresent.message = doit \u00eatre une date dans le pr\u00e9sent ou le futur +javax.validation.constraints.Max.message = doit \u00eatre inf\u00e9rieur ou \u00e9gal \u00e0 {value} +javax.validation.constraints.Min.message = doit \u00eatre sup\u00e9rieur ou \u00e9gal \u00e0 {value} +javax.validation.constraints.Negative.message = doit \u00eatre inf\u00e9rieur \u00e0 0 +javax.validation.constraints.NegativeOrZero.message = doit \u00eatre inf\u00e9rieur ou \u00e9gal \u00e0 0 +javax.validation.constraints.NotBlank.message = ne doit pas \u00eatre vide +javax.validation.constraints.NotEmpty.message = ne doit pas \u00eatre vide +javax.validation.constraints.NotNull.message = ne doit pas tre nul javax.validation.constraints.Null.message = doit \u00EAtre nul -javax.validation.constraints.Past.message = doit \u00EAtre dans le pass\u00E9 -javax.validation.constraints.PastOrPresent.message = doit \u00EAtre dans le pass\u00E9 ou dans le pr\u00E9sent -javax.validation.constraints.Pattern.message = doit respecter "{regexp}" -javax.validation.constraints.Positive.message = doit \u00EAtre strictement positif -javax.validation.constraints.PositiveOrZero.message = doit \u00EAtre positif ou \u00E9gal à 0 -javax.validation.constraints.Size.message = la taille doit \u00EAtre comprise entre {min} et {max} +javax.validation.constraints.Past.message = doit \u00eatre une date dans le pass\u00e9 +javax.validation.constraints.PastOrPresent.message = doit \u00eatre une date dans le pass\u00e9 ou le pr\u00e9sent +javax.validation.constraints.Pattern.message = doit correspondre \u00e0 "{regexp}" +javax.validation.constraints.Positive.message = doit \u00eatre sup\u00e9rieur \u00e0 +javax.validation.constraints.PositiveOrZero.message = doit \u00eatre sup\u00e9rieur ou \u00e9gal \u00e0 0 +javax.validation.constraints.Size.message = la taille doit \u00eatre comprise entre {min} et {max} -org.hibernate.validator.constraints.CreditCardNumber.message = num\u00E9ro de carte de cr\u00E9dit invalide -org.hibernate.validator.constraints.Currency.message = devise invalide (doit faire partie de {value}) -org.hibernate.validator.constraints.EAN.message = code barre {type} invalide -org.hibernate.validator.constraints.Email.message = adresse email mal form\u00E9e -org.hibernate.validator.constraints.ISBN.message = ISBN invalide -org.hibernate.validator.constraints.Length.message = la longueur doit \u00EAtre comprise entre {min} et {max} caractères -org.hibernate.validator.constraints.CodePointLength.message = la longueur doit \u00EAtre comprise entre {min} et {max} caractères -org.hibernate.validator.constraints.LuhnCheck.message = le chiffre de contr\u00F4le pour ${validatedValue} est invalide, le contr\u00F4le Luhn Modulo 10 a \u00E9chou\u00E9 -org.hibernate.validator.constraints.Mod10Check.message = le chiffre de contr\u00F4le pour ${validatedValue} est invalide, le contr\u00F4le Modulo 10 a \u00E9chou\u00E9 -org.hibernate.validator.constraints.Mod11Check.message = le chiffre de contr\u00F4le pour ${validatedValue} est invalide, le contr\u00F4le Modulo 11 a \u00E9chou\u00E9 -org.hibernate.validator.constraints.ModCheck.message = le chiffre de contr\u00F4le pour ${validatedValue} est invalide, le contr\u00F4le ${modType} a \u00E9chou\u00E9 -org.hibernate.validator.constraints.NotBlank.message = ne peut pas \u00EAtre vide -org.hibernate.validator.constraints.NotEmpty.message = ne peut pas \u00EAtre vide +org.hibernate.validator.constraints.CreditCardNumber.message = num\u00e9ro de carte de cr\u00e9dit non valide +org.hibernate.validator.constraints.Currency.message = devise non valide (il doit s'agir de l'une des valeurs suivantes : {value}) +org.hibernate.validator.constraints.EAN.message = code-barres {type} non valide +org.hibernate.validator.constraints.Email.message = doit \u00eatre une adresse \u00e9lectronique syntaxiquement correcte +org.hibernate.validator.constraints.ISBN.message = ISBN non valide +org.hibernate.validator.constraints.Length.message = la longueur doit \u00eatre comprise entre {min} et {max} +org.hibernate.validator.constraints.CodePointLength.message = la longueur doit \u00eatre comprise entre {min} et {max} +org.hibernate.validator.constraints.LuhnCheck.message = la cl\u00e9 de contr\u00f4le pour ${validatedValue} n'est pas valide ; la somme de contr\u00f4le de l'algorithme de Luhn (modulo 10) a \u00e9chou\u00e9 +org.hibernate.validator.constraints.Mod10Check.message = la cl\u00e9 de contr\u00f4le pour ${validatedValue} n'est pas valide ; la somme de contr\u00f4le de l'algorithme modulo 10 a \u00e9chou\u00e9 +org.hibernate.validator.constraints.Mod11Check.message = la cl\u00e9 de contr\u00f4le pour ${validatedValue} n'est pas valide ; la somme de contr\u00f4le de l'algorithme modulo 11 a \u00e9chou\u00e9 +org.hibernate.validator.constraints.ModCheck.message = la cl\u00e9 de contr\u00f4le pour ${validatedValue} n'est pas valide ; la somme de contr\u00f4le de l'algorithme ${modType} a \u00e9chou\u00e9 +org.hibernate.validator.constraints.NotBlank.message = ne doit pas \u00eatre vide +org.hibernate.validator.constraints.NotEmpty.message = ne doit pas \u00eatre vide org.hibernate.validator.constraints.ParametersScriptAssert.message = le script "{script}" n'a pas \u00E9t\u00E9 \u00E9valu\u00E9 \u00E0 vrai -org.hibernate.validator.constraints.Range.message = doit \u00EAtre entre {min} et {max} -org.hibernate.validator.constraints.SafeHtml.message = peut contenir du HTML dangereux -org.hibernate.validator.constraints.ScriptAssert.message = le script "{script}" n'a pas \u00E9t\u00E9 \u00E9valu\u00E9 \u00E0 vrai -org.hibernate.validator.constraints.UniqueElements.message = ne doit contenir que des \u00E9l\u00E9ments uniques -org.hibernate.validator.constraints.URL.message = URL mal form\u00E9e +org.hibernate.validator.constraints.Range.message = doit \u00eatre compris entre {min} et {max} +org.hibernate.validator.constraints.SafeHtml.message = peut comporter un contenu HTML non s\u00e9curis\u00e9 +org.hibernate.validator.constraints.ScriptAssert.message = le script "{script}" n'a pas \u00E9t\u00E9 \u00E9valu\u00E9 \u00E0 vrai +org.hibernate.validator.constraints.UniqueElements.message = ne doit contenir que des \u00e9l\u00e9ments uniques +org.hibernate.validator.constraints.URL.message = doit \u00eatre une URL valide -org.hibernate.validator.constraints.br.CNPJ.message = numéro d'enregistrement brésilien de société contribuable (CNPJ) invalide -org.hibernate.validator.constraints.br.CPF.message = numéro d'enregistrement brésilien de contribuable individuel (CPF) invalide -org.hibernate.validator.constraints.br.TituloEleitoral.message = numéro de carte d'électeur brésilienne invalide +org.hibernate.validator.constraints.br.CNPJ.message = num\u00e9ro de registre d'entreprise contribuable br\u00e9silien non valide (CNPJ) +org.hibernate.validator.constraints.br.CPF.message = num\u00e9ro de registre de contribuable individuel br\u00e9silien non valide (CPF) +org.hibernate.validator.constraints.br.TituloEleitoral.message = num\u00e9ro de carte d'\u00e9lecteur br\u00e9silien non valide -org.hibernate.validator.constraints.pl.REGON.message = numéro d'imposition polonais (REGON) invalide -org.hibernate.validator.constraints.pl.NIP.message = numéro d'identification de TVA polonais (NIP) invalide -org.hibernate.validator.constraints.pl.PESEL.message = numéro d'identification national polonais (PESEL) invalide +org.hibernate.validator.constraints.pl.REGON.message = num\u00e9ro d'identification de contribuable polonais non valide (REGON) +org.hibernate.validator.constraints.pl.NIP.message = num\u00e9ro de TVA non valide (NIP) +org.hibernate.validator.constraints.pl.PESEL.message = num\u00e9ro de carte d'identit\u00e9 nationale polonais non valide (PESEL) -org.hibernate.validator.constraints.time.DurationMax.message = doit \u00EAtre plus court que${inclusive == true ? ' ou \u00E9gal \u00E0' : ''}${days == 0 ? '' : days == 1 ? ' 1 jour' : ' ' += days += ' jours'}${hours == 0 ? '' : hours == 1 ? ' 1 heure' : ' ' += hours += ' heures'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 seconde' : ' ' += seconds += ' secondes'}${millis == 0 ? '' : millis == 1 ? ' 1 milliseconde' : ' ' += millis += ' millisecondes'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nanoseconde' : ' ' += nanos += ' nanosecondes'} -org.hibernate.validator.constraints.time.DurationMin.message = doit \u00EAtre plus long que${inclusive == true ? ' ou \u00E9gal \u00E0' : ''}${days == 0 ? '' : days == 1 ? ' 1 jour' : ' ' += days += ' jours'}${hours == 0 ? '' : hours == 1 ? ' 1 heure' : ' ' += hours += ' heures'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 seconde' : ' ' += seconds += ' secondes'}${millis == 0 ? '' : millis == 1 ? ' 1 milliseconde' : ' ' += millis += ' millisecondes'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nanoseconde' : ' ' += nanos += ' nanosecondes'} +org.hibernate.validator.constraints.time.DurationMax.message = doit \u00eatre plus court que ${inclusive == true ? ' ou \u00e9gal \u00e0 ' : ''}${days == 0 ? '' : days == 1 ? ' 1 jour' : ' ' += days += ' jours'}${hours == 0 ? '' : hours == 1 ? ' 1 heure' : ' ' += hours += ' heures'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 seconde' : ' ' += seconds += ' secondes'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} +org.hibernate.validator.constraints.time.DurationMin.message = doit \u00eatre plus long que ${inclusive == true ? ' ou \u00e9gal \u00e0 ' : ''}${days == 0 ? '' : days == 1 ? ' 1 jour' : ' ' += days += ' jours'}${hours == 0 ? '' : hours == 1 ? ' 1 heure' : ' ' += hours += ' heures'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 seconde' : ' ' += seconds += ' secondes'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_hu.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_hu.properties index 022339af3b..79ab3d3170 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_hu.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_hu.properties @@ -1,27 +1,53 @@ -javax.validation.constraints.AssertFalse.message = hamis kell legyen -javax.validation.constraints.AssertTrue.message = igaz kell legyen -javax.validation.constraints.DecimalMax.message = kisebb vagy egyenl\u0151 kell legyen mint {value} -javax.validation.constraints.DecimalMin.message = nagyobb vagy egyenl\u0151 kell legyen mint {value} -javax.validation.constraints.Digits.message = a sz\u00E1mform\u00E1tum nem felel meg a k\u00F6vetkez\u0151 form\u00E1nak\: <{integer} sz\u00E1mjegy>.<{fraction} tizedesjegy> -javax.validation.constraints.Email.message = nem felel meg az email c\u00EDmek form\u00E1tum\u00E1nak -javax.validation.constraints.Future.message = a j\u00F6v\u0151ben kell lennie -javax.validation.constraints.Max.message = kisebb vagy egyenl\u0151 kell legyen mint {value} -javax.validation.constraints.Min.message = nagyobb vagy egyenl\u0151 kell legyen mint {value} -javax.validation.constraints.NotBlank.message = nem lehet \u00FCres -javax.validation.constraints.NotEmpty.message = nem lehet \u00FCres -javax.validation.constraints.NotNull.message = nem lehet null-\u00E9rt\u00E9k -javax.validation.constraints.Null.message = null-\u00E9rt\u00E9knek kell lennie -javax.validation.constraints.Past.message = a m\u00FAltban kell lennie -javax.validation.constraints.Pattern.message = meg kell felelnie a "{regexp}" regul\u00E1ris kifejez\u00E9snek -javax.validation.constraints.Size.message = a m\u00E9retnek {min} \u00E9s {max} k\u00F6z\u00F6tt kell lennie +javax.validation.constraints.AssertFalse.message = hamis \u00e9rt\u00e9k\u0171nek kell lennie +javax.validation.constraints.AssertTrue.message = igaz \u00e9rt\u00e9k\u0171nek kell lennie +javax.validation.constraints.DecimalMax.message = kisebbnek ${inclusive == true ? 'or equal to ' : ''} kell lennie, mint {value} +javax.validation.constraints.DecimalMin.message = nagyobbnak ${inclusive == true ? 'or equal to ' : ''} kell lennie, mint {value} +javax.validation.constraints.Digits.message = a numerikus \u00e9rt\u00e9k a korl\u00e1tokon k\u00edv\u00fcl esik (<{integer} sz\u00e1mjegy>.<{fraction} sz\u00e1mjegy> sz\u00e1mot v\u00e1rt a rendszer) +javax.validation.constraints.Email.message = helyes form\u00e1tum\u00fa e-mail c\u00edmnek kell lennie +javax.validation.constraints.Future.message = j\u00f6v\u0151beli d\u00e1tumnak kell lennie +javax.validation.constraints.FutureOrPresent.message = jelen vagy j\u00f6v\u0151beli d\u00e1tumnak kell lennie +javax.validation.constraints.Max.message = kisebbnek, vagy egyenl\u0151nek kell lennie, mint {value} +javax.validation.constraints.Min.message = nagyobbnak, vagy egyenl\u0151nek kell lennie, mint {value} +javax.validation.constraints.Negative.message = kisebbnek kell lennie, mint 0 +javax.validation.constraints.NegativeOrZero.message = kisebbnek, vagy egyenl\u0151nek kell lennie, mint 0 +javax.validation.constraints.NotBlank.message = nem lehet \u00fcres +javax.validation.constraints.NotEmpty.message = nem lehet \u00fcres +javax.validation.constraints.NotNull.message = nem lehet null +javax.validation.constraints.Null.message = null\u00e9rt\u00e9k\u0171nek kell lennie +javax.validation.constraints.Past.message = m\u00faltbeli d\u00e1tumnak kell lennie +javax.validation.constraints.PastOrPresent.message = m\u00faltbeli vagy jelen d\u00e1tumnak kell lennie +javax.validation.constraints.Pattern.message = meg kell felelnie a(z) "{regexp}" kifejez\u00e9snek +javax.validation.constraints.Positive.message = nagyobbnak kell lennie, mint 0 +javax.validation.constraints.PositiveOrZero.message = nagyobbnak vagy egyenl\u0151nek kell lennie, mint 0 +javax.validation.constraints.Size.message = a m\u00e9retnek a(z) {min} \u00e9s {max} \u00e9rt\u00e9kek k\u00f6z\u00f6tt kell lennie -org.hibernate.validator.constraints.CreditCardNumber.message = hib\u00E1s hitelk\u00E1rtyasz\u00E1m -org.hibernate.validator.constraints.Email.message = nem felel meg az email c\u00EDmek form\u00E1tum\u00E1nak -org.hibernate.validator.constraints.Length.message = a hossznak {min} \u00E9s {max} k\u00F6z\u00F6tt kell lennie -org.hibernate.validator.constraints.CodePointLength.message = a hossznak {min} \u00E9s {max} k\u00F6z\u00F6tt kell lennie -org.hibernate.validator.constraints.NotBlank.message = nem lehet \u00FCres -org.hibernate.validator.constraints.NotEmpty.message = nem lehet \u00FCres -org.hibernate.validator.constraints.Range.message = az \u00E9rt\u00E9knek {min} \u00E9s {max} k\u00F6z\u00F6tt kell lennie -org.hibernate.validator.constraints.SafeHtml.message = Lehet, hogy nem biztons\u00E1gos html tartalom -org.hibernate.validator.constraints.ScriptAssert.message = script kifejez\u00E9s "{script}" nem \u00E9rt\u00E9kel\u0151d\u00F6tt ki igazz\u00E1 -org.hibernate.validator.constraints.URL.message = \u00E9rv\u00E9nyes URL-nek kell lennie +org.hibernate.validator.constraints.CreditCardNumber.message = \u00e9rv\u00e9nytelen hitelk\u00e1rtyasz\u00e1m +org.hibernate.validator.constraints.Currency.message = \u00e9rv\u00e9nytelen p\u00e9nznem (a k\u00f6vetkez\u0151k egyike lehet: {value}) +org.hibernate.validator.constraints.EAN.message = \u00e9rv\u00e9nytelen {type} vonalk\u00f3d +org.hibernate.validator.constraints.Email.message = helyes form\u00e1tum\u00fa e-mail c\u00edmnek kell lennie +org.hibernate.validator.constraints.ISBN.message = \u00e9rv\u00e9nytelen ISBN +org.hibernate.validator.constraints.Length.message = a hossznak a(z) {min} \u00e9s {max} \u00e9rt\u00e9kek k\u00f6z\u00f6tt kell lennie +org.hibernate.validator.constraints.CodePointLength.message = a hossznak a(z) {min} \u00e9s {max} \u00e9rt\u00e9kek k\u00f6z\u00f6tt kell lennie +org.hibernate.validator.constraints.LuhnCheck.message = a(z) ${validatedValue} ellen\u0151rz\u0151 sz\u00e1mjegye \u00e9rv\u00e9nytelen, a Luhn Modulo 10 ellen\u0151rz\u0151\u00f6sszeg meghi\u00fasult +org.hibernate.validator.constraints.Mod10Check.message = a(z) ${validatedValue} ellen\u0151rz\u0151 sz\u00e1mjegye \u00e9rv\u00e9nytelen, a Modulo 10 ellen\u0151rz\u0151\u00f6sszeg meghi\u00fasult +org.hibernate.validator.constraints.Mod11Check.message = a(z) ${validatedValue} ellen\u0151rz\u0151 sz\u00e1mjegye \u00e9rv\u00e9nytelen, a Modulo 11 ellen\u0151rz\u0151\u00f6sszeg meghi\u00fasult +org.hibernate.validator.constraints.ModCheck.message = a(z) ${validatedValue} ellen\u0151rz\u0151 sz\u00e1mjegye \u00e9rv\u00e9nytelen, a(z) ${modType} ellen\u0151rz\u0151\u00f6sszeg meghi\u00fasult +org.hibernate.validator.constraints.NotBlank.message = nem lehet \u00fcres +org.hibernate.validator.constraints.NotEmpty.message = nem lehet \u00fcres +org.hibernate.validator.constraints.ParametersScriptAssert.message = a(z) "{script}" parancsf\u00e1jl kifejez\u00e9s nem true \u00e9rt\u00e9kre \u00e9rt\u00e9kel\u0151d\u00f6tt ki +org.hibernate.validator.constraints.Range.message = a(z) {min} \u00e9s {max} \u00e9rt\u00e9kek k\u00f6z\u00f6tt kell lennie +org.hibernate.validator.constraints.SafeHtml.message = lehets\u00e9ges nem biztons\u00e1gos html tartalom +org.hibernate.validator.constraints.ScriptAssert.message = a(z) "{script}" parancsf\u00e1jl kifejez\u00e9s nem true \u00e9rt\u00e9kre \u00e9rt\u00e9kel\u0151d\u00f6tt ki +org.hibernate.validator.constraints.UniqueElements.message = csak egyedi elemeket tartalmazhat +org.hibernate.validator.constraints.URL.message = egy \u00e9rv\u00e9nyes URL c\u00edmnek kell lennie + +org.hibernate.validator.constraints.br.CNPJ.message = \u00e9rv\u00e9nytelen brazil v\u00e1llalati ad\u00f3nyilv\u00e1ntart\u00e1si sz\u00e1m (CNPJ) +org.hibernate.validator.constraints.br.CPF.message = \u00e9rv\u00e9nytelen brazil egy\u00e9ni ad\u00f3nyilv\u00e1ntart\u00e1si sz\u00e1m (CPF) +org.hibernate.validator.constraints.br.TituloEleitoral.message = \u00e9rv\u00e9nytelen brazil szavaz\u00f3azonos\u00edt\u00f3 k\u00e1rtyasz\u00e1m + +org.hibernate.validator.constraints.pl.REGON.message = \u00e9rv\u00e9nytelen lengyel ad\u00f3z\u00f3i azonos\u00edt\u00f3 sz\u00e1m (REGON) +org.hibernate.validator.constraints.pl.NIP.message = \u00e9rv\u00e9nytelen \u00c1FA azonos\u00edt\u00f3sz\u00e1m (NIP) +org.hibernate.validator.constraints.pl.PESEL.message = \u00e9rv\u00e9nytelen lengyel nemzeti azonos\u00edt\u00f3sz\u00e1m (PESEL) + +org.hibernate.validator.constraints.time.DurationMax.message = r\u00f6videbbnek kell lennie, mint ${inclusive == true ? ' or equal to' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} +org.hibernate.validator.constraints.time.DurationMin.message = hosszabbnak kell lennie, mint ${inclusive == true ? ' or equal to' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_it.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_it.properties new file mode 100644 index 0000000000..368780928e --- /dev/null +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_it.properties @@ -0,0 +1,53 @@ +javax.validation.constraints.AssertFalse.message = deve essere false +javax.validation.constraints.AssertTrue.message = deve essere true +javax.validation.constraints.DecimalMax.message = deve essere inferiore a ${inclusive == true ? 'o uguale a ' : ''}{value} +javax.validation.constraints.DecimalMin.message = deve essere superiore a ${inclusive == true ? 'o uguale a ' : ''}{value} +javax.validation.constraints.Digits.message = valore numerico fuori dai limiti (previsto <{integer} digits>.<{fraction} digits>) +javax.validation.constraints.Email.message = deve essere un indirizzo email nel formato corretto +javax.validation.constraints.Future.message = deve essere una data nel futuro +javax.validation.constraints.FutureOrPresent.message = deve essere una data nel presente o nel futuro +javax.validation.constraints.Max.message = deve essere inferiore o uguale a {value} +javax.validation.constraints.Min.message = deve essere superiore o uguale a {value} +javax.validation.constraints.Negative.message = deve essere inferiore a 0 +javax.validation.constraints.NegativeOrZero.message = deve essere inferiore o uguale a 0 +javax.validation.constraints.NotBlank.message = non deve essere spazio +javax.validation.constraints.NotEmpty.message = non deve essere vuoto +javax.validation.constraints.NotNull.message = non deve essere null +javax.validation.constraints.Null.message = deve essere null +javax.validation.constraints.Past.message = deve essere una data nel passato +javax.validation.constraints.PastOrPresent.message = deve essere una data nel passato o nel presente +javax.validation.constraints.Pattern.message = deve corrispondere a "{regexp}" +javax.validation.constraints.Positive.message = deve essere superiore a 0 +javax.validation.constraints.PositiveOrZero.message = deve essere superiore o uguale a 0 +javax.validation.constraints.Size.message = la dimensione deve essere compresa tra {min} e {max} + +org.hibernate.validator.constraints.CreditCardNumber.message = numero carta di credito non valido +org.hibernate.validator.constraints.Currency.message = valuta non valida (deve essere una di {value}) +org.hibernate.validator.constraints.EAN.message = codicie a barre {type} non valido +org.hibernate.validator.constraints.Email.message = non \u00e8 un indirizzo email nel formato corretto +org.hibernate.validator.constraints.ISBN.message = ISBN non valido +org.hibernate.validator.constraints.Length.message = la lunghezza deve essere compresa tra {min} e {max} +org.hibernate.validator.constraints.CodePointLength.message = la lunghezza deve essere compresa tra {min} e {max} +org.hibernate.validator.constraints.LuhnCheck.message = la cifra di controllo per ${validatedValue} non \u00e8 valida, checksum Luhn Modulo 10 non riuscito +org.hibernate.validator.constraints.Mod10Check.message = la cifra di controllo per ${validatedValue} non \u00e8 valida, checksum Modulo 10 non riuscito +org.hibernate.validator.constraints.Mod11Check.message = la cifra di controllo per ${validatedValue} non \u00e8 valida, checksum Modulo 11 non riuscito +org.hibernate.validator.constraints.ModCheck.message = la cifra di controllo per ${validatedValue} non \u00e8 valida, checksum ${modType} non riuscito +org.hibernate.validator.constraints.NotBlank.message = non deve essere spazio +org.hibernate.validator.constraints.NotEmpty.message = non deve essere vuoto +org.hibernate.validator.constraints.ParametersScriptAssert.message = espressione script "{script}" non valutata true +org.hibernate.validator.constraints.Range.message = deve essere compresa tra {min} e {max} +org.hibernate.validator.constraints.SafeHtml.message = ha contenuto html non sicuro +org.hibernate.validator.constraints.ScriptAssert.message = espressione script "{script}" non valutata true +org.hibernate.validator.constraints.UniqueElements.message = deve contenere solo elementi univoci +org.hibernate.validator.constraints.URL.message = deve essere un URL valido + +org.hibernate.validator.constraints.br.CNPJ.message = numero registro contribuente aziendale (CNPJ) brasiliano non valido +org.hibernate.validator.constraints.br.CPF.message = numero registro contribuente individuale (CPF) brasiliano non valido +org.hibernate.validator.constraints.br.TituloEleitoral.message = numero scheda ID votante brasiliano non valido + +org.hibernate.validator.constraints.pl.REGON.message = numero identificativo contribuente polacco (REGON) non valido +org.hibernate.validator.constraints.pl.NIP.message = numero identificativo IVA (NIP) non valido +org.hibernate.validator.constraints.pl.PESEL.message = numero identificativo nazionale polacco (PESEL) non valido + +org.hibernate.validator.constraints.time.DurationMax.message = deve essere inferiore a ${inclusive == true ? ' o uguale a' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} +org.hibernate.validator.constraints.time.DurationMin.message = deve essere superiore a ${inclusive == true ? ' o uguale a' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_ja.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_ja.properties new file mode 100644 index 0000000000..71994d2ae4 --- /dev/null +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_ja.properties @@ -0,0 +1,53 @@ +javax.validation.constraints.AssertFalse.message = false \u306b\u3057\u3066\u304f\u3060\u3055\u3044 +javax.validation.constraints.AssertTrue.message = true \u306b\u3057\u3066\u304f\u3060\u3055\u3044 +javax.validation.constraints.DecimalMax.message = {value} ${inclusive == true ? '\u4ee5\u4e0b\u306e\u5024\u306b\u3057\u3066\u304f\u3060\u3055\u3044' : '\u3088\u308a\u5c0f\u3055\u306a\u5024\u306b\u3057\u3066\u304f\u3060\u3055\u3044'} +javax.validation.constraints.DecimalMin.message = {value} ${inclusive == true ? '\u4ee5\u4e0a\u306e\u5024\u306b\u3057\u3066\u304f\u3060\u3055\u3044' : '\u3088\u308a\u5927\u304d\u306a\u5024\u306b\u3057\u3066\u304f\u3060\u3055\u3044'} +javax.validation.constraints.Digits.message = \u5024\u306f\u6b21\u306e\u7bc4\u56f2\u306b\u3057\u3066\u304f\u3060\u3055\u3044 (<\u6574\u6570 {integer} \u6841>.<\u5c0f\u6570\u70b9\u4ee5\u4e0b {fraction} \u6841>) +javax.validation.constraints.Email.message = \u96fb\u5b50\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u3068\u3057\u3066\u6b63\u3057\u3044\u5f62\u5f0f\u306b\u3057\u3066\u304f\u3060\u3055\u3044 +javax.validation.constraints.Future.message = \u672a\u6765\u306e\u65e5\u4ed8\u306b\u3057\u3066\u304f\u3060\u3055\u3044 +javax.validation.constraints.FutureOrPresent.message = \u73fe\u5728\u3082\u3057\u304f\u306f\u672a\u6765\u306e\u65e5\u4ed8\u306b\u3057\u3066\u304f\u3060\u3055\u3044 +javax.validation.constraints.Max.message = {value} \u4ee5\u4e0b\u306e\u5024\u306b\u3057\u3066\u304f\u3060\u3055\u3044 +javax.validation.constraints.Min.message = {value} \u4ee5\u4e0a\u306e\u5024\u306b\u3057\u3066\u304f\u3060\u3055\u3044 +javax.validation.constraints.Negative.message = 0 \u3088\u308a\u5c0f\u3055\u306a\u5024\u306b\u3057\u3066\u304f\u3060\u3055\u3044 +javax.validation.constraints.NegativeOrZero.message = 0 \u4ee5\u4e0b\u306e\u5024\u306b\u3057\u3066\u304f\u3060\u3055\u3044 +javax.validation.constraints.NotBlank.message = \u7a7a\u767d\u306f\u8a31\u53ef\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +javax.validation.constraints.NotEmpty.message = \u7a7a\u8981\u7d20\u306f\u8a31\u53ef\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +javax.validation.constraints.NotNull.message = null \u306f\u8a31\u53ef\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +javax.validation.constraints.Null.message = null \u306b\u3057\u3066\u304f\u3060\u3055\u3044 +javax.validation.constraints.Past.message = \u904e\u53bb\u306e\u65e5\u4ed8\u306b\u3057\u3066\u304f\u3060\u3055\u3044 +javax.validation.constraints.PastOrPresent.message = \u73fe\u5728\u3082\u3057\u304f\u306f\u904e\u53bb\u306e\u65e5\u4ed8\u306b\u3057\u3066\u304f\u3060\u3055\u3044 +javax.validation.constraints.Pattern.message = \u6b63\u898f\u8868\u73fe "{regexp}" \u306b\u30de\u30c3\u30c1\u3055\u305b\u3066\u304f\u3060\u3055\u3044 +javax.validation.constraints.Positive.message = 0 \u3088\u308a\u5927\u304d\u306a\u5024\u306b\u3057\u3066\u304f\u3060\u3055\u3044 +javax.validation.constraints.PositiveOrZero.message = 0 \u4ee5\u4e0a\u306e\u5024\u306b\u3057\u3066\u304f\u3060\u3055\u3044 +javax.validation.constraints.Size.message = {min} \u304b\u3089 {max} \u306e\u9593\u306e\u30b5\u30a4\u30ba\u306b\u3057\u3066\u304f\u3060\u3055\u3044 + +org.hibernate.validator.constraints.CreditCardNumber.message = \u6b63\u3057\u304f\u306a\u3044\u30af\u30ec\u30b8\u30c3\u30c8\u30ab\u30fc\u30c9\u306e\u756a\u53f7\u3067\u3059 +org.hibernate.validator.constraints.Currency.message = \u6b63\u3057\u304f\u306a\u3044\u901a\u8ca8\u5358\u4f4d\u3067\u3059 ({value} \u306e\u4e2d\u304b\u3089\u9078\u3093\u3067\u304f\u3060\u3055\u3044) +org.hibernate.validator.constraints.EAN.message = \u6b63\u3057\u304f\u306a\u3044 {type} \u30d0\u30fc\u30b3\u30fc\u30c9\u3067\u3059 +org.hibernate.validator.constraints.Email.message = \u96fb\u5b50\u30e1\u30fc\u30eb\u3068\u3057\u3066\u6b63\u3057\u3044\u5f62\u5f0f\u306b\u3057\u3066\u304f\u3060\u3055\u3044 +org.hibernate.validator.constraints.ISBN.message = \u6b63\u3057\u304f\u306a\u3044 ISBN \u3067\u3059 +org.hibernate.validator.constraints.Length.message = {min} \u304b\u3089 {max} \u306e\u9593\u306e\u9577\u3055\u306b\u3057\u3066\u304f\u3060\u3055\u3044 +org.hibernate.validator.constraints.CodePointLength.message = {min} \u304b\u3089 {max} \u306e\u9593\u306e\u9577\u3055\u306b\u3057\u3066\u304f\u3060\u3055\u3044 +org.hibernate.validator.constraints.LuhnCheck.message = ${validatedValue} \u306e\u30c1\u30a7\u30c3\u30af\u30c7\u30b8\u30c3\u30c8\u304c\u6b63\u3057\u304f\u3042\u308a\u307e\u305b\u3093 (Luhn \u30e2\u30c7\u30e5\u30e9\u30b910\u30a2\u30eb\u30b4\u30ea\u30ba\u30e0) +org.hibernate.validator.constraints.Mod10Check.message = ${validatedValue} \u306e\u30c1\u30a7\u30c3\u30af\u30c7\u30b8\u30c3\u30c8\u304c\u6b63\u3057\u304f\u3042\u308a\u307e\u305b\u3093 (\u30e2\u30c7\u30e5\u30e9\u30b910) +org.hibernate.validator.constraints.Mod11Check.message = ${validatedValue} \u306e\u30c1\u30a7\u30c3\u30af\u30c7\u30b8\u30c3\u30c8\u304c\u6b63\u3057\u304f\u3042\u308a\u307e\u305b\u3093 (\u30e2\u30c7\u30e5\u30e9\u30b911) +org.hibernate.validator.constraints.ModCheck.message = ${validatedValue} \u306e\u30c1\u30a7\u30c3\u30af\u30c7\u30b8\u30c3\u30c8\u304c\u6b63\u3057\u304f\u3042\u308a\u307e\u305b\u3093 (${modType} \u306e\u30c1\u30a7\u30c3\u30af\u30b5\u30e0\u304c\u5931\u6557\u3057\u307e\u3057\u305f) +org.hibernate.validator.constraints.NotBlank.message = \u7a7a\u767d\u306f\u8a31\u53ef\u3055\u308c\u3066\u307e\u305b\u3093 +org.hibernate.validator.constraints.NotEmpty.message = \u7a7a\u8981\u7d20\u306f\u8a31\u53ef\u3055\u308c\u3066\u3044\u307e\u305b\u3093 +org.hibernate.validator.constraints.ParametersScriptAssert.message = \u6b21\u306e\u30b9\u30af\u30ea\u30d7\u30c8\u5f0f "{script}" \u306b\u3088\u308b\u8a55\u4fa1\u7d50\u679c\u304c true \u306b\u306a\u308a\u307e\u305b\u3093\u3067\u3057\u305f +org.hibernate.validator.constraints.Range.message = {min} \u304b\u3089 {max} \u306e\u9593\u306b\u3057\u3066\u304f\u3060\u3055\u3044 +org.hibernate.validator.constraints.SafeHtml.message = \u5b89\u5168\u3067\u306f\u306a\u3044 HTML \u30b3\u30f3\u30c6\u30f3\u30c4\u3067\u3059 +org.hibernate.validator.constraints.ScriptAssert.message = \u6b21\u306e\u30b9\u30af\u30ea\u30d7\u30c8\u5f0f "{script}" \u306b\u3088\u308b\u8a55\u4fa1\u7d50\u679c\u304c true \u306b\u306a\u308a\u307e\u305b\u3093\u3067\u3057\u305f +org.hibernate.validator.constraints.UniqueElements.message = \u8981\u7d20\u306f\u5168\u3066\u30e6\u30cb\u30fc\u30af\u306b\u3057\u3066\u304f\u3060\u3055\u3044 +org.hibernate.validator.constraints.URL.message = URL \u3068\u3057\u3066\u6b63\u3057\u3044\u5f62\u5f0f\u306b\u3057\u3066\u304f\u3060\u3055\u3044 + +org.hibernate.validator.constraints.br.CNPJ.message = \u6b63\u3057\u304f\u306a\u3044\u30d6\u30e9\u30b8\u30eb\u6cd5\u4eba\u7528\u7a0e\u52d9\u767b\u8a18\u756a\u53f7 (CNPJ) \u3067\u3059 +org.hibernate.validator.constraints.br.CPF.message = \u6b63\u3057\u304f\u306a\u3044\u30d6\u30e9\u30b8\u30eb\u500b\u4eba\u7528\u7d0d\u7a0e\u8005\u756a\u53f7 (CPF) \u3067\u3059 +org.hibernate.validator.constraints.br.TituloEleitoral.message = \u6b63\u3057\u304f\u306a\u3044\u30d6\u30e9\u30b8\u30eb\u6295\u7968\u8005ID\u30ab\u30fc\u30c9\u756a\u53f7\u3067\u3059 + +org.hibernate.validator.constraints.pl.REGON.message = \u6b63\u3057\u304f\u306a\u3044\u30dd\u30fc\u30e9\u30f3\u30c9\u56fd\u5185\u7d4c\u6e08\u767b\u9332\u756a\u53f7 (REGON) \u3067\u3059 +org.hibernate.validator.constraints.pl.NIP.message = \u6b63\u3057\u304f\u306a\u3044\u30dd\u30fc\u30e9\u30f3\u30c9\u7d0d\u7a0e\u8005\u756a\u53f7 (NIP) \u3067\u3059 +org.hibernate.validator.constraints.pl.PESEL.message = \u6b63\u3057\u304f\u306a\u3044\u30dd\u30fc\u30e9\u30f3\u30c9\u500b\u4ebaID\u756a\u53f7 (PESEL) \u3067\u3059 + +org.hibernate.validator.constraints.time.DurationMax.message = ${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} ${inclusive == true ? '\u3068\u540c\u3058\u304b\u3088\u308a\u77ed\u3044\u6642\u9593\u306b\u3057\u3066\u304f\u3060\u3055\u3044' : '\u3088\u308a\u77ed\u3044\u6642\u9593\u306b\u3057\u3066\u304f\u3060\u3055\u3044'} +org.hibernate.validator.constraints.time.DurationMin.message = ${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} ${inclusive == true ? '\u3068\u540c\u3058\u304b\u3088\u308a\u9577\u3044\u6642\u9593\u306b\u3057\u3066\u304f\u3060\u3055\u3044' : '\u3088\u308a\u9577\u3044\u6642\u9593\u306b\u3057\u3066\u304f\u3060\u3055\u3044'} diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_ko.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_ko.properties index aa839b8366..510d70b968 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_ko.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_ko.properties @@ -1,36 +1,53 @@ -javax.validation.constraints.AssertFalse.message = \ubc18\ub4dc\uc2dc \uac70\uc9d3(false)\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4. -javax.validation.constraints.AssertTrue.message = \ubc18\ub4dc\uc2dc \ucc38(true)\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4. -javax.validation.constraints.DecimalMax.message = \ubc18\ub4dc\uc2dc {value}\ubcf4\ub2e4 ${inclusive == true ? '\uac19\uac70\ub098 \uc791\uc5b4\uc57c \ud569\ub2c8\ub2e4. ' : '\uc791\uc544\uc57c \ud569\ub2c8\ub2e4.'} -javax.validation.constraints.DecimalMin.message = \ubc18\ub4dc\uc2dc {value}\ubcf4\ub2e4 ${inclusive == true ? '\uac19\uac70\ub098 \ucee4\uc57c \ud569\ub2c8\ub2e4. ' : '\ucee4\uc57c \ud569\ub2c8\ub2e4.'} -javax.validation.constraints.Digits.message = \uc22b\uc790 \uac12\uc774 \ud5c8\uc6a9 \ubc94\uc704\ub97c \ubc97\uc5b4\ub0a9\ub2c8\ub2e4. (\ud5c8\uc6a9 \ubc94\uc704: <{integer} \uc790\ub9ac>.<{fraction} \uc790\ub9ac>) -javax.validation.constraints.Email.message = \uc774\uba54\uc77c \uc8fc\uc18c\uac00 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. -javax.validation.constraints.Future.message = \ubc18\ub4dc\uc2dc \ubbf8\ub798 \ub0a0\uc9dc\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4. -javax.validation.constraints.Max.message = \ubc18\ub4dc\uc2dc {value}\ubcf4\ub2e4 \uac19\uac70\ub098 \uc791\uc544\uc57c \ud569\ub2c8\ub2e4. -javax.validation.constraints.Min.message = \ubc18\ub4dc\uc2dc {value}\ubcf4\ub2e4 \uac19\uac70\ub098 \ucee4\uc57c \ud569\ub2c8\ub2e4. -javax.validation.constraints.NotBlank.message = \ubc18\ub4dc\uc2dc \uac12\uc774 \uc874\uc7ac\ud558\uace0 \uacf5\ubc31 \ubb38\uc790\ub97c \uc81c\uc678\ud55c \uae38\uc774\uac00 0\ubcf4\ub2e4 \ucee4\uc57c \ud569\ub2c8\ub2e4. -javax.validation.constraints.NotEmpty.message = \ubc18\ub4dc\uc2dc \uac12\uc774 \uc874\uc7ac\ud558\uace0 \uae38\uc774 \ud639\uc740 \ud06c\uae30\uac00 0\ubcf4\ub2e4 \ucee4\uc57c \ud569\ub2c8\ub2e4. -javax.validation.constraints.NotNull.message = \ubc18\ub4dc\uc2dc \uac12\uc774 \uc788\uc5b4\uc57c \ud569\ub2c8\ub2e4. -javax.validation.constraints.Null.message = \ubc18\ub4dc\uc2dc \uac12\uc774 \uc5c6\uc5b4\uc57c \ud569\ub2c8\ub2e4. -javax.validation.constraints.Past.message = \ubc18\ub4dc\uc2dc \uacfc\uac70 \ub0a0\uc9dc\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4. -javax.validation.constraints.Pattern.message = \uc815\uaddc \ud45c\ud604\uc2dd "{regexp}" \ud328\ud134\uacfc \uc77c\uce58\ud574\uc57c \ud569\ub2c8\ub2e4. -javax.validation.constraints.Size.message = \ubc18\ub4dc\uc2dc \ucd5c\uc18c\uac12 {min}\uacfc(\uc640) \ucd5c\ub300\uac12 {max} \uc0ac\uc774\uc758 \ud06c\uae30\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4. +javax.validation.constraints.AssertFalse.message = false\uc5ec\uc57c \ud569\ub2c8\ub2e4 +javax.validation.constraints.AssertTrue.message = true\uc5ec\uc57c \ud569\ub2c8\ub2e4 +javax.validation.constraints.DecimalMax.message = \ub2e4\uc74c \uac12 \uc774\ud558\uc5ec\uc57c \ud569\ub2c8\ub2e4${inclusive == true ? ' ' : ''}{value} +javax.validation.constraints.DecimalMin.message = \ub2e4\uc74c \uac12 \uc774\uc0c1\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4${inclusive == true ? ' ' : ''}{value} +javax.validation.constraints.Digits.message = \uc22b\uc790 \uac12\uc774 \ud55c\uacc4\ub97c \ucd08\uacfc\ud569\ub2c8\ub2e4(<{integer} \uc790\ub9ac>.<{fraction} \uc790\ub9ac> \uc608\uc0c1) +javax.validation.constraints.Email.message = \uc62c\ubc14\ub978 \ud615\uc2dd\uc758 \uc774\uba54\uc77c \uc8fc\uc18c\uc5ec\uc57c \ud569\ub2c8\ub2e4 +javax.validation.constraints.Future.message = \ubbf8\ub798 \ub0a0\uc9dc\uc5ec\uc57c \ud569\ub2c8\ub2e4 +javax.validation.constraints.FutureOrPresent.message = \ud604\uc7ac \ub610\ub294 \ubbf8\ub798\uc758 \ub0a0\uc9dc\uc5ec\uc57c \ud569\ub2c8\ub2e4 +javax.validation.constraints.Max.message = {value} \uc774\ud558\uc5ec\uc57c \ud569\ub2c8\ub2e4 +javax.validation.constraints.Min.message = {value} \uc774\uc0c1\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4 +javax.validation.constraints.Negative.message = 0 \ubbf8\ub9cc\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4 +javax.validation.constraints.NegativeOrZero.message = 0 \uc774\ud558\uc5ec\uc57c \ud569\ub2c8\ub2e4 +javax.validation.constraints.NotBlank.message = \uacf5\ubc31\uc77c \uc218 \uc5c6\uc2b5\ub2c8\ub2e4 +javax.validation.constraints.NotEmpty.message = \ube44\uc5b4 \uc788\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4 +javax.validation.constraints.NotNull.message = \ub110\uc774\uc5b4\uc11c\ub294 \uc548\ub429\ub2c8\ub2e4 +javax.validation.constraints.Null.message = \ub110\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4 +javax.validation.constraints.Past.message = \uacfc\uac70 \ub0a0\uc9dc\uc5ec\uc57c \ud569\ub2c8\ub2e4 +javax.validation.constraints.PastOrPresent.message = \uacfc\uac70 \ub610\ub294 \ud604\uc7ac\uc758 \ub0a0\uc9dc\uc5ec\uc57c \ud569\ub2c8\ub2e4 +javax.validation.constraints.Pattern.message = "{regexp}"\uc640 \uc77c\uce58\ud574\uc57c \ud569\ub2c8\ub2e4 +javax.validation.constraints.Positive.message = 0\ubcf4\ub2e4 \ucee4\uc57c \ud569\ub2c8\ub2e4 +javax.validation.constraints.PositiveOrZero.message = 0 \uc774\uc0c1\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4 +javax.validation.constraints.Size.message = \ud06c\uae30\uac00 {min}\uc5d0\uc11c {max} \uc0ac\uc774\uc5ec\uc57c \ud569\ub2c8\ub2e4 -org.hibernate.validator.constraints.CreditCardNumber.message = \uc2e0\uc6a9\uce74\ub4dc \ubc88\ud638\uac00 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. -org.hibernate.validator.constraints.EAN.message = {type} \ubc14\ucf54\ub4dc\uac00 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. -org.hibernate.validator.constraints.Email.message = \uc774\uba54\uc77c \uc8fc\uc18c\uac00 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. -org.hibernate.validator.constraints.Length.message = \ubc18\ub4dc\uc2dc \ucd5c\uc18c\uac12 {min}\uacfc(\uc640) \ucd5c\ub300\uac12 {max} \uc0ac\uc774\uc758 \uae38\uc774\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4. -org.hibernate.validator.constraints.CodePointLength.message = \ubc18\ub4dc\uc2dc \ucd5c\uc18c\uac12 {min}\uacfc(\uc640) \ucd5c\ub300\uac12 {max} \uc0ac\uc774\uc758 \uae38\uc774\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4. -org.hibernate.validator.constraints.LuhnCheck.message = ${value}\uc758 check digit\uac00 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. Luhn Modulo 10 checksum \uc624\ub958\uac00 \ubc1c\uc0dd\ud558\uc600\uc2b5\ub2c8\ub2e4. -org.hibernate.validator.constraints.Mod10Check.message = ${value}\uc758 check digit\uac00 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. Modulo 10 checksum \uc624\ub958\uac00 \ubc1c\uc0dd\ud558\uc600\uc2b5\ub2c8\ub2e4. -org.hibernate.validator.constraints.Mod11Check.message = ${value}\uc758 check digit\uac00 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. Modulo 11 checksum \uc624\ub958\uac00 \ubc1c\uc0dd\ud558\uc600\uc2b5\ub2c8\ub2e4. -org.hibernate.validator.constraints.ModCheck.message = ${value}\uc758 check digit\uac00 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. ${modType} checksum \uc624\ub958\uac00 \ubc1c\uc0dd\ud558\uc600\uc2b5\ub2c8\ub2e4. -org.hibernate.validator.constraints.NotBlank.message = \ubc18\ub4dc\uc2dc \uac12\uc774 \uc874\uc7ac\ud558\uace0 \uacf5\ubc31 \ubb38\uc790\ub97c \uc81c\uc678\ud55c \uae38\uc774\uac00 0\ubcf4\ub2e4 \ucee4\uc57c \ud569\ub2c8\ub2e4. -org.hibernate.validator.constraints.NotEmpty.message = \ubc18\ub4dc\uc2dc \uac12\uc774 \uc874\uc7ac\ud558\uace0 \uae38\uc774 \ud639\uc740 \ud06c\uae30\uac00 0\ubcf4\ub2e4 \ucee4\uc57c \ud569\ub2c8\ub2e4. -org.hibernate.validator.constraints.ParametersScriptAssert.message = \uc2a4\ud06c\ub9bd\ud2b8 \ud45c\ud604\uc2dd "{script}"\uc758 \uacb0\uacfc\uac00 \ucc38(true)\uc774 \uc544\ub2d9\ub2c8\ub2e4. -org.hibernate.validator.constraints.Range.message = \ubc18\ub4dc\uc2dc \ucd5c\uc18c\uac12 {min}\uacfc(\uc640) \ucd5c\ub300\uac12 {max} \uc0ac\uc774\uc758 \ubc94\uc704\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4. -org.hibernate.validator.constraints.SafeHtml.message = \ud5c8\uc6a9\ub418\uc9c0 \uc54a\uc740 HTML \ucf54\ub4dc\uac00 \ud3ec\ud568\ub418\uc5b4 \uc788\uc2b5\ub2c8\ub2e4. -org.hibernate.validator.constraints.ScriptAssert.message = \uc2a4\ud06c\ub9bd\ud2b8 \ud45c\ud604\uc2dd "{script}"\uc758 \uacb0\uacfc\uac00 \ucc38(true)\uc774 \uc544\ub2d9\ub2c8\ub2e4. -org.hibernate.validator.constraints.URL.message = URL\uc774 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. -org.hibernate.validator.constraints.br.CNPJ.message = \ube0c\ub77c\uc9c8 \uae30\uc5c5 \ub0a9\uc138\uc790 \ub4f1\ub85d \ubc88\ud638\uac00 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. (CNPJ) -org.hibernate.validator.constraints.br.CPF.message = \ube0c\ub77c\uc9c8 \uac1c\uc778 \ub0a9\uc138\uc790 \ub4f1\ub85d \ubc88\ud638\uac00 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. (CPF) -org.hibernate.validator.constraints.br.TituloEleitoral.message = \ube0c\ub77c\uc9c8 \uc720\uad8c\uc790 ID \uce74\ub4dc \ubc88\ud638\uac00 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. +org.hibernate.validator.constraints.CreditCardNumber.message = \uc62c\ubc14\ub974\uc9c0 \uc54a\uc740 \uc2e0\uc6a9\uce74\ub4dc \ubc88\ud638\uc785\ub2c8\ub2e4 +org.hibernate.validator.constraints.Currency.message = \uc62c\ubc14\ub974\uc9c0 \uc54a\uc740 \ud1b5\ud654\uc785\ub2c8\ub2e4({value} \uc911 \ud558\ub098\uc5ec\uc57c \ud568) +org.hibernate.validator.constraints.EAN.message = \uc62c\ubc14\ub974\uc9c0 \uc54a\uc740 {type} \ubc14\ucf54\ub4dc\uc785\ub2c8\ub2e4 +org.hibernate.validator.constraints.Email.message = \uc62c\ubc14\ub978 \ud615\uc2dd\uc758 \uc774\uba54\uc77c \uc8fc\uc18c\uc5ec\uc57c \ud569\ub2c8\ub2e4 +org.hibernate.validator.constraints.ISBN.message = \uc62c\ubc14\ub974\uc9c0 \uc54a\uc740 ISBN\uc785\ub2c8\ub2e4 +org.hibernate.validator.constraints.Length.message = \uae38\uc774\uac00 {min}\uc5d0\uc11c {max} \uc0ac\uc774\uc5ec\uc57c \ud569\ub2c8\ub2e4 +org.hibernate.validator.constraints.CodePointLength.message = \uae38\uc774\uac00 {min}\uc5d0\uc11c {max} \uc0ac\uc774\uc5ec\uc57c \ud569\ub2c8\ub2e4 +org.hibernate.validator.constraints.LuhnCheck.message = ${validatedValue}\uc758 \uccb4\ud06c \ub514\uc9c0\ud2b8\uac00 \uc62c\ubc14\ub974\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. Luhn Modulo 10 \uccb4\ud06c\uc12c\uc774 \uc2e4\ud328\ud588\uc2b5\ub2c8\ub2e4 +org.hibernate.validator.constraints.Mod10Check.message = ${validatedValue}\uc758 \uccb4\ud06c \ub514\uc9c0\ud2b8\uac00 \uc62c\ubc14\ub974\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. Modulo 10 \uccb4\ud06c\uc12c\uc774 \uc2e4\ud328\ud588\uc2b5\ub2c8\ub2e4 +org.hibernate.validator.constraints.Mod11Check.message = ${validatedValue}\uc758 \uccb4\ud06c \ub514\uc9c0\ud2b8\uac00 \uc62c\ubc14\ub974\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. Modulo 11 \uccb4\ud06c\uc12c\uc774 \uc2e4\ud328\ud588\uc2b5\ub2c8\ub2e4 +org.hibernate.validator.constraints.ModCheck.message = ${validatedValue}\uc758 \uccb4\ud06c \ub514\uc9c0\ud2b8\uac00 \uc62c\ubc14\ub974\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. ${modType} \uccb4\ud06c\uc12c\uc774 \uc2e4\ud328\ud588\uc2b5\ub2c8\ub2e4 +org.hibernate.validator.constraints.NotBlank.message = \uacf5\ubc31\uc77c \uc218 \uc5c6\uc2b5\ub2c8\ub2e4 +org.hibernate.validator.constraints.NotEmpty.message = \ube44\uc5b4 \uc788\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4 +org.hibernate.validator.constraints.ParametersScriptAssert.message = \uc2a4\ud06c\ub9bd\ud2b8 \ud45c\ud604\uc2dd "{script}"\uac00 true\ub85c \ud3c9\uac00\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4 +org.hibernate.validator.constraints.Range.message = {min}\uc5d0\uc11c {max} \uc0ac\uc774\uc5ec\uc57c \ud569\ub2c8\ub2e4 +org.hibernate.validator.constraints.SafeHtml.message = \uc548\uc804\ud558\uc9c0 \uc54a\uc740 html \ucee8\ud150\uce20\uac00 \uc788\uc744 \uc218 \uc788\uc2b5\ub2c8\ub2e4 +org.hibernate.validator.constraints.ScriptAssert.message = \uc2a4\ud06c\ub9bd\ud2b8 \ud45c\ud604\uc2dd "{script}"\uac00 true\ub85c \ud3c9\uac00\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4 +org.hibernate.validator.constraints.UniqueElements.message = \uace0\uc720 \uc694\uc18c\ub9cc \ud3ec\ud568\ud574\uc57c \ud569\ub2c8\ub2e4 +org.hibernate.validator.constraints.URL.message = \uc62c\ubc14\ub978 URL\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4 + +org.hibernate.validator.constraints.br.CNPJ.message = \uc62c\ubc14\ub974\uc9c0 \uc54a\uc740 \ube0c\ub77c\uc9c8 \ubc95\uc778 \ub0a9\uc138\uc790 \ub4f1\ub85d \ubc88\ud638(CNPJ)\uc785\ub2c8\ub2e4 +org.hibernate.validator.constraints.br.CPF.message = \uc62c\ubc14\ub974\uc9c0 \uc54a\uc740 \ube0c\ub77c\uc9c8 \uac1c\uc778 \ub0a9\uc138\uc790 \ub4f1\ub85d \ubc88\ud638(CPF)\uc785\ub2c8\ub2e4 +org.hibernate.validator.constraints.br.TituloEleitoral.message = \uc62c\ubc14\ub974\uc9c0 \uc54a\uc740 \ube0c\ub77c\uc9c8 \uc720\uad8c\uc790 ID \uce74\ub4dc \ubc88\ud638\uc785\ub2c8\ub2e4 + +org.hibernate.validator.constraints.pl.REGON.message = \uc62c\ubc14\ub974\uc9c0 \uc54a\uc740 \ud3f4\ub780\ub4dc \ub0a9\uc138\uc790 \uc2dd\ubcc4 \ubc88\ud638(REGON)\uc785\ub2c8\ub2e4 +org.hibernate.validator.constraints.pl.NIP.message = \uc62c\ubc14\ub974\uc9c0 \uc54a\uc740 VAT \uc2dd\ubcc4 \ubc88\ud638(NIP)\uc785\ub2c8\ub2e4 +org.hibernate.validator.constraints.pl.PESEL.message = \uc62c\ubc14\ub974\uc9c0 \uc54a\uc740 \ud3f4\ub780\ub4dc \uad6d\ubbfc \uc2dd\ubcc4 \ubc88\ud638(PESEL)\uc785\ub2c8\ub2e4 + +org.hibernate.validator.constraints.time.DurationMax.message = \ub2e4\uc74c \uac12\ubcf4\ub2e4 \uc9e7\uac70\ub098 \uac19\uc544\uc57c \ud569\ub2c8\ub2e4${inclusive == true ? ' ' : ''}${days == 0 ? '' : days == 1 ? ' 1\uc77c' : ' ' += days += '\uc77c'}${hours == 0 ? '' : hours == 1 ? ' 1\uc2dc\uac04' : ' ' += hours += '\uc2dc\uac04'}${minutes == 0 ? '' : minutes == 1 ? ' 1\ubd84' : ' ' += minutes += '\ubd84'}${seconds == 0 ? '' : seconds == 1 ? ' 1\ucd08' : ' ' += seconds += '\ucd08'}${millis == 0 ? '' : millis == 1 ? ' 1\ubc00\ub9ac' : ' ' += millis += '\ubc00\ub9ac'}${nanos == 0 ? '' : nanos == 1 ? ' 1\ub098\ub178' : ' ' += nanos += '\ub098\ub178'} +org.hibernate.validator.constraints.time.DurationMin.message = \ub2e4\uc74c \uac12\ubcf4\ub2e4 \uae38\uac70\ub098 \uac19\uc544\uc57c \ud569\ub2c8\ub2e4${inclusive == true ? ' ' : ''}${days == 0 ? '' : days == 1 ? ' 1\uc77c' : ' ' += days += '\uc77c'}${hours == 0 ? '' : hours == 1 ? ' 1\uc2dc\uac04' : ' ' += hours += '\uc2dc\uac04'}${minutes == 0 ? '' : minutes == 1 ? ' 1\ubd84' : ' ' += minutes += '\ubd84'}${seconds == 0 ? '' : seconds == 1 ? ' 1\ucd08' : ' ' += seconds += '\ucd08'}${millis == 0 ? '' : millis == 1 ? ' 1\ubc00\ub9ac' : ' ' += millis += '\ubc00\ub9ac'}${nanos == 0 ? '' : nanos == 1 ? ' 1\ub098\ub178' : ' ' += nanos += '\ub098\ub178'} diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_nl.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_nl.properties new file mode 100644 index 0000000000..526f3cfb62 --- /dev/null +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_nl.properties @@ -0,0 +1,45 @@ +javax.validation.constraints.AssertFalse.message = moet onwaar zijn +javax.validation.constraints.AssertTrue.message = moet waar zijn +javax.validation.constraints.DecimalMax.message = moet kleiner dan ${inclusive == true ? 'of gelijk aan ' : ''}{value} zijn +javax.validation.constraints.DecimalMin.message = moet groter dan ${inclusive == true ? 'of gelijk aan ' : ''}{value} zijn +javax.validation.constraints.Digits.message = numerieke waarde ligt buiten het toegestane bereik (<{integer} cijfers>,<{fraction} cijfers> verwacht) +javax.validation.constraints.Email.message = het e-mailadres is ongeldig +javax.validation.constraints.Future.message = moet in de toekomst zijn +javax.validation.constraints.FutureOrPresent.message = moet in het heden of in de toekomst zijn +javax.validation.constraints.Max.message = moet kleiner of gelijk aan {value} zijn +javax.validation.constraints.Min.message = moet groter of gelijk aan {value} zijn +javax.validation.constraints.Negative.message = moet kleiner dan 0 zijn +javax.validation.constraints.NegativeOrZero.message = moet kleiner dan of gelijk aan 0 zijn +javax.validation.constraints.NotBlank.message = mag niet onbeschreven zijn +javax.validation.constraints.NotEmpty.message = mag niet leeg zijn +javax.validation.constraints.NotNull.message = mag niet null zijn +javax.validation.constraints.Null.message = moet null zijn +javax.validation.constraints.Past.message = moet in het verleden zijn +javax.validation.constraints.PastOrPresent.message = moet in het heden of in het verleden zijn +javax.validation.constraints.Pattern.message = moet overeenkomen met "{regexp}" +javax.validation.constraints.Positive.message = moet groter dan 0 zijn +javax.validation.constraints.PositiveOrZero.message = moet groter of gelijk zijn aan 0 +javax.validation.constraints.Size.message = moet tussen {min} en {max} liggen + +org.hibernate.validator.constraints.CreditCardNumber.message = creditcard nummer ongeldig +org.hibernate.validator.constraints.Currency.message = ongeldige valuta (toegestane waarden: {value}) +org.hibernate.validator.constraints.EAN.message = ongeldige barcode +org.hibernate.validator.constraints.Email.message = geen geldig e-mailadres +org.hibernate.validator.constraints.ISBN.message = ongeldig ISBN +org.hibernate.validator.constraints.Length.message = moet tussen {min} en {max} tekens lang zijn +org.hibernate.validator.constraints.CodePointLength.message = moet tussen {min} en {max} tekens lang zijn +org.hibernate.validator.constraints.LuhnCheck.message = het controlecijfer voor ${validatedValue} is ongeldig, Luhn Modulo 10 checksum mislukt +org.hibernate.validator.constraints.Mod10Check.message = het controlecijfer voor ${validatedValue} is ongeldig, Modulo 10 checksum mislukt +org.hibernate.validator.constraints.Mod11Check.message = het controlecijfer voor ${validatedValue} is ongeldig, Modulo 11 checksum mislukt +org.hibernate.validator.constraints.ModCheck.message = het controlecijfer voor ${validatedValue} is ongeldig, ${modType} checksum mislukt +org.hibernate.validator.constraints.NotBlank.message = mag niet onbeschreven zijn +org.hibernate.validator.constraints.NotEmpty.message = mag niet leeg zijn +org.hibernate.validator.constraints.ParametersScriptAssert.message = scriptexpressie "{script}" is ongeldig +org.hibernate.validator.constraints.Range.message = moet tussen {min} en {max} zijn +org.hibernate.validator.constraints.SafeHtml.message = kan onveilige HTML-inhoud bevatten +org.hibernate.validator.constraints.ScriptAssert.message = scriptexpressie "{script}" is ongeldig +org.hibernate.validator.constraints.UniqueElements.message = mag geen duplicaten bevatten +org.hibernate.validator.constraints.URL.message = moet een geldige URL zijn + +org.hibernate.validator.constraints.time.DurationMax.message = moet korter${inclusive == true ? ' of gelijk' : ' dan'}${days == 0 ? '' : days == 1 ? ' 1 dag' : ' ' += days += ' dagen'}${hours == 0 ? '' : hours == 1 ? ' 1 uur' : ' ' += hours += ' uren'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minuut' : ' ' += minutes += ' Minuten'}${seconds == 0 ? '' : seconds == 1 ? ' 1 seconde' : ' ' += seconds += ' seconden'}${millis == 0 ? '' : millis == 1 ? ' 1 milliseconde' : ' ' += millis += ' milliseconden'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nanoseconde' : ' ' += nanos += ' nanoseconden'} zijn +org.hibernate.validator.constraints.time.DurationMin.message = moet langer${inclusive == true ? ' of gelijk' : ' dan'}${days == 0 ? '' : days == 1 ? ' 1 dag' : ' ' += days += ' dagen'}${hours == 0 ? '' : hours == 1 ? ' 1 uur' : ' ' += hours += ' uren'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minuut' : ' ' += minutes += ' minuten'}${seconds == 0 ? '' : seconds == 1 ? ' 1 seconde' : ' ' += seconds += ' seconden'}${millis == 0 ? '' : millis == 1 ? ' 1 milliseconde' : ' ' += millis += ' milliseconden'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nanoseconde' : ' ' += nanos += ' nanoseconden'} zijn diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_pl.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_pl.properties new file mode 100644 index 0000000000..b0a9853af3 --- /dev/null +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_pl.properties @@ -0,0 +1,53 @@ +javax.validation.constraints.AssertFalse.message = musi mie\u0107 warto\u015b\u0107 false +javax.validation.constraints.AssertTrue.message = musi mie\u0107 warto\u015b\u0107 true +javax.validation.constraints.DecimalMax.message = musi by\u0107 ${inclusive == true ? 'r\u00f3wne lub ' : ''}mniejsze od {value} +javax.validation.constraints.DecimalMin.message = musi by\u0107 ${inclusive == true ? 'r\u00f3wne lub ' : ''}wi\u0119ksze od {value} +javax.validation.constraints.Digits.message = warto\u015b\u0107 liczbowa spoza zakresu (oczekiwano ,) +javax.validation.constraints.Email.message = musi by\u0107 poprawnie sformatowanym adresem e-mail +javax.validation.constraints.Future.message = musi by\u0107 dat\u0105 w przysz\u0142o\u015bci +javax.validation.constraints.FutureOrPresent.message = musi by\u0107 dat\u0105 bie\u017c\u0105c\u0105 lub w przysz\u0142o\u015bci +javax.validation.constraints.Max.message = musi by\u0107 r\u00f3wne lub mniejsze od {value} +javax.validation.constraints.Min.message = musi by\u0107 r\u00f3wne lub wi\u0119ksze od {value} +javax.validation.constraints.Negative.message = musi by\u0107 mniejsze od 0 +javax.validation.constraints.NegativeOrZero.message = musi by\u0107 r\u00f3wne lub mniejsze od 0 +javax.validation.constraints.NotBlank.message = nie mo\u017ce by\u0107 odst\u0119pem +javax.validation.constraints.NotEmpty.message = nie mo\u017ce by\u0107 puste +javax.validation.constraints.NotNull.message = nie mo\u017ce mie\u0107 warto\u015bci null +javax.validation.constraints.Null.message = musi mie\u0107 warto\u015b\u0107 null +javax.validation.constraints.Past.message = musi by\u0107 dat\u0105 w przesz\u0142o\u015bci +javax.validation.constraints.PastOrPresent.message = musi by\u0107 dat\u0105 bie\u017c\u0105c\u0105 lub w przesz\u0142o\u015bci +javax.validation.constraints.Pattern.message = musi pasowa\u0107 do wyra\u017cenia {regexp} +javax.validation.constraints.Positive.message = musi by\u0107 wi\u0119ksze od 0 +javax.validation.constraints.PositiveOrZero.message = musi by\u0107 r\u00f3wne lub wi\u0119ksze od 0 +javax.validation.constraints.Size.message = wielko\u015b\u0107 musi nale\u017ce\u0107 do zakresu od {min} do {max} + +org.hibernate.validator.constraints.CreditCardNumber.message = niepoprawny numer karty kredytowej +org.hibernate.validator.constraints.Currency.message = niepoprawna waluta (musi by\u0107 jedn\u0105 z nast\u0119puj\u0105cych: {value}) +org.hibernate.validator.constraints.EAN.message = niepoprawny kod paskowy {type} +org.hibernate.validator.constraints.Email.message = musi by\u0107 poprawnie sformatowanym adresem e-mail +org.hibernate.validator.constraints.ISBN.message = niepoprawny numer ISBN +org.hibernate.validator.constraints.Length.message = d\u0142ugo\u015b\u0107 musi wynosi\u0107 od {min} do {max} +org.hibernate.validator.constraints.CodePointLength.message = d\u0142ugo\u015b\u0107 musi wynosi\u0107 od {min} do {max} +org.hibernate.validator.constraints.LuhnCheck.message = cyfra kontrolna dla warto\u015bci ${validatedValue} jest niepoprawna, sprawdzenie sumy kontrolnej za pomoc\u0105 algorytmu Luhn Modulo 10 nie powiod\u0142o si\u0119 +org.hibernate.validator.constraints.Mod10Check.message = cyfra kontrolna dla warto\u015bci ${validatedValue} jest niepoprawna, sprawdzenie sumy kontrolnej za pomoc\u0105 algorytmu Modulo 10 nie powiod\u0142o si\u0119 +org.hibernate.validator.constraints.Mod11Check.message = cyfra kontrolna dla warto\u015bci ${validatedValue} jest niepoprawna, sprawdzenie sumy kontrolnej za pomoc\u0105 algorytmu Modulo 11 nie powiod\u0142o si\u0119 +org.hibernate.validator.constraints.ModCheck.message = cyfra kontrolna dla warto\u015bci ${validatedValue} jest niepoprawna, sprawdzenie sumy kontrolnej za pomoc\u0105 algorytmu ${modType} nie powiod\u0142o si\u0119 +org.hibernate.validator.constraints.NotBlank.message = nie mo\u017ce by\u0107 odst\u0119pem +org.hibernate.validator.constraints.NotEmpty.message = nie mo\u017ce by\u0107 puste +org.hibernate.validator.constraints.ParametersScriptAssert.message = warto\u015bciowanie wyra\u017cenia skryptu {script} nie zwr\u00f3ci\u0142o warto\u015bci true +org.hibernate.validator.constraints.Range.message = musi nale\u017ce\u0107 do zakresu od {min} do {max} +org.hibernate.validator.constraints.SafeHtml.message = mo\u017ce zawiera\u0107 niebezpieczn\u0105 tre\u015b\u0107 HTML +org.hibernate.validator.constraints.ScriptAssert.message = warto\u015bciowanie wyra\u017cenia skryptu {script} nie zwr\u00f3ci\u0142o warto\u015bci true +org.hibernate.validator.constraints.UniqueElements.message = musi zawiera\u0107 tylko unikalne elementy +org.hibernate.validator.constraints.URL.message = musi by\u0107 poprawnym adresem URL + +org.hibernate.validator.constraints.br.CNPJ.message = niepoprawny numer brazylijskiego rejestru podatkowego os\u00f3b prawnych (CNPJ) +org.hibernate.validator.constraints.br.CPF.message = niepoprawny numer brazylijskiego rejestru podatkowego os\u00f3b fizycznych (CPF) +org.hibernate.validator.constraints.br.TituloEleitoral.message = niepoprawny numer brazylijskiej karty identyfikacyjnej g\u0142osuj\u0105cego + +org.hibernate.validator.constraints.pl.REGON.message = niepoprawny numer identyfikacyjny polskiego podatnika (REGON) +org.hibernate.validator.constraints.pl.NIP.message = niepoprawny number identyfikacyjny VAT (NIP) +org.hibernate.validator.constraints.pl.PESEL.message = niepoprawny polski narodowy numer identyfikacyjny (PESEL) + +org.hibernate.validator.constraints.time.DurationMax.message = musi by\u0107 ${inclusive == true ? 'r\u00f3wne lub ' : ''}kr\u00f3tsze ni\u017c${days == 0 ? '' : days == 1 ? ' 1 dzie\u0144' : ' ' += days += ' dni'}${hours == 0 ? '' : hours == 1 ? ' 1 godzina' : ' ' += hours += ' godz.'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minuta' : ' ' += minutes += ' min'}${seconds == 0 ? '' : seconds == 1 ? ' 1 sekunda' : ' ' += seconds += ' s'}${millis == 0 ? '' : millis == 1 ? ' 1 milisekunda' : ' ' += millis += ' ms'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nanosekunda' : ' ' += nanos += ' ns'} +org.hibernate.validator.constraints.time.DurationMin.message = musi by\u0107 ${inclusive == true ? 'r\u00f3wne lub ' : ''}d\u0142u\u017csze ni\u017c${days == 0 ? '' : days == 1 ? ' 1 dzie\u0144' : ' ' += days += ' dni'}${hours == 0 ? '' : hours == 1 ? ' 1 godzina' : ' ' += hours += ' godz.'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minuta' : ' ' += minutes += ' min'}${seconds == 0 ? '' : seconds == 1 ? ' 1 sekunda' : ' ' += seconds += ' s'}${millis == 0 ? '' : millis == 1 ? ' 1 milisekunda' : ' ' += millis += ' ms'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nanosekunda' : ' ' += nanos += ' ns'} diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_pt_BR.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_pt_BR.properties index 0d6c0a89c7..6cf00dc834 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_pt_BR.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_pt_BR.properties @@ -1,42 +1,53 @@ -javax.validation.constraints.AssertFalse.message = deve ser falso -javax.validation.constraints.AssertTrue.message = deve ser verdadeiro -javax.validation.constraints.DecimalMax.message = deve ser menor ou igual a {value} -javax.validation.constraints.DecimalMin.message = deve ser maior ou igual a {value} -javax.validation.constraints.Digits.message = valor num\u00E9rico fora do limite (<{integer} d\u00EDgitos>.<{fraction} d\u00EDgitos> esperado) -javax.validation.constraints.Email.message = N\u00E3o \u00E9 um endere\u00E7o de e-mail -javax.validation.constraints.Future.message = deve estar no futuro -javax.validation.constraints.FutureOrPresent.message = deve de ser uma data em presente ou em futuro -javax.validation.constraints.Max.message = deve ser menor ou igual a {value} -javax.validation.constraints.Min.message = deve ser maior ou igual a {value} -javax.validation.constraints.Negative.message = deve ser menor que 0 -javax.validation.constraints.NegativeOrZero.message = deve ser menor ou igual a 0 -javax.validation.constraints.NotBlank.message = N\u00E3o pode estar em branco -javax.validation.constraints.NotEmpty.message = N\u00E3o pode estar vazio -javax.validation.constraints.NotNull.message = n\u00E3o pode ser nulo -javax.validation.constraints.Null.message = deve ser nulo -javax.validation.constraints.Past.message = deve estar no passado -javax.validation.constraints.PastOrPresent.message = deve de ser uma data em passado ou em presente -javax.validation.constraints.Pattern.message = deve corresponder \u00E0 "{regexp}" -javax.validation.constraints.Positive.message = deve ser maior que 0 -javax.validation.constraints.PositiveOrZero.message = deve ser maior ou igual a 0 -javax.validation.constraints.Size.message = tamanho deve estar entre {min} e {max} +javax.validation.constraints.AssertFalse.message = deve ser falso +javax.validation.constraints.AssertTrue.message = deve ser verdadeiro +javax.validation.constraints.DecimalMax.message = deve ser menor que ${inclusive == true ? 'ou igual a ' : ''}{valor} +javax.validation.constraints.DecimalMin.message = deve ser maior que ${inclusive == true ? 'ou igual a ' : ''}{valor} +javax.validation.constraints.Digits.message = valor n\u00famerico fora do limite (<{inteiro} d\u00edgito>.<{fra\u00e7\u00e3o} d\u00edgitos> esperar) +javax.validation.constraints.Email.message = deve ser um endere\u00e7o de e-mail bem formado +javax.validation.constraints.Future.message = deve ser uma data futura +javax.validation.constraints.FutureOrPresent.message = deve ser uma data no presente ou no futuro +javax.validation.constraints.Max.message = deve ser menor que ou igual \u00e0 {valor} +javax.validation.constraints.Min.message = deve ser maior que ou igual \u00e0 {valor} +javax.validation.constraints.Negative.message = deve ser menor que 0 +javax.validation.constraints.NegativeOrZero.message = deve ser menor ou igual a 0 +javax.validation.constraints.NotBlank.message = n\u00e3o deve estar em branco +javax.validation.constraints.NotEmpty.message = n\u00e3o deve estar vazio +javax.validation.constraints.NotNull.message = n\u00e3o deve ser nulo +javax.validation.constraints.Null.message = deve ser nulo +javax.validation.constraints.Past.message = deve ser uma data passada +javax.validation.constraints.PastOrPresent.message = deve ser uma data no passado ou no presente +javax.validation.constraints.Pattern.message = deve corresponder a "{regexp}" +javax.validation.constraints.Positive.message = deve ser maior que 0 +javax.validation.constraints.PositiveOrZero.message = deve ser maior ou igual a 0 +javax.validation.constraints.Size.message = tamanho deve ser entre {m\u00edn} e {max} -org.hibernate.validator.constraints.CreditCardNumber.message = N\u00FAmero de Cart\u00E3o de Cr\u00E9dito inv\u00E1lido -org.hibernate.validator.constraints.Email.message = N\u00E3o \u00E9 um endere\u00E7o de e-mail -org.hibernate.validator.constraints.EAN.message = C\u00F3digo de barras {type} inv\u00E1lido -org.hibernate.validator.constraints.Length.message = Tamanho deve estar entre {min} e {max} -org.hibernate.validator.constraints.CodePointLength.message = Tamanho deve estar entre {min} e {max} -org.hibernate.validator.constraints.LuhnCheck.message = Digito verificador para ${validatedValue} \u00E9 inv\u00E1lido, verifica\u00E7\u00E3o M\u00F3dulo 10 de Luhn falhou -org.hibernate.validator.constraints.Mod10Check.message = Digito verificador para ${validatedValue} \u00E9 inv\u00E1lido, verifica\u00E7\u00E3o M\u00F3dulo 10 falhou -org.hibernate.validator.constraints.Mod11Check.message = Digito verificador para ${validatedValue} \u00E9 inv\u00E1lido, verifica\u00E7\u00E3o M\u00F3dulo 10 falhou -org.hibernate.validator.constraints.ModCheck.message = Digito verificador para ${validatedValue} \u00E9 inv\u00E1lido, verifica\u00E7\u00E3o ${modType} falhou -org.hibernate.validator.constraints.NotBlank.message = N\u00E3o pode estar em branco -org.hibernate.validator.constraints.NotEmpty.message = N\u00E3o pode estar vazio -org.hibernate.validator.constraints.ParametersScriptAssert.message = O script "{script}" n\u00E3o retornou verdadeiro -org.hibernate.validator.constraints.Range.message = O valor precisa estar entre {min} e {max} -org.hibernate.validator.constraints.SafeHtml.message = Pode ter conte\u00FAdo inseguro no html -org.hibernate.validator.constraints.ScriptAssert.message = Express\u00E3o de script "{script}" n\u00E3o retornou verdadeiro -org.hibernate.validator.constraints.URL.message = Deve ser uma URL v\u00E1lida -org.hibernate.validator.constraints.br.CNPJ.message = CNPJ inv\u00E1lido -org.hibernate.validator.constraints.br.CPF.message = CPF inv\u00E1lido -org.hibernate.validator.constraints.br.TituloEleitoral.message = T\u00EDtulo Eleitoral inv\u00E1lido +org.hibernate.validator.constraints.CreditCardNumber.message = n\u00famero do cart\u00e3o de cr\u00e9dito inv\u00e1lido +org.hibernate.validator.constraints.Currency.message = moeda inv\u00e1lida (deve ser uma de {valor}) +org.hibernate.validator.constraints.EAN.message = c\u00f3digo de barras {tipo} inv\u00e1lido +org.hibernate.validator.constraints.Email.message = deve ser um endere\u00e7o de e-mail bem formado +org.hibernate.validator.constraints.ISBN.message = ISBN inv\u00e1lido +org.hibernate.validator.constraints.Length.message = o comprimento deve ser entre {m\u00edn} e {m\u00e1x} +org.hibernate.validator.constraints.CodePointLength.message = o comprimento deve ser entre {m\u00edn} e {m\u00e1x} +org.hibernate.validator.constraints.LuhnCheck.message = o d\u00edgito de verifica\u00e7\u00e3o para ${validatedValue} \u00e9 inv\u00e1lido, soma de verifica\u00e7\u00e3o Luhn Modulo 10 com falha +org.hibernate.validator.constraints.Mod10Check.message = o d\u00edgito de verifica\u00e7\u00e3o para ${validatedValue} \u00e9 inv\u00e1lido, soma de verifica\u00e7\u00e3o Modulo 10 com falha +org.hibernate.validator.constraints.Mod11Check.message = o d\u00edgito de verifica\u00e7\u00e3o para ${validatedValue} \u00e9 inv\u00e1lido, soma de verifica\u00e7\u00e3o Modulo 11 com falha +org.hibernate.validator.constraints.ModCheck.message = o d\u00edgito de verifica\u00e7\u00e3o para ${validatedValue} \u00e9 inv\u00e1lido, soma de verifica\u00e7\u00e3o ${modType} com falha +org.hibernate.validator.constraints.NotBlank.message = n\u00e3o deve estar em branco +org.hibernate.validator.constraints.NotEmpty.message = n\u00e3o deve estar vazio +org.hibernate.validator.constraints.ParametersScriptAssert.message = express\u00e3o de script "{script}" n\u00e3o avaliou para true +org.hibernate.validator.constraints.Range.message = deve estar entre {m\u00edn} e {m\u00e1x} +org.hibernate.validator.constraints.SafeHtml.message = pode ter conte\u00fado HTML n\u00e3o seguro +org.hibernate.validator.constraints.ScriptAssert.message = express\u00e3o de script "{script}" n\u00e3o avaliou para true +org.hibernate.validator.constraints.UniqueElements.message = deve conter apenas elementos exclusivos +org.hibernate.validator.constraints.URL.message = deve ser uma URL v\u00e1lida + +org.hibernate.validator.constraints.br.CNPJ.message = n\u00famero do registro de contribuinte corporativo brasileiro (CNPJ) inv\u00e1lido +org.hibernate.validator.constraints.br.CPF.message = n\u00famero do registro de contribuinte individual brasileiro (CPF) inv\u00e1lido +org.hibernate.validator.constraints.br.TituloEleitoral.message = n\u00famero do t\u00edtulo de eleitor brasileiro inv\u00e1lido + +org.hibernate.validator.constraints.pl.REGON.message = n\u00famero de identifica\u00e7\u00e3o de contribuinte polon\u00eas (REGON) inv\u00e1lido +org.hibernate.validator.constraints.pl.NIP.message = n\u00famero de identifica\u00e7\u00e3o de VAT (NIP) inv\u00e1lido +org.hibernate.validator.constraints.pl.PESEL.message = n\u00famero de identifica\u00e7\u00e3o nacional polonesa (PESEL) inv\u00e1lido + +org.hibernate.validator.constraints.time.DurationMax.message = deve ser menor que${inclusive == true ? ' ou igual a' : ''}${dias == 0 ? '' : dias == 1 ? ' 1 dia' : ' ' += dias += ' dias'}${horas == 0 ? '' : horas == 1 ? ' 1 hora' : ' ' += horas += ' horas'}${minutos == 0 ? '' : minutos == 1 ? ' 1 minuto' : ' ' += minutos += ' minutos'}${segundos == 0 ? '' : segundos == 1 ? ' 1 segundo' : ' ' += segundos += ' segundos'}${miliss == 0 ? '' : miliss == 1 ? ' 1 mili' : ' ' += miliss += ' miliss'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} +org.hibernate.validator.constraints.time.DurationMin.message = deve ser maior que${inclusive == true ? ' ou igual a' : ''}${dias == 0 ? '' : dias == 1 ? ' 1 dia' : ' ' += dias += ' dias'}${horas == 0 ? '' : horas == 1 ? ' 1 hora' : ' ' += horas += ' horas'}${minutos == 0 ? '' : minutos == 1 ? ' 1 minuto' : ' ' += minutos += ' minutos'}${segundos == 0 ? '' : segundos == 1 ? ' 1 segundo' : ' ' += segundos += ' segundos'}${miliss == 0 ? '' : miliss == 1 ? ' 1 mili' : ' ' += miliss += ' miliss'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_ro.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_ro.properties new file mode 100644 index 0000000000..453a27e153 --- /dev/null +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_ro.properties @@ -0,0 +1,53 @@ +javax.validation.constraints.AssertFalse.message = trebuie s\u0103 fie false +javax.validation.constraints.AssertTrue.message = trebuie s\u0103 fie true +javax.validation.constraints.DecimalMax.message = trebuie s\u0103 fie mai mic dec\u00e2t ${inclusiv == true ? 'sau egal cu ' : ''}{valoare} +javax.validation.constraints.DecimalMin.message = trebuie s\u0103 fie mai mare dec\u00e2t ${inclusiv == true ? 'sau egal cu ' : ''}{valoare} +javax.validation.constraints.Digits.message = valoare numeric\u0103 \u00een afara limitelor (<{num\u0103r \u00eentreg} digi\u0163i>.<{num\u0103r frac\u0163ionar} digi\u0163i> a\u015fteptat) +javax.validation.constraints.Email.message = trebuie s\u0103 fie o adres\u0103 de e-mail cu format corect +javax.validation.constraints.Future.message = trebuie s\u0103 fie o dat\u0103 viitoare +javax.validation.constraints.FutureOrPresent.message = trebuie s\u0103 fie o dat\u0103 \u00een prezent sau \u00een viitor +javax.validation.constraints.Max.message = trebuie s\u0103 fie mai mic sau egal dec\u00e2t {valoare} +javax.validation.constraints.Min.message = trebuie s\u0103 fie mai mare sau egal dec\u00e2t {valoare} +javax.validation.constraints.Negative.message = trebuie s\u0103 fie mai mic dec\u00e2t 0 +javax.validation.constraints.NegativeOrZero.message = trebuie s\u0103 fie mai mic sau egal dec\u00e2t 0 +javax.validation.constraints.NotBlank.message = nu trebuie s\u0103 fie blanc +javax.validation.constraints.NotEmpty.message = nu trebuie s\u0103 fie gol +javax.validation.constraints.NotNull.message = nu trebuie s\u0103 fie null +javax.validation.constraints.Null.message = trebuie s\u0103 fie null +javax.validation.constraints.Past.message = trebuie s\u0103 fie o dat\u0103 anterioar\u0103 +javax.validation.constraints.PastOrPresent.message = trebuie s\u0103 fie o dat\u0103 anterioar\u0103 sau din prezent +javax.validation.constraints.Pattern.message = trebuie s\u0103 se potriveasc\u0103 "{regexp}" +javax.validation.constraints.Positive.message = trebuie s\u0103 fie mai mare dec\u00e2t 0 +javax.validation.constraints.PositiveOrZero.message = trebuie s\u0103 fie mai mare sau egal dec\u00e2t 0 +javax.validation.constraints.Size.message = dimensiunea trebuie s\u0103 fie \u00eentre {min} \u015fi {max} + +org.hibernate.validator.constraints.CreditCardNumber.message = num\u0103r invalid de card de credit +org.hibernate.validator.constraints.Currency.message = moned\u0103 invalid\u0103 trebuie s\u0103 fie una din {valoare}) +org.hibernate.validator.constraints.EAN.message = cod de bare {tip} invalid +org.hibernate.validator.constraints.Email.message = trebuie s\u0103 fie o adres\u0103 de e-mail cu format corect +org.hibernate.validator.constraints.ISBN.message = ISBN invalid +org.hibernate.validator.constraints.Length.message = lungimea trebuie s\u0103 fie \u00eentre {min} \u015fi {max} +org.hibernate.validator.constraints.CodePointLength.message = lungimea trebuie s\u0103 fie \u00eentre {min} \u015fi {max} +org.hibernate.validator.constraints.LuhnCheck.message = cifra de verificare pentru ${validatedValue} este invalid\u0103, suma de control Luhn Modulo 10 a e\u015fuat +org.hibernate.validator.constraints.Mod10Check.message = cifra de verificare pentru ${validatedValue} este invalid\u0103, suma de control Modulo 10 a e\u015fuat +org.hibernate.validator.constraints.Mod11Check.message = cifra de verificare pentru ${validatedValue} este invalid\u0103, suma de control Modulo 11 a e\u015fuat +org.hibernate.validator.constraints.ModCheck.message = cifra de verificare pentru ${validatedValue} este invalid\u0103, suma de control ${modType} a e\u015fuat +org.hibernate.validator.constraints.NotBlank.message = nu trebuie s\u0103 fie blanc +org.hibernate.validator.constraints.NotEmpty.message = nu trebuie s\u0103 fie gol +org.hibernate.validator.constraints.ParametersScriptAssert.message = expresia de script "{script}" nu s-a evaluat la true +org.hibernate.validator.constraints.Range.message = trebuie s\u0103 fie \u00eentre {min} \u015fi {max} +org.hibernate.validator.constraints.SafeHtml.message = poate avea con\u0163inut HTML periculos +org.hibernate.validator.constraints.ScriptAssert.message = expresia de script "{script}" nu s-a evaluat la true +org.hibernate.validator.constraints.UniqueElements.message = trebuie s\u0103 con\u0163in\u0103 numai elemente unice +org.hibernate.validator.constraints.URL.message = trebuie s\u0103 fie un URL valid + +org.hibernate.validator.constraints.br.CNPJ.message = num\u0103r de registru de contribuabil corporatist brazilian invalid (CNPJ) +org.hibernate.validator.constraints.br.CPF.message = num\u0103r de registru de contribuabil individual brazilian invalid (CPF) +org.hibernate.validator.constraints.br.TituloEleitoral.message = num\u0103r invalid de carte de identitate de votant brazilian + +org.hibernate.validator.constraints.pl.REGON.message = num\u0103r invalid de identificare contribuabil polonez (REGON) +org.hibernate.validator.constraints.pl.NIP.message = num\u0103r invalid de identificare VAT (NIP) +org.hibernate.validator.constraints.pl.PESEL.message = num\u0103r invalid de identificare na\u0163ional polonez (PESEL) + +org.hibernate.validator.constraints.time.DurationMax.message = trebuie s\u0103 fie mai scurt dec\u00e2t${inclusiv == true ? ' sau egal cu' : ''}${zile == 0 ? '' : zile == 1 ? ' 1 zi' : ' ' += zile += ' zile'}${ore == 0 ? '' : ore == 1 ? ' 1 or\u0103' : ' ' += ore += ' ore'}${minute == 0 ? '' : minute == 1 ? ' 1 minut' : ' ' += minute += ' minute'}${secunde == 0 ? '' : secunde == 1 ? ' 1 secund\u0103' : ' ' += secunde += ' secunde'}${milisecunde == 0 ? '' : milisecunde == 1 ? ' 1 milisecund\u0103' : ' ' += milisecunde += ' milisecunde'}${nanosecunde == 0 ? '' : nanosecunde == 1 ? ' 1 nanosecund\u0103' : ' ' += nanosecunde += ' nanosecunde'} +org.hibernate.validator.constraints.time.DurationMin.message = trebuie s\u0103 fie mai lung dec\u00e2t${inclusiv == true ? ' sau egal cu' : ''}${zile == 0 ? '' : zile == 1 ? ' 1 zi' : ' ' += zile += ' zile'}${ore == 0 ? '' : ore == 1 ? ' 1 or\u0103' : ' ' += ore += ' ore'}${minute == 0 ? '' : minute == 1 ? ' 1 minut' : ' ' += minute += ' minute'}${secunde == 0 ? '' : secunde == 1 ? ' 1 secund\u0103' : ' ' += secunde += ' secunde'}${milisecunde == 0 ? '' : milisecunde == 1 ? ' 1 milisecund\u0103' : ' ' += milisecunde += ' milisecunde'}${nanosecunde == 0 ? '' : nanosecunde == 1 ? ' 1 nanosecund\u0103' : ' ' += nanosecunde += ' nanosecunde'} diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_ru.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_ru.properties index 2886acb9d3..94e70ec254 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_ru.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_ru.properties @@ -1,32 +1,53 @@ -javax.validation.constraints.AssertFalse.message = \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043B\u043E\u0436\u043D\u044B\u043C -javax.validation.constraints.AssertTrue.message = \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u0438\u0441\u0442\u0438\u043D\u043D\u044B\u043C -javax.validation.constraints.DecimalMax.message = \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043C\u0435\u043D\u044C\u0448\u0435 \u0447\u0435\u043C ${inclusive == true ? '\u0438\u043B\u0438 \u0440\u0430\u0432\u043D\u043E ' : ''}{value} -javax.validation.constraints.DecimalMin.message = \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u0431\u043E\u043B\u044C\u0448\u0435 \u0447\u0435\u043C ${inclusive == true ? '\u0438\u043B\u0438 \u0440\u0430\u0432\u043D\u043E ' : ''}{value} -javax.validation.constraints.Email.message = email \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D \u0432 \u043D\u0435\u0432\u0435\u0440\u043D\u043E\u043C \u0444\u043E\u0440\u043C\u0430\u0442\u0435 -javax.validation.constraints.Future.message = \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043C -javax.validation.constraints.Max.message = \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043C\u0435\u043D\u044C\u0448\u0435 \u0438\u043B\u0438 \u0440\u0430\u0432\u043D\u043E {value} -javax.validation.constraints.Min.message = \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u0431\u043E\u043B\u044C\u0448\u0435 \u0438\u043B\u0438 \u0440\u0430\u0432\u043D\u043E {value} -javax.validation.constraints.NotBlank.message = \u043D\u0435 \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u043F\u0443\u0441\u0442\u043E -javax.validation.constraints.NotEmpty.message = \u043D\u0435 \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u043F\u0443\u0441\u0442\u043E -javax.validation.constraints.NotNull.message = \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u0437\u0430\u0434\u0430\u043D\u043E -javax.validation.constraints.Null.message = \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043D\u0435\u0437\u0430\u0434\u0430\u043D\u043E -javax.validation.constraints.Past.message = \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u0432 \u043F\u0440\u043E\u0448\u043B\u043E\u043C -javax.validation.constraints.Pattern.message = \u0434\u043E\u043B\u0436\u043D\u043E \u0441\u043E\u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u043E\u0432\u0430\u0442\u044C \u0448\u0430\u0431\u043B\u043E\u043D\u0443 "{regexp}" -javax.validation.constraints.Size.message = \u0440\u0430\u0437\u043C\u0435\u0440 \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u043C\u0435\u0436\u0434\u0443 {min} \u0438 {max} +javax.validation.constraints.AssertFalse.message = \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0440\u0430\u0432\u043d\u043e false +javax.validation.constraints.AssertTrue.message = \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0440\u0430\u0432\u043d\u043e true +javax.validation.constraints.DecimalMax.message = \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043c\u0435\u043d\u044c\u0448\u0435, \u0447\u0435\u043c ${inclusive == true ? '\u0438\u043b\u0438 \u0440\u0430\u0432\u043d\u043e ' : ''}{value} +javax.validation.constraints.DecimalMin.message = \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435, \u0447\u0435\u043c ${inclusive == true ? '\u0438\u043b\u0438 \u0440\u0430\u0432\u043d\u043e ' : ''}{value} +javax.validation.constraints.Digits.message = \u0447\u0438\u0441\u043b\u043e \u0432\u043d\u0435 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0433\u043e \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0430 (\u043e\u0436\u0438\u0434\u0430\u043b\u043e\u0441\u044c <{integer} \u0440\u0430\u0437\u0440\u044f\u0434\u043e\u0432>.<{fraction} \u0440\u0430\u0437\u0440\u044f\u0434\u043e\u0432>) +javax.validation.constraints.Email.message = \u0434\u043e\u043b\u0436\u043d\u043e \u0438\u043c\u0435\u0442\u044c \u0444\u043e\u0440\u043c\u0430\u0442 \u0430\u0434\u0440\u0435\u0441\u0430 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b +javax.validation.constraints.Future.message = \u0434\u043e\u043b\u0436\u043d\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0434\u0430\u0442\u0443, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0435\u0449\u0435 \u043d\u0435 \u043d\u0430\u0441\u0442\u0443\u043f\u0438\u043b\u0430 +javax.validation.constraints.FutureOrPresent.message = \u0434\u043e\u043b\u0436\u043d\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0441\u0435\u0433\u043e\u0434\u043d\u044f\u0448\u043d\u0435\u0435 \u0447\u0438\u0441\u043b\u043e \u0438\u043b\u0438 \u0434\u0430\u0442\u0443, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0435\u0449\u0435 \u043d\u0435 \u043d\u0430\u0441\u0442\u0443\u043f\u0438\u043b\u0430 +javax.validation.constraints.Max.message = \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043d\u0435 \u0431\u043e\u043b\u044c\u0448\u0435 {value} +javax.validation.constraints.Min.message = \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043d\u0435 \u043c\u0435\u043d\u044c\u0448\u0435 {value} +javax.validation.constraints.Negative.message = \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043c\u0435\u043d\u044c\u0448\u0435 0 +javax.validation.constraints.NegativeOrZero.message = \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043d\u0435 \u0431\u043e\u043b\u044c\u0448\u0435 0 +javax.validation.constraints.NotBlank.message = \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043f\u0443\u0441\u0442\u044b\u043c +javax.validation.constraints.NotEmpty.message = \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043f\u0443\u0441\u0442\u044b\u043c +javax.validation.constraints.NotNull.message = \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0440\u0430\u0432\u043d\u044f\u0442\u044c\u0441\u044f null +javax.validation.constraints.Null.message = \u0434\u043e\u043b\u0436\u043d\u043e \u0440\u0430\u0432\u043d\u044f\u0442\u044c\u0441\u044f null +javax.validation.constraints.Past.message = \u0434\u043e\u043b\u0436\u043d\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043f\u0440\u043e\u0448\u0435\u0434\u0448\u0443\u044e \u0434\u0430\u0442\u0443 +javax.validation.constraints.PastOrPresent.message = \u0434\u043e\u043b\u0436\u043d\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043f\u0440\u043e\u0448\u0435\u0434\u0448\u0443\u044e \u0434\u0430\u0442\u0443 \u0438\u043b\u0438 \u0441\u0435\u0433\u043e\u0434\u043d\u044f\u0448\u043d\u0435\u0435 \u0447\u0438\u0441\u043b\u043e +javax.validation.constraints.Pattern.message = \u0434\u043e\u043b\u0436\u043d\u043e \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c "{regexp}" +javax.validation.constraints.Positive.message = \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435 0 +javax.validation.constraints.PositiveOrZero.message = \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435 \u0438\u043b\u0438 \u0440\u0430\u0432\u043d\u043e 0 +javax.validation.constraints.Size.message = \u0440\u0430\u0437\u043c\u0435\u0440 \u0434\u043e\u043b\u0436\u0435\u043d \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0432 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0435 \u043e\u0442 {min} \u0434\u043e {max} -org.hibernate.validator.constraints.CreditCardNumber.message = \u043D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u043D\u043E\u043C\u0435\u0440 \u043A\u0440\u0435\u0434\u0438\u0442\u043D\u043E\u0439 \u043A\u0430\u0440\u0442\u044B -org.hibernate.validator.constraints.EAN.message = \u043D\u0435\u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u044B\u0439 \u0448\u0442\u0440\u0438\u0445\u043A\u043E\u0434 {type} -org.hibernate.validator.constraints.Email.message = email \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D \u0432 \u043D\u0435\u0432\u0435\u0440\u043D\u043E\u043C \u0444\u043E\u0440\u043C\u0430\u0442\u0435 -org.hibernate.validator.constraints.Length.message = \u0434\u043B\u0438\u043D\u0430 \u0434\u043E\u043B\u0436\u043D\u0430 \u0431\u044B\u0442\u044C \u043C\u0435\u0436\u0434\u0443 {min} \u0438 {max} -org.hibernate.validator.constraints.CodePointLength.message = \u0434\u043B\u0438\u043D\u0430 \u0434\u043E\u043B\u0436\u043D\u0430 \u0431\u044B\u0442\u044C \u043C\u0435\u0436\u0434\u0443 {min} \u0438 {max} -org.hibernate.validator.constraints.LuhnCheck.message = \u041A\u043E\u043D\u0442\u0440\u043E\u043B\u044C\u043D\u0430\u044F \u0446\u0438\u0444\u0440\u0430 \u0434\u043B\u044F ${validatedValue} \u043D\u0435\u0432\u0435\u0440\u043D\u0430, \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u043E \u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u0443 \u041B\u0443\u043D\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0430 \u0441 \u043E\u0448\u0438\u0431\u043A\u043E\u0439 -org.hibernate.validator.constraints.Mod10Check.message = \u041A\u043E\u043D\u0442\u0440\u043E\u043B\u044C\u043D\u0430\u044F \u0446\u0438\u0444\u0440\u0430 \u0434\u043B\u044F ${validatedValue} \u043D\u0435\u0432\u0435\u0440\u043D\u0430, \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u043E \u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u0443 Mod10 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0430 \u0441 \u043E\u0448\u0438\u0431\u043A\u043E\u0439 -org.hibernate.validator.constraints.Mod11Check.message = \u041A\u043E\u043D\u0442\u0440\u043E\u043B\u044C\u043D\u0430\u044F \u0446\u0438\u0444\u0440\u0430 \u0434\u043B\u044F ${validatedValue} \u043D\u0435\u0432\u0435\u0440\u043D\u0430, \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u043E \u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u0443 Mod11 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0430 \u0441 \u043E\u0448\u0438\u0431\u043A\u043E\u0439 -org.hibernate.validator.constraints.ModCheck.message = \u041A\u043E\u043D\u0442\u0440\u043E\u043B\u044C\u043D\u0430\u044F \u0446\u0438\u0444\u0440\u0430 \u0434\u043B\u044F ${validatedValue} \u043D\u0435\u0432\u0435\u0440\u043D\u0430, \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u043E ${modType} \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0430 \u0441 \u043E\u0448\u0438\u0431\u043A\u043E\u0439 -org.hibernate.validator.constraints.NotBlank.message = \u043D\u0435 \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u043F\u0443\u0441\u0442\u043E -org.hibernate.validator.constraints.NotEmpty.message = \u043D\u0435 \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u043F\u0443\u0441\u0442\u043E -org.hibernate.validator.constraints.ParametersScriptAssert.message = \u0432\u044B\u0440\u0430\u0436\u0435\u043D\u0438\u0435 "{script}" \u043D\u0435 \u044F\u0432\u043B\u044F\u0435\u0442\u0441\u044F \u0438\u0441\u0442\u0438\u043D\u043D\u044B\u043C -org.hibernate.validator.constraints.Range.message = \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043C\u0435\u0436\u0434\u0443 {min} \u0438 {max} -org.hibernate.validator.constraints.SafeHtml.message = \u043C\u043E\u0436\u0435\u0442 \u0441\u043E\u0434\u0435\u0440\u0436\u0430\u0442\u044C \u043D\u0435\u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u044B\u0439 html \u043A\u043E\u043D\u0442\u0435\u043D\u0442 -org.hibernate.validator.constraints.ScriptAssert.message = \u0432\u044B\u0440\u0430\u0436\u0435\u043D\u0438\u0435 "{script}" \u043D\u0435 \u044F\u0432\u043B\u044F\u0435\u0442\u0441\u044F \u0438\u0441\u0442\u0438\u043D\u043D\u044B\u043C -org.hibernate.validator.constraints.URL.message = \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043B\u044C\u043D\u044B\u043C URL +org.hibernate.validator.constraints.CreditCardNumber.message = \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u043d\u043e\u043c\u0435\u0440 \u043a\u0440\u0435\u0434\u0438\u0442\u043d\u043e\u0439 \u043a\u0430\u0440\u0442\u044b +org.hibernate.validator.constraints.Currency.message = \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u0430\u044f \u0434\u0435\u043d\u0435\u0436\u043d\u0430\u044f \u0435\u0434\u0438\u043d\u0438\u0446\u0430 (\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f: {value}) +org.hibernate.validator.constraints.EAN.message = \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0448\u0442\u0440\u0438\u0445\u043e\u0432\u043e\u0439 \u043a\u043e\u0434 {type} +org.hibernate.validator.constraints.Email.message = \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u043c \u0430\u0434\u0440\u0435\u0441\u043e\u043c \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b +org.hibernate.validator.constraints.ISBN.message = \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 ISBN +org.hibernate.validator.constraints.Length.message = \u0434\u043b\u0438\u043d\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0441\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u043e\u0442 {min} \u0434\u043e {max} +org.hibernate.validator.constraints.CodePointLength.message = \u0434\u043b\u0438\u043d\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0441\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u043e\u0442 {min} \u0434\u043e {max} +org.hibernate.validator.constraints.LuhnCheck.message = \u041a\u043e\u043d\u0442\u0440\u043e\u043b\u044c\u043d\u044b\u0439 \u0440\u0430\u0437\u0440\u044f\u0434 \u0434\u043b\u044f ${validatedValue} \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c, \u043e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c\u043d\u043e\u0439 \u0441\u0443\u043c\u043c\u044b Luhn Modulo 10 +org.hibernate.validator.constraints.Mod10Check.message = \u041a\u043e\u043d\u0442\u0440\u043e\u043b\u044c\u043d\u044b\u0439 \u0440\u0430\u0437\u0440\u044f\u0434 \u0434\u043b\u044f ${validatedValue} \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c, \u043e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c\u043d\u043e\u0439 \u0441\u0443\u043c\u043c\u044b Modulo 10 +org.hibernate.validator.constraints.Mod11Check.message = \u041a\u043e\u043d\u0442\u0440\u043e\u043b\u044c\u043d\u044b\u0439 \u0440\u0430\u0437\u0440\u044f\u0434 \u0434\u043b\u044f ${validatedValue} \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c, \u043e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c\u043d\u043e\u0439 \u0441\u0443\u043c\u043c\u044b Modulo 11 +org.hibernate.validator.constraints.ModCheck.message = \u041a\u043e\u043d\u0442\u0440\u043e\u043b\u044c\u043d\u044b\u0439 \u0440\u0430\u0437\u0440\u044f\u0434 \u0434\u043b\u044f ${validatedValue} \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c, \u043e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c\u043d\u043e\u0439 \u0441\u0443\u043c\u043c\u044b ${modType} +org.hibernate.validator.constraints.NotBlank.message = \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043f\u0443\u0441\u0442\u044b\u043c +org.hibernate.validator.constraints.NotEmpty.message = \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043f\u0443\u0441\u0442\u044b\u043c +org.hibernate.validator.constraints.ParametersScriptAssert.message = \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f "{script}" \u043d\u0435 \u0440\u0430\u0432\u0435\u043d true +org.hibernate.validator.constraints.Range.message = \u0434\u043e\u043b\u0436\u043d\u043e \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0432 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0435 \u043e\u0442 {min} \u0434\u043e {max} +org.hibernate.validator.constraints.SafeHtml.message = \u043c\u043e\u0436\u0435\u0442 \u0438\u043c\u0435\u0442\u044c \u043d\u0435\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 html +org.hibernate.validator.constraints.ScriptAssert.message = \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f "{script}" \u043d\u0435 \u0440\u0430\u0432\u0435\u043d true +org.hibernate.validator.constraints.UniqueElements.message = \u0434\u043e\u043b\u0436\u043d\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b +org.hibernate.validator.constraints.URL.message = \u0434\u043e\u043b\u0436\u043d\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 URL + +org.hibernate.validator.constraints.br.CNPJ.message = \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0431\u0440\u0430\u0437\u0438\u043b\u044c\u0441\u043a\u0438\u0439 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u043d\u043e\u043c\u0435\u0440 \u043d\u0430\u043b\u043e\u0433\u043e\u043f\u043b\u0430\u0442\u0435\u043b\u044c\u0449\u0438\u043a\u0430-\u044e\u0440\u0438\u0434\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043b\u0438\u0446\u0430 (CNPJ) +org.hibernate.validator.constraints.br.CPF.message = \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0431\u0440\u0430\u0437\u0438\u043b\u044c\u0441\u043a\u0438\u0439 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u043d\u043e\u043c\u0435\u0440 \u043d\u0430\u043b\u043e\u0433\u043e\u043f\u043b\u0430\u0442\u0435\u043b\u044c\u0449\u0438\u043a\u0430-\u0444\u0438\u0437\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043b\u0438\u0446\u0430 (CPF) +org.hibernate.validator.constraints.br.TituloEleitoral.message = \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0431\u0440\u0430\u0437\u0438\u043b\u044c\u0441\u043a\u0438\u0439 \u043d\u043e\u043c\u0435\u0440 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438 \u0438\u0437\u0431\u0438\u0440\u0430\u0442\u0435\u043b\u044f + +org.hibernate.validator.constraints.pl.REGON.message = \u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u043f\u043e\u043b\u044c\u0441\u043a\u0438\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u043d\u043e\u043c\u0435\u0440 \u043d\u0430\u043b\u043e\u0433\u043e\u043f\u043b\u0430\u0442\u0435\u043b\u044c\u0449\u0438\u043a\u0430 (REGON) +org.hibernate.validator.constraints.pl.NIP.message = \u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u043d\u043e\u043c\u0435\u0440 VAT (NIP) +org.hibernate.validator.constraints.pl.PESEL.message = \u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u043f\u043e\u043b\u044c\u0441\u043a\u0438\u0439 \u043d\u0430\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u043d\u043e\u043c\u0435\u0440 (PESEL) + +org.hibernate.validator.constraints.time.DurationMax.message = \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043a\u043e\u0440\u043e\u0447\u0435${inclusive == true ? ' \u0438\u043b\u0438 \u0440\u0430\u0432\u043d\u043e' : ''}${days == 0 ? '' : days == 1 ? ' 1 \u0434\u043d' : ' ' += days += ' \u0434\u043d'}${hours == 0 ? '' : hours == 1 ? ' 1 \u0447' : ' ' += hours += ' \u0447'}${minutes == 0 ? '' : minutes == 1 ? ' 1 \u043c\u0438\u043d' : ' ' += minutes += ' \u043c\u0438\u043d'}${seconds == 0 ? '' : seconds == 1 ? ' 1 \u0441' : ' ' += seconds += ' \u0441'}${millis == 0 ? '' : millis == 1 ? ' 1 \u043c\u0441' : ' ' += millis += ' \u043c\u0441'}${nanos == 0 ? '' : nanos == 1 ? ' 1 \u043d\u0441' : ' ' += nanos += ' \u043d\u0441'} +org.hibernate.validator.constraints.time.DurationMin.message = \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0434\u043b\u0438\u043d\u043d\u0435\u0435${inclusive == true ? ' \u0438\u043b\u0438 \u0440\u0430\u0432\u043d\u043e' : ''}${days == 0 ? '' : days == 1 ? ' 1 \u0434\u043d' : ' ' += days += ' \u0434\u043d'}${hours == 0 ? '' : hours == 1 ? ' 1 \u0447' : ' ' += hours += ' \u0447'}${minutes == 0 ? '' : minutes == 1 ? ' 1 \u043c\u0438\u043d' : ' ' += minutes += ' \u043c\u0438\u043d'}${seconds == 0 ? '' : seconds == 1 ? ' 1 \u0441' : ' ' += seconds += ' \u0441'}${millis == 0 ? '' : millis == 1 ? ' 1 \u043c\u0441' : ' ' += millis += ' \u043c\u0441'}${nanos == 0 ? '' : nanos == 1 ? ' 1 \u043d\u0441' : ' ' += nanos += ' \u043d\u0441'} diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_sk.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_sk.properties index 24dc9234ea..1268340cd9 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_sk.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_sk.properties @@ -20,10 +20,10 @@ org.hibernate.validator.constraints.EAN.message = nespr\u00e org.hibernate.validator.constraints.Email.message = nespr\u00e1vny form\u00e1t emailovej adresy org.hibernate.validator.constraints.Length.message = d\u013a\u017eka mus\u00ed by\u0165 medzi {min} a {max} org.hibernate.validator.constraints.CodePointLength.message = d\u013a\u017eka mus\u00ed by\u0165 medzi {min} a {max} -org.hibernate.validator.constraints.LuhnCheck.message = Kontroln\u00e1 \u010d\u00edslica pre ${validatedValue} nie je spr\u00e1vna, kontroln\u00fd s\u00fa\u010det Luhn Modulo 10 zlyhal -org.hibernate.validator.constraints.Mod10Check.message = Kontroln\u00e1 \u010d\u00edslica pre ${validatedValue} nie je spr\u00e1vna, kontroln\u00fd s\u00fa\u010det Modulo 10 zlyhal -org.hibernate.validator.constraints.Mod11Check.message = Kontroln\u00e1 \u010d\u00edslica pre ${validatedValue} nie je spr\u00e1vna, kontroln\u00fd s\u00fa\u010det Modulo 11 zlyhal -org.hibernate.validator.constraints.ModCheck.message = Kontroln\u00e1 \u010d\u00edslica pre ${validatedValue} nie je spr\u00e1vna, kontroln\u00fd s\u00fa\u010det ${modType} zlyhal +org.hibernate.validator.constraints.LuhnCheck.message = kontroln\u00e1 \u010d\u00edslica pre ${validatedValue} nie je spr\u00e1vna, kontroln\u00fd s\u00fa\u010det Luhn Modulo 10 zlyhal +org.hibernate.validator.constraints.Mod10Check.message = kontroln\u00e1 \u010d\u00edslica pre ${validatedValue} nie je spr\u00e1vna, kontroln\u00fd s\u00fa\u010det Modulo 10 zlyhal +org.hibernate.validator.constraints.Mod11Check.message = kontroln\u00e1 \u010d\u00edslica pre ${validatedValue} nie je spr\u00e1vna, kontroln\u00fd s\u00fa\u010det Modulo 11 zlyhal +org.hibernate.validator.constraints.ModCheck.message = kontroln\u00e1 \u010d\u00edslica pre ${validatedValue} nie je spr\u00e1vna, kontroln\u00fd s\u00fa\u010det ${modType} zlyhal org.hibernate.validator.constraints.NotBlank.message = nem\u00f4\u017ee by\u0165 pr\u00e1zdne org.hibernate.validator.constraints.NotEmpty.message = nem\u00f4\u017ee by\u0165 pr\u00e1zdne org.hibernate.validator.constraints.ParametersScriptAssert.message = skriptovac\u00ed v\u00fdraz "{script}" nebol vyhodnoten\u00fd na \u00e1no diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_zh.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_zh.properties new file mode 100644 index 0000000000..3c4c3de950 --- /dev/null +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_zh.properties @@ -0,0 +1,53 @@ +javax.validation.constraints.AssertFalse.message = \u5fc5\u987b\u4e3a false +javax.validation.constraints.AssertTrue.message = \u5fc5\u987b\u4e3a true +javax.validation.constraints.DecimalMax.message = \u5fc5\u987b\u5c0f\u4e8e ${inclusive == true ? 'or equal to ' : ''}{value} +javax.validation.constraints.DecimalMin.message = \u5fc5\u987b\u5927\u4e8e ${inclusive == true ? 'or equal to ' : ''}{value} +javax.validation.constraints.Digits.message = \u6570\u5b57\u503c\u8d85\u51fa\u4e86\u8fb9\u754c\uff08\u671f\u671b <{integer} digits>.<{fraction} digits>\uff09 +javax.validation.constraints.Email.message = \u5fc5\u987b\u4e3a\u683c\u5f0f\u89c4\u8303\u7684\u7535\u5b50\u90ae\u4ef6\u5730\u5740 +javax.validation.constraints.Future.message = \u5fc5\u987b\u662f\u672a\u6765\u7684\u65e5\u671f +javax.validation.constraints.FutureOrPresent.message = \u5fc5\u987b\u662f\u73b0\u5728\u6216\u5c06\u6765\u7684\u65e5\u671f +javax.validation.constraints.Max.message = \u5fc5\u987b\u5c0f\u4e8e\u6216\u7b49\u4e8e {value} +javax.validation.constraints.Min.message = \u5fc5\u987b\u5927\u4e8e\u6216\u7b49\u4e8e {value} +javax.validation.constraints.Negative.message = \u5fc5\u987b\u5c0f\u4e8e 0 +javax.validation.constraints.NegativeOrZero.message = \u5fc5\u987b\u5c0f\u4e8e\u6216\u7b49\u4e8e 0 +javax.validation.constraints.NotBlank.message = \u4e0d\u5f97\u4e3a\u7a7a\u767d +javax.validation.constraints.NotEmpty.message = \u4e0d\u5f97\u4e3a\u7a7a +javax.validation.constraints.NotNull.message = \u4e0d\u5f97\u4e3a null +javax.validation.constraints.Null.message = \u5fc5\u987b\u4e3a null +javax.validation.constraints.Past.message = \u5fc5\u987b\u662f\u8fc7\u53bb\u7684\u65e5\u671f +javax.validation.constraints.PastOrPresent.message = \u5fc5\u987b\u662f\u8fc7\u53bb\u6216\u73b0\u5728\u7684\u65e5\u671f +javax.validation.constraints.Pattern.message = \u5fc5\u987b\u4e0e "{regexp}" \u5339\u914d +javax.validation.constraints.Positive.message = \u5fc5\u987b\u5927\u4e8e 0 +javax.validation.constraints.PositiveOrZero.message = \u5fc5\u987b\u5927\u4e8e\u6216\u7b49\u4e8e 0 +javax.validation.constraints.Size.message = \u5927\u5c0f\u5fc5\u987b\u5728 {min} \u548c {max} \u4e4b\u95f4 + +org.hibernate.validator.constraints.CreditCardNumber.message = \u65e0\u6548\u4fe1\u7528\u5361\u5361\u53f7 +org.hibernate.validator.constraints.Currency.message = \u65e0\u6548\u8d27\u5e01\uff08\u5fc5\u987b\u4e3a {value} \u4e4b\u4e00\uff09 +org.hibernate.validator.constraints.EAN.message = \u65e0\u6548 {type} \u6761\u5f62\u7801 +org.hibernate.validator.constraints.Email.message = \u7535\u5b50\u90ae\u4ef6\u5730\u5740\u683c\u5f0f\u4e0d\u89c4\u8303 +org.hibernate.validator.constraints.ISBN.message = \u65e0\u6548 ISBN +org.hibernate.validator.constraints.Length.message = \u957f\u5ea6\u5fc5\u987b\u4ecb\u4e8e {min} \u4e0e {max} \u4e4b\u95f4 +org.hibernate.validator.constraints.CodePointLength.message = \u957f\u5ea6\u5fc5\u987b\u4ecb\u4e8e {min} \u4e0e {max} \u4e4b\u95f4 +org.hibernate.validator.constraints.LuhnCheck.message = ${validatedValue} \u7684\u6821\u9a8c\u4f4d\u65e0\u6548\uff0cLuhn Modulo 10 \u6821\u9a8c\u548c\u5931\u8d25 +org.hibernate.validator.constraints.Mod10Check.message = ${validatedValue} \u7684\u6821\u9a8c\u4f4d\u65e0\u6548\uff0cModulo 10 \u6821\u9a8c\u548c\u5931\u8d25 +org.hibernate.validator.constraints.Mod11Check.message = ${validatedValue} \u7684\u6821\u9a8c\u4f4d\u65e0\u6548\uff0cModulo 11 \u6821\u9a8c\u548c\u5931\u8d25 +org.hibernate.validator.constraints.ModCheck.message = ${validatedValue} \u7684\u6821\u9a8c\u4f4d\u65e0\u6548\uff0c${modType} \u6821\u9a8c\u548c\u5931\u8d25 +org.hibernate.validator.constraints.NotBlank.message = \u53ef\u80fd\u4e0d\u4e3a\u7a7a +org.hibernate.validator.constraints.NotEmpty.message = \u53ef\u80fd\u4e0d\u4e3a\u7a7a +org.hibernate.validator.constraints.ParametersScriptAssert.message = \u811a\u672c\u8868\u8fbe\u5f0f "{script}" \u672a\u6c42\u503c\u4e3a true +org.hibernate.validator.constraints.Range.message = \u5fc5\u987b\u4ecb\u4e8e {min} \u4e0e {max} \u4e4b\u95f4 +org.hibernate.validator.constraints.SafeHtml.message = \u53ef\u80fd\u5177\u6709\u4e0d\u5b89\u5168\u7684 HTML \u5185\u5bb9 +org.hibernate.validator.constraints.ScriptAssert.message = \u811a\u672c\u8868\u8fbe\u5f0f "{script}" \u672a\u6c42\u503c\u4e3a true +org.hibernate.validator.constraints.UniqueElements.message = \u5fc5\u987b\u4ec5\u5305\u542b\u552f\u4e00\u5143\u7d20 +org.hibernate.validator.constraints.URL.message = \u5fc5\u987b\u4e3a\u6709\u6548 URL + +org.hibernate.validator.constraints.br.CNPJ.message = \u65e0\u6548\u5df4\u897f\u4f01\u4e1a\u7eb3\u7a0e\u4eba\u767b\u8bb0\u53f7 (CNPJ) +org.hibernate.validator.constraints.br.CPF.message = \u65e0\u6548\u5df4\u897f\u4e2a\u4eba\u7eb3\u7a0e\u4eba\u767b\u8bb0\u53f7 (CPF) +org.hibernate.validator.constraints.br.TituloEleitoral.message = \u65e0\u6548\u5df4\u897f\u6295\u7968\u4eba\u8eab\u4efd\u8bc1\u53f7 + +org.hibernate.validator.constraints.pl.REGON.message = \u65e0\u6548\u6ce2\u5170\u7eb3\u7a0e\u4eba\u8bc6\u522b\u53f7 (REGON) +org.hibernate.validator.constraints.pl.NIP.message = \u65e0\u6548 VAT \u8bc6\u522b\u53f7 (NIP) +org.hibernate.validator.constraints.pl.PESEL.message = \u65e0\u6548\u6ce2\u5170\u8eab\u4efd\u8bc1\u53f7 (PESEL) + +org.hibernate.validator.constraints.time.DurationMax.message = \u5fc5\u987b\u77ed\u4e8e ${inclusive == true ? ' or equal to' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} +org.hibernate.validator.constraints.time.DurationMin.message = \u5fc5\u987b\u957f\u4e8e ${inclusive == true ? ' or equal to' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_zh_TW.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_zh_TW.properties new file mode 100644 index 0000000000..de7aca4d3e --- /dev/null +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_zh_TW.properties @@ -0,0 +1,53 @@ +javax.validation.constraints.AssertFalse.message = \u5fc5\u9808\u662f false +javax.validation.constraints.AssertTrue.message = \u5fc5\u9808\u662f true +javax.validation.constraints.DecimalMax.message = \u5fc5\u9808\u5c0f\u65bc ${inclusive == true ? 'or equal to ' : ''}{value} +javax.validation.constraints.DecimalMin.message = \u5fc5\u9808\u5927\u65bc ${inclusive == true ? 'or equal to ' : ''}{value} +javax.validation.constraints.Digits.message = \u6578\u503c\u8d85\u51fa\u7bc4\u570d\uff08\u9810\u671f\u70ba <{integer} digits>.<{fraction} digits>\uff09 +javax.validation.constraints.Email.message = \u5fc5\u9808\u662f\u5f62\u5f0f\u5b8c\u6574\u7684\u96fb\u5b50\u90f5\u4ef6\u4f4d\u5740 +javax.validation.constraints.Future.message = \u5fc5\u9808\u662f\u672a\u4f86\u7684\u65e5\u671f +javax.validation.constraints.FutureOrPresent.message = \u5fc5\u9808\u662f\u7576\u5929\u6216\u672a\u4f86\u7684\u65e5\u671f +javax.validation.constraints.Max.message = \u5fc5\u9808\u5c0f\u65bc\u6216\u7b49\u65bc {value} +javax.validation.constraints.Min.message = \u5fc5\u9808\u5927\u65bc\u6216\u7b49\u65bc {value} +javax.validation.constraints.Negative.message = \u5fc5\u9808\u5c0f\u65bc 0 +javax.validation.constraints.NegativeOrZero.message = \u5fc5\u9808\u5c0f\u65bc\u6216\u7b49\u65bc 0 +javax.validation.constraints.NotBlank.message = \u4e0d\u5f97\u7a7a\u767d +javax.validation.constraints.NotEmpty.message = \u4e0d\u5f97\u662f\u7a7a\u7684 +javax.validation.constraints.NotNull.message = \u4e0d\u5f97\u662f\u7a7a\u503c +javax.validation.constraints.Null.message = \u5fc5\u9808\u662f\u7a7a\u503c +javax.validation.constraints.Past.message = \u5fc5\u9808\u662f\u904e\u53bb\u7684\u65e5\u671f +javax.validation.constraints.PastOrPresent.message = \u5fc5\u9808\u662f\u904e\u53bb\u6216\u7576\u5929\u7684\u65e5\u671f +javax.validation.constraints.Pattern.message = \u5fc5\u9808\u7b26\u5408 "{regexp}" +javax.validation.constraints.Positive.message = \u5fc5\u9808\u5927\u65bc 0 +javax.validation.constraints.PositiveOrZero.message = \u5fc5\u9808\u5927\u65bc\u6216\u7b49\u65bc 0 +javax.validation.constraints.Size.message = \u5927\u5c0f\u5fc5\u9808\u5728 {min} \u548c {max} \u4e4b\u9593 + +org.hibernate.validator.constraints.CreditCardNumber.message = \u7121\u6548\u7684\u4fe1\u7528\u5361\u5361\u865f +org.hibernate.validator.constraints.Currency.message = \u7121\u6548\u7684\u8ca8\u5e63\uff08\u5fc5\u9808\u662f {value} \u4e4b\u4e00\uff09 +org.hibernate.validator.constraints.EAN.message = \u7121\u6548\u7684 {type} \u689d\u78bc +org.hibernate.validator.constraints.Email.message = \u4e0d\u662f\u5f62\u5f0f\u5b8c\u6574\u7684\u96fb\u5b50\u90f5\u4ef6\u4f4d\u5740 +org.hibernate.validator.constraints.ISBN.message = \u7121\u6548\u7684 ISBN +org.hibernate.validator.constraints.Length.message = \u9577\u5ea6\u5fc5\u9808\u5728 {min} \u548c {max} \u4e4b\u9593 +org.hibernate.validator.constraints.CodePointLength.message = \u9577\u5ea6\u5fc5\u9808\u5728 {min} \u548c {max} \u4e4b\u9593 +org.hibernate.validator.constraints.LuhnCheck.message = ${validatedValue} \u7684\u6aa2\u67e5\u78bc\u7121\u6548\uff0cLuhn \u6a21\u6578 10 \u7e3d\u548c\u6aa2\u67e5\u5931\u6557 +org.hibernate.validator.constraints.Mod10Check.message = ${validatedValue} \u7684\u6aa2\u67e5\u78bc\u7121\u6548\uff0c\u6a21\u6578 10 \u7e3d\u548c\u6aa2\u67e5\u5931\u6557 +org.hibernate.validator.constraints.Mod11Check.message = ${validatedValue} \u7684\u6aa2\u67e5\u78bc\u7121\u6548\uff0c\u6a21\u6578 11 \u7e3d\u548c\u6aa2\u67e5\u5931\u6557 +org.hibernate.validator.constraints.ModCheck.message = ${validatedValue} \u7684\u6aa2\u67e5\u78bc\u7121\u6548\uff0c${modType} \u7e3d\u548c\u6aa2\u67e5\u5931\u6557 +org.hibernate.validator.constraints.NotBlank.message = \u4e0d\u80fd\u662f\u7a7a\u7684 +org.hibernate.validator.constraints.NotEmpty.message = \u4e0d\u80fd\u662f\u7a7a\u7684 +org.hibernate.validator.constraints.ParametersScriptAssert.message = Script \u8868\u793a\u5f0f "{script}" \u4e0d\u662f\u6c42\u503c\u70ba true +org.hibernate.validator.constraints.Range.message = \u5fc5\u9808\u5728 {min} \u548c {max} \u4e4b\u9593 +org.hibernate.validator.constraints.SafeHtml.message = \u53ef\u80fd\u5177\u6709\u4e0d\u5b89\u5168\u7684 html \u5167\u5bb9 +org.hibernate.validator.constraints.ScriptAssert.message = Script \u8868\u793a\u5f0f "{script}" \u4e0d\u662f\u6c42\u503c\u70ba true +org.hibernate.validator.constraints.UniqueElements.message = \u53ea\u80fd\u5305\u542b\u552f\u4e00\u5143\u7d20 +org.hibernate.validator.constraints.URL.message = \u5fc5\u9808\u662f\u6709\u6548\u7684 URL + +org.hibernate.validator.constraints.br.CNPJ.message = \u7121\u6548\u7684\u5df4\u897f\u516c\u53f8\u7d0d\u7a05\u767b\u9304\u78bc (CNPJ) +org.hibernate.validator.constraints.br.CPF.message = \u7121\u6548\u7684\u5df4\u897f\u500b\u4eba\u7d0d\u7a05\u767b\u9304\u78bc (CPF) +org.hibernate.validator.constraints.br.TituloEleitoral.message = \u7121\u6548\u7684\u5df4\u897f\u9078\u6c11 ID \u5361\u865f + +org.hibernate.validator.constraints.pl.REGON.message = \u7121\u6548\u7684\u6ce2\u862d\u7d0d\u7a05\u4eba\u8b58\u5225\u78bc (REGON) +org.hibernate.validator.constraints.pl.NIP.message = \u7121\u6548\u7684 VAT \u8b58\u5225\u78bc (NIP) +org.hibernate.validator.constraints.pl.PESEL.message = \u7121\u6548\u7684\u6ce2\u862d\u570b\u5bb6\u8b58\u5225\u78bc (PESEL) + +org.hibernate.validator.constraints.time.DurationMax.message = \u5fc5\u9808\u77ed\u65bc ${inclusive == true ? ' or equal to' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} +org.hibernate.validator.constraints.time.DurationMin.message = \u5fc5\u9808\u9577\u65bc ${inclusive == true ? ' or equal to' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} diff --git a/engine/src/main/xjb/binding-customization.xjb b/engine/src/main/xjb/binding-customization.xjb deleted file mode 100644 index 8dc5132c86..0000000000 --- a/engine/src/main/xjb/binding-customization.xjb +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/engine/src/test/java/org/hibernate/validator/test/cfg/CascadingWithConstraintMappingTest.java b/engine/src/test/java/org/hibernate/validator/test/cfg/CascadingWithConstraintMappingTest.java index 829f6d6aa2..212ed48509 100644 --- a/engine/src/test/java/org/hibernate/validator/test/cfg/CascadingWithConstraintMappingTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/cfg/CascadingWithConstraintMappingTest.java @@ -7,8 +7,6 @@ package org.hibernate.validator.test.cfg; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.pathWith; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; @@ -45,11 +43,11 @@ public void testProgrammaticCascadingValidationFieldAccess() { ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( C.class ) - .property( "string", FIELD ) - .constraint( new NotNullDef() ) + .field( "string" ) + .constraint( new NotNullDef() ) .type( A.class ) - .property( "c", FIELD ) - .valid(); + .field( "c" ) + .valid(); config.addMapping( newMapping ); Validator validator = config.buildValidatorFactory().getValidator(); @@ -69,11 +67,11 @@ public void testProgrammaticCascadingValidationMethodAccess() { ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( C.class ) - .property( "string", METHOD ) - .constraint( new NotNullDef() ) + .getter( "string" ) + .constraint( new NotNullDef() ) .type( A.class ) - .property( "c", METHOD ) - .valid(); + .getter( "c" ) + .valid(); config.addMapping( newMapping ); Validator validator = config.buildValidatorFactory().getValidator(); @@ -93,11 +91,11 @@ public void testProgrammaticCascadingMethodValidation() { ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( C.class ) - .property( "string", METHOD ) - .constraint( new NotNullDef() ) + .getter( "string" ) + .constraint( new NotNullDef() ) .type( A.class ) - .property( "c", METHOD ) - .valid(); + .getter( "c" ) + .valid(); config.addMapping( newMapping ); Validator validator = config.buildValidatorFactory().getValidator(); @@ -120,11 +118,11 @@ public void testProgrammaticCascadingOnArray() { ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( Bean.class ) - .property( "property", FIELD ) - .constraint( new NotNullDef() ) + .field( "property" ) + .constraint( new NotNullDef() ) .type( ArrayHolder.class ) - .property( "beans", FIELD ) - .valid(); + .field( "beans" ) + .valid(); config.addMapping( newMapping ); Validator validator = config.buildValidatorFactory().getValidator(); 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 39583d57cc..f5af6eb037 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 @@ -31,10 +31,12 @@ public class ConfigurationFilePropertiesTest { /** * The following test assumes that the file META-INF/validation.xml is present and * contains: + *
    {@code
     	 * true
     	 * true
     	 * true
     	 * true
    +	 * }
    * * The Maven build runs this test in a separate execution of surefire, which adds the * path to the required file onto its classpath. @@ -142,7 +144,7 @@ private T findPropertyOfType(Object subject, Class clazz) boolean accessible = field.isAccessible(); try { field.setAccessible( true ); - return (T) field.get( subject ); + return clazz.cast( field.get( subject ) ); } catch (IllegalArgumentException e) { e.printStackTrace(); diff --git a/engine/src/test/java/org/hibernate/validator/test/cfg/ConstraintMappingContributorConfiguredInValidationXmlTest.java b/engine/src/test/java/org/hibernate/validator/test/cfg/ConstraintMappingContributorConfiguredInValidationXmlTest.java index 5bf8fbcced..3f419025a2 100644 --- a/engine/src/test/java/org/hibernate/validator/test/cfg/ConstraintMappingContributorConfiguredInValidationXmlTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/cfg/ConstraintMappingContributorConfiguredInValidationXmlTest.java @@ -6,8 +6,6 @@ */ package org.hibernate.validator.test.cfg; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; @@ -24,9 +22,9 @@ import org.hibernate.validator.cfg.defs.NotNullDef; import org.hibernate.validator.spi.cfg.ConstraintMappingContributor; import org.hibernate.validator.testutil.TestForIssue; -import org.testng.annotations.Test; import org.hibernate.validator.testutil.ValidationXmlTestHelper; import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.Test; /** * Tests the contribution of constraint mappings via a contributor configured in {@code META-INF/validation.xml}. @@ -92,9 +90,9 @@ public static class MyConstraintMappingContributor1 implements ConstraintMapping public void createConstraintMappings(ConstraintMappingBuilder builder) { builder.addConstraintMapping() .type( Marathon.class ) - .property( "name", METHOD ) + .getter( "name" ) .constraint( new NotNullDef() ) - .property( "numberOfHelpers", FIELD ) + .field( "numberOfHelpers" ) .constraint( new MinDef().value( 1 ) ); } } @@ -105,7 +103,7 @@ public static class MyConstraintMappingContributor2 implements ConstraintMapping public void createConstraintMappings(ConstraintMappingBuilder builder) { builder.addConstraintMapping() .type( Runner.class ) - .property( "paidEntryFee", FIELD ) + .field( "paidEntryFee" ) .constraint( new AssertTrueDef() ); } } diff --git a/engine/src/test/java/org/hibernate/validator/test/cfg/ConstraintMappingTest.java b/engine/src/test/java/org/hibernate/validator/test/cfg/ConstraintMappingTest.java index d0fc924caa..e1c5ca96fc 100644 --- a/engine/src/test/java/org/hibernate/validator/test/cfg/ConstraintMappingTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/cfg/ConstraintMappingTest.java @@ -6,18 +6,16 @@ */ package org.hibernate.validator.test.cfg; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; +import static org.hibernate.validator.testutils.ConstraintValidatorInitializationHelper.getDummyConstraintCreationContext; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import java.lang.annotation.ElementType; import java.util.Arrays; import java.util.Calendar; -import java.util.Collections; import java.util.GregorianCalendar; import java.util.List; import java.util.Set; @@ -50,14 +48,11 @@ import org.hibernate.validator.constraints.Range; import org.hibernate.validator.group.GroupSequenceProvider; import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping; -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.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.util.TypeResolutionHelper; import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider; import org.hibernate.validator.testutils.ValidatorUtil; import org.testng.annotations.BeforeMethod; @@ -92,10 +87,10 @@ public void testNullConstraintMapping() { @Test public void testConstraintMappingWithConstraintDefs() { mapping.type( Marathon.class ) - .property( "name", METHOD ) - .constraint( new NotNullDef() ) - .property( "numberOfHelpers", FIELD ) - .constraint( new MinDef().value( 1 ) ); + .getter( "name" ) + .constraint( new NotNullDef() ) + .field( "numberOfHelpers" ) + .constraint( new MinDef().value( 1 ) ); BeanConfiguration beanConfiguration = getBeanConfiguration( Marathon.class ); assertNotNull( beanConfiguration ); @@ -106,10 +101,10 @@ public void testConstraintMappingWithConstraintDefs() { @Test public void testConstraintMappingWithGenericConstraints() { mapping.type( Marathon.class ) - .property( "name", METHOD ) - .constraint( new GenericConstraintDef<>( NotNull.class ) ) - .property( "numberOfHelpers", FIELD ) - .constraint( new GenericConstraintDef<>( Min.class ).param( "value", 1L ) ); + .getter( "name" ) + .constraint( new GenericConstraintDef<>( NotNull.class ) ) + .field( "numberOfHelpers" ) + .constraint( new GenericConstraintDef<>( Min.class ).param( "value", 1L ) ); BeanConfiguration beanConfiguration = getBeanConfiguration( Marathon.class ); assertNotNull( beanConfiguration ); @@ -120,9 +115,9 @@ public void testConstraintMappingWithGenericConstraints() { @Test public void testDefConstraintFollowedByGenericConstraint() { mapping.type( Marathon.class ) - .property( "numberOfHelpers", FIELD ) - .constraint( new MinDef().value( 1 ) ) - .constraint( new GenericConstraintDef<>( Min.class ).param( "value", 2L ) ); + .field( "numberOfHelpers" ) + .constraint( new MinDef().value( 1 ) ) + .constraint( new GenericConstraintDef<>( Min.class ).param( "value", 2L ) ); BeanConfiguration beanConfiguration = getBeanConfiguration( Marathon.class ); assertNotNull( beanConfiguration ); @@ -142,8 +137,8 @@ public void testNoConstraintViolationForUnmappedEntity() { @Test public void testSingleConstraint() { mapping.type( Marathon.class ) - .property( "name", METHOD ) - .constraint( new NotNullDef() ); + .getter( "name" ) + .constraint( new NotNullDef() ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); @@ -156,10 +151,8 @@ public void testSingleConstraint() { @Test public void testThatSpecificParameterCanBeSetAfterInvokingMethodFromBaseType() { mapping.type( Marathon.class ) - .property( "name", METHOD ) - .constraint( - new SizeDef().message( "too short" ).min( 3 ) - ); + .getter( "name" ) + .constraint( new SizeDef().message( "too short" ).min( 3 ) ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); @@ -174,11 +167,9 @@ public void testThatSpecificParameterCanBeSetAfterInvokingMethodFromBaseType() { @Test(description = "HV-404: Introducing ConstraintsForType#genericConstraint(Class) allows to set specific parameters on following specific constraints.") public void testThatSpecificParameterCanBeSetAfterAddingGenericConstraintDef() { mapping.type( Marathon.class ) - .constraint( - new GenericConstraintDef<>( MarathonConstraint.class ).param( "minRunner", 1 ) - ) - .property( "name", METHOD ) - .constraint( new SizeDef().message( "name too short" ).min( 3 ) ); + .constraint( new GenericConstraintDef<>( MarathonConstraint.class ).param( "minRunner", 1 ) ) + .getter( "name" ) + .constraint( new SizeDef().message( "name too short" ).min( 3 ) ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); @@ -194,11 +185,11 @@ public void testThatSpecificParameterCanBeSetAfterAddingGenericConstraintDef() { @Test public void testInheritedConstraint() { mapping.type( Marathon.class ) - .property( "name", METHOD ) - .constraint( new NotNullDef() ) + .getter( "name" ) + .constraint( new NotNullDef() ) .type( Tournament.class ) - .property( "tournamentDate", METHOD ) - .constraint( new FutureDef() ); + .getter( "tournamentDate" ) + .constraint( new FutureDef() ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); @@ -217,11 +208,11 @@ public void testInheritedConstraint() { @Test public void testValid() { mapping.type( Marathon.class ) - .property( "runners", METHOD ) - .valid() + .getter( "runners" ) + .valid() .type( Runner.class ) - .property( "paidEntryFee", FIELD ) - .constraint( new AssertTrueDef() ); + .field( "paidEntryFee" ) + .constraint( new AssertTrueDef() ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); @@ -241,12 +232,12 @@ public void testValid() { @Test public void testValidWithGroupConversion() { mapping.type( Marathon.class ) - .property( "runners", METHOD ) - .valid() - .convertGroup( Default.class ).to( Foo.class ) + .getter( "runners" ) + .valid() + .convertGroup( Default.class ).to( Foo.class ) .type( Runner.class ) - .property( "paidEntryFee", FIELD ) - .constraint( new AssertTrueDef().groups( Foo.class ) ); + .field( "paidEntryFee" ) + .constraint( new AssertTrueDef().groups( Foo.class ) ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); @@ -263,14 +254,14 @@ public void testValidWithGroupConversion() { @Test public void testValidWithSeveralGroupConversions() { mapping.type( Marathon.class ) - .property( "runners", METHOD ) - .valid() - .convertGroup( Default.class ).to( Foo.class ) - .convertGroup( Bar.class ).to( Default.class ) + .getter( "runners" ) + .valid() + .convertGroup( Default.class ).to( Foo.class ) + .convertGroup( Bar.class ).to( Default.class ) .type( Runner.class ) - .property( "paidEntryFee", FIELD ) - .constraint( new AssertTrueDef().groups( Foo.class ) ) - .constraint( new AssertTrueDef().message( "really, it must be true" ) ); + .field( "paidEntryFee" ) + .constraint( new AssertTrueDef().groups( Foo.class ) ) + .constraint( new AssertTrueDef().message( "really, it must be true" ) ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); @@ -291,18 +282,18 @@ public void testValidWithSeveralGroupConversions() { ) public void testSingleConstraintWrongAccessType() throws Throwable { mapping.type( Marathon.class ) - .property( "numberOfHelpers", METHOD ) - .constraint( new NotNullDef() ); + .getter( "numberOfHelpers" ) + .constraint( new NotNullDef() ); } @Test public void testDefaultGroupSequence() { mapping.type( Marathon.class ) .defaultGroupSequence( Foo.class, Marathon.class ) - .property( "name", METHOD ) - .constraint( new NotNullDef().groups( Foo.class ) ) - .property( "runners", METHOD ) - .constraint( new NotEmptyDef() ); + .getter( "name" ) + .constraint( new NotNullDef().groups( Foo.class ) ) + .getter( "runners" ) + .constraint( new NotEmptyDef() ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); @@ -324,10 +315,10 @@ public void testDefaultGroupSequence() { public void testDefaultGroupSequenceProvider() { mapping.type( Marathon.class ) .defaultGroupSequenceProviderClass( MarathonDefaultGroupSequenceProvider.class ) - .property( "name", METHOD ) - .constraint( new NotNullDef().groups( Foo.class ) ) - .property( "runners", METHOD ) - .constraint( new NotEmptyDef() ); + .getter( "name" ) + .constraint( new NotNullDef().groups( Foo.class ) ) + .getter( "runners" ) + .constraint( new NotEmptyDef() ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); @@ -353,10 +344,10 @@ public void testProgrammaticDefaultGroupSequenceAndDefaultGroupSequenceProviderD mapping.type( Marathon.class ) .defaultGroupSequence( Foo.class, Marathon.class ) .defaultGroupSequenceProviderClass( MarathonDefaultGroupSequenceProvider.class ) - .property( "name", METHOD ) - .constraint( new NotNullDef().groups( Foo.class ) ) - .property( "runners", METHOD ) - .constraint( new NotEmptyDef() ); + .getter( "name" ) + .constraint( new NotNullDef().groups( Foo.class ) ) + .getter( "runners" ) + .constraint( new NotEmptyDef() ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); validator.validate( new Marathon() ); @@ -369,8 +360,8 @@ public void testProgrammaticDefaultGroupSequenceAndDefaultGroupSequenceProviderD public void testProgrammaticDefaultGroupSequenceDefinedOnClassWithGroupProviderAnnotation() { mapping.type( B.class ) .defaultGroupSequence( Foo.class, B.class ) - .property( "b", FIELD ) - .constraint( new NotNullDef() ); + .field( "b" ) + .constraint( new NotNullDef() ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); validator.validate( new B() ); @@ -383,8 +374,8 @@ public void testProgrammaticDefaultGroupSequenceDefinedOnClassWithGroupProviderA public void testProgrammaticDefaultGroupSequenceProviderDefinedOnClassWithGroupSequenceAnnotation() { mapping.type( A.class ) .defaultGroupSequenceProviderClass( ADefaultGroupSequenceProvider.class ) - .property( "a", FIELD ) - .constraint( new NotNullDef() ); + .field( "a" ) + .constraint( new NotNullDef() ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); validator.validate( new A() ); @@ -393,9 +384,9 @@ public void testProgrammaticDefaultGroupSequenceProviderDefinedOnClassWithGroupS @Test public void testMultipleConstraintOfTheSameType() { mapping.type( Marathon.class ) - .property( "name", METHOD ) - .constraint( new SizeDef().min( 5 ) ) - .constraint( new SizeDef().min( 10 ) ); + .getter( "name" ) + .constraint( new SizeDef().min( 5 ) ) + .constraint( new SizeDef().min( 10 ) ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); @@ -469,8 +460,8 @@ public void testNullBean() { @Test(description = "HV-355 (parameter names of RangeDef wrong)") public void testRangeDef() { mapping.type( Runner.class ) - .property( "age", METHOD ) - .constraint( new RangeDef().min( 12 ).max( 99 ) ); + .getter( "age" ) + .constraint( new RangeDef().min( 12 ).max( 99 ) ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); Set> violations = validator.validate( new Runner() ); @@ -482,12 +473,12 @@ public void testRangeDef() { @Test(description = "HV-444") public void testDefaultGroupSequenceDefinedOnClassWithNoConstraints() { mapping.type( Marathon.class ) - .property( "name", METHOD ) - .constraint( new NotNullDef().groups( Foo.class ) ) - .property( "runners", METHOD ) - .constraint( new NotEmptyDef() ) + .getter( "name" ) + .constraint( new NotNullDef().groups( Foo.class ) ) + .getter( "runners" ) + .constraint( new NotEmptyDef() ) .type( ExtendedMarathon.class ) - .defaultGroupSequence( Foo.class, ExtendedMarathon.class ); + .defaultGroupSequence( Foo.class, ExtendedMarathon.class ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); @@ -508,8 +499,8 @@ public void testDefaultGroupSequenceDefinedOnClassWithNoConstraints() { @Test public void testProgrammaticAndAnnotationFieldConstraintsAddUp() { mapping.type( User.class ) - .property( "firstName", ElementType.FIELD ) - .constraint( new SizeDef().min( 2 ).max( 10 ) ); + .field( "firstName" ) + .constraint( new SizeDef().min( 2 ).max( 10 ) ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); Set> violations = validator.validateProperty( new User( "", "" ), "firstName" ); @@ -523,8 +514,8 @@ public void testProgrammaticAndAnnotationFieldConstraintsAddUp() { @Test public void testProgrammaticAndAnnotationPropertyConstraintsAddUp() { mapping.type( User.class ) - .property( "lastName", ElementType.METHOD ) - .constraint( new SizeDef().min( 4 ).max( 10 ) ); + .getter( "lastName" ) + .constraint( new SizeDef().min( 4 ).max( 10 ) ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); Set> violations = validator.validateProperty( new User( "", "" ), "lastName" ); @@ -535,11 +526,32 @@ public void testProgrammaticAndAnnotationPropertyConstraintsAddUp() { ); } + @Test + @SuppressWarnings("deprecation") + public void testDeprecatedPropertyMethodForFieldAndGetterProgrammaticConstraintDefinition() { + mapping.type( Marathon.class ) + .property( "name", ElementType.METHOD ) + .constraint( new SizeDef().min( 5 ) ) + .constraint( new SizeDef().min( 10 ) ) + .property( "runners", ElementType.FIELD ) + .constraint( new SizeDef().max( 10 ).min( 1 ) ); + config.addMapping( mapping ); + Validator validator = config.buildValidatorFactory().getValidator(); + + Marathon marathon = new Marathon(); + marathon.setName( "Foo" ); + + Set> violations = validator.validate( marathon ); + assertThat( violations ).containsOnlyViolations( + violationOf( Size.class ).withMessage( "size must be between 10 and 2147483647" ), + violationOf( Size.class ).withMessage( "size must be between 5 and 2147483647" ), + violationOf( Size.class ).withMessage( "size must be between 1 and 10" ) + ); + } + private BeanConfiguration getBeanConfiguration(Class type) { Set> beanConfigurations = mapping.getBeanConfigurations( - new ConstraintHelper(), - new TypeResolutionHelper(), - new ValueExtractorManager( Collections.emptySet() ) + getDummyConstraintCreationContext() ); for ( BeanConfiguration beanConfiguration : beanConfigurations ) { @@ -556,7 +568,7 @@ private BeanConfiguration getBeanConfiguration(Class type) { private ConstrainedField getConstrainedField(BeanConfiguration beanConfiguration, String fieldName) { for ( ConstrainedElement constrainedElement : beanConfiguration.getConstrainedElements() ) { if ( constrainedElement.getKind() == ConstrainedElementKind.FIELD && - ( (ConstrainedField) constrainedElement ).getField().getName().equals( fieldName ) ) { + ( (ConstrainedField) constrainedElement ).getField().getPropertyName().equals( fieldName ) ) { return (ConstrainedField) constrainedElement; } } @@ -566,8 +578,8 @@ private ConstrainedField getConstrainedField(BeanConfiguration beanConfigurat private ConstrainedExecutable getConstrainedExecutable(BeanConfiguration beanConfiguration, String executableName) { for ( ConstrainedElement constrainedElement : beanConfiguration.getConstrainedElements() ) { - if ( constrainedElement.getKind() == ConstrainedElementKind.METHOD && - ( (ConstrainedExecutable) constrainedElement ).getExecutable().getName().equals( executableName ) ) { + if ( constrainedElement.getKind().isMethod() && + ( (ConstrainedExecutable) constrainedElement ).getCallable().getName().equals( executableName ) ) { return (ConstrainedExecutable) constrainedElement; } } diff --git a/engine/src/test/java/org/hibernate/validator/test/cfg/ConstraintMappingWithAnnotationProcessingOptionsTest.java b/engine/src/test/java/org/hibernate/validator/test/cfg/ConstraintMappingWithAnnotationProcessingOptionsTest.java index 4f081c5087..082cdbee00 100644 --- a/engine/src/test/java/org/hibernate/validator/test/cfg/ConstraintMappingWithAnnotationProcessingOptionsTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/cfg/ConstraintMappingWithAnnotationProcessingOptionsTest.java @@ -6,8 +6,6 @@ */ package org.hibernate.validator.test.cfg; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; @@ -93,7 +91,7 @@ public void testIgnoreClassConstraints() { public void testIgnoreAnnotationsOnProperty() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( Foo.class ) - .property( "property", FIELD ) + .field( "property" ) .ignoreAnnotations( true ); config.addMapping( mapping ); @@ -105,7 +103,7 @@ public void testIgnoreAnnotationsOnProperty() { public void testIgnoreAnnotationsRespectsFieldVsGetterAccess() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( Foo.class ) - .property( "property", METHOD ) + .getter( "property" ) .ignoreAnnotations( true ); config.addMapping( mapping ); @@ -123,7 +121,7 @@ public void testConvertNotNullToNull() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( Bar.class ) - .property( "property", FIELD ) + .field( "property" ) .ignoreAnnotations( true ) .constraint( new NullDef() ); config.addMapping( mapping ); diff --git a/engine/src/test/java/org/hibernate/validator/test/cfg/ConstructorConstraintMappingTest.java b/engine/src/test/java/org/hibernate/validator/test/cfg/ConstructorConstraintMappingTest.java index d9fb71647a..8d0cd738a0 100644 --- a/engine/src/test/java/org/hibernate/validator/test/cfg/ConstructorConstraintMappingTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/cfg/ConstructorConstraintMappingTest.java @@ -14,7 +14,6 @@ import static org.hibernate.validator.testutils.ValidatorUtil.getConfiguration; import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -124,7 +123,7 @@ public void testCascadingConstructorParameterDefinitionWithGroupConversion() thr .valid() .convertGroup( Default.class ).to( TestGroup.class ) .type( User.class ) - .property( "name", ElementType.FIELD ) + .field( "name" ) .constraint( new NotNullDef().message( "name must not be null" ).groups( TestGroup.class ) ); config.addMapping( mapping ); diff --git a/engine/src/test/java/org/hibernate/validator/test/cfg/MethodConstraintMappingTest.java b/engine/src/test/java/org/hibernate/validator/test/cfg/MethodConstraintMappingTest.java index 06b1298695..f888deecdd 100644 --- a/engine/src/test/java/org/hibernate/validator/test/cfg/MethodConstraintMappingTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/cfg/MethodConstraintMappingTest.java @@ -14,7 +14,6 @@ import static org.testng.Assert.assertNull; import static org.testng.Assert.fail; -import java.lang.annotation.ElementType; import java.util.Set; import javax.validation.ConstraintDeclarationException; @@ -94,7 +93,7 @@ public void testCascadingMethodReturnDefinitionWithGroupConversion() { .valid() .convertGroup( Default.class ).to( TestGroup.class ) .type( Message.class ) - .property( "message", ElementType.FIELD ) + .field( "message" ) .constraint( new NotNullDef() .message( "message must not be null" ) @@ -607,7 +606,7 @@ public void testProgrammaticAndAnnotationReturnValueConstraintsAddUp() { public void constraintConfiguredOnPropertyIsEvaluatedByMethodValidation() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) - .property( "hello", ElementType.METHOD ) + .getter( "hello" ) .constraint( new NotNullDef() ); config.addMapping( mapping ); @@ -636,7 +635,7 @@ public void constraintConfiguredOnPropertyIsEvaluatedByMethodValidation() { public void cascadeConfiguredOnPropertyIsEvaluatedByMethodValidation() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingService.class ) - .property( "user", ElementType.METHOD ) + .getter( "user" ) .valid(); config.addMapping( mapping ); @@ -666,7 +665,7 @@ public void cascadeConfiguredOnPropertyIsEvaluatedByMethodValidation() { public void constraintConfiguredOnFieldIsNotEvaluatedByMethodValidation() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingServiceImpl.class ) - .property( "hello", ElementType.FIELD ) + .field( "hello" ) .constraint( new NotNullDef() ); config.addMapping( mapping ); @@ -678,7 +677,7 @@ public void constraintConfiguredOnFieldIsNotEvaluatedByMethodValidation() { public void cascadeConfiguredOnFieldIsNotEvaluatedByMethodValidation() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( GreetingServiceImpl.class ) - .property( "user", ElementType.FIELD ) + .field( "user" ) .valid(); config.addMapping( mapping ); diff --git a/engine/src/test/java/org/hibernate/validator/test/cfg/MultipleConstraintMappingsTest.java b/engine/src/test/java/org/hibernate/validator/test/cfg/MultipleConstraintMappingsTest.java index b4afab011e..2af6a5165f 100644 --- a/engine/src/test/java/org/hibernate/validator/test/cfg/MultipleConstraintMappingsTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/cfg/MultipleConstraintMappingsTest.java @@ -6,17 +6,19 @@ */ package org.hibernate.validator.test.cfg; +import static org.hibernate.validator.testutils.ValidatorUtil.getConfiguration; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + import java.util.Arrays; import java.util.Date; import java.util.List; + import javax.validation.GroupDefinitionException; import javax.validation.ValidationException; import javax.validation.Validator; import javax.validation.metadata.BeanDescriptor; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - import org.hibernate.validator.HibernateValidator; import org.hibernate.validator.HibernateValidatorConfiguration; import org.hibernate.validator.cfg.ConstraintMapping; @@ -26,10 +28,8 @@ import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider; import org.hibernate.validator.testutil.TestForIssue; -import static java.lang.annotation.ElementType.METHOD; -import static org.hibernate.validator.testutils.ValidatorUtil.getConfiguration; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; /** * Unit test for {@link org.hibernate.validator.cfg.ConstraintMapping} et al. @@ -49,12 +49,12 @@ public void setUp() { public void testMultipleConstraintMappings() { ConstraintMapping marathonMapping = config.createConstraintMapping(); marathonMapping.type( Marathon.class ) - .property( "name", METHOD ) + .getter( "name" ) .constraint( new NotNullDef() ); ConstraintMapping runnerMapping = config.createConstraintMapping(); runnerMapping.type( Runner.class ) - .property( "name", METHOD ) + .getter( "name" ) .constraint( new NotNullDef() ); config.addMapping( marathonMapping ); @@ -122,9 +122,9 @@ public void testConfigurationOfSequenceProviderAndGroupSequenceCausesException() public void testSamePropertyConfiguredSeveralTimesCausesException() { ConstraintMapping marathonMapping = config.createConstraintMapping(); marathonMapping.type( Marathon.class ) - .property( "name", METHOD ) + .getter( "name" ) .constraint( new NotNullDef() ) - .property( "name", METHOD ); + .getter( "name" ); } @Test(expectedExceptions = ValidationException.class, expectedExceptionsMessageRegExp = "HV000173.*") diff --git a/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticConstraintDefinitionsTest.java b/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticConstraintDefinitionsTest.java index 7c56d67a25..d84d947da5 100644 --- a/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticConstraintDefinitionsTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticConstraintDefinitionsTest.java @@ -13,7 +13,6 @@ import static org.testng.Assert.fail; import java.lang.annotation.Annotation; -import java.lang.annotation.ElementType; import java.util.Set; import javax.validation.ConstraintViolation; @@ -61,14 +60,15 @@ public void countrySpecificProgrammaticDefinition() { "invalid Brazilian corporate taxpayer registry number (CNPJ)" ); - doProgrammaticTest( REGON.class, new REGONDef(), "49905531368510", "49905531368512", "Invalid Polish Taxpayer Identification Number (REGON)" ); - doProgrammaticTest( REGON.class, new REGONDef(), "858336997", "691657185", "Invalid Polish Taxpayer Identification Number (REGON)" ); - doProgrammaticTest( PESEL.class, new PESELDef(), "12252918020", "44051401358", "Invalid Polish National Identification Number (PESEL)" ); - doProgrammaticTest( NIP.class, new NIPDef(), "1786052059", "2596048505", "Invalid VAT Identification Number (NIP)" ); + doProgrammaticTest( REGON.class, new REGONDef(), "49905531368510", "49905531368512", "invalid Polish Taxpayer Identification Number (REGON)" ); + doProgrammaticTest( REGON.class, new REGONDef(), "858336997", "691657185", "invalid Polish Taxpayer Identification Number (REGON)" ); + doProgrammaticTest( PESEL.class, new PESELDef(), "12252918020", "44051401358", "invalid Polish National Identification Number (PESEL)" ); + doProgrammaticTest( NIP.class, new NIPDef(), "1786052059", "2596048505", "invalid VAT Identification Number (NIP)" ); } @Test + @SuppressWarnings("deprecation") public void safeHtmlProgrammaticDefinition() { doProgrammaticTest( SafeHtml.class, new SafeHtmlDef().whitelistType( SafeHtml.WhiteListType.BASIC ), "1234qwertd>", 1 ); doProgrammaticTest( SafeHtml.class, new SafeHtmlDef().whitelistType( SafeHtml.WhiteListType.NONE ), "test", 0 ); @@ -225,7 +225,7 @@ private void doProgrammaticTest(Class constraint, Constrai ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( Bar.class ) .ignoreAllAnnotations() - .property( "source", ElementType.FIELD ) + .field( "source" ) .constraint( def ); Validator validator = config.addMapping( mapping ) @@ -248,7 +248,7 @@ private void doProgrammaticTest(Class constraint, Constrai ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( OtherPerson.class ) .ignoreAllAnnotations() - .property( "number", ElementType.FIELD ) + .field( "number" ) .constraint( def ); Validator validator = config.addMapping( mapping ) diff --git a/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticContainerElementConstraintsForFieldTest.java b/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticContainerElementConstraintsForFieldTest.java index cb9aa67121..cb2bf72b5f 100644 --- a/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticContainerElementConstraintsForFieldTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticContainerElementConstraintsForFieldTest.java @@ -7,7 +7,6 @@ package org.hibernate.validator.test.cfg; -import static java.lang.annotation.ElementType.FIELD; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; import static org.hibernate.validator.testutils.ValidatorUtil.getConfiguration; @@ -30,9 +29,11 @@ import org.hibernate.validator.HibernateValidator; import org.hibernate.validator.HibernateValidatorConfiguration; import org.hibernate.validator.cfg.ConstraintMapping; +import org.hibernate.validator.cfg.defs.LengthDef; 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.constraints.Length; import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.testutil.TestForIssue; import org.testng.annotations.BeforeMethod; @@ -56,10 +57,10 @@ public void canDeclareContainerElementConstraintsForFieldProgrammatically() { ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( FishTank.class ) - .property( "model", FIELD ) + .field( "model" ) .containerElementType() .constraint( new SizeDef().max( 5 ) ) - .property( "fishCountByType", FIELD ) + .field( "fishCountByType" ) .containerElementType( 0 ) .constraint( new SizeDef().min( 3 ).max( 10 ) ) .containerElementType( 1 ) @@ -85,7 +86,7 @@ public void canDeclareNestedContainerElementConstraintsForFieldProgrammatically( ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( FishTank.class ) - .property( "fishOfTheMonth", FIELD ) + .field( "fishOfTheMonth" ) .containerElementType( 1, 0 ) .constraint( new NotNullDef() ); @@ -105,7 +106,7 @@ public void canDeclareDeeplyNestedContainerElementConstraintsForFieldProgrammati ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( FishTank.class ) - .property( "tagsOfFishOfTheMonth", FIELD ) + .field( "tagsOfFishOfTheMonth" ) .containerElementType( 0, 1, 0 ) .constraint( new NotNullDef() ); @@ -125,11 +126,11 @@ public void canDeclareContainerElementCascadesForFieldProgrammatically() { ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( FishTank.class ) - .property( "boss", FIELD ) + .field( "boss" ) .containerElementType() .valid() .type( Fish.class ) - .property( "name", FIELD ) + .field( "name" ) .constraint( new NotNullDef() ); config.addMapping( newMapping ); @@ -142,6 +143,29 @@ public void canDeclareContainerElementCascadesForFieldProgrammatically() { ); } + @Test + @TestForIssue(jiraKey = "HV-1614") + public void canDeclareDeeplyNestedContainerElementConstraintsOnMultipleDifferentTypeArgumentsForFieldProgrammatically() { + ConstraintMapping newMapping = config.createConstraintMapping(); + newMapping + .type( FishTank.class ) + .field( "tagsOfFishOfTheMonth" ) + .containerElementType( 0, 0 ) + .constraint( new LengthDef().min( 10 ).max( 20 ) ) + .containerElementType( 0, 1, 0 ) + .constraint( new NotNullDef() ); + + config.addMapping( newMapping ); + Validator validator = config.buildValidatorFactory().getValidator(); + + Set> violations = validator.validate( new FishTank() ); + + assertThat( violations ).containsOnlyViolations( + violationOf( NotNull.class ).withMessage( "must not be null" ), + violationOf( Length.class ).withMessage( "length must be between 10 and 20" ) + ); + } + // HV-1428 Container element support is disabled for arrays @Test(expectedExceptions = ValidationException.class, expectedExceptionsMessageRegExp = "HV000226:.*") @TestForIssue(jiraKey = "HV-1239") @@ -149,7 +173,7 @@ public void canDeclareContainerElementConstraintsForArrayTypedFieldProgrammatica ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( FishTank.class ) - .property( "fishNames", FIELD ) + .field( "fishNames" ) .containerElementType() .constraint( new SizeDef().max( 5 ) ); @@ -170,7 +194,7 @@ public void canDeclareContainerElementConstraintsForListContainingArrayTypeField ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( FishTank.class ) - .property( "fishNamesByMonth", FIELD ) + .field( "fishNamesByMonth" ) .containerElementType( 0, 0 ) .constraint( new SizeDef().max( 5 ) ); @@ -191,7 +215,7 @@ public void canDeclareContainerElementConstraintsForMultiDimensionalArrayTypeFie ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( FishTank.class ) - .property( "fishNamesByMonthAsArray", FIELD ) + .field( "fishNamesByMonthAsArray" ) .containerElementType( 0, 0 ) .constraint( new SizeDef().max( 5 ) ); @@ -211,7 +235,7 @@ public void declaringContainerElementConstraintOnNonGenericFieldCausesException( ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( FishTank.class ) - .property( "size", FIELD ) + .field( "size" ) .containerElementType( 1 ); } @@ -221,7 +245,7 @@ public void declaringContainerElementConstraintForNonExistingTypeArgumentIndexOn ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( FishTank.class ) - .property( "model", FIELD ) + .field( "model" ) .containerElementType( 2 ); } @@ -231,7 +255,7 @@ public void declaringContainerElementConstraintForNonExistingNestedTypeArgumentI ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( FishTank.class ) - .property( "fishOfTheMonth", FIELD ) + .field( "fishOfTheMonth" ) .containerElementType( 1, 2 ); } @@ -241,7 +265,7 @@ public void omittingTypeArgumentForMultiTypeArgumentTypeOnFieldCausesException() ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( FishTank.class ) - .property( "fishCountByType", FIELD ) + .field( "fishCountByType" ) .containerElementType(); } @@ -251,7 +275,7 @@ public void configuringSameContainerElementTwiceCausesException() { ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( FishTank.class ) - .property( "tagsOfFishOfTheMonth", FIELD ) + .field( "tagsOfFishOfTheMonth" ) .containerElementType( 0, 1, 0 ) .constraint( new NotNullDef() ) .containerElementType( 0, 1, 0 ); diff --git a/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticContainerElementConstraintsForGetterTest.java b/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticContainerElementConstraintsForGetterTest.java index 2be200c3da..f99be2c3ab 100644 --- a/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticContainerElementConstraintsForGetterTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticContainerElementConstraintsForGetterTest.java @@ -7,7 +7,6 @@ package org.hibernate.validator.test.cfg; -import static java.lang.annotation.ElementType.METHOD; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; import static org.hibernate.validator.testutils.ValidatorUtil.getConfiguration; @@ -53,10 +52,10 @@ public void canDeclareContainerElementConstraintsForGetterProgrammatically() { ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( FishTank.class ) - .property( "model", METHOD ) + .getter( "model" ) .containerElementType() .constraint( new SizeDef().max( 5 ) ) - .property( "fishCountByType", METHOD ) + .getter( "fishCountByType" ) .containerElementType( 0 ) .constraint( new SizeDef().min( 3 ).max( 10 ) ) .containerElementType( 1 ) @@ -82,7 +81,7 @@ public void canDeclareNestedContainerElementConstraintsForGetterProgrammatically ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( FishTank.class ) - .property( "fishOfTheMonth", METHOD ) + .getter( "fishOfTheMonth" ) .containerElementType( 1, 0 ) .constraint( new NotNullDef() ); @@ -102,7 +101,7 @@ public void canDeclareDeeplyNestedContainerElementConstraintsForGetterProgrammat ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( FishTank.class ) - .property( "tagsOfFishOfTheMonth", METHOD ) + .getter( "tagsOfFishOfTheMonth" ) .containerElementType( 0, 1, 0 ) .constraint( new NotNullDef() ); @@ -122,11 +121,11 @@ public void canDeclareContainerElementCascadesForGetterProgrammatically() { ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( FishTank.class ) - .property( "boss", METHOD ) + .getter( "boss" ) .containerElementType() .valid() .type( Fish.class ) - .property( "name", METHOD ) + .getter( "name" ) .constraint( new NotNullDef() ); config.addMapping( newMapping ); @@ -146,7 +145,7 @@ public void canDeclareContainerElementConstraintsForArrayTypedGetterProgrammatic ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( FishTank.class ) - .property( "fishNames", METHOD ) + .getter( "fishNames" ) .containerElementType() .constraint( new SizeDef().max( 5 ) ); @@ -167,7 +166,7 @@ public void canDeclareContainerElementConstraintsForListContainingArrayTypeGette ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( FishTank.class ) - .property( "fishNamesByMonth", METHOD ) + .getter( "fishNamesByMonth" ) .containerElementType( 0, 0 ) .constraint( new SizeDef().max( 5 ) ); @@ -188,7 +187,7 @@ public void canDeclareContainerElementConstraintsForMultiDimensionalArrayTypeGet ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( FishTank.class ) - .property( "fishNamesByMonthAsArray", METHOD ) + .getter( "fishNamesByMonthAsArray" ) .containerElementType( 0, 0 ) .constraint( new SizeDef().max( 5 ) ); @@ -208,7 +207,7 @@ public void declaringContainerElementConstraintOnNonGenericGetterCausesException ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( FishTank.class ) - .property( "size", METHOD ) + .getter( "size" ) .containerElementType( 1 ); } @@ -218,7 +217,7 @@ public void declaringContainerElementConstraintForNonExistingTypeArgumentIndexOn ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( FishTank.class ) - .property( "model", METHOD ) + .getter( "model" ) .containerElementType( 2 ); } @@ -228,7 +227,7 @@ public void declaringContainerElementConstraintForNonExistingNestedTypeArgumentI ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( FishTank.class ) - .property( "fishOfTheMonth", METHOD ) + .getter( "fishOfTheMonth" ) .containerElementType( 1, 2 ); } @@ -238,7 +237,7 @@ public void omittingTypeArgumentForMultiTypeArgumentTypeOnGetterCausesException( ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( FishTank.class ) - .property( "fishCountByType", METHOD ) + .getter( "fishCountByType" ) .containerElementType(); } @@ -248,7 +247,7 @@ public void configuringSameContainerElementTwiceCausesException() { ConstraintMapping newMapping = config.createConstraintMapping(); newMapping .type( FishTank.class ) - .property( "tagsOfFishOfTheMonth", METHOD ) + .getter( "tagsOfFishOfTheMonth" ) .containerElementType( 0, 1, 0 ) .constraint( new NotNullDef() ) .containerElementType( 0, 1, 0 ); diff --git a/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticContainerElementConstraintsForParameterTest.java b/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticContainerElementConstraintsForParameterTest.java index 73b575544c..16e80de751 100644 --- a/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticContainerElementConstraintsForParameterTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticContainerElementConstraintsForParameterTest.java @@ -7,11 +7,10 @@ package org.hibernate.validator.test.cfg; -import static java.lang.annotation.ElementType.FIELD; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; -import static org.testng.Assert.fail; import static org.hibernate.validator.testutils.ValidatorUtil.getConfiguration; +import static org.testng.Assert.fail; import java.util.ArrayList; import java.util.Arrays; @@ -172,7 +171,7 @@ public void canDeclareContainerElementCascadesForParameterProgrammatically() { .containerElementType() .valid() .type( Fish.class ) - .property( "name", FIELD ) + .field( "name" ) .constraint( new NotNullDef() ); config.addMapping( newMapping ); diff --git a/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticContainerElementConstraintsForReturnValueTest.java b/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticContainerElementConstraintsForReturnValueTest.java index 23910c6302..afa7acdd75 100644 --- a/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticContainerElementConstraintsForReturnValueTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticContainerElementConstraintsForReturnValueTest.java @@ -7,7 +7,6 @@ package org.hibernate.validator.test.cfg; -import static java.lang.annotation.ElementType.FIELD; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; import static org.hibernate.validator.testutils.ValidatorUtil.getConfiguration; @@ -154,7 +153,7 @@ public void canDeclareContainerElementCascadesForReturnValueProgrammatically() { .containerElementType() .valid() .type( Fish.class ) - .property( "name", FIELD ) + .field( "name" ) .constraint( new NotNullDef() ); config.addMapping( newMapping ); diff --git a/engine/src/test/java/org/hibernate/validator/test/cfg/UniqueElementsDefTest.java b/engine/src/test/java/org/hibernate/validator/test/cfg/UniqueElementsDefTest.java index b87bb888e2..017af21a96 100644 --- a/engine/src/test/java/org/hibernate/validator/test/cfg/UniqueElementsDefTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/cfg/UniqueElementsDefTest.java @@ -9,7 +9,6 @@ import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; -import java.lang.annotation.ElementType; import java.util.Arrays; import java.util.List; import java.util.Set; @@ -38,7 +37,7 @@ public void testUniqueElementsDef() { final ConstraintMapping programmaticMapping = configuration.createConstraintMapping(); programmaticMapping.type( Library.class ) - .property( "books", ElementType.FIELD ).constraint( new UniqueElementsDef() ); + .field( "books" ).constraint( new UniqueElementsDef() ); configuration.addMapping( programmaticMapping ); Validator validator = configuration.buildValidatorFactory().getValidator(); diff --git a/engine/src/test/java/org/hibernate/validator/test/constraints/ConstraintValidatorContextImplTest.java b/engine/src/test/java/org/hibernate/validator/test/constraints/ConstraintValidatorContextImplTest.java index 74031960a0..436536db8f 100644 --- a/engine/src/test/java/org/hibernate/validator/test/constraints/ConstraintValidatorContextImplTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/constraints/ConstraintValidatorContextImplTest.java @@ -226,7 +226,7 @@ private ConstraintValidatorContextImpl createEmptyConstraintValidatorContextImpl PathImpl path = PathImpl.createRootPath(); path.addBeanNode(); - ConstraintValidatorContextImpl context = new ConstraintValidatorContextImpl( null, null, path, null, null ); + ConstraintValidatorContextImpl context = new ConstraintValidatorContextImpl( null, path, null, null ); context.disableDefaultConstraintViolation(); return context; } diff --git a/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/pl/NIPValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/pl/NIPValidatorTest.java index 59680961d2..678ec69422 100644 --- a/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/pl/NIPValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/pl/NIPValidatorTest.java @@ -24,10 +24,26 @@ public class NIPValidatorTest extends AbstractConstrainedTest { @Test - public void testCorrectNipNumber() { + public void testAdditionalCharactersAreAllowed() { assertNoViolations( validator.validate( new Person( "123-456-78-19" ) ) ); assertNoViolations( validator.validate( new Person( "123-45-67-819" ) ) ); assertNoViolations( validator.validate( new Person( "123-456-32-18" ) ) ); + } + + @Test + public void testIncorrectLength() { + assertThat( validator.validate( new Person( "123-456-78-14113-312-310" ) ) ) + .containsOnlyViolations( + violationOf( NIP.class ).withProperty( "nip" ) + ); + assertThat( validator.validate( new Person( "123-45-62" ) ) ) + .containsOnlyViolations( + violationOf( NIP.class ).withProperty( "nip" ) + ); + } + + @Test + public void testCorrectNipNumber() { assertNoViolations( validator.validate( new Person( "5931423811" ) ) ); assertNoViolations( validator.validate( new Person( "2596048500" ) ) ); assertNoViolations( validator.validate( new Person( "4163450312" ) ) ); diff --git a/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/pl/PESELValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/pl/PESELValidatorTest.java index 352344df31..7b7752df0f 100644 --- a/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/pl/PESELValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/pl/PESELValidatorTest.java @@ -23,6 +23,30 @@ */ public class PESELValidatorTest extends AbstractConstrainedTest { + @Test + public void testAdditionalCharactersNotAllowed() { + assertThat( validator.validate( new Person( "9204-190-37-90" ) ) ) + .containsOnlyViolations( + violationOf( PESEL.class ).withProperty( "pesel" ) + ); + assertThat( validator.validate( new Person( "44-0-5-1-4-01359" ) ) ) + .containsOnlyViolations( + violationOf( PESEL.class ).withProperty( "pesel" ) + ); + } + + @Test + public void testIncorrectLength() { + assertThat( validator.validate( new Person( "920419795" ) ) ) + .containsOnlyViolations( + violationOf( PESEL.class ).withProperty( "pesel" ) + ); + assertThat( validator.validate( new Person( "92041903790123" ) ) ) + .containsOnlyViolations( + violationOf( PESEL.class ).withProperty( "pesel" ) + ); + } + @Test public void testCorrectPESELNumber() { assertNoViolations( validator.validate( new Person( "92041903790" ) ) ); diff --git a/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/pl/REGONValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/pl/REGONValidatorTest.java index 50ea4b4c08..f54e4bf041 100644 --- a/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/pl/REGONValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/pl/REGONValidatorTest.java @@ -23,6 +23,30 @@ */ public class REGONValidatorTest extends AbstractConstrainedTest { + @Test + public void testAdditionalCharactersNotAllowed() { + assertThat( validator.validate( new Company( "123-456-785" ) ) ) + .containsOnlyViolations( + violationOf( REGON.class ).withProperty( "regon" ) + ); + assertThat( validator.validate( new Company( "6-9-1-6-5-7-1-8-2" ) ) ) + .containsOnlyViolations( + violationOf( REGON.class ).withProperty( "regon" ) + ); + } + + @Test + public void testIncorrectLength() { + assertThat( validator.validate( new Company( "1234567845" ) ) ) + .containsOnlyViolations( + violationOf( REGON.class ).withProperty( "regon" ) + ); + assertThat( validator.validate( new Company( "12345673" ) ) ) + .containsOnlyViolations( + violationOf( REGON.class ).withProperty( "regon" ) + ); + } + @Test public void testCorrectRegon9Number() { assertNoViolations( validator.validate( new Company( "123456785" ) ) ); diff --git a/engine/src/test/java/org/hibernate/validator/test/constraints/boolcomposition/IsBlank.java b/engine/src/test/java/org/hibernate/validator/test/constraints/boolcomposition/IsBlank.java index f1b8781d5a..790a396182 100644 --- a/engine/src/test/java/org/hibernate/validator/test/constraints/boolcomposition/IsBlank.java +++ b/engine/src/test/java/org/hibernate/validator/test/constraints/boolcomposition/IsBlank.java @@ -11,9 +11,9 @@ import java.lang.annotation.Target; import javax.validation.Constraint; import javax.validation.Payload; +import javax.validation.constraints.NotBlank; import org.hibernate.validator.constraints.ConstraintComposition; -import org.hibernate.validator.constraints.NotBlank; import static java.lang.annotation.ElementType.ANNOTATION_TYPE; import static java.lang.annotation.ElementType.FIELD; diff --git a/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/ConstraintDefinitionTypeMismatchTest.java b/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/ConstraintDefinitionTypeMismatchTest.java new file mode 100644 index 0000000000..56ea9c5d48 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/ConstraintDefinitionTypeMismatchTest.java @@ -0,0 +1,41 @@ +/* + * 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.constraintvalidator; + +import static org.hibernate.validator.testutils.ValidatorUtil.getValidator; + +import javax.validation.ConstraintDefinitionException; +import javax.validation.Validator; + +import org.hibernate.validator.testutil.TestForIssue; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * @author Guillaume Smet + */ +@TestForIssue(jiraKey = "HV-1592") +public class ConstraintDefinitionTypeMismatchTest { + + private Validator validator; + + @BeforeMethod + public void setUp() { + validator = getValidator(); + } + + @Test(expectedExceptions = ConstraintDefinitionException.class, expectedExceptionsMessageRegExp = "^HV000243:.*") + public void constraint_validator_constraint_type_mismatch_causes_exception() { + validator.validate( new TypeMismatchBean() ); + } + + public class TypeMismatchBean { + + @TypeMismatchConstraint + private String property; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/TypeMismatchConstraint.java b/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/TypeMismatchConstraint.java new file mode 100644 index 0000000000..98237a2b14 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/TypeMismatchConstraint.java @@ -0,0 +1,31 @@ +/* + * 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.constraintvalidator; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.Payload; + + +@Documented +@Constraint(validatedBy = MustNotMatchValidator.class) +@Target({ METHOD, FIELD }) +@Retention(RUNTIME) +public @interface TypeMismatchConstraint { + String message() default "{org.hibernate.validator.test.constraintvalidator.TypeMismatchConstraint.message}"; + + Class[] groups() default { }; + + Class[] payload() default { }; +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/bootstrap/BootstrappingTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/bootstrap/BootstrappingTest.java index 58f3d21b3b..fea137653c 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/bootstrap/BootstrappingTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/bootstrap/BootstrappingTest.java @@ -24,13 +24,13 @@ import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; +import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import javax.validation.executable.ExecutableValidator; import org.hibernate.validator.HibernateValidator; import org.hibernate.validator.HibernateValidatorConfiguration; -import org.hibernate.validator.constraints.NotEmpty; -import org.hibernate.validator.internal.constraintvalidators.bv.NotNullValidator; +import org.hibernate.validator.internal.constraintvalidators.bv.notempty.NotEmptyValidatorForCharSequence; import org.hibernate.validator.internal.engine.ConfigurationImpl; import org.hibernate.validator.internal.engine.ValidatorFactoryImpl; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl; @@ -81,8 +81,8 @@ public void testCustomConstraintValidatorFactory() { new ConstraintValidatorFactory() { @Override public > T getInstance(Class key) { - if ( key == NotNullValidator.class ) { - return (T) new BadlyBehavedNotNullConstraintValidator(); + if ( key == NotEmptyValidatorForCharSequence.class ) { + return key.cast( new BadlyBehavedNotEmptyValidatorForCharSequence() ); } return new ConstraintValidatorFactoryImpl().getInstance( key ); } @@ -142,9 +142,9 @@ private void assertDefaultFactory(ValidatorFactory factory) { assertTrue( factory instanceof ValidatorFactoryImpl ); } - class BadlyBehavedNotNullConstraintValidator extends NotNullValidator { + class BadlyBehavedNotEmptyValidatorForCharSequence extends NotEmptyValidatorForCharSequence { @Override - public boolean isValid(Object object, ConstraintValidatorContext constraintValidatorContext) { + public boolean isValid(CharSequence charSequence, ConstraintValidatorContext constraintValidatorContext) { return true; } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/bootstrap/Customer.java b/engine/src/test/java/org/hibernate/validator/test/internal/bootstrap/Customer.java index dedffe300c..4005a9c247 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/bootstrap/Customer.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/bootstrap/Customer.java @@ -8,10 +8,11 @@ import java.util.HashSet; import java.util.Set; + import javax.validation.Valid; +import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; -import org.hibernate.validator.constraints.NotEmpty; /** * @author Hardy Ferentschik diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/BaseMinMaxValidatorForNumberTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/BaseMinMaxValidatorForNumberTest.java index 9e826882af..db61ffb393 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/BaseMinMaxValidatorForNumberTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/BaseMinMaxValidatorForNumberTest.java @@ -10,8 +10,10 @@ import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; +import java.lang.annotation.Annotation; import java.math.BigDecimal; import java.math.BigInteger; + import javax.validation.ConstraintValidator; /** @@ -19,7 +21,7 @@ */ public class BaseMinMaxValidatorForNumberTest { - protected void testNumberValidator(ConstraintValidator constraint, boolean inclusive, boolean isMax) { + protected void testNumberValidator(ConstraintValidator constraint, boolean inclusive, boolean isMax) { byte b = 1; Byte bWrapper = 127; if ( inclusive ) { @@ -38,6 +40,18 @@ protected void testNumberValidator(ConstraintValidator constraint, bo assertEquals( constraint.isValid( b, null ), isMax ); assertEquals( constraint.isValid( 14.99, null ), isMax ); assertEquals( constraint.isValid( -14.99, null ), isMax ); + assertEquals( constraint.isValid( 14.99F, null ), isMax ); + assertEquals( constraint.isValid( -14.99F, null ), isMax ); + assertEquals( constraint.isValid( 14, null ), isMax ); + assertEquals( constraint.isValid( 16, null ), !isMax ); + assertEquals( constraint.isValid( (short) 14, null ), isMax ); + assertEquals( constraint.isValid( (short) 16, null ), !isMax ); + assertEquals( constraint.isValid( BigInteger.valueOf( 14L ), null ), isMax ); + assertEquals( constraint.isValid( BigInteger.valueOf( 16L ), null ), !isMax ); + assertEquals( constraint.isValid( BigDecimal.valueOf( 14L ), null ), isMax ); + assertEquals( constraint.isValid( BigDecimal.valueOf( 16L ), null ), !isMax ); + assertEquals( constraint.isValid( new BigDecimal( "14.99" ), null ), isMax ); + assertEquals( constraint.isValid( new BigDecimal( "15.001" ), null ), !isMax ); assertEquals( constraint.isValid( bWrapper, null ), !isMax ); assertEquals( constraint.isValid( 20, null ), !isMax ); } @@ -74,6 +88,51 @@ protected void testValidatorBigInteger(ConstraintValidator constr assertEquals( constraint.isValid( BigInteger.valueOf( 1560000000 ), null ), !isMax ); } + protected void testValidatorByte(ConstraintValidator constraint, boolean inclusive, boolean isMax) { + if ( inclusive ) { + assertTrue( constraint.isValid( (byte) 15, null ) ); + } + else { + assertFalse( constraint.isValid( (byte) 15, null ) ); + } + + assertTrue( constraint.isValid( null, null ) ); + assertEquals( constraint.isValid( (byte) 14, null ), isMax ); + assertEquals( constraint.isValid( (byte) 16, null ), !isMax ); + assertEquals( constraint.isValid( Byte.MIN_VALUE, null ), isMax ); + assertEquals( constraint.isValid( Byte.MAX_VALUE, null ), !isMax ); + } + + protected void testValidatorShort(ConstraintValidator constraint, boolean inclusive, boolean isMax) { + if ( inclusive ) { + assertTrue( constraint.isValid( (short) 15, null ) ); + } + else { + assertFalse( constraint.isValid( (short) 15, null ) ); + } + + assertTrue( constraint.isValid( null, null ) ); + assertEquals( constraint.isValid( (short) 14, null ), isMax ); + assertEquals( constraint.isValid( (short) 16, null ), !isMax ); + assertEquals( constraint.isValid( Short.MIN_VALUE, null ), isMax ); + assertEquals( constraint.isValid( Short.MAX_VALUE, null ), !isMax ); + } + + protected void testValidatorInteger(ConstraintValidator constraint, boolean inclusive, boolean isMax) { + if ( inclusive ) { + assertTrue( constraint.isValid( 15, null ) ); + } + else { + assertFalse( constraint.isValid( 15, null ) ); + } + + assertTrue( constraint.isValid( null, null ) ); + assertEquals( constraint.isValid( 14, null ), isMax ); + assertEquals( constraint.isValid( 16, null ), !isMax ); + assertEquals( constraint.isValid( Integer.MIN_VALUE, null ), isMax ); + assertEquals( constraint.isValid( Integer.MAX_VALUE, null ), !isMax ); + } + protected void testValidatorLong(ConstraintValidator constraint, boolean inclusive, boolean isMax) { if ( inclusive ) { assertTrue( constraint.isValid( 15L, null ) ); @@ -97,6 +156,7 @@ protected void testValidatorDouble(ConstraintValidator constraint, bo assertTrue( constraint.isValid( null, null ) ); assertEquals( constraint.isValid( 14.99, null ), isMax ); + assertEquals( constraint.isValid( 15.001, null ), !isMax ); assertEquals( constraint.isValid( -14.99, null ), isMax ); assertEquals( constraint.isValid( -1560000000D, null ), isMax ); assertEquals( constraint.isValid( Double.NEGATIVE_INFINITY, null ), isMax ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/DecimalMinMaxValidatorBoundaryTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/DecimalMinMaxValidatorBoundaryTest.java index 3ae2c4ace2..58b9841f74 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/DecimalMinMaxValidatorBoundaryTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/DecimalMinMaxValidatorBoundaryTest.java @@ -6,7 +6,6 @@ */ package org.hibernate.validator.test.internal.constraintvalidators.bv; -import static java.lang.annotation.ElementType.FIELD; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; @@ -45,7 +44,7 @@ public void setUp() { public void testDecimalMinValue() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( DecimalMinMaxValidatorBoundaryTest.class ) - .property( "d", FIELD ) + .field( "d" ) .constraint( new DecimalMinDef().value( "0.100000000000000005" ) ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); @@ -62,7 +61,7 @@ public void testDecimalMinValue() { public void testDecimalMaxValue() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( DecimalMinMaxValidatorBoundaryTest.class ) - .property( "d", FIELD ) + .field( "d" ) .constraint( new DecimalMaxDef().value( "0.1" ) ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); @@ -79,7 +78,7 @@ public void testDecimalMaxValue() { public void testDoubleTrouble() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( DecimalMinMaxValidatorBoundaryTest.class ) - .property( "d", FIELD ) + .field( "d" ) .constraint( new DecimalMaxDef().value( "1.2" ) ); config.addMapping( mapping ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MaxValidatorForNumberTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MaxValidatorForNumberTest.java index d75b087855..880eb6771d 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MaxValidatorForNumberTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MaxValidatorForNumberTest.java @@ -6,25 +6,42 @@ */ package org.hibernate.validator.test.internal.constraintvalidators.bv; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; +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 static org.testng.Assert.assertFalse; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import javax.validation.Validator; import javax.validation.constraints.DecimalMax; import javax.validation.constraints.Max; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.AbstractMaxValidator; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForBigDecimal; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForBigInteger; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForByte; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForDouble; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForFloat; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForInteger; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForLong; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForNumber; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForShort; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.AbstractDecimalMaxValidator; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForBigDecimal; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForBigInteger; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForByte; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForDouble; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForFloat; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForInteger; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForLong; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForNumber; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForShort; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; import org.hibernate.validator.testutil.TestForIssue; + import org.testng.annotations.Test; /** @@ -55,6 +72,40 @@ public void testIsValidDecimalMax() { testDecimalMax( m, true ); } + @Test + public void testIsValidDecimalMaxWithDecimalFractionInConstraint() { + class Foo { + @DecimalMax("15.0001") + private final Number num; + + Foo(final Number num) { + this.num = num; + } + } + + Validator validator = getValidator(); + + assertThat( validator.validate( new Foo( 16 ) ) ).containsOnlyViolations( violationOf( DecimalMax.class ) ); + assertNoViolations( validator.validate( new Foo( 15 ) ) ); + + assertThat( validator.validate( new Foo( 15.01 ) ) ).containsOnlyViolations( violationOf( DecimalMax.class ) ); + assertNoViolations( validator.validate( new Foo( 15.00001 ) ) ); + + assertThat( validator.validate( new Foo( BigDecimal.valueOf( 15.01 ) ) ) ).containsOnlyViolations( violationOf( DecimalMax.class ) ); + assertNoViolations( validator.validate( new Foo( BigDecimal.valueOf( 15.00001 ) ) ) ); + } + + @Test + public void testIsValidDecimalMax1() { + ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( DecimalMax.class ); + descriptorBuilder.setAttribute( "value", Integer.toString( Integer.MAX_VALUE - 1 ) ); + DecimalMax m = descriptorBuilder.build().getAnnotation(); + + DecimalMaxValidatorForNumber constraint = new DecimalMaxValidatorForNumber(); + constraint.initialize( m ); + assertFalse( constraint.isValid( Double.POSITIVE_INFINITY, null ) ); + } + @Test(expectedExceptions = IllegalArgumentException.class) public void testInitializeDecimalMaxWithInvalidValue() { ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( DecimalMax.class ); @@ -79,6 +130,23 @@ public void testIsValidDecimalMaxExclusive() { } + @Test + @TestForIssue(jiraKey = "HV-1699") + public void testIsValidNumberForFloatingPointOrBigNumbersStoredAsNumber() { + ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( Max.class ); + descriptorBuilder.setAttribute( "value", 1L ); + Max m = descriptorBuilder.build().getAnnotation(); + MaxValidatorForNumber validator = new MaxValidatorForNumber(); + validator.initialize( m ); + + assertFalse( validator.isValid( 1.01, null ) ); + assertFalse( validator.isValid( 1.01F, null ) ); + assertFalse( validator.isValid( new BigDecimal( "1.01" ), null ) ); + assertFalse( validator.isValid( new BigInteger( "2" ), null ) ); + assertFalse( validator.isValid( Double.POSITIVE_INFINITY, null ) ); + assertFalse( validator.isValid( Float.POSITIVE_INFINITY, null ) ); + } + private void testDecimalMax(DecimalMax m, boolean inclusive) { AbstractDecimalMaxValidator constraint = new DecimalMaxValidatorForNumber(); constraint.initialize( m ); @@ -92,6 +160,18 @@ private void testDecimalMax(DecimalMax m, boolean inclusive) { constraint.initialize( m ); testValidatorBigInteger( constraint, inclusive, true ); + constraint = new DecimalMaxValidatorForByte(); + constraint.initialize( m ); + testValidatorByte( constraint, inclusive, true ); + + constraint = new DecimalMaxValidatorForShort(); + constraint.initialize( m ); + testValidatorShort( constraint, inclusive, true ); + + constraint = new DecimalMaxValidatorForInteger(); + constraint.initialize( m ); + testValidatorInteger( constraint, inclusive, true ); + constraint = new DecimalMaxValidatorForLong(); constraint.initialize( m ); testValidatorLong( constraint, inclusive, true ); @@ -118,6 +198,18 @@ private void testMax(Max m, boolean inclusive) { constraint.initialize( m ); testValidatorBigInteger( constraint, inclusive, true ); + constraint = new MaxValidatorForByte(); + constraint.initialize( m ); + testValidatorByte( constraint, inclusive, true ); + + constraint = new MaxValidatorForShort(); + constraint.initialize( m ); + testValidatorShort( constraint, inclusive, true ); + + constraint = new MaxValidatorForInteger(); + constraint.initialize( m ); + testValidatorInteger( constraint, inclusive, true ); + constraint = new MaxValidatorForLong(); constraint.initialize( m ); testValidatorLong( constraint, inclusive, true ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MinValidatorForNumberTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MinValidatorForNumberTest.java index 5482548957..2b1e742649 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MinValidatorForNumberTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MinValidatorForNumberTest.java @@ -6,25 +6,40 @@ */ package org.hibernate.validator.test.internal.constraintvalidators.bv; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; +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.math.BigDecimal; + +import javax.validation.Validator; import javax.validation.constraints.DecimalMin; import javax.validation.constraints.Min; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.AbstractMinValidator; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForBigDecimal; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForBigInteger; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForByte; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForDouble; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForFloat; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForInteger; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForLong; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForNumber; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForShort; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.AbstractDecimalMinValidator; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForBigDecimal; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForBigInteger; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForByte; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForDouble; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForFloat; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForInteger; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForLong; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForNumber; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForShort; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; import org.hibernate.validator.testutil.TestForIssue; + import org.testng.annotations.Test; /** @@ -55,6 +70,29 @@ public void testIsValidDecimalMinValidator() { testDecimalMin( m, true ); } + @Test + public void testIsValidDecimalMinWithDecimalFractionInConstraint() { + class Foo { + @DecimalMin("15.0001") + private final Number num; + + Foo(final Number num) { + this.num = num; + } + } + + Validator validator = getValidator(); + + assertThat( validator.validate( new Foo( 15 ) ) ).containsOnlyViolations( violationOf( DecimalMin.class ) ); + assertNoViolations( validator.validate( new Foo( 16 ) ) ); + + assertThat( validator.validate( new Foo( 15.00001 ) ) ).containsOnlyViolations( violationOf( DecimalMin.class ) ); + assertNoViolations( validator.validate( new Foo( 15.01 ) ) ); + + assertThat( validator.validate( new Foo( BigDecimal.valueOf( 15.00001 ) ) ) ).containsOnlyViolations( violationOf( DecimalMin.class ) ); + assertNoViolations( validator.validate( new Foo( BigDecimal.valueOf( 15.01 ) ) ) ); + } + @Test(expectedExceptions = IllegalArgumentException.class) public void testInitializeDecimalMinWithInvalidValue() { ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( DecimalMin.class ); @@ -92,6 +130,18 @@ private void testDecimalMin(DecimalMin m, boolean inclusive) { constraint.initialize( m ); testValidatorBigInteger( constraint, inclusive, false ); + constraint = new DecimalMinValidatorForByte(); + constraint.initialize( m ); + testValidatorByte( constraint, inclusive, false ); + + constraint = new DecimalMinValidatorForShort(); + constraint.initialize( m ); + testValidatorShort( constraint, inclusive, false ); + + constraint = new DecimalMinValidatorForInteger(); + constraint.initialize( m ); + testValidatorInteger( constraint, inclusive, false ); + constraint = new DecimalMinValidatorForLong(); constraint.initialize( m ); testValidatorLong( constraint, inclusive, false ); @@ -118,6 +168,18 @@ private void testMin(Min m, boolean inclusive) { constraint.initialize( m ); testValidatorBigInteger( constraint, inclusive, false ); + constraint = new MinValidatorForByte(); + constraint.initialize( m ); + testValidatorByte( constraint, inclusive, false ); + + constraint = new MinValidatorForShort(); + constraint.initialize( m ); + testValidatorShort( constraint, inclusive, false ); + + constraint = new MinValidatorForInteger(); + constraint.initialize( m ); + testValidatorInteger( constraint, inclusive, false ); + constraint = new MinValidatorForLong(); constraint.initialize( m ); testValidatorLong( constraint, inclusive, false ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/money/CurrencyValidatorForMonetaryAmountTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/money/CurrencyValidatorForMonetaryAmountTest.java index 9768d6daa3..540832606e 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/money/CurrencyValidatorForMonetaryAmountTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/money/CurrencyValidatorForMonetaryAmountTest.java @@ -11,7 +11,6 @@ import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; -import java.lang.annotation.ElementType; import java.util.Set; import javax.money.MonetaryAmount; @@ -75,7 +74,7 @@ public void programmaticDefinition() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( Order.class ) .ignoreAllAnnotations() - .property( "amount", ElementType.FIELD ) + .field( "amount" ) .constraint( new CurrencyDef().value( "EUR", "USD" ) ); Validator validator = config.addMapping( mapping ) diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/money/DigitsValidatorForMonetaryAmountTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/money/DigitsValidatorForMonetaryAmountTest.java new file mode 100644 index 0000000000..2fc8739eb1 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/money/DigitsValidatorForMonetaryAmountTest.java @@ -0,0 +1,117 @@ +/* + * 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.constraintvalidators.bv.money; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import java.math.BigDecimal; +import javax.money.CurrencyUnit; +import javax.money.Monetary; +import javax.validation.constraints.Digits; + +import org.hibernate.validator.internal.constraintvalidators.bv.money.DigitsValidatorForMonetaryAmount; +import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; +import org.hibernate.validator.testutil.TestForIssue; + +import org.javamoney.moneta.Money; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * @author Dario Seidl + */ +@TestForIssue(jiraKey = "HV-1723") +public class DigitsValidatorForMonetaryAmountTest { + + private ConstraintAnnotationDescriptor.Builder descriptorBuilder; + + @BeforeMethod + public void setUp() { + descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( Digits.class ); + descriptorBuilder.setMessage( "{validator.digits}" ); + } + + @Test + public void testIsValid() { + descriptorBuilder.setAttribute( "integer", 5 ); + descriptorBuilder.setAttribute( "fraction", 2 ); + Digits p = descriptorBuilder.build().getAnnotation(); + + DigitsValidatorForMonetaryAmount constraint = new DigitsValidatorForMonetaryAmount(); + constraint.initialize( p ); + + CurrencyUnit currency = Monetary.getCurrency( "EUR" ); + + assertTrue( constraint.isValid( null, null ) ); + assertTrue( constraint.isValid( Money.of( Byte.valueOf( "0" ), currency ), null ) ); + assertTrue( constraint.isValid( Money.of( Double.valueOf( "500.2" ), currency ), null ) ); + + assertTrue( constraint.isValid( Money.of( new BigDecimal( "-12345.12" ), currency ), null ) ); + assertFalse( constraint.isValid( Money.of( new BigDecimal( "-123456.12" ), currency ), null ) ); + assertFalse( constraint.isValid( Money.of( new BigDecimal( "-123456.123" ), currency ), null ) ); + assertFalse( constraint.isValid( Money.of( new BigDecimal( "-12345.123" ), currency ), null ) ); + assertFalse( constraint.isValid( Money.of( new BigDecimal( "12345.123" ), currency ), null ) ); + + assertTrue( constraint.isValid( Money.of( Float.valueOf( "-000000000.22" ), currency ), null ) ); + assertFalse( constraint.isValid( Money.of( Integer.valueOf( "256874" ), currency ), null ) ); + assertFalse( constraint.isValid( Money.of( Double.valueOf( "12.0001" ), currency ), null ) ); + } + + @Test + public void testIsValidZeroLength() { + descriptorBuilder.setAttribute( "integer", 0 ); + descriptorBuilder.setAttribute( "fraction", 0 ); + Digits p = descriptorBuilder.build().getAnnotation(); + + DigitsValidatorForMonetaryAmount constraint = new DigitsValidatorForMonetaryAmount(); + constraint.initialize( p ); + + CurrencyUnit currency = Monetary.getCurrency( "EUR" ); + + assertTrue( constraint.isValid( null, null ) ); + assertFalse( constraint.isValid( Money.of( Byte.valueOf( "0" ), currency ), null ) ); + assertFalse( constraint.isValid( Money.of( Double.valueOf( "500.2" ), currency ), null ) ); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNegativeIntegerLength() { + descriptorBuilder.setAttribute( "integer", -1 ); + descriptorBuilder.setAttribute( "fraction", 1 ); + Digits p = descriptorBuilder.build().getAnnotation(); + + DigitsValidatorForMonetaryAmount constraint = new DigitsValidatorForMonetaryAmount(); + constraint.initialize( p ); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNegativeFractionLength() { + descriptorBuilder.setAttribute( "integer", 1 ); + descriptorBuilder.setAttribute( "fraction", -1 ); + Digits p = descriptorBuilder.build().getAnnotation(); + + DigitsValidatorForMonetaryAmount constraint = new DigitsValidatorForMonetaryAmount(); + constraint.initialize( p ); + } + + @Test + public void testTrailingZerosAreTrimmed() { + descriptorBuilder.setAttribute( "integer", 12 ); + descriptorBuilder.setAttribute( "fraction", 3 ); + Digits p = descriptorBuilder.build().getAnnotation(); + + DigitsValidatorForMonetaryAmount constraint = new DigitsValidatorForMonetaryAmount(); + constraint.initialize( p ); + + CurrencyUnit currency = Monetary.getCurrency( "EUR" ); + + assertTrue( constraint.isValid( Money.of( 0.001d, currency ), null ) ); + assertTrue( constraint.isValid( Money.of( 0.00100d, currency ), null ) ); + assertFalse( constraint.isValid( Money.of( 0.0001d, currency ), null ) ); + } + +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/size/SizeValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/size/SizeValidatorTest.java index 09e3da8900..5a841e602b 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/size/SizeValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/size/SizeValidatorTest.java @@ -177,7 +177,7 @@ private ConstraintValidator getValidatorMin1Max2(Class validator descriptorBuilder.setMessage( "{validator.max}" ); Size m = descriptorBuilder.build().getAnnotation(); @SuppressWarnings("unchecked") - ConstraintValidator validator = (ConstraintValidator) validatorClass.newInstance(); + ConstraintValidator validator = (ConstraintValidator) validatorClass.getConstructor().newInstance(); validator.initialize( m ); return validator; } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/BlankValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/BlankValidatorTest.java index 6d28835bdd..9da8f9d987 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/BlankValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/BlankValidatorTest.java @@ -26,6 +26,7 @@ /** * @author Hardy Ferentschik */ +@SuppressWarnings("deprecation") public class BlankValidatorTest { @Test public void testConstraintValidator() { diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/EAN13Test.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/EAN13Test.java index 0054b58be9..5cedfb9893 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/EAN13Test.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/EAN13Test.java @@ -6,7 +6,6 @@ */ package org.hibernate.validator.test.internal.constraintvalidators.hv; -import static java.lang.annotation.ElementType.FIELD; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; @@ -80,7 +79,7 @@ public void testProgrammaticConstraint() { final HibernateValidatorConfiguration config = getConfiguration( HibernateValidator.class ); ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( Product.class ) - .property( "ean", FIELD ) + .field( "ean" ) .constraint( new EANDef().type( EAN.Type.EAN13 ) ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/EAN8Test.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/EAN8Test.java index 9b7e23be31..ba38c8e7b8 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/EAN8Test.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/EAN8Test.java @@ -6,7 +6,6 @@ */ package org.hibernate.validator.test.internal.constraintvalidators.hv; -import static java.lang.annotation.ElementType.FIELD; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; @@ -80,7 +79,7 @@ public void testProgrammaticConstraint() { final HibernateValidatorConfiguration config = getConfiguration( HibernateValidator.class ); ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( Product.class ) - .property( "ean", FIELD ) + .field( "ean" ) .constraint( new EANDef().type( EAN.Type.EAN8 ) ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/EmailValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/EmailValidatorTest.java index 804e36658c..88d0f43ffc 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/EmailValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/EmailValidatorTest.java @@ -6,7 +6,6 @@ */ package org.hibernate.validator.test.internal.constraintvalidators.hv; -import static java.lang.annotation.ElementType.METHOD; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; import static org.hibernate.validator.testutils.ValidatorUtil.getConfiguration; @@ -25,6 +24,7 @@ import org.hibernate.validator.cfg.ConstraintMapping; import org.hibernate.validator.cfg.defs.EmailDef; import org.hibernate.validator.internal.constraintvalidators.hv.EmailValidator; +import org.hibernate.validator.internal.util.StringHelper; import org.hibernate.validator.testutil.MyCustomStringImpl; import org.hibernate.validator.testutil.TestForIssue; import org.hibernate.validator.testutils.ValidatorUtil; @@ -157,7 +157,7 @@ public void testEmailRegExp() { final HibernateValidatorConfiguration config = getConfiguration( HibernateValidator.class ); ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( EmailContainer.class ) - .property( "email", METHOD ) + .getter( "email" ) .constraint( new EmailDef().regexp( noOrgEmailAddressRegexp ) .message( "ORG addresses are not valid" ) @@ -255,7 +255,7 @@ private void assertOrgAddressesAreNotValid(Setalert('Doh')World !", null ) ); } + @Test + // A "downlevel revealed" conditional 'comment' is not an (X)HTML comment at all, + // despite the misleading name, it is default Microsoft syntax. + // The tag is unrecognized by therefore executed + public void testDownlevelRevealedConditionalComment() throws Exception { + descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); + + assertFalse( getSafeHtmlValidator().isValid( "\n\n", null ) ); + } + + @Test + public void testDownlevelHiddenConditionalComment() throws Exception { + descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); + + assertFalse( getSafeHtmlValidator().isValid( "", null ) ); + } + + @Test + public void testSimpleComment() throws Exception { + descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); + + assertFalse( getSafeHtmlValidator().isValid( "", null ) ); + } + + @Test + public void testServerSideIncludesSSI() throws Exception { + descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); + + assertFalse( getSafeHtmlValidator().isValid( "alert{\"XSS\"}'}; ?>", null ) ); + } + + @Test + public void testPHPScript() throws Exception { + descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); + + assertFalse( getSafeHtmlValidator().isValid( "alert{\"XSS\"}'}; ?>", null ) ); + } + @Test public void testInvalidIncompleteImgTagWithScriptIncluded() { descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/URLValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/URLValidatorTest.java index 844a48a313..c9b001c65f 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/URLValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/URLValidatorTest.java @@ -6,7 +6,6 @@ */ package org.hibernate.validator.test.internal.constraintvalidators.hv; -import static java.lang.annotation.ElementType.METHOD; import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; @@ -172,7 +171,7 @@ public void explicit_regular_expression_can_be_specified_via_programmatic_config HibernateValidatorConfiguration config = ValidatorUtil.getConfiguration( HibernateValidator.class ); ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( URLContainer.class ) - .property( "url", METHOD ) + .getter( "url" ) .constraint( new URLDef().regexp( "^http://\\S+[\\.htm|\\.html]{1}$" ) ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); @@ -195,7 +194,7 @@ public void optional_regular_expression_can_be_refined_with_flags_using_programm HibernateValidatorConfiguration config = ValidatorUtil.getConfiguration( HibernateValidator.class ); ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( URLContainer.class ) - .property( "url", METHOD ) + .getter( "url" ) .constraint( new URLDef().regexp( "^http://\\S+[\\.htm|\\.html]{1}$" ).flags( Flag.CASE_INSENSITIVE ) ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/time/DurationMaxValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/time/DurationMaxValidatorTest.java index 6d1065746d..cd1f35d516 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/time/DurationMaxValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/time/DurationMaxValidatorTest.java @@ -6,7 +6,6 @@ */ package org.hibernate.validator.test.internal.constraintvalidators.hv.time; -import static java.lang.annotation.ElementType.FIELD; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; @@ -76,7 +75,7 @@ public void testProgrammaticConstraint() { final HibernateValidatorConfiguration config = getConfiguration( HibernateValidator.class ); ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( AnotherTask.class ) - .property( "timeToComplete", FIELD ) + .field( "timeToComplete" ) .constraint( new DurationMaxDef() .days( 1 ).hours( 1 ) .minutes( 1 ).seconds( 1 ) @@ -102,7 +101,7 @@ public void testMessage() { final HibernateValidatorConfiguration config = getConfiguration( HibernateValidator.class, Locale.ENGLISH ); ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( AnotherTask.class ) - .property( "timeToComplete", FIELD ) + .field( "timeToComplete" ) .constraint( new DurationMaxDef() .days( 1 ) .nanos( 100 ) diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/time/DurationMinValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/time/DurationMinValidatorTest.java index 3adee0f35c..4ca62f9953 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/time/DurationMinValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/time/DurationMinValidatorTest.java @@ -6,7 +6,6 @@ */ package org.hibernate.validator.test.internal.constraintvalidators.hv.time; -import static java.lang.annotation.ElementType.FIELD; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; @@ -76,7 +75,7 @@ public void testProgrammaticConstraint() { final HibernateValidatorConfiguration config = getConfiguration( HibernateValidator.class ); ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( AnotherTask.class ) - .property( "timeToComplete", FIELD ) + .field( "timeToComplete" ) .constraint( new DurationMinDef() .days( 1 ).hours( 1 ) .minutes( 1 ).seconds( 1 ) @@ -102,7 +101,7 @@ public void testMessage() { final HibernateValidatorConfiguration config = getConfiguration( HibernateValidator.class, Locale.ENGLISH ); ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( AnotherTask.class ) - .property( "timeToComplete", FIELD ) + .field( "timeToComplete" ) .constraint( new DurationMinDef() .days( 30 ) .hours( 12 ) diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/ConstraintValidatorCachingTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/ConstraintValidatorCachingTest.java index f9cbdbb3b3..a983610039 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/ConstraintValidatorCachingTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/ConstraintValidatorCachingTest.java @@ -41,7 +41,7 @@ import javax.validation.constraints.Size; import org.hibernate.validator.internal.constraintvalidators.bv.NotNullValidator; -import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForNumber; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForInteger; import org.hibernate.validator.internal.constraintvalidators.bv.size.SizeValidatorForCollection; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl; import org.hibernate.validator.testutil.TestForIssue; @@ -78,7 +78,7 @@ public void testConstraintValidatorInstancesAreCached() { constraintValidatorFactory.assertSize( 3 ); constraintValidatorFactory.assertKeyExists( SizeValidatorForCollection.class ); - constraintValidatorFactory.assertKeyExists( MinValidatorForNumber.class ); + constraintValidatorFactory.assertKeyExists( MinValidatorForInteger.class ); constraintValidatorFactory.assertKeyExists( NotNullValidator.class ); // getting a new validator from the same factory should have the same instances cached diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/EnhancedBeanAccessorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/EnhancedBeanAccessorTest.java new file mode 100644 index 0000000000..0671f6feed --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/EnhancedBeanAccessorTest.java @@ -0,0 +1,123 @@ +/* + * 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.engine; + +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.Collections; +import java.util.List; +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.Validator; +import javax.validation.constraints.AssertTrue; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Positive; + +import org.hibernate.validator.constraints.Length; +import org.hibernate.validator.engine.HibernateValidatorEnhancedBean; +import org.hibernate.validator.testutil.TestForIssue; + +import org.testng.annotations.Test; + +/** + * @author Marko Bekhta + */ +@TestForIssue(jiraKey = "HV-1680") +public class EnhancedBeanAccessorTest { + @Test + public void testValidatePropertyWithRedefinedDefaultGroupOnMainEntity() { + Validator validator = getValidator(); + Foo foo = new Bar(); + + Set> constraintViolations = validator.validate( foo ); + + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( Pattern.class ).withProperty( "string" ), + violationOf( Positive.class ).withProperty( "num" ), + violationOf( Min.class ).withProperty( "looooong" ), + violationOf( Length.class ).withProperty( "message" ), + violationOf( AssertTrue.class ).withProperty( "key" ), + violationOf( NotEmpty.class ).withProperty( "strings" ) + ); + } + + private static class Bar extends Foo implements HibernateValidatorEnhancedBean { + @NotEmpty + private final List strings; + + private Bar() { + this.strings = Collections.emptyList(); + } + + @Override + public Object $$_hibernateValidator_getFieldValue(String name) { + if ( "strings".equals( name ) ) { + return strings; + } + return super.$$_hibernateValidator_getFieldValue( name ); + } + } + + private static class Foo implements HibernateValidatorEnhancedBean { + @Pattern(regexp = "[A-Z]") + private String string; + @Positive + private Integer num; + @Min(100_000L) + private long looooong; + + public Foo() { + this( "test", -1, 100L ); + } + + public Foo(final String string, final Integer num, final long looooong) { + this.string = string; + this.num = num; + this.looooong = looooong; + } + + @Length(min = 100) + public String getMessage() { + return "messssssage"; + } + + @AssertTrue + public boolean getKey() { + return false; + } + + @Override + public Object $$_hibernateValidator_getFieldValue(String name) { + if ( "string".equals( name ) ) { + return string; + } + if ( "num".equals( name ) ) { + return num; + } + if ( "looooong".equals( name ) ) { + return looooong; + } + throw new IllegalArgumentException( "No such property as '" + name + "'" ); + } + + @Override + public Object $$_hibernateValidator_getGetterValue(String name) { + if ( "getKey".equals( name ) ) { + return getKey(); + } + if ( "getMessage".equals( name ) ) { + return getMessage(); + } + throw new IllegalArgumentException( "No such property as '" + name + "'" ); + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/ValidatorFactoryNoELBootstrapTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/ValidatorFactoryNoELBootstrapTest.java index 2efac1fdcf..869915b1b9 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/ValidatorFactoryNoELBootstrapTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/ValidatorFactoryNoELBootstrapTest.java @@ -27,6 +27,7 @@ import javax.validation.Validator; import javax.validation.constraints.Min; +import org.hibernate.validator.internal.IgnoreForbiddenApisErrors; import org.hibernate.validator.internal.util.privilegedactions.GetClassLoader; import org.hibernate.validator.internal.util.privilegedactions.SetContextClassLoader; import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator; @@ -143,6 +144,7 @@ public ELIgnoringClassLoader( String packageMissing ) { } @Override + @IgnoreForbiddenApisErrors(reason = "getPackage() is deprecated but getDefinedPackage() is only available from JDK 9.") public Class loadClass(String className) throws ClassNotFoundException { // This is what we in the end want to achieve. Throw ClassNotFoundException for javax.el classes if ( className.startsWith( packageMissing ) ) { @@ -230,7 +232,7 @@ private void runWithoutElLibs(Class delegateType, String packageMissing) thro ClassLoader classLoader = new ELIgnoringClassLoader( packageMissing ); run( SetContextClassLoader.action( classLoader ) ); - Object test = classLoader.loadClass( delegateType.getName() ).newInstance(); + Object test = classLoader.loadClass( delegateType.getName() ).getConstructor().newInstance(); test.getClass().getMethod( "run" ).invoke( test ); } finally { diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/constraintvalidation/ConstraintValidatorManagerTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/constraintvalidation/ConstraintValidatorManagerTest.java index 26f5df550f..42a9a4badf 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/constraintvalidation/ConstraintValidatorManagerTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/constraintvalidation/ConstraintValidatorManagerTest.java @@ -36,7 +36,7 @@ import org.hibernate.validator.internal.constraintvalidators.bv.NotNullValidator; import org.hibernate.validator.internal.engine.DefaultClockProvider; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl; -import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManagerImpl; import org.hibernate.validator.internal.engine.scripting.DefaultScriptEvaluatorFactory; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; @@ -49,14 +49,14 @@ * @author Gunnar Morling */ public class ConstraintValidatorManagerTest { - private ConstraintValidatorManager constraintValidatorManager; + private ConstraintValidatorManagerImpl constraintValidatorManager; private ConstraintValidatorFactory constraintValidatorFactory; private Validator validator; @BeforeMethod public void setUp() { constraintValidatorFactory = new ConstraintValidatorFactoryImpl(); - constraintValidatorManager = new ConstraintValidatorManager( constraintValidatorFactory, getDummyConstraintValidatorInitializationContext() ); + constraintValidatorManager = new ConstraintValidatorManagerImpl( constraintValidatorFactory, getDummyConstraintValidatorInitializationContext() ); validator = getValidator(); } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/failfast/FailFastTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/failfast/FailFastTest.java index 1098e29f1f..a75b3c71df 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/failfast/FailFastTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/failfast/FailFastTest.java @@ -30,16 +30,16 @@ import javax.validation.ValidationException; import javax.validation.Validator; import javax.validation.ValidatorFactory; +import javax.validation.constraints.Email; import javax.validation.constraints.Max; import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; import org.hibernate.validator.HibernateValidator; import org.hibernate.validator.HibernateValidatorConfiguration; import org.hibernate.validator.HibernateValidatorFactory; -import org.hibernate.validator.constraints.Email; -import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; import org.hibernate.validator.testutil.TestForIssue; @@ -162,8 +162,9 @@ public void testFailFastMethodValidationSetOnValidatorFactory() { fail(); } catch (ConstraintViolationException e) { - assertThat( e.getConstraintViolations() ).containsOnlyViolations( - violationOf( NotBlank.class ) + assertThat( e.getConstraintViolations() ).containsOneOfViolations( + violationOf( NotBlank.class ), + violationOf( Min.class ) ); } } @@ -212,8 +213,9 @@ public void testFailFastMethodValidationSetWithProperty() { fail(); } catch (ConstraintViolationException e) { - assertThat( e.getConstraintViolations() ).containsOnlyViolations( - violationOf( NotBlank.class ) + assertThat( e.getConstraintViolations() ).containsOneOfViolations( + violationOf( NotBlank.class ), + violationOf( Min.class ) ); } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/conversion/AbstractGroupConversionTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/conversion/AbstractGroupConversionTest.java index 5075439f51..c78abcee4c 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/conversion/AbstractGroupConversionTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/conversion/AbstractGroupConversionTest.java @@ -23,6 +23,7 @@ import javax.validation.GroupSequence; import javax.validation.Valid; import javax.validation.Validator; +import javax.validation.constraints.AssertTrue; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import javax.validation.groups.ConvertGroup; @@ -30,6 +31,7 @@ import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.testutils.CandidateForTck; import org.testng.annotations.Test; /** @@ -214,6 +216,19 @@ public void conversionFromSequenceCausesException() { validator.validate( new User8() ); } + @Test + @CandidateForTck + public void sameBeanDifferentGroups() { + Set> violations = validator.validate( new User9() ); + assertThat( violations ).containsOnlyViolations( + violationOf( AssertTrue.class ).withPropertyPath( pathWith() + .property( "a" ) + .property( "b" ) + ) + ); + } + + public interface Complete extends Default { } @@ -341,4 +356,12 @@ private static class User8 { @ConvertGroup(from = PostalSequence.class, to = BasicPostal.class) private final List
    addresses = Arrays.asList( new Address() ); } + + private static class User9 { + @Valid + @ConvertGroup(from = Default.class, to = BasicNumber.class) + User9 a = this; + @AssertTrue(groups = BasicNumber.class) + boolean b; + } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/sequence/SequenceOnObjectsWithCycles.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/sequence/SequenceOnObjectsWithCycles.java new file mode 100644 index 0000000000..97e8439ee4 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/sequence/SequenceOnObjectsWithCycles.java @@ -0,0 +1,64 @@ +/* + * 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.engine.groups.sequence; + +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.GroupSequence; +import javax.validation.Valid; +import javax.validation.Validator; + +import org.hibernate.validator.testutil.TestForIssue; +import org.hibernate.validator.testutils.ValidatorUtil; + +import org.testng.Assert; +import org.testng.annotations.Test; + +@TestForIssue(jiraKey = "HV-1692") +public class SequenceOnObjectsWithCycles { + + @Test + public void groupSequenceOfGroupSequences() { + Validator validator = ValidatorUtil.getValidator(); + + YourAnnotatedBean yourEntity1 = new YourAnnotatedBean(); + AnotherBean anotherBean = new AnotherBean(); + anotherBean.setYourAnnotatedBean( yourEntity1 ); + yourEntity1.setBean( anotherBean ); + + Set> constraintViolations = validator.validate( yourEntity1 ); + Assert.assertEquals( 0, constraintViolations.size() ); + + } + + @GroupSequence({ AnotherBean.class, Magic.class }) + public class AnotherBean { + + @Valid + private YourAnnotatedBean yourAnnotatedBean; + + + public void setYourAnnotatedBean(YourAnnotatedBean yourAnnotatedBean) { + this.yourAnnotatedBean = yourAnnotatedBean; + } + } + + @GroupSequence({ YourAnnotatedBean.class, Magic.class }) + public class YourAnnotatedBean { + + @Valid + private AnotherBean bean; + + public void setBean(AnotherBean bean) { + this.bean = bean; + } + } + + public interface Magic { + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/sequence/SequenceTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/sequence/SequenceTest.java new file mode 100644 index 0000000000..e1d11295f6 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/sequence/SequenceTest.java @@ -0,0 +1,89 @@ +/* + * 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.engine.groups.sequence; + +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; + +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.GroupSequence; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.testutil.TestForIssue; +import org.hibernate.validator.testutils.ValidatorUtil; + +import org.testng.annotations.Test; + +/** + * @author Marko Bekhta + */ +@TestForIssue( jiraKey = "HV-1715") +public class SequenceTest { + + @Test + public void groupSequenceOfGroupSequences() { + Validator validator = ValidatorUtil.getValidator(); + + Set> violations = validator.validate( new Foo( null, "", null ) ); + + assertThat( violations ).containsOnlyViolations( + violationOf( NotNull.class ).withProperty( "str1" ) + ); + } + + interface Group1 extends Group11, Group12, Group13, Group14, Group15, Group16, Group17, Group18, Group19 { + } + + interface Group11 { + } + + interface Group12 { + } + + interface Group13 { + } + + interface Group14 { + } + + interface Group15 { + } + + interface Group16 { + } + + interface Group17 { + } + + interface Group18 { + } + + interface Group19 { + } + + interface Group2 { + } + + @GroupSequence({ Foo.class, SequenceTest.Group1.class, SequenceTest.Group2.class }) + private static class Foo { + @NotNull(groups = SequenceTest.Group11.class) + private String str1; + @NotNull(groups = SequenceTest.Group12.class) + private String str2; + @NotNull(groups = SequenceTest.Group2.class) + private String str3; + + public Foo(String str1, String str2, String str3) { + this.str1 = str1; + this.str2 = str2; + this.str3 = str3; + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ExpressionLanguageMessageInterpolationTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ExpressionLanguageMessageInterpolationTest.java index b9515ff476..b80ccd14f1 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ExpressionLanguageMessageInterpolationTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ExpressionLanguageMessageInterpolationTest.java @@ -18,6 +18,7 @@ import org.hibernate.validator.internal.engine.MessageInterpolatorContext; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; import org.hibernate.validator.testutil.TestForIssue; @@ -44,7 +45,7 @@ public void setUp() { new ConstraintHelper(), null, notNullAnnotationDescriptorBuilder.build(), - java.lang.annotation.ElementType.FIELD + ConstraintLocationKind.FIELD ); ConstraintAnnotationDescriptor.Builder sizeAnnotationDescriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( Size.class ); @@ -52,7 +53,7 @@ public void setUp() { new ConstraintHelper(), null, sizeAnnotationDescriptorBuilder.build(), - java.lang.annotation.ElementType.FIELD + ConstraintLocationKind.FIELD ); interpolatorUnderTest = new ResourceBundleMessageInterpolator(); @@ -66,9 +67,9 @@ public void testExpressionLanguageGraphNavigation() { notNullDescriptor, user, null, + null, Collections.emptyMap(), - Collections.emptyMap() - ); + Collections.emptyMap() ); String expected = "18"; String actual = interpolatorUnderTest.interpolate( "${validatedValue.age}", context ); @@ -81,9 +82,9 @@ public void testUnknownPropertyInExpressionLanguageGraphNavigation() { notNullDescriptor, new User(), null, + null, Collections.emptyMap(), - Collections.emptyMap() - ); + Collections.emptyMap() ); String expected = "${validatedValue.foo}"; String actual = interpolatorUnderTest.interpolate( "${validatedValue.foo}", context ); @@ -171,9 +172,9 @@ public void testLocaleBasedFormatting() { notNullDescriptor, 42.00000d, null, + null, Collections.emptyMap(), - Collections.emptyMap() - ); + Collections.emptyMap() ); // german locale String expected = "42,00"; @@ -231,9 +232,9 @@ public void testCallingWrongFormatterMethod() { notNullDescriptor, 42.00000d, null, + null, Collections.emptyMap(), - Collections.emptyMap() - ); + Collections.emptyMap() ); String expected = "${formatter.foo('%1$.2f', validatedValue)}"; String actual = interpolatorUnderTest.interpolate( @@ -307,8 +308,8 @@ private MessageInterpolatorContext createMessageInterpolatorContext(ConstraintDe descriptor, null, null, + null, Collections.emptyMap(), - Collections.emptyMap() - ); + Collections.emptyMap() ); } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/MessageInterpolationWithDefaultBundleTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/MessageInterpolationWithDefaultBundleTest.java index 9e6f39fd75..cb8922b041 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/MessageInterpolationWithDefaultBundleTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/MessageInterpolationWithDefaultBundleTest.java @@ -17,8 +17,8 @@ import javax.validation.Validator; import javax.validation.constraints.DecimalMax; import javax.validation.constraints.DecimalMin; +import javax.validation.constraints.Email; -import org.hibernate.validator.constraints.Email; import org.hibernate.validator.constraints.Range; import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; import org.hibernate.validator.testutil.TestForIssue; @@ -58,7 +58,7 @@ public void testEmailAndRangeMessageEnglishLocale() { user.setAge( 16 ); Set> constraintViolations = validator.validate( user ); assertThat( constraintViolations ).containsOnlyViolations( - violationOf( Email.class ).withMessage( "not a well-formed email address" ), + violationOf( Email.class ).withMessage( "must be a well-formed email address" ), violationOf( Range.class ).withMessage( "must be between 18 and 21" ) ); } @@ -74,8 +74,8 @@ public void testEmailAndRangeMessageGermanLocale() { user.setAge( 16 ); Set> constraintViolations = validator.validate( user ); assertThat( constraintViolations ).containsOnlyViolations( - violationOf( Email.class ).withMessage( "keine g\u00FCltige E-Mail-Adresse" ), - violationOf( Range.class ).withMessage( "muss zwischen 18 und 21 liegen" ) + violationOf( Email.class ).withMessage( "muss eine korrekt formatierte E-Mail-Adresse sein" ), + violationOf( Range.class ).withMessage( "muss zwischen 18 und 21 sein" ) ); } @@ -90,8 +90,8 @@ public void testEmailAndRangeMessageFrenchLocale() { user.setAge( 16 ); Set> constraintViolations = validator.validate( user ); assertThat( constraintViolations ).containsOnlyViolations( - violationOf( Email.class ).withMessage( "adresse email mal form\u00E9e" ), - violationOf( Range.class ).withMessage( "doit \u00EAtre entre 18 et 21" ) + violationOf( Email.class ).withMessage( "doit être une adresse électronique syntaxiquement correcte" ), + violationOf( Range.class ).withMessage( "doit être compris entre 18 et 21" ) ); } @@ -110,7 +110,7 @@ public void testThatExplicitlySetEnglishLocaleHasPrecedenceOverDefaultLocale() { user.setAge( 16 ); Set> constraintViolations = validator.validate( user ); assertThat( constraintViolations ).containsOnlyViolations( - violationOf( Email.class ).withMessage( "not a well-formed email address" ), + violationOf( Email.class ).withMessage( "must be a well-formed email address" ), violationOf( Range.class ).withMessage( "must be between 18 and 21" ) ); } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/MessageInterpolatorContextTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/MessageInterpolatorContextTest.java index 8635bc1eda..c989b6b3ad 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/MessageInterpolatorContextTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/MessageInterpolatorContextTest.java @@ -7,35 +7,46 @@ package org.hibernate.validator.test.internal.engine.messageinterpolation; -import static org.easymock.EasyMock.createMock; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.replay; -import static org.easymock.EasyMock.verify; -import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; -import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertTrue; - -import java.util.Collections; -import java.util.Set; +import org.hibernate.validator.internal.engine.MessageInterpolatorContext; +import org.hibernate.validator.messageinterpolation.HibernateMessageInterpolatorContext; +import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; +import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; +import org.hibernate.validator.testutil.TestForIssue; +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; import javax.validation.Configuration; import javax.validation.ConstraintViolation; import javax.validation.MessageInterpolator; import javax.validation.MessageInterpolator.Context; +import javax.validation.Path; +import javax.validation.Valid; import javax.validation.ValidationException; import javax.validation.Validator; import javax.validation.constraints.Size; import javax.validation.metadata.BeanDescriptor; import javax.validation.metadata.ConstraintDescriptor; import javax.validation.metadata.PropertyDescriptor; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.ResourceBundle; +import java.util.Set; -import org.hibernate.validator.internal.engine.MessageInterpolatorContext; -import org.hibernate.validator.messageinterpolation.HibernateMessageInterpolatorContext; -import org.hibernate.validator.testutil.TestForIssue; -import org.hibernate.validator.testutils.ValidatorUtil; - -import org.testng.annotations.Test; +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; +import static org.hibernate.validator.testutils.ValidatorUtil.getConfiguration; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; /** * @author Hardy Ferentschik @@ -44,6 +55,16 @@ public class MessageInterpolatorContextTest { private static final String MESSAGE = "{foo}"; + Validator validator; + + @BeforeTest + public void setUp() { + validator = getConfiguration() + .messageInterpolator( new PathResourceBundleMessageInterpolator( new TestResourceBundleLocator() ) ) + .buildValidatorFactory() + .getValidator(); + } + @Test @TestForIssue(jiraKey = "HV-333") public void testContextWithRightDescriptorAndValueAndRootBeanClassIsPassedToMessageInterpolator() { @@ -69,9 +90,9 @@ public void testContextWithRightDescriptorAndValueAndRootBeanClassIsPassedToMess constraintDescriptors.iterator().next(), validatedValue, TestBean.class, + null, Collections.emptyMap(), - Collections.emptyMap() - ) + Collections.emptyMap() ) ) ) .andReturn( "invalid" ); @@ -88,13 +109,13 @@ public void testContextWithRightDescriptorAndValueAndRootBeanClassIsPassedToMess @Test(expectedExceptions = ValidationException.class) public void testUnwrapToImplementationCausesValidationException() { - Context context = new MessageInterpolatorContext( null, null, null, Collections.emptyMap(), Collections.emptyMap() ); + Context context = new MessageInterpolatorContext( null, null, null, null, Collections.emptyMap(), Collections.emptyMap() ); context.unwrap( MessageInterpolatorContext.class ); } @Test public void testUnwrapToInterfaceTypesSucceeds() { - Context context = new MessageInterpolatorContext( null, null, null, Collections.emptyMap(), Collections.emptyMap() ); + Context context = new MessageInterpolatorContext( null, null, null, null, Collections.emptyMap(), Collections.emptyMap() ); MessageInterpolator.Context asMessageInterpolatorContext = context.unwrap( MessageInterpolator.Context.class ); assertSame( asMessageInterpolatorContext, context ); @@ -115,13 +136,46 @@ public void testGetRootBeanType() { null, null, rootBeanType, + null, Collections.emptyMap(), - Collections.emptyMap() - ); + Collections.emptyMap() ); assertSame( context.unwrap( HibernateMessageInterpolatorContext.class ).getRootBeanType(), rootBeanType ); } + @Test + @TestForIssue(jiraKey = "HV-1657") + public void testGetPropertyPath() { + Path pathMock = createMock( Path.class ); + MessageInterpolator.Context context = new MessageInterpolatorContext( + null, + null, + null, + pathMock, + Collections.emptyMap(), + Collections.emptyMap() ); + + assertSame( context.unwrap( HibernateMessageInterpolatorContext.class ).getPropertyPath(), pathMock ); + } + + @Test + @TestForIssue(jiraKey = "HV-1657") + public void testUsageOfPathInInterpolation() { + Employee employee = createEmployee( "farTooLongStreet", "workPlaza" ); + Set> constraintViolations = validator.validate( employee ); + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( Size.class ) + .withMessage( "Employee Street should be smaller than 15" ) + ); + + employee = createEmployee( "mySquare", "farTooLongStreet" ); + constraintViolations = validator.validate( employee ); + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( Size.class ) + .withMessage( "Company Street should be smaller than 15" ) + ); + } + private static class TestBean { @Size(min = 10, message = MESSAGE) private final String test; @@ -130,4 +184,144 @@ public TestBean(String test) { this.test = test; } } + + /** + * Interpolator demonstrator for {@link MessageInterpolatorContextTest#testUsageOfPathInInterpolation} + */ + public class PathResourceBundleMessageInterpolator extends ResourceBundleMessageInterpolator { + + public PathResourceBundleMessageInterpolator(ResourceBundleLocator userResourceBundleLocator) { + super( userResourceBundleLocator ); + } + + @Override + public String interpolate(String message, Context context) { + String newMessage = super.interpolate( message, context ); + newMessage = newMessage.replace( "#path#", "{" + pathToString( context ) + "}" ); + return super.interpolate( newMessage, context ); + } + + private String pathToString(Context context) { + HibernateMessageInterpolatorContext hContext = context.unwrap( HibernateMessageInterpolatorContext.class ); + StringBuilder baseNodeBuilder = new StringBuilder( hContext.getRootBeanType().getSimpleName() ); + for ( Path.Node node : hContext.getPropertyPath() ) { + if ( node.getName() != null ) { + baseNodeBuilder.append( "." ).append( node.getName() ); + } + } + return baseNodeBuilder.toString(); + } + + } + + /** + * creating a test employee with 2 properties of the same type (same annotation). + * + * @param employeeStreet + * @param employerStreet + * @return + */ + public static Employee createEmployee(String employeeStreet, String employerStreet) { + Employee employee = new Employee(); + employee.address = new Address(); + employee.address.street = employeeStreet; + employee.employer = new Employer(); + employee.employer.address = new Address(); + employee.employer.address.street = employerStreet; + return employee; + } + + /** + * Test bean for {@link MessageInterpolatorContextTest#testUsageOfPathInInterpolation} + */ + public static class Address { + + @Size(max = 15) + private String street; + + } + + /** + * Test bean for {@link MessageInterpolatorContextTest#testUsageOfPathInInterpolation} + */ + public static class Employee { + + @Valid + private Address address; + + @Valid + private Employer employer; + } + + /** + * Test bean for {@link MessageInterpolatorContextTest#testUsageOfPathInInterpolation} + */ + public static class Employer { + + @Valid + private Address address; + } + + /** + * A dummy locator for {@link MessageInterpolatorContextTest#testUsageOfPathInInterpolation} + */ + private static class TestResourceBundleLocator implements ResourceBundleLocator { + + private final ResourceBundle resourceBundle; + + public TestResourceBundleLocator() { + this( new TestResourceBundle() ); + } + + public TestResourceBundleLocator(ResourceBundle bundle) { + resourceBundle = bundle; + } + + @Override + public ResourceBundle getResourceBundle(Locale locale) { + return resourceBundle; + } + } + + /** + * A dummy resource bundle for {@link MessageInterpolatorContextTest#testUsageOfPathInInterpolation} + */ + private static class TestResourceBundle extends ResourceBundle implements Enumeration { + private final Map testResources; + Iterator iter; + + public TestResourceBundle() { + testResources = new HashMap(); + // add some test messages + testResources.put( "Employee.address.street", "Employee Street" ); + testResources.put( "Employee.employer.address.street", "Company Street" ); + testResources.put( "javax.validation.constraints.Size.message", "#path# should be smaller than {max}" ); + iter = testResources.keySet().iterator(); + } + + @Override + public Object handleGetObject(String key) { + return testResources.get( key ); + } + + @Override + public Enumeration getKeys() { + return this; + } + + @Override + public boolean hasMoreElements() { + return iter.hasNext(); + } + + @Override + public String nextElement() { + if ( hasMoreElements() ) { + return iter.next(); + } + else { + throw new NoSuchElementException(); + } + } + } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ResourceBundleMessageInterpolatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ResourceBundleMessageInterpolatorTest.java index 889120f615..5636bafda4 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ResourceBundleMessageInterpolatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ResourceBundleMessageInterpolatorTest.java @@ -27,6 +27,7 @@ import org.hibernate.validator.internal.engine.MessageInterpolatorContext; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; @@ -54,7 +55,7 @@ public void setUp() { new ConstraintHelper(), null, descriptorBuilder.build(), - java.lang.annotation.ElementType.FIELD + ConstraintLocationKind.FIELD ); ConstraintAnnotationDescriptor.Builder sizeAnnotationDescriptorBuilder = new ConstraintAnnotationDescriptor.Builder( Size.class ); @@ -62,7 +63,7 @@ public void setUp() { new ConstraintHelper(), null, sizeAnnotationDescriptorBuilder.build(), - java.lang.annotation.ElementType.FIELD + ConstraintLocationKind.FIELD ); } @@ -216,7 +217,7 @@ public void testRecursiveMessageInterpolation() { new ConstraintHelper(), null, descriptorBuilder.build(), - java.lang.annotation.ElementType.FIELD + ConstraintLocationKind.FIELD ); interpolator = new ResourceBundleMessageInterpolator( @@ -245,7 +246,7 @@ public void testCorrectMessageInterpolationIfParameterCannotBeReplaced() { new ConstraintHelper(), null, maxDescriptor, - java.lang.annotation.ElementType.FIELD + ConstraintLocationKind.FIELD ); interpolator = new ResourceBundleMessageInterpolator( @@ -278,9 +279,9 @@ private MessageInterpolatorContext createMessageInterpolatorContext(ConstraintDe descriptor, null, null, + null, Collections.emptyMap(), - Collections.emptyMap() - ); + Collections.emptyMap() ); } private void runInterpolation(boolean cachingEnabled) { diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/User.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/User.java index 97e549af83..f233c1cf12 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/User.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/User.java @@ -6,7 +6,8 @@ */ package org.hibernate.validator.test.internal.engine.messageinterpolation; -import org.hibernate.validator.constraints.Email; +import javax.validation.constraints.Email; + import org.hibernate.validator.constraints.Range; /** diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/AbstractMethodValidationTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/AbstractMethodValidationTest.java index 79927132d4..b0b5a21750 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/AbstractMethodValidationTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/AbstractMethodValidationTest.java @@ -15,6 +15,7 @@ import static org.testng.Assert.assertNotNull; import static org.testng.Assert.fail; +import java.time.LocalDate; import java.util.Arrays; import java.util.Iterator; import java.util.List; @@ -41,7 +42,6 @@ import org.hibernate.validator.test.internal.engine.methodvalidation.service.RepositoryBase; import org.hibernate.validator.testutil.TestForIssue; -import org.joda.time.DateMidnight; import org.testng.annotations.Test; /** @@ -756,8 +756,8 @@ public void shouldValidateGetterLikeNamedMethodWithParameter() { @Test public void validationOfCrossParameterConstraint() { //given - DateMidnight startDate = new DateMidnight( 2012, 11, 5 ); - DateMidnight endDate = new DateMidnight( 2012, 11, 4 ); + LocalDate startDate = LocalDate.of( 2012, 11, 5 ); + LocalDate endDate = LocalDate.of( 2012, 11, 4 ); try { //when @@ -787,8 +787,8 @@ public void validationOfCrossParameterConstraint() { assertMethod( constraintViolation, "methodWithCrossParameterConstraint", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ); } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/IllegalMethodParameterConstraintsTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/IllegalMethodParameterConstraintsTest.java index db20900cab..2ebe164a08 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/IllegalMethodParameterConstraintsTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/IllegalMethodParameterConstraintsTest.java @@ -8,16 +8,17 @@ import static org.hibernate.validator.testutils.ValidatorUtil.getValidator; +import java.time.LocalDate; + import javax.validation.ConstraintDeclarationException; import javax.validation.Valid; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; -import org.joda.time.DateMidnight; -import org.testng.annotations.Test; - import org.hibernate.validator.test.internal.engine.methodvalidation.service.ConsistentDateParameters; +import org.testng.annotations.Test; + /** * Integration test for {@link org.hibernate.validator.internal.engine.ValidatorImpl} and * {@link org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl} which @@ -153,7 +154,7 @@ public void zap(@Valid String s) { } private interface Zip { - void zip(DateMidnight start, DateMidnight end); + void zip(LocalDate start, LocalDate end); } private static class ZipImpl implements Zip { @@ -162,7 +163,7 @@ private static class ZipImpl implements Zip { */ @Override @ConsistentDateParameters - public void zip(DateMidnight start, DateMidnight end) { + public void zip(LocalDate start, LocalDate end) { } } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/MethodParameterConstraintsInParallelHierarchyTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/MethodParameterConstraintsInParallelHierarchyTest.java new file mode 100644 index 0000000000..408a91c19e --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/MethodParameterConstraintsInParallelHierarchyTest.java @@ -0,0 +1,72 @@ +/* + * 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.engine.methodvalidation; + +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; + +import java.lang.reflect.Method; +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.testutil.TestForIssue; + +import org.testng.annotations.Test; + +/** + * @author Marko Bekhta + */ +public class MethodParameterConstraintsInParallelHierarchyTest { + + /** + * NOTE: prior to the changes made for HV-1450, this test was failing randomly. + */ + @Test + @TestForIssue(jiraKey = "HV-1450") + public void testDeepParallelHierarchyIsProcessedCorrectly() throws Exception { + + WebServiceImpl service = new WebServiceImpl(); + Method method = WebServiceImpl.class.getMethod( "getEntityVersion", Long.class ); + Object[] params = new Object[] { null }; + + for ( int i = 0; i < 100; i++ ) { + Validator validator = Validation.byDefaultProvider().configure() + .buildValidatorFactory().getValidator(); + + Set> violations = validator.forExecutables().validateParameters( service, method, params ); + + assertThat( violations ).containsOnlyViolations( violationOf( NotNull.class ) ); + } + } + + private class WebServiceImpl extends AbstractWebService implements ExtendedWebService { + + } + + private abstract class AbstractWebService implements WebService { + + @Override + public int getEntityVersion(Long id) { + return id.intValue(); + } + } + + private interface ExtendedWebService extends WebService { + + @Override + int getEntityVersion(Long id); + } + + private interface WebService { + + int getEntityVersion(@NotNull Long id); + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/PureCompositeConstraintsOnReturnValueTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/PureCompositeConstraintsOnReturnValueTest.java index 0a2324ad3b..09bd15d7c7 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/PureCompositeConstraintsOnReturnValueTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/PureCompositeConstraintsOnReturnValueTest.java @@ -28,13 +28,13 @@ import javax.validation.Payload; import javax.validation.ReportAsSingleViolation; import javax.validation.Validator; +import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; import javax.validation.constraintvalidation.SupportedValidationTarget; import javax.validation.constraintvalidation.ValidationTarget; -import org.hibernate.validator.constraints.NotEmpty; import org.hibernate.validator.testutil.TestForIssue; import org.hibernate.validator.testutils.ValidatorUtil; @@ -44,7 +44,6 @@ /** * @author Marko Bekhta */ -@SuppressWarnings("deprecation") public class PureCompositeConstraintsOnReturnValueTest { private Validator validator; private Foo foo; diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/TypeVariableMethodParameterResolutionTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/TypeVariableMethodParameterResolutionTest.java new file mode 100644 index 0000000000..da14457d59 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/TypeVariableMethodParameterResolutionTest.java @@ -0,0 +1,41 @@ +/* + * 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.engine.methodvalidation; + +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.constraints.Size; +import javax.validation.executable.ExecutableValidator; + +import org.testng.annotations.Test; + +public class TypeVariableMethodParameterResolutionTest { + + @Test + public void testTypeVariableMethodParameterResolution() throws NoSuchMethodException, SecurityException { + ExecutableValidator validator = Validation.buildDefaultValidatorFactory() + .getValidator().forExecutables(); + + Set> violations = validator.validateParameters( new Bean(), Bean.class.getMethod( "method", List.class ), + new Object[]{ new ArrayList<>() } ); + assertThat( violations ).containsOnlyViolations( violationOf( Size.class ) ); + } + + @SuppressWarnings("unused") + private class Bean { + + public > void method(@Size(min = 1) T myList) { + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/crossparameter/DodgyConstraint.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/crossparameter/DodgyConstraint.java index 837a37d379..c23db0079e 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/crossparameter/DodgyConstraint.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/crossparameter/DodgyConstraint.java @@ -6,24 +6,25 @@ */ package org.hibernate.validator.test.internal.engine.methodvalidation.crossparameter; +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; + import javax.validation.Constraint; import javax.validation.Payload; -import static java.lang.annotation.ElementType.ANNOTATION_TYPE; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.TYPE; - /** * @author Hardy Ferentschik */ @Target({ TYPE, FIELD, METHOD, ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) -@Constraint(validatedBy = { CrossParameterValidator1.class }) +@Constraint(validatedBy = { DodgyConstraintValidator.class }) @Documented public @interface DodgyConstraint { String message() default "{ConsistentDateParameters.message}"; diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/crossparameter/DodgyConstraintValidator.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/crossparameter/DodgyConstraintValidator.java new file mode 100644 index 0000000000..a1d3cb4edd --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/crossparameter/DodgyConstraintValidator.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.test.internal.engine.methodvalidation.crossparameter; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.constraintvalidation.SupportedValidationTarget; +import javax.validation.constraintvalidation.ValidationTarget; + +/** + * @author Hardy Ferentschik + */ +@SupportedValidationTarget( value = ValidationTarget.PARAMETERS) +public class DodgyConstraintValidator implements ConstraintValidator { + + @Override + public boolean isValid(Object[] value, ConstraintValidatorContext context) { + return false; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/returnvaluevalidation/ContactBean.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/returnvaluevalidation/ContactBean.java index 327420d330..010c2d7443 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/returnvaluevalidation/ContactBean.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/returnvaluevalidation/ContactBean.java @@ -6,10 +6,9 @@ */ package org.hibernate.validator.test.internal.engine.methodvalidation.returnvaluevalidation; +import javax.validation.constraints.Email; import javax.validation.constraints.Pattern; -import org.hibernate.validator.constraints.Email; - /** * @author Hardy Ferentschik */ diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/service/ConsistentDateParametersValidator.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/service/ConsistentDateParametersValidator.java index 326ce079be..a3c9ae047b 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/service/ConsistentDateParametersValidator.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/service/ConsistentDateParametersValidator.java @@ -6,17 +6,17 @@ */ package org.hibernate.validator.test.internal.engine.methodvalidation.service; +import java.time.LocalDate; + import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import javax.validation.constraintvalidation.SupportedValidationTarget; import javax.validation.constraintvalidation.ValidationTarget; -import org.joda.time.DateMidnight; - /** * @author Gunnar Morling */ -@SupportedValidationTarget( value = ValidationTarget.PARAMETERS) +@SupportedValidationTarget(value = ValidationTarget.PARAMETERS) public class ConsistentDateParametersValidator implements ConstraintValidator { @Override @@ -29,10 +29,10 @@ public boolean isValid(Object[] value, ConstraintValidatorContext context) { return true; } - if ( !( value[0] instanceof DateMidnight ) || !( value[1] instanceof DateMidnight ) ) { + if ( !( value[0] instanceof LocalDate ) || !( value[1] instanceof LocalDate ) ) { throw new IllegalArgumentException( "Unexpected method signature" ); } - return ( (DateMidnight) value[0] ).isBefore( (DateMidnight) value[1] ); + return ( (LocalDate) value[0] ).isBefore( (LocalDate) value[1] ); } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/service/CustomerRepository.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/service/CustomerRepository.java index 5a9ce32fcd..e5475ba12f 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/service/CustomerRepository.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/service/CustomerRepository.java @@ -6,16 +6,16 @@ */ package org.hibernate.validator.test.internal.engine.methodvalidation.service; +import java.time.LocalDate; import java.util.List; import java.util.Map; import javax.validation.Valid; import javax.validation.constraints.Min; +import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; -import org.hibernate.validator.constraints.NotEmpty; import org.hibernate.validator.test.internal.engine.methodvalidation.model.Customer; -import org.joda.time.DateMidnight; /** * @author Gunnar Morling @@ -76,7 +76,7 @@ public interface CustomerRepository extends RepositoryBase { int getFoo(@NotEmpty String s); @ConsistentDateParameters - void methodWithCrossParameterConstraint(@NotNull DateMidnight start, @NotNull DateMidnight end); + void methodWithCrossParameterConstraint(@NotNull LocalDate start, @NotNull LocalDate end); public interface ValidationGroup { } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/service/CustomerRepositoryImpl.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/service/CustomerRepositoryImpl.java index e76ed17cfb..a358f11db7 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/service/CustomerRepositoryImpl.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/service/CustomerRepositoryImpl.java @@ -13,6 +13,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; +import java.time.LocalDate; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -25,7 +26,6 @@ import javax.validation.constraints.NotNull; import org.hibernate.validator.test.internal.engine.methodvalidation.model.Customer; -import org.joda.time.DateMidnight; /** * @author Gunnar Morling @@ -148,7 +148,7 @@ public int getFoo(String s) { } @Override - public void methodWithCrossParameterConstraint(DateMidnight start, DateMidnight end) { + public void methodWithCrossParameterConstraint(LocalDate start, LocalDate end) { } @Constraint(validatedBy = { ValidB2BRepositoryValidator.class }) 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 1153454aa4..6c4a45fcbd 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 @@ -9,6 +9,7 @@ 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.ConstraintValidatorInitializationHelper.getDummyConstraintCreationContext; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; @@ -28,19 +29,20 @@ import javax.validation.constraints.NotNull; import org.hibernate.validator.internal.engine.DefaultParameterNameProvider; +import org.hibernate.validator.internal.engine.DefaultPropertyNodeNameProvider; import org.hibernate.validator.internal.engine.MethodValidationConfiguration; 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.BeanMetaDataManagerImpl; 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.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.hibernate.validator.testutils.ValidatorUtil; - import org.testng.annotations.Test; /** @@ -65,7 +67,7 @@ public void testParsing() { elem = propIter.next(); assertEquals( elem.getName(), "deliveryAddress" ); assertTrue( elem.isInIterable() ); - assertEquals( elem.getIndex(), new Integer( 3 ) ); + assertEquals( elem.getIndex(), Integer.valueOf( 3 ) ); assertTrue( propIter.hasNext() ); elem = propIter.next(); @@ -76,7 +78,7 @@ public void testParsing() { elem = propIter.next(); assertEquals( elem.getName(), null ); assertTrue( elem.isInIterable() ); - assertEquals( elem.getIndex(), new Integer( 1 ) ); + assertEquals( elem.getIndex(), Integer.valueOf( 1 ) ); assertFalse( propIter.hasNext() ); @@ -191,12 +193,11 @@ public void testNonStringMapKey() { public void testCreationOfExecutablePath() throws Exception { Method executable = Container.class.getMethod( "addItem", Key.class, Item.class ); - BeanMetaDataManager beanMetaDataManager = new BeanMetaDataManager( - new ConstraintHelper(), + BeanMetaDataManager beanMetaDataManager = new BeanMetaDataManagerImpl( + getDummyConstraintCreationContext(), new ExecutableHelper( new TypeResolutionHelper() ), - new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), - new ValueExtractorManager( Collections.emptySet() ), + new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy(), new DefaultPropertyNodeNameProvider() ), new ValidationOrderGenerator(), Collections.emptyList(), new MethodValidationConfiguration.Builder().build() diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/serialization/CustomConstraintSerializableTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/serialization/CustomConstraintSerializableTest.java index 24021cf527..12318193c7 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/serialization/CustomConstraintSerializableTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/serialization/CustomConstraintSerializableTest.java @@ -22,10 +22,9 @@ * simple custom Email validation constraint taken from this blog gives a validation result that is not Serializable with * Hibernate Validator 4.0.2.GA with underlying cause that - *

    + *

    * {@code org.hibernate.validator.internal.util.annotationfactory.AnnotationProxy} - *

    - *

    + *

    * Note that Hibernate Validator does not guarantee at all that a * {@link ConstraintViolation} is Serializable because an entity need not be * Serializable, but otherwise there should not be much of a problem (right?). @@ -77,6 +76,7 @@ static class CustomEmail implements Serializable { } + @SuppressWarnings("deprecation") static class HibernateEmail implements Serializable { private static final long serialVersionUID = 7206154160792549270L; diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/traversableresolver/JpaTraversableResolverTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/traversableresolver/JpaTraversableResolverTest.java index 200d5836e3..12871b2080 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/traversableresolver/JpaTraversableResolverTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/traversableresolver/JpaTraversableResolverTest.java @@ -14,7 +14,7 @@ import javax.validation.ConstraintViolation; import javax.validation.Validator; -import org.hibernate.validator.internal.engine.resolver.TraversableResolvers; +import org.hibernate.validator.internal.engine.resolver.JPATraversableResolver; import org.hibernate.validator.testutil.TestForIssue; import org.hibernate.validator.testutils.ValidatorUtil; import org.testng.annotations.BeforeTest; @@ -32,7 +32,7 @@ public class JpaTraversableResolverTest { @BeforeTest public void setUp() { Configuration configuration = ValidatorUtil.getConfiguration(); - configuration.traversableResolver( TraversableResolvers.getDefault() ); + configuration.traversableResolver( new JPATraversableResolver() ); validator = configuration.buildValidatorFactory().getValidator(); } @@ -55,5 +55,3 @@ public void testWithoutBooks() { assertTrue( results.isEmpty() ); } } - - diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/ContainerWithWildcardTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/ContainerWithWildcardTest.java new file mode 100644 index 0000000000..e33456e2e8 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/ContainerWithWildcardTest.java @@ -0,0 +1,84 @@ +/* + * 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.engine.valueextraction; + +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.math.BigDecimal; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.testutil.TestForIssue; + +import org.testng.annotations.Test; + +/** + * @author Marko Bekhta + */ +@TestForIssue(jiraKey = "HV-1720") +public class ContainerWithWildcardTest { + + @Test + public void containerWithUpperBoundWildcard() { + Set> constraintViolations = getValidator().validate( new Foo( Arrays.asList( null, null ) ) ); + + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( NotNull.class ), + violationOf( NotNull.class ) + ); + } + + @Test + public void containerWithWildcard() { + Set> constraintViolations = getValidator().validate( new Bar( Arrays.asList( null, null ) ) ); + + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( NotNull.class ), + violationOf( NotNull.class ) + ); + } + + @Test + public void containerWithLowerBoundWildcard() { + Set> constraintViolations = getValidator().validate( new FooBar( Arrays.asList( null, null ) ) ); + + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( NotNull.class ), + violationOf( NotNull.class ) + ); + } + + private static class Foo { + private final List<@NotNull ? extends BigDecimal> list; + + private Foo(List list) { + this.list = list; + } + } + + private static class Bar { + private final List<@NotNull ?> list; + + private Bar(List list) { + this.list = list; + } + } + + private static class FooBar { + private final List<@NotNull ? super BigDecimal> list; + + private FooBar(List list) { + this.list = list; + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/JavaFXClassLoadingTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/JavaFXClassLoadingTest.java index 4aef341306..d9e95139cb 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/JavaFXClassLoadingTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/JavaFXClassLoadingTest.java @@ -27,9 +27,9 @@ public class JavaFXClassLoadingTest { /** - * This class will be present in the TCCL because is part of JDK 8 + * This class will be present in the TCCL because it is either part of the JDK (JDK 10-) or in the classpath (JDK 11+). */ - private static final String JAVAFX_APPLICATION_CLASS = "javafx.application.Application"; + private static final String JAVAFX_APPLICATION_CLASS = "javafx.beans.value.ObservableValue"; @Test public void shouldBeAbleToFindTheClassInTCCL() throws Exception { diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnConstructorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnConstructorTest.java index ef44331eef..3e1c3e737d 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnConstructorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnConstructorTest.java @@ -31,13 +31,13 @@ import javax.validation.Payload; import javax.validation.ReportAsSingleViolation; import javax.validation.Validator; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Null; import javax.validation.valueextraction.Unwrapping; import org.hibernate.validator.constraints.CompositionType; import org.hibernate.validator.constraints.ConstraintComposition; -import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.testutil.TestForIssue; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnFieldTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnFieldTest.java index 9aca67acc9..6d7db82280 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnFieldTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnFieldTest.java @@ -29,13 +29,13 @@ import javax.validation.Payload; import javax.validation.ReportAsSingleViolation; import javax.validation.Validator; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Null; import javax.validation.valueextraction.Unwrapping; import org.hibernate.validator.constraints.CompositionType; import org.hibernate.validator.constraints.ConstraintComposition; -import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.testutil.TestForIssue; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnGetterTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnGetterTest.java index a9ad70d37b..3e8f788c76 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnGetterTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnGetterTest.java @@ -29,13 +29,13 @@ import javax.validation.Payload; import javax.validation.ReportAsSingleViolation; import javax.validation.Validator; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Null; import javax.validation.valueextraction.Unwrapping; import org.hibernate.validator.constraints.CompositionType; import org.hibernate.validator.constraints.ConstraintComposition; -import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.testutil.TestForIssue; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnMethodTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnMethodTest.java index 929d8109c3..2c097f7517 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnMethodTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnMethodTest.java @@ -31,13 +31,13 @@ import javax.validation.Payload; import javax.validation.ReportAsSingleViolation; import javax.validation.Validator; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Null; import javax.validation.valueextraction.Unwrapping; import org.hibernate.validator.constraints.CompositionType; import org.hibernate.validator.constraints.ConstraintComposition; -import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.testutil.TestForIssue; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintUsingValidatePropertyTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintUsingValidatePropertyTest.java index 661f736d05..c0b999a1a1 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintUsingValidatePropertyTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintUsingValidatePropertyTest.java @@ -29,19 +29,19 @@ import javax.validation.Payload; import javax.validation.ReportAsSingleViolation; import javax.validation.Validator; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Null; import javax.validation.valueextraction.Unwrapping; import org.hibernate.validator.constraints.CompositionType; import org.hibernate.validator.constraints.ConstraintComposition; -import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.testutil.TestForIssue; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; /** - * Test combination of {@link Optional} and {@link UnwrapValidatedValue} on fields using validate property. + * Test combination of {@link Optional} and {@link Unwrapping.Unwrap} on fields using validate property. * * @author Davide D'Alto */ diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintUsingValidateValueTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintUsingValidateValueTest.java index ffe0b5f403..754f83347c 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintUsingValidateValueTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintUsingValidateValueTest.java @@ -28,11 +28,11 @@ import javax.validation.Payload; import javax.validation.ReportAsSingleViolation; import javax.validation.Validator; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.Null; import org.hibernate.validator.constraints.CompositionType; import org.hibernate.validator.constraints.ConstraintComposition; -import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.testutil.TestForIssue; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/UnwrapValidatedValueTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/UnwrapValidatedValueTest.java index 3a447017fd..38580e385d 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/UnwrapValidatedValueTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/UnwrapValidatedValueTest.java @@ -9,7 +9,6 @@ import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; -import java.lang.annotation.ElementType; import java.lang.reflect.Method; import java.util.Set; @@ -40,7 +39,7 @@ import org.testng.annotations.Test; /** - * Test for unwrapping validated values via {@link org.hibernate.validator.valuehandling.UnwrapValidatedValue}. + * Test for unwrapping validated values via {@link Unwrapping.Unwrap}. * * @author Gunnar Morling */ @@ -162,7 +161,7 @@ public void shouldUnwrapPropertyValueBasedOnProgrammaticConfiguration() { HibernateValidatorConfiguration configuration = ValidatorUtil.getConfiguration(); ConstraintMapping mapping = configuration.createConstraintMapping(); mapping.type( OrderLine.class ) - .property( "id", ElementType.FIELD ) + .field( "id" ) .constraint( new MaxDef().value( 5 ) ); Validator validator = configuration.addMapping( mapping ) @@ -182,7 +181,7 @@ public void shouldTakeIntoAccountUnwrappingConfigurationConstraintOverrideOnProg HibernateValidatorConfiguration configuration = ValidatorUtil.getConfiguration(); ConstraintMapping mapping = configuration.createConstraintMapping(); mapping.type( OrderLine.class ) - .property( "id", ElementType.FIELD ) + .field( "id" ) .constraint( new MaxDef().value( 5 ).payload( Unwrapping.Skip.class ) ); Validator validator = configuration.addMapping( mapping ) @@ -199,7 +198,7 @@ public void shouldThrowAnExceptionInCaseOfInvalidUnwrappingConfiguration() { HibernateValidatorConfiguration configuration = ValidatorUtil.getConfiguration(); ConstraintMapping mapping = configuration.createConstraintMapping(); mapping.type( OrderLine.class ) - .property( "id", ElementType.FIELD ) + .field( "id" ) .constraint( new MaxDef().value( 5 ).payload( Unwrapping.Skip.class, Unwrapping.Unwrap.class ) ); validator = configuration.addMapping( mapping ) 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/BeanMetaDataManagerTest.java index 9b402186b8..55ec5dd048 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/BeanMetaDataManagerTest.java @@ -6,6 +6,7 @@ */ package org.hibernate.validator.test.internal.metadata; +import static org.hibernate.validator.testutils.ConstraintValidatorInitializationHelper.getDummyConstraintCreationContext; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotSame; import static org.testng.Assert.assertTrue; @@ -21,14 +22,15 @@ import java.util.List; import org.hibernate.validator.internal.engine.DefaultParameterNameProvider; +import org.hibernate.validator.internal.engine.DefaultPropertyNodeNameProvider; 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.BeanMetaDataManagerImpl; 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.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; @@ -46,16 +48,15 @@ public class BeanMetaDataManagerTest { private static final int LOOP_COUNT = 100000; private static final int ARRAY_ALLOCATION_SIZE = 100000; - private BeanMetaDataManager metaDataManager; + private BeanMetaDataManagerImpl metaDataManager; @BeforeMethod public void setUpBeanMetaDataManager() { - metaDataManager = new BeanMetaDataManager( - new ConstraintHelper(), + metaDataManager = new BeanMetaDataManagerImpl( + getDummyConstraintCreationContext(), new ExecutableHelper( new TypeResolutionHelper() ), - new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), - new ValueExtractorManager( Collections.emptySet() ), + new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy(), new DefaultPropertyNodeNameProvider() ), new ValidationOrderGenerator(), Collections.emptyList(), new MethodValidationConfiguration.Builder().build() diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/ConsistentDateParametersValidator.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/ConsistentDateParametersValidator.java index 9a0b8d0a3c..02adb7ad19 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/ConsistentDateParametersValidator.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/ConsistentDateParametersValidator.java @@ -6,13 +6,13 @@ */ package org.hibernate.validator.test.internal.metadata; +import java.time.LocalDate; + import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import javax.validation.constraintvalidation.SupportedValidationTarget; import javax.validation.constraintvalidation.ValidationTarget; -import org.joda.time.DateMidnight; - /** * @author Gunnar Morling */ @@ -29,10 +29,10 @@ public boolean isValid(Object[] value, ConstraintValidatorContext context) { return true; } - if ( !( value[0] instanceof DateMidnight ) || !( value[1] instanceof DateMidnight ) ) { + if ( !( value[0] instanceof LocalDate ) || !( value[1] instanceof LocalDate ) ) { throw new IllegalArgumentException( "Unexpected method signature" ); } - return ( (DateMidnight) value[0] ).isBefore( (DateMidnight) value[1] ); + return ( (LocalDate) value[0] ).isBefore( (LocalDate) value[1] ); } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/CustomerRepository.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/CustomerRepository.java index 2f112710c0..2c138fd24f 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/CustomerRepository.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/CustomerRepository.java @@ -6,6 +6,7 @@ */ package org.hibernate.validator.test.internal.metadata; +import java.time.LocalDate; import java.util.Set; import javax.validation.Valid; @@ -15,7 +16,6 @@ import javax.validation.groups.Default; import org.hibernate.validator.constraints.ScriptAssert; -import org.joda.time.DateMidnight; /** * @author Gunnar Morling @@ -31,7 +31,7 @@ public CustomerRepository() { } @ConsistentDateParameters - public CustomerRepository(DateMidnight start, DateMidnight end) { + public CustomerRepository(LocalDate start, LocalDate end) { } public Customer createCustomer(CharSequence firstName, @NotNull String lastName) { @@ -69,7 +69,7 @@ public void zap(@Max(1) int i) { } @ConsistentDateParameters(groups = ValidationGroup.class) - public void methodWithCrossParameterConstraint(DateMidnight start, DateMidnight end) { + public void methodWithCrossParameterConstraint(LocalDate start, LocalDate end) { } public void methodWithParameterGroupConversion( diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/CustomerRepositoryExt.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/CustomerRepositoryExt.java index 38182d02a6..627790bb70 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/CustomerRepositoryExt.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/CustomerRepositoryExt.java @@ -12,6 +12,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; +import java.time.LocalDate; import javax.validation.Constraint; import javax.validation.ConstraintValidator; @@ -25,7 +26,6 @@ import org.hibernate.validator.test.internal.metadata.Customer.CustomerBasic; import org.hibernate.validator.test.internal.metadata.Customer.CustomerComplex; -import org.joda.time.DateMidnight; /** * @author Gunnar Morling @@ -57,7 +57,7 @@ public CustomerRepositoryExt(int bar) { } @Valid - public CustomerRepositoryExt(DateMidnight start, DateMidnight end) { + public CustomerRepositoryExt(LocalDate start, LocalDate end) { } public CustomerRepositoryExt(long l) { @@ -123,7 +123,7 @@ public int zip(int i) { } @Override - public void methodWithCrossParameterConstraint(DateMidnight start, DateMidnight end) { + public void methodWithCrossParameterConstraint(LocalDate start, LocalDate end) { } @Constraint(validatedBy = { ValidB2BRepositoryValidator.class }) diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/Person.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/Person.java index 17577ead3a..7272eb738b 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/Person.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/Person.java @@ -6,7 +6,7 @@ */ package org.hibernate.validator.test.internal.metadata; -import org.hibernate.validator.constraints.NotEmpty; +import javax.validation.constraints.NotEmpty; /** * @author Hardy Ferentschik 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 e1a80396f4..dee545145b 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 @@ -7,6 +7,7 @@ package org.hibernate.validator.test.internal.metadata.aggregated; import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.validator.testutils.ConstraintValidatorInitializationHelper.getDummyConstraintCreationContext; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; @@ -14,6 +15,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import java.time.LocalDate; import java.util.Collections; import java.util.UUID; @@ -22,15 +24,17 @@ import javax.validation.groups.Default; import org.hibernate.validator.internal.engine.DefaultParameterNameProvider; +import org.hibernate.validator.internal.engine.DefaultPropertyNodeNameProvider; 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.BeanMetaDataManagerImpl; 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.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; @@ -39,7 +43,6 @@ 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.joda.time.DateMidnight; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -55,12 +58,11 @@ public class ExecutableMetaDataTest { @BeforeMethod public void setupBeanMetaData() { - beanMetaDataManager = new BeanMetaDataManager( - new ConstraintHelper(), + beanMetaDataManager = new BeanMetaDataManagerImpl( + getDummyConstraintCreationContext(), new ExecutableHelper( new TypeResolutionHelper() ), - new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), - new ValueExtractorManager( Collections.emptySet() ), + new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy(), new DefaultPropertyNodeNameProvider() ), new ValidationOrderGenerator(), Collections.emptyList(), new MethodValidationConfiguration.Builder().build() @@ -258,8 +260,8 @@ public void methodWithCascadedParameter() throws Exception { public void methodWithCrossParameterConstraint() throws Exception { Method method = CustomerRepositoryExt.class.getMethod( "methodWithCrossParameterConstraint", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ); ExecutableMetaData methodMetaData = beanMetaData.getMetaDataFor( method ).get(); 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 bccf0c208b..cb8b39c6f1 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 @@ -7,6 +7,7 @@ package org.hibernate.validator.test.internal.metadata.aggregated; import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.validator.testutils.ConstraintValidatorInitializationHelper.getDummyConstraintCreationContext; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; @@ -24,15 +25,17 @@ import javax.validation.groups.Default; import org.hibernate.validator.internal.engine.DefaultParameterNameProvider; +import org.hibernate.validator.internal.engine.DefaultPropertyNodeNameProvider; 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.BeanMetaDataManagerImpl; 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.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; @@ -55,12 +58,11 @@ public class ParameterMetaDataTest { @BeforeMethod public void setupBeanMetaData() { - BeanMetaDataManager beanMetaDataManager = new BeanMetaDataManager( - new ConstraintHelper(), + BeanMetaDataManager beanMetaDataManager = new BeanMetaDataManagerImpl( + getDummyConstraintCreationContext(), new ExecutableHelper( new TypeResolutionHelper() ), - new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), - new ValueExtractorManager( Collections.emptySet() ), + new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy(), new DefaultPropertyNodeNameProvider() ), new ValidationOrderGenerator(), Collections.emptyList(), new MethodValidationConfiguration.Builder().build() @@ -125,12 +127,11 @@ 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( - new ConstraintHelper(), + BeanMetaDataManager beanMetaDataManager = new BeanMetaDataManagerImpl( + getDummyConstraintCreationContext(), new ExecutableHelper( new TypeResolutionHelper() ), - new TypeResolutionHelper(), new ExecutableParameterNameProvider( new SkewedParameterNameProvider() ), - new ValueExtractorManager( Collections.emptySet() ), + new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy(), new DefaultPropertyNodeNameProvider() ), new ValidationOrderGenerator(), Collections.emptyList(), 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 fe2577ea2e..cd83158b14 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 @@ -7,6 +7,7 @@ package org.hibernate.validator.test.internal.metadata.aggregated; import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.validator.testutils.ConstraintValidatorInitializationHelper.getDummyConstraintCreationContext; import java.util.Collections; import java.util.Set; @@ -17,13 +18,15 @@ import javax.validation.groups.Default; import org.hibernate.validator.internal.engine.DefaultParameterNameProvider; +import org.hibernate.validator.internal.engine.DefaultPropertyNodeNameProvider; 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.BeanMetaDataManagerImpl; 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.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; @@ -39,12 +42,11 @@ public class PropertyMetaDataTest { @BeforeMethod public void setupBeanMetaDataManager() { - beanMetaDataManager = new BeanMetaDataManager( - new ConstraintHelper(), + beanMetaDataManager = new BeanMetaDataManagerImpl( + getDummyConstraintCreationContext(), new ExecutableHelper( new TypeResolutionHelper() ), - new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), - new ValueExtractorManager( Collections.emptySet() ), + new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy(), new DefaultPropertyNodeNameProvider() ), new ValidationOrderGenerator(), Collections.emptyList(), new MethodValidationConfiguration.Builder().build() diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/bytebuddy/ByteBuddyWrapperTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/bytebuddy/ByteBuddyWrapperTest.java new file mode 100644 index 0000000000..41bdc52424 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/bytebuddy/ByteBuddyWrapperTest.java @@ -0,0 +1,377 @@ +/* + * 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.metadata.bytebuddy; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.assertj.core.api.Assertions.assertThat; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; + +import org.hibernate.validator.engine.HibernateValidatorEnhancedBean; +import org.hibernate.validator.internal.util.StringHelper; + +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.ClassFileLocator; +import net.bytebuddy.dynamic.loading.ByteArrayClassLoader; +import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; +import net.bytebuddy.implementation.Implementation; +import net.bytebuddy.implementation.bytecode.ByteCodeAppender; +import net.bytebuddy.implementation.bytecode.assign.Assigner; +import net.bytebuddy.implementation.bytecode.assign.primitive.PrimitiveBoxingDelegate; +import net.bytebuddy.implementation.bytecode.assign.reference.ReferenceTypeAwareAssigner; +import net.bytebuddy.jar.asm.Label; +import net.bytebuddy.jar.asm.MethodVisitor; +import net.bytebuddy.jar.asm.Opcodes; +import net.bytebuddy.jar.asm.Type; +import net.bytebuddy.matcher.ElementMatchers; +import org.testng.annotations.Test; + +/** + * Note that this implementation is not complete and is only for testing purposes. + *

    + * It typically doesn't implement the support for instrumented parent classes. + * + * @author Marko Bekhta + */ +public class ByteBuddyWrapperTest { + + @Test + public void testByteBuddy() throws Exception { + Class clazz = Foo.class; + + ClassLoader classLoader = new ByteArrayClassLoader( + ClassLoadingStrategy.BOOTSTRAP_LOADER, + ClassFileLocator.ForClassLoader.readToNames( Foo.class, HibernateValidatorEnhancedBean.class, + MyContracts.class, StringHelper.class ) ); + + Class aClass = new ByteBuddy().rebase( clazz ) + .implement( HibernateValidatorEnhancedBean.class ) + .method( + named( HibernateValidatorEnhancedBean.GET_FIELD_VALUE_METHOD_NAME ) + .and( ElementMatchers.takesArguments( String.class ) ) + .and( ElementMatchers.returns( Object.class ) ) + ) + .intercept( new Implementation.Simple( new GetFieldValue( clazz ) ) ) + .method( + named( HibernateValidatorEnhancedBean.GET_GETTER_VALUE_METHOD_NAME ) + .and( ElementMatchers.takesArguments( String.class ) ) + .and( ElementMatchers.returns( Object.class ) ) + ) + .intercept( new Implementation.Simple( new GetGetterValue( clazz ) ) ) + .make() + .load( classLoader, ClassLoadingStrategy.Default.INJECTION ) + .getLoaded(); + + Object object = aClass.newInstance(); + + Method getFieldValue = aClass.getMethod( HibernateValidatorEnhancedBean.GET_FIELD_VALUE_METHOD_NAME, String.class ); + + assertThat( getFieldValue.invoke( object, "num" ) ).isEqualTo( -1 ); + assertThat( getFieldValue.invoke( object, "string" ) ).isEqualTo( "test" ); + assertThat( getFieldValue.invoke( object, "looooong" ) ).isEqualTo( 100L ); + + Method getGetterValue = aClass.getMethod( HibernateValidatorEnhancedBean.GET_GETTER_VALUE_METHOD_NAME, String.class ); + + assertThat( getGetterValue.invoke( object, "getMessage" ) ).isEqualTo( "messssssage" ); + assertThat( getGetterValue.invoke( object, "getKey" ) ).isEqualTo( false ); + } + + private static class GetFieldValue implements ByteCodeAppender { + + @SuppressWarnings("rawtypes") + private final Class clazz; + + private final Field[] fields; + + @SuppressWarnings("rawtypes") + public GetFieldValue(Class clazz) { + this.clazz = clazz; + this.fields = clazz.getDeclaredFields(); + } + + @Override + public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext, MethodDescription instrumentedMethod) { + try { + // Contracts.assertNotEmpty(propertyName, "Property cannot be blank"); + Label contractsPropertyNameCheckLabel = new Label(); + methodVisitor.visitLabel( contractsPropertyNameCheckLabel ); + methodVisitor.visitVarInsn( Opcodes.ALOAD, 1 ); + methodVisitor.visitLdcInsn( "Property cannot be blank" ); + methodVisitor.visitMethodInsn( + Opcodes.INVOKESTATIC, + Type.getType( MyContracts.class ).getInternalName(), + "assertNotEmpty", + Type.getType( MyContracts.class.getDeclaredMethod( "assertNotEmpty", String.class, String.class ) ).getDescriptor(), + false + ); + + Label l1 = new Label(); + methodVisitor.visitLabel( l1 ); + int index = 0; + for ( Field field : fields ) { + String fieldName = field.getName(); + + if ( index > 0 ) { + methodVisitor.visitFrame( Opcodes.F_SAME, 0, null, 0, null ); + } + + // if (propertyName.equals(field_name_goes_here)) { + // return field; + // } + methodVisitor.visitVarInsn( Opcodes.ALOAD, 1 ); + methodVisitor.visitLdcInsn( fieldName ); + methodVisitor.visitMethodInsn( + Opcodes.INVOKEVIRTUAL, + Type.getType( String.class ).getInternalName(), + "equals", + Type.getType( String.class.getDeclaredMethod( "equals", Object.class ) ).getDescriptor(), + false + ); + + Label ifCheckLabel = new Label(); + methodVisitor.visitJumpInsn( Opcodes.IFEQ, ifCheckLabel ); + + Label returnFieldLabel = new Label(); + methodVisitor.visitLabel( returnFieldLabel ); + methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 ); + methodVisitor.visitFieldInsn( + Opcodes.GETFIELD, + Type.getInternalName( clazz ), + fieldName, + Type.getDescriptor( field.getType() ) + ); + if ( field.getType().isPrimitive() ) { + PrimitiveBoxingDelegate.forPrimitive( new TypeDescription.ForLoadedType( field.getType() ) ) + .assignBoxedTo( + TypeDescription.Generic.OBJECT, + ReferenceTypeAwareAssigner.INSTANCE, + Assigner.Typing.STATIC + ) + .apply( methodVisitor, implementationContext ); + } + methodVisitor.visitInsn( Opcodes.ARETURN ); + methodVisitor.visitLabel( ifCheckLabel ); + + index++; + } + + // throw new IllegalArgumentException("No property was found for a given name"); + + methodVisitor.visitFrame( Opcodes.F_SAME, 0, null, 0, null ); + methodVisitor.visitTypeInsn( Opcodes.NEW, Type.getInternalName( IllegalArgumentException.class ) ); + methodVisitor.visitInsn( Opcodes.DUP ); + methodVisitor.visitLdcInsn( "No property was found for a given name" ); + methodVisitor.visitMethodInsn( + Opcodes.INVOKESPECIAL, + Type.getInternalName( IllegalArgumentException.class ), + "", + Type.getType( IllegalArgumentException.class.getDeclaredConstructor( String.class ) ).getDescriptor(), + false + ); + methodVisitor.visitInsn( Opcodes.ATHROW ); + + Label label = new Label(); + methodVisitor.visitLabel( label ); + methodVisitor.visitLocalVariable( + "this", + Type.getDescriptor( clazz ), + null, + contractsPropertyNameCheckLabel, + label, + 0 + ); + methodVisitor.visitLocalVariable( + "name", + Type.getDescriptor( String.class ), + null, + contractsPropertyNameCheckLabel, + label, + 1 + ); + methodVisitor.visitMaxs( 3, 2 ); + + return new Size( 6, instrumentedMethod.getStackSize() ); + } + catch (NoSuchMethodException e) { + throw new IllegalArgumentException( e ); + } + } + } + + private static class GetGetterValue implements ByteCodeAppender { + + @SuppressWarnings("rawtypes") + private final Class clazz; + + private final Method[] methods; + + @SuppressWarnings("rawtypes") + public GetGetterValue(Class clazz) { + this.clazz = clazz; + this.methods = clazz.getDeclaredMethods(); + } + + @Override + public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext, MethodDescription instrumentedMethod) { + try { + // Contracts.assertNotEmpty(propertyName, "Property cannot be blank"); + Label contractsPropertyNameCheckLabel = new Label(); + methodVisitor.visitLabel( contractsPropertyNameCheckLabel ); + methodVisitor.visitVarInsn( Opcodes.ALOAD, 1 ); + methodVisitor.visitLdcInsn( "Property cannot be blank" ); + methodVisitor.visitMethodInsn( + Opcodes.INVOKESTATIC, + Type.getType( MyContracts.class ).getInternalName(), + "assertNotEmpty", + Type.getType( MyContracts.class.getDeclaredMethod( "assertNotEmpty", String.class, String.class ) ).getDescriptor(), + false + ); + + Label l1 = new Label(); + methodVisitor.visitLabel( l1 ); + int index = 0; + for ( Method method : methods ) { + String fieldName = method.getName(); + + if ( index > 0 ) { + methodVisitor.visitFrame( Opcodes.F_SAME, 0, null, 0, null ); + } + + // if (propertyName.equals(field_name_goes_here)) { + // return field; + // } + methodVisitor.visitVarInsn( Opcodes.ALOAD, 1 ); + methodVisitor.visitLdcInsn( fieldName ); + methodVisitor.visitMethodInsn( + Opcodes.INVOKEVIRTUAL, + Type.getType( String.class ).getInternalName(), + "equals", + Type.getType( String.class.getDeclaredMethod( "equals", Object.class ) ).getDescriptor(), + false + ); + + Label ifCheckLabel = new Label(); + methodVisitor.visitJumpInsn( Opcodes.IFEQ, ifCheckLabel ); + + Label returnFieldLabel = new Label(); + methodVisitor.visitLabel( returnFieldLabel ); + methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 ); + methodVisitor.visitMethodInsn( + Opcodes.INVOKEVIRTUAL, + Type.getInternalName( clazz ), + method.getName(), + Type.getMethodDescriptor( method ), + method.getDeclaringClass().isInterface() + ); + if ( method.getReturnType().isPrimitive() ) { + PrimitiveBoxingDelegate.forPrimitive( new TypeDescription.ForLoadedType( method.getReturnType() ) ) + .assignBoxedTo( + TypeDescription.Generic.OBJECT, + ReferenceTypeAwareAssigner.INSTANCE, + Assigner.Typing.STATIC + ) + .apply( methodVisitor, implementationContext ); + } + methodVisitor.visitInsn( Opcodes.ARETURN ); + methodVisitor.visitLabel( ifCheckLabel ); + + index++; + } + + // throw new IllegalArgumentException("No property was found for a given name"); + + methodVisitor.visitFrame( Opcodes.F_SAME, 0, null, 0, null ); + methodVisitor.visitTypeInsn( Opcodes.NEW, Type.getInternalName( IllegalArgumentException.class ) ); + methodVisitor.visitInsn( Opcodes.DUP ); + methodVisitor.visitLdcInsn( "No property was found for a given name" ); + methodVisitor.visitMethodInsn( + Opcodes.INVOKESPECIAL, + Type.getInternalName( IllegalArgumentException.class ), + "", + Type.getType( IllegalArgumentException.class.getDeclaredConstructor( String.class ) ).getDescriptor(), + false + ); + methodVisitor.visitInsn( Opcodes.ATHROW ); + + Label label = new Label(); + methodVisitor.visitLabel( label ); + methodVisitor.visitLocalVariable( + "this", + Type.getDescriptor( clazz ), + null, + contractsPropertyNameCheckLabel, + label, + 0 + ); + methodVisitor.visitLocalVariable( + "name", + Type.getDescriptor( String.class ), + null, + contractsPropertyNameCheckLabel, + label, + 1 + ); + methodVisitor.visitMaxs( 3, 2 ); + + return new Size( 6, instrumentedMethod.getStackSize() ); + } + catch (NoSuchMethodException e) { + throw new IllegalArgumentException( e ); + } + } + } + + + @SuppressWarnings("unused") + public static class Foo { + private String string; + private Integer num; + private long looooong; + + public Foo() { + this( "test", -1 ); + this.looooong = 100L; + } + + public Foo(String string, Integer num) { + this.string = string; + this.num = num; + } + + public String getMessage() { + return "messssssage"; + } + + public boolean getKey() { + return false; + } + } + + @SuppressWarnings("unused") + public static class Bar extends Foo { + private final List strings; + + public Bar() { + super(); + this.strings = Collections.emptyList(); + } + } + + public static final class MyContracts { + public static void assertNotEmpty(String s, String message) { + if ( StringHelper.isNullOrEmptyString( s ) ) { + throw new IllegalArgumentException( message ); + } + } + } + +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/core/MetaConstraintTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/core/MetaConstraintTest.java index 29cfcc5550..cb8a4a04b2 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/core/MetaConstraintTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/core/MetaConstraintTest.java @@ -6,7 +6,7 @@ */ package org.hibernate.validator.test.internal.metadata.core; -import static java.lang.annotation.ElementType.METHOD; +import static org.hibernate.validator.testutils.ConstraintValidatorInitializationHelper.getDummyConstraintValidatorInitializationContext; import static org.testng.Assert.assertEquals; import java.lang.reflect.Method; @@ -14,15 +14,21 @@ import javax.validation.constraints.NotNull; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManagerImpl; 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.core.MetaConstraints; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; +import org.hibernate.validator.internal.properties.javabean.JavaBeanGetter; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; import org.hibernate.validator.testutil.TestForIssue; + import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -33,6 +39,7 @@ public class MetaConstraintTest { private ConstraintHelper constraintHelper; private TypeResolutionHelper typeResolutionHelper; private ValueExtractorManager valueExtractorManager; + private ConstraintValidatorManager constraintValidatorManager; private Method barMethod; private ConstraintAnnotationDescriptor constraintAnnotationDescriptor; @@ -41,6 +48,7 @@ public void setUp() throws Exception { constraintHelper = new ConstraintHelper(); typeResolutionHelper = new TypeResolutionHelper(); valueExtractorManager = new ValueExtractorManager( Collections.emptySet() ); + constraintValidatorManager = new ConstraintValidatorManagerImpl( new ConstraintValidatorFactoryImpl(), getDummyConstraintValidatorInitializationContext() ); barMethod = Foo.class.getMethod( "getBar" ); constraintAnnotationDescriptor = new ConstraintAnnotationDescriptor.Builder<>( barMethod.getAnnotation( NotNull.class ) ).build(); } @@ -48,18 +56,20 @@ public void setUp() throws Exception { @Test @TestForIssue(jiraKey = "HV-930") public void two_meta_constraints_for_the_same_constraint_should_be_equal() throws Exception { + JavaBeanGetter javaBeanGetter = new JavaBeanGetter( Foo.class, Foo.class.getDeclaredMethod( "getBar" ), "bar", "bar" ); ConstraintDescriptorImpl constraintDescriptor1 = new ConstraintDescriptorImpl<>( - constraintHelper, barMethod, constraintAnnotationDescriptor, METHOD + constraintHelper, javaBeanGetter, constraintAnnotationDescriptor, ConstraintLocationKind.METHOD ); ConstraintLocation location1 = ConstraintLocation.forClass( Foo.class ); - MetaConstraint metaConstraint1 = MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintDescriptor1, location1 ); - + MetaConstraint metaConstraint1 = MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintValidatorManager, + constraintDescriptor1, location1 ); ConstraintDescriptorImpl constraintDescriptor2 = new ConstraintDescriptorImpl<>( - constraintHelper, barMethod, constraintAnnotationDescriptor, METHOD + constraintHelper, javaBeanGetter, constraintAnnotationDescriptor, ConstraintLocationKind.METHOD ); ConstraintLocation location2 = ConstraintLocation.forClass( Foo.class ); - MetaConstraint metaConstraint2 = MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintDescriptor2, location2 ); + MetaConstraint metaConstraint2 = MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintValidatorManager, + constraintDescriptor2, location2 ); assertEquals( metaConstraint1, metaConstraint2, "Two MetaConstraint instances for the same constraint should be equal" diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/BeanDescriptorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/BeanDescriptorTest.java index 2ab01b0016..30e44cc64e 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/BeanDescriptorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/BeanDescriptorTest.java @@ -16,10 +16,12 @@ import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; +import java.time.LocalDate; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Set; + import javax.validation.ConstraintDeclarationException; import javax.validation.Valid; import javax.validation.constraints.NotNull; @@ -31,15 +33,13 @@ import javax.validation.metadata.ParameterDescriptor; import javax.validation.metadata.PropertyDescriptor; -import org.joda.time.DateMidnight; -import org.testng.annotations.Test; - import org.hibernate.validator.constraints.ScriptAssert; import org.hibernate.validator.test.internal.metadata.Customer; import org.hibernate.validator.test.internal.metadata.CustomerRepository; import org.hibernate.validator.test.internal.metadata.CustomerRepositoryExt; import org.hibernate.validator.test.internal.metadata.IllegalCustomerRepositoryExt; import org.hibernate.validator.testutil.TestForIssue; +import org.testng.annotations.Test; /** * Unit test for {@link BeanDescriptor} and its creation. @@ -313,7 +313,7 @@ public void testGetConstrainedConstructors() { Arrays.>asList( String.class ), Arrays.>asList( String.class, Customer.class ), Collections.>emptyList(), - Arrays.>asList( DateMidnight.class, DateMidnight.class ) + Arrays.>asList( LocalDate.class, LocalDate.class ) ); } @@ -463,7 +463,7 @@ public void foo(String foo) { } } - @ScriptAssert(lang = "some lang", script = "some script") + @ScriptAssert(lang = "groovy", script = "true") private static class ClassLevelConstrainedType { } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/CrossParameterDescriptorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/CrossParameterDescriptorTest.java index bd13aaa6a9..17e4fd46de 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/CrossParameterDescriptorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/CrossParameterDescriptorTest.java @@ -14,13 +14,13 @@ import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; +import java.time.LocalDate; import java.util.Set; import javax.validation.groups.Default; import javax.validation.metadata.ConstraintDescriptor; import javax.validation.metadata.CrossParameterDescriptor; import javax.validation.metadata.Scope; -import org.joda.time.DateMidnight; import org.testng.annotations.Test; import org.hibernate.validator.test.internal.metadata.ConsistentDateParameters; @@ -38,8 +38,8 @@ public void testGetElementClass() { CrossParameterDescriptor descriptor = getMethodDescriptor( CustomerRepository.class, "methodWithCrossParameterConstraint", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ).getCrossParameterDescriptor(); assertEquals( @@ -53,8 +53,8 @@ public void testGetConstraintDescriptorsForMethod() { CrossParameterDescriptor descriptor = getMethodDescriptor( CustomerRepository.class, "methodWithCrossParameterConstraint", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ).getCrossParameterDescriptor(); assertConstraintTypes( descriptor.getConstraintDescriptors(), @@ -66,8 +66,8 @@ public void testGetConstraintDescriptorsForMethod() { public void testGetConstraintDescriptorsForConstructor() { CrossParameterDescriptor descriptor = getConstructorDescriptor( CustomerRepository.class, - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ).getCrossParameterDescriptor(); assertConstraintTypes( descriptor.getConstraintDescriptors(), @@ -80,8 +80,8 @@ public void testGetConstraintDescriptorsForMethodConsidersConstraintsFromSuperTy CrossParameterDescriptor descriptor = getMethodDescriptor( CustomerRepositoryExt.class, "methodWithCrossParameterConstraint", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ).getCrossParameterDescriptor(); assertConstraintTypes( descriptor.getConstraintDescriptors(), @@ -93,8 +93,8 @@ public void testGetConstraintDescriptorsForMethodConsidersConstraintsFromSuperTy public void testGetConstraintDescriptorsForConstructorDoesNotConsiderConstraintsFromSuperType() { CrossParameterDescriptor descriptor = getConstructorDescriptor( CustomerRepositoryExt.class, - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ).getCrossParameterDescriptor(); assertTrue( descriptor.getConstraintDescriptors().isEmpty() @@ -115,8 +115,8 @@ public void testHasConstraintsForMethod() { descriptor = getMethodDescriptor( CustomerRepository.class, "methodWithCrossParameterConstraint", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ).getCrossParameterDescriptor(); assertTrue( descriptor.hasConstraints(), @@ -136,8 +136,8 @@ public void testHasConstraintsForConstructor() { descriptor = getConstructorDescriptor( CustomerRepository.class, - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ).getCrossParameterDescriptor(); assertTrue( descriptor.hasConstraints(), @@ -150,8 +150,8 @@ public void testHasConstraintsForMethodConsidersConstraintsFromSuperType() { CrossParameterDescriptor descriptor = getMethodDescriptor( CustomerRepositoryExt.class, "methodWithCrossParameterConstraint", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ).getCrossParameterDescriptor(); assertTrue( descriptor.hasConstraints(), @@ -163,8 +163,8 @@ public void testHasConstraintsForMethodConsidersConstraintsFromSuperType() { public void testHasConstraintsForConstructorDoesNotConsiderConstraintsFromSuperType() { CrossParameterDescriptor descriptor = getConstructorDescriptor( CustomerRepositoryExt.class, - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ).getCrossParameterDescriptor(); assertFalse( descriptor.hasConstraints(), @@ -177,8 +177,8 @@ public void testFindConstraintsMatchingGroups() { CrossParameterDescriptor descriptor = getMethodDescriptor( CustomerRepositoryExt.class, "methodWithCrossParameterConstraint", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ).getCrossParameterDescriptor(); assertTrue( @@ -199,8 +199,8 @@ public void testFindConstraintsLookingAt() { CrossParameterDescriptor descriptor = getMethodDescriptor( CustomerRepositoryExt.class, "methodWithCrossParameterConstraint", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ).getCrossParameterDescriptor(); Set> constraintDescriptors = descriptor.findConstraints() diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/MethodDescriptorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/MethodDescriptorTest.java index 3537d2c331..96ce622fc4 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/MethodDescriptorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/MethodDescriptorTest.java @@ -13,8 +13,10 @@ import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; +import java.time.LocalDate; import java.util.List; import java.util.Set; + import javax.validation.ConstraintDeclarationException; import javax.validation.constraints.NotNull; import javax.validation.metadata.ConstraintDescriptor; @@ -22,15 +24,13 @@ import javax.validation.metadata.ParameterDescriptor; import javax.validation.metadata.Scope; -import org.joda.time.DateMidnight; -import org.testng.annotations.Test; - import org.hibernate.validator.test.internal.metadata.Customer; import org.hibernate.validator.test.internal.metadata.CustomerRepository; import org.hibernate.validator.test.internal.metadata.CustomerRepositoryExt; import org.hibernate.validator.test.internal.metadata.CustomerRepositoryExt.CustomerExtension; import org.hibernate.validator.test.internal.metadata.IllegalCustomerRepositoryExt; import org.hibernate.validator.testutil.TestForIssue; +import org.testng.annotations.Test; /** * @author Gunnar Morling @@ -75,8 +75,8 @@ public void testHasConstraints() { descriptor = getMethodDescriptor( CustomerRepository.class, "methodWithCrossParameterConstraint", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ); assertFalse( descriptor.hasConstraints(), @@ -106,8 +106,8 @@ public void testGetConstraintDescriptors() { descriptor = getMethodDescriptor( CustomerRepository.class, "methodWithCrossParameterConstraint", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ); assertTrue( descriptor.getConstraintDescriptors().isEmpty() ); } @@ -117,8 +117,8 @@ public void testFindConstraintsMatchingGroups() { MethodDescriptor descriptor = getMethodDescriptor( CustomerRepositoryExt.class, "methodWithCrossParameterConstraint", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ); assertTrue( @@ -264,7 +264,7 @@ public void testAreParametersConstrainedForParameterCascadedMethod() { public void testAreParametersConstrainedForCrossParameterConstrainedMethod() { MethodDescriptor methodDescriptor = getMethodDescriptor( CustomerRepositoryExt.class, - "methodWithCrossParameterConstraint", DateMidnight.class, DateMidnight.class + "methodWithCrossParameterConstraint", LocalDate.class, LocalDate.class ); assertThat( methodDescriptor.hasConstrainedParameters() ).isTrue(); } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/location/ConstraintLocationTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/location/ConstraintLocationTest.java index 8e424bbd4a..b83a97c1fc 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/location/ConstraintLocationTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/location/ConstraintLocationTest.java @@ -11,6 +11,7 @@ import java.lang.reflect.Method; import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.properties.javabean.JavaBeanGetter; import org.hibernate.validator.testutil.TestForIssue; import org.testng.annotations.Test; @@ -32,8 +33,8 @@ public void two_constraint_locations_for_the_same_type_should_be_equal() { @TestForIssue(jiraKey = "HV-930") public void two_constraint_locations_for_the_same_member_should_be_equal() throws Exception { Method getter = Foo.class.getMethod( "getBar" ); - ConstraintLocation location1 = ConstraintLocation.forGetter( getter ); - ConstraintLocation location2 = ConstraintLocation.forGetter( getter ); + ConstraintLocation location1 = ConstraintLocation.forGetter( new JavaBeanGetter( Foo.class, getter, "bar", "bar" ) ); + ConstraintLocation location2 = ConstraintLocation.forGetter( new JavaBeanGetter( Foo.class, getter, "bar", "bar" ) ); assertEquals( location1, location2, "Two constraint locations for the same type should be equal" ); } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/provider/AnnotationMetaDataProviderTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/provider/AnnotationMetaDataProviderTest.java index 9953a487f8..1f4016a66f 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/provider/AnnotationMetaDataProviderTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/provider/AnnotationMetaDataProviderTest.java @@ -10,13 +10,13 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; import static org.assertj.core.api.Assertions.assertThat; import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; +import static org.hibernate.validator.testutils.ConstraintValidatorInitializationHelper.getDummyConstraintCreationContext; import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import java.util.Collections; +import java.time.LocalDate; import java.util.Map; import javax.validation.Constraint; @@ -29,10 +29,10 @@ import javax.validation.metadata.ConstraintDescriptor; import org.hibernate.validator.constraints.ScriptAssert; -import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.engine.DefaultPropertyNodeNameProvider; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; -import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; import org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider; import org.hibernate.validator.internal.metadata.raw.BeanConfiguration; import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; @@ -40,9 +40,9 @@ 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.util.TypeResolutionHelper; +import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; import org.hibernate.validator.testutil.TestForIssue; -import org.joda.time.DateMidnight; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -58,9 +58,8 @@ public class AnnotationMetaDataProviderTest extends AnnotationMetaDataProviderTe @BeforeMethod public void setUpProvider() { provider = new AnnotationMetaDataProvider( - new ConstraintHelper(), - new TypeResolutionHelper(), - new ValueExtractorManager( Collections.emptySet() ), + getDummyConstraintCreationContext(), + new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy(), new DefaultPropertyNodeNameProvider() ), new AnnotationProcessingOptionsImpl() ); } @@ -78,7 +77,7 @@ public void testGetConstructorMetaData() throws Exception { MetaConstraint constraint = constructor.getConstraints().iterator().next(); assertThat( constraint.getDescriptor().getAnnotation().annotationType() ).isEqualTo( NotNull.class ); - assertThat( constraint.getElementType() ).isEqualTo( ElementType.CONSTRUCTOR ); + assertThat( constraint.getConstraintLocationKind() ).isEqualTo( ConstraintLocationKind.CONSTRUCTOR ); } @Test @@ -90,8 +89,8 @@ public void testGetCrossParameterMetaData() throws Exception { beanConfiguration, Calendar.class, "createEvent", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ); //then @@ -101,12 +100,10 @@ public void testGetCrossParameterMetaData() throws Exception { assertThat( createEvent.getConstraints() ).as( "No return value constraints expected" ).isEmpty(); assertThat( createEvent.getCrossParameterConstraints() ).hasSize( 1 ); - assertThat( createEvent.getExecutable() ).isEqualTo( - Calendar.class.getMethod( - "createEvent", - DateMidnight.class, - DateMidnight.class - ) + assertThat( createEvent.getCallable() ).isEqualTo( + new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy(), new DefaultPropertyNodeNameProvider() ) + .findDeclaredMethod( Calendar.class, "createEvent", LocalDate.class, LocalDate.class ) + .get() ); MetaConstraint constraint = createEvent.getCrossParameterConstraints().iterator().next(); @@ -315,7 +312,7 @@ public Foo(@NotNull String foo) { private static class Calendar { @ConsistentDateParameters - public void createEvent(DateMidnight start, DateMidnight end) { + public void createEvent(LocalDate start, LocalDate end) { } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/provider/AnnotationMetaDataProviderTestBase.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/provider/AnnotationMetaDataProviderTestBase.java index fca67eae25..a75be4b083 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/provider/AnnotationMetaDataProviderTestBase.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/provider/AnnotationMetaDataProviderTestBase.java @@ -6,16 +6,21 @@ */ package org.hibernate.validator.test.internal.metadata.provider; +import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.lang.reflect.Member; +import org.hibernate.validator.internal.engine.DefaultPropertyNodeNameProvider; import org.hibernate.validator.internal.metadata.raw.BeanConfiguration; 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.Constrainable; +import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; /** * @author Gunnar Morling @@ -56,17 +61,28 @@ protected ConstrainedType findConstrainedType(BeanConfiguration beanConfi throw new RuntimeException( "Found no constrained element for type " + type ); } - protected ConstrainedElement findConstrainedElement(BeanConfiguration beanConfiguration, - Member member) { + protected ConstrainedElement findConstrainedElement(BeanConfiguration beanConfiguration, Member member) { + JavaBeanHelper javaBeanHelper = new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy(), new DefaultPropertyNodeNameProvider() ); + Constrainable constrainable; + if ( member instanceof Field ) { + constrainable = javaBeanHelper.findDeclaredField( member.getDeclaringClass(), member.getName() ).get(); + } + else if ( member instanceof Constructor ) { + constrainable = javaBeanHelper.findDeclaredConstructor( member.getDeclaringClass(), ( (Constructor) member ).getParameterTypes() ).get(); + } + else { + Executable executable = (Executable) member; + constrainable = javaBeanHelper.findDeclaredMethod( member.getDeclaringClass(), executable.getName(), executable.getParameterTypes() ).get(); + } for ( ConstrainedElement constrainedElement : beanConfiguration.getConstrainedElements() ) { if ( member instanceof Executable && constrainedElement instanceof ConstrainedExecutable ) { - if ( member.equals( ( (ConstrainedExecutable) constrainedElement ).getExecutable() ) ) { + if ( constrainable.equals( ( (ConstrainedExecutable) constrainedElement ).getCallable() ) ) { return constrainedElement; } } - else if ( member instanceof Field && constrainedElement instanceof ConstrainedField ) { - if ( member.equals( ( (ConstrainedField) constrainedElement ).getField() ) ) { + else if ( constrainedElement instanceof ConstrainedField ) { + if ( constrainable.equals( ( (ConstrainedField) constrainedElement ).getField() ) ) { return constrainedElement; } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/provider/TypeAnnotationMetaDataRetrievalTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/provider/TypeAnnotationMetaDataRetrievalTest.java index 75e1aaba1b..e7895e5f3e 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/provider/TypeAnnotationMetaDataRetrievalTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/provider/TypeAnnotationMetaDataRetrievalTest.java @@ -7,6 +7,7 @@ package org.hibernate.validator.test.internal.metadata.provider; import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.validator.testutils.ConstraintValidatorInitializationHelper.getDummyConstraintCreationContext; import java.lang.annotation.Annotation; import java.util.Collection; @@ -15,19 +16,19 @@ import java.util.stream.Collectors; import javax.validation.Valid; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; -import org.hibernate.validator.constraints.NotBlank; -import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.engine.DefaultPropertyNodeNameProvider; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; -import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider; import org.hibernate.validator.internal.metadata.raw.BeanConfiguration; import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable; import org.hibernate.validator.internal.metadata.raw.ConstrainedField; import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter; -import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -43,9 +44,8 @@ public class TypeAnnotationMetaDataRetrievalTest extends AnnotationMetaDataProvi @BeforeClass public void setup() { provider = new AnnotationMetaDataProvider( - new ConstraintHelper(), - new TypeResolutionHelper(), - new ValueExtractorManager( Collections.emptySet() ), + getDummyConstraintCreationContext(), + new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy(), new DefaultPropertyNodeNameProvider() ), new AnnotationProcessingOptionsImpl() ); } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/properties/javabean/JavaBeanExecutableTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/properties/javabean/JavaBeanExecutableTest.java new file mode 100644 index 0000000000..e2929b4967 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/properties/javabean/JavaBeanExecutableTest.java @@ -0,0 +1,82 @@ +/* + * 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.properties.javabean; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.lang.reflect.ParameterizedType; +import java.util.List; +import java.util.Map; + +import org.hibernate.validator.internal.properties.javabean.JavaBeanConstructor; +import org.hibernate.validator.testutil.TestForIssue; +import org.testng.annotations.Test; + +public class JavaBeanExecutableTest { + + @Test + @TestForIssue(jiraKey = "HV-1634") + public void testGenericTypeParametersWithImplicitParameters() throws NoSuchMethodException, SecurityException { + JavaBeanConstructor constructor = new JavaBeanConstructor( Bean.class.getDeclaredConstructors()[0] ); + + assertThat( constructor.getParameters() ).hasSize( 3 ); + + ParameterizedType parameterizedType1 = (ParameterizedType) constructor.getParameterGenericType( 1 ); + assertThat( parameterizedType1.getRawType() ).isEqualTo( Map.class ); + + ParameterizedType parameterizedType2 = (ParameterizedType) constructor.getParameterGenericType( 2 ); + assertThat( parameterizedType2.getRawType() ).isEqualTo( List.class ); + } + + @Test + @TestForIssue(jiraKey = "HV-1634") + public void testGenericTypeParametersWithoutImplicitParameters() throws NoSuchMethodException, SecurityException { + JavaBeanConstructor constructor = new JavaBeanConstructor( StaticBean.class.getDeclaredConstructors()[0] ); + + assertThat( constructor.getParameters() ).hasSize( 2 ); + + ParameterizedType parameterizedType0 = (ParameterizedType) constructor.getParameterGenericType( 0 ); + assertThat( parameterizedType0.getRawType() ).isEqualTo( Map.class ); + + ParameterizedType parameterizedType1 = (ParameterizedType) constructor.getParameterGenericType( 1 ); + assertThat( parameterizedType1.getRawType() ).isEqualTo( List.class ); + } + + @Test + @TestForIssue(jiraKey = "HV-1634") + public void testGenericTypeParametersWithSyntheticParameters() throws NoSuchMethodException, SecurityException { + JavaBeanConstructor constructor = new JavaBeanConstructor( MyEnum.class.getDeclaredConstructors()[0] ); + + assertThat( constructor.getParameters() ).hasSize( 4 ); + + ParameterizedType parameterizedType1 = (ParameterizedType) constructor.getParameterGenericType( 2 ); + assertThat( parameterizedType1.getRawType() ).isEqualTo( Map.class ); + + ParameterizedType parameterizedType2 = (ParameterizedType) constructor.getParameterGenericType( 3 ); + assertThat( parameterizedType2.getRawType() ).isEqualTo( List.class ); + } + + private class Bean { + + private Bean(Map map, List list) { + } + } + + private static class StaticBean { + + private StaticBean(Map map, List list) { + } + } + + private enum MyEnum { + + ; + + private MyEnum(Map map, List list) { + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/util/IdentitySetTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/util/IdentitySetTest.java deleted file mode 100644 index 76090e2325..0000000000 --- a/engine/src/test/java/org/hibernate/validator/test/internal/util/IdentitySetTest.java +++ /dev/null @@ -1,71 +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.test.internal.util; - -import static org.testng.Assert.assertTrue; - -import java.util.HashSet; -import java.util.Set; - -import org.hibernate.validator.internal.util.IdentitySet; -import org.testng.annotations.Test; - -/** - * @author Hardy Ferentschik - */ -public class IdentitySetTest { - - @SuppressWarnings("unchecked") - @Test - public void testAddIdenticalInstance() { - Set identitySet = new IdentitySet(); - Set hashSet = new HashSet(); - assertTrue( identitySet.size() == 0 ); - assertTrue( hashSet.size() == 0 ); - - Object o1 = new Object() { - int counter = 0; - - @Override - public int hashCode() { - return counter++; - } - - @Override - public boolean equals(Object other) { - return false; - } - }; - identitySet.add( o1 ); - hashSet.add( o1 ); - assertTrue( identitySet.size() == 1 ); - assertTrue( hashSet.size() == 1 ); - - identitySet.add( o1 ); - hashSet.add( o1 ); - assertTrue( identitySet.size() == 1 ); - assertTrue( hashSet.size() == 2 ); - - Object o2 = new Object() { - int counter = 0; - - @Override - public int hashCode() { - return counter++; - } - - @Override - public boolean equals(Object other) { - return false; - } - }; - identitySet.add( o2 ); - hashSet.add( o2 ); - assertTrue( identitySet.size() == 2 ); - assertTrue( hashSet.size() == 3 ); - } -} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/util/ReflectionHelperTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/util/ReflectionHelperTest.java index 784a47a9b9..d50b9046ee 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/util/ReflectionHelperTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/util/ReflectionHelperTest.java @@ -12,7 +12,6 @@ import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; -import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; @@ -25,7 +24,7 @@ import java.util.TreeSet; import org.hibernate.validator.internal.util.ReflectionHelper; -import org.hibernate.validator.testutil.TestForIssue; + import org.testng.annotations.Test; /** @@ -161,16 +160,6 @@ public void testGetIndexedValueForNull() { assertNull( value ); } - @Test - @TestForIssue(jiraKey = "HV-622") - public void testIsGetterMethod() throws Exception { - Method method = Bar.class.getMethod( "getBar" ); - assertTrue( ReflectionHelper.isGetterMethod( method ) ); - - method = Bar.class.getMethod( "getBar", String.class ); - assertFalse( ReflectionHelper.isGetterMethod( method ) ); - } - private void doTestGetIndexedValueForArray(Object array, Object firstValue, Object secondValue) { Object value = ReflectionHelper.getIndexedValue( array, 0 ); assertEquals( value, firstValue, "We should be able to retrieve the indexed object" ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/util/TypeHelperTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/util/TypeHelperTest.java index a37d99ef0b..1e1dcf547a 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/util/TypeHelperTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/util/TypeHelperTest.java @@ -40,9 +40,11 @@ import java.util.Map; import java.util.Set; +import org.hibernate.validator.engine.HibernateValidatorEnhancedBean; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorDescriptor; import org.hibernate.validator.internal.util.TypeHelper; import org.hibernate.validator.testutil.TestForIssue; + import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -858,7 +860,7 @@ public void isArrayWithParameterizedType() { @Test public void testTypeDiscovery() { List> validatorDescriptors = new ArrayList<>(); - validatorDescriptors.add( ConstraintValidatorDescriptor.forClass( PositiveConstraintValidator.class ) ); + validatorDescriptors.add( ConstraintValidatorDescriptor.forClass( PositiveConstraintValidator.class, Positive.class ) ); Map> validatorsTypes = TypeHelper .getValidatorTypes( Positive.class, validatorDescriptors ); @@ -875,6 +877,25 @@ public void testTypeHelperDoesntGoIntoInfiniteLoop() { assertTrue( TypeHelper.isAssignable( parentEntityType, childEntityType ) ); } + @Test + public void testIsHibernateValidatorEnhancedBean() { + class Foo { + } + class Bar implements HibernateValidatorEnhancedBean { + @Override + public Object $$_hibernateValidator_getFieldValue(String name) { + return null; + } + + @Override + public Object $$_hibernateValidator_getGetterValue(String name) { + return null; + } + } + assertTrue( TypeHelper.isHibernateValidatorEnhancedBean( Bar.class ) ); + assertFalse( TypeHelper.isHibernateValidatorEnhancedBean( Foo.class ) ); + } + private static void assertAsymmetricallyAssignable(Type supertype, Type type) { assertAssignable( supertype, type ); assertUnassignable( type, supertype ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/util/privilegedactions/GetAnnotationsParameterTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/util/privilegedactions/GetAnnotationsParameterTest.java index 1f679862c4..2e001f6d4e 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/util/privilegedactions/GetAnnotationsParameterTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/util/privilegedactions/GetAnnotationsParameterTest.java @@ -21,7 +21,7 @@ import org.testng.annotations.Test; /** - * Unit test for {@link GetAnnotationsParameter}. + * Unit test for {@link GetAnnotationAttribute}. * * @author Gunnar Morling * diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/xml/Child.java b/engine/src/test/java/org/hibernate/validator/test/internal/xml/Child.java new file mode 100644 index 0000000000..9d12cbfd83 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/xml/Child.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.test.internal.xml; + +/** + * Test class for HV-1534. + * + * @author Rob Dickinson + */ +public class Child extends Parent { + + public Child( String parentAttribute ) { + super( parentAttribute ); + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/xml/MappingXmlParserTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/xml/MappingXmlParserTest.java index 517980d41d..0609a452a2 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/xml/MappingXmlParserTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/xml/MappingXmlParserTest.java @@ -7,13 +7,13 @@ package org.hibernate.validator.test.internal.xml; import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; +import static org.hibernate.validator.testutils.ConstraintValidatorInitializationHelper.getDummyConstraintCreationContext; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; import java.io.InputStream; -import java.util.Collections; import java.util.List; import java.util.Set; @@ -22,11 +22,12 @@ import javax.validation.ValidationException; import javax.validation.constraints.DecimalMin; +import org.hibernate.validator.internal.engine.ConstraintCreationContext; +import org.hibernate.validator.internal.engine.DefaultPropertyNodeNameProvider; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorDescriptor; -import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.core.ConstraintHelper; -import org.hibernate.validator.internal.util.TypeResolutionHelper; -import org.hibernate.validator.internal.xml.MappingXmlParser; +import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; +import org.hibernate.validator.internal.xml.mapping.MappingXmlParser; import org.hibernate.validator.testutil.TestForIssue; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -37,20 +38,21 @@ public class MappingXmlParserTest { private MappingXmlParser xmlMappingParser; - private ConstraintHelper constraintHelper; + private ConstraintCreationContext constraintCreationContext; @BeforeMethod public void setupParserHelper() { - constraintHelper = new ConstraintHelper(); + constraintCreationContext = getDummyConstraintCreationContext(); xmlMappingParser = new MappingXmlParser( - constraintHelper, new TypeResolutionHelper(), new ValueExtractorManager( Collections.emptySet() ), null + constraintCreationContext, + new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy(), new DefaultPropertyNodeNameProvider() ), null ); } @Test @TestForIssue(jiraKey = "HV-782") public void testAdditionalConstraintValidatorsGetAddedAndAreLastInList() { - List> validatorDescriptors = constraintHelper.getAllValidatorDescriptors( + List> validatorDescriptors = constraintCreationContext.getConstraintHelper().getAllValidatorDescriptors( DecimalMin.class ); @@ -62,7 +64,7 @@ public void testAdditionalConstraintValidatorsGetAddedAndAreLastInList() { xmlMappingParser.parse( mappingStreams ); - validatorDescriptors = constraintHelper.getAllValidatorDescriptors( DecimalMin.class ); + validatorDescriptors = constraintCreationContext.getConstraintHelper().getAllValidatorDescriptors( DecimalMin.class ); assertFalse( validatorDescriptors.isEmpty(), "Wrong number of default validators" ); assertEquals( getIndex( validatorDescriptors, DecimalMinValidatorForFoo.class ), validatorDescriptors.size() - 1, "The custom validator must be last" ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/xml/Parent.java b/engine/src/test/java/org/hibernate/validator/test/internal/xml/Parent.java new file mode 100644 index 0000000000..6b662e1286 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/xml/Parent.java @@ -0,0 +1,37 @@ +/* + * 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.xml; + +import java.util.ArrayList; +import java.util.List; + +/** + * Test class for HV-1534. + * + * @author Rob Dickinson + */ +public class Parent { + + private String parentAttribute = null; + private List parentListAttribute = new ArrayList<>(); + + public Parent( String parentAttribute ) { + this.parentAttribute = parentAttribute; + } + + public final String getParentAttribute() { + return parentAttribute; + } + + public void addToListAttribute(String element) { + parentListAttribute.add( element ); + } + + public final List getParentListAttribute() { + return parentListAttribute; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/xml/XmlMappingTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/xml/XmlMappingTest.java index f62ee1f70b..288c437014 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/xml/XmlMappingTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/xml/XmlMappingTest.java @@ -9,11 +9,11 @@ import static org.hibernate.validator.internal.util.CollectionHelper.asSet; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; 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.testng.Assert.assertEquals; import java.io.InputStream; -import java.lang.annotation.ElementType; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -141,7 +141,7 @@ public void testConstraintsFromXmlAndProgrammaticApiAddUp() { //given final ConstraintMapping programmaticMapping = configuration.createConstraintMapping(); programmaticMapping.type( Customer.class ) - .property( "firstName", ElementType.FIELD ) + .field( "firstName" ) .constraint( new SizeDef().min( 2 ).max( 10 ) ); final InputStream xmlMapping = XmlMappingTest.class.getResourceAsStream( "hv-480-mapping.xml" ); @@ -376,4 +376,59 @@ public void testCascadedValidation() { violationOf( NotNull.class ) ); } + + @Test + @TestForIssue(jiraKey = "HV-1534") + public void test_constraint_is_applied_to_inherited_getter() { + final Configuration configuration = ValidatorUtil.getConfiguration(); + configuration.addMapping( XmlMappingTest.class.getResourceAsStream( "hv-1534-mapping.xml" ) ); + + final ValidatorFactory validatorFactory = configuration.buildValidatorFactory(); + final Validator validator = validatorFactory.getValidator(); + + Parent parent = new Parent( null ); + + Set> parentViolations = validator.validate( parent ); + + assertNoViolations( parentViolations ); + + Child child = new Child( null ); + + Set> childViolations = validator.validate( child ); + + assertThat( childViolations ).containsOnlyViolations( + violationOf( NotNull.class ).withProperty( "parentAttribute" ) + ); + } + + @Test + @TestForIssue(jiraKey = "HV-1534") + public void test_constraint_is_applied_to_type_argument_of_inherited_getter() { + final Configuration configuration = ValidatorUtil.getConfiguration(); + configuration.addMapping( XmlMappingTest.class.getResourceAsStream( "hv-1534-mapping.xml" ) ); + + final ValidatorFactory validatorFactory = configuration.buildValidatorFactory(); + final Validator validator = validatorFactory.getValidator(); + + Parent parent = new Parent( "someValue" ); + parent.addToListAttribute( null ); + + Set> parentViolations = validator.validate( parent ); + + assertNoViolations( parentViolations ); + + Child child = new Child( "someValue" ); + child.addToListAttribute( null ); + + Set> childViolations = validator.validate( child ); + + assertThat( childViolations ).containsOnlyViolations( + violationOf( NotNull.class ) + .withPropertyPath( + pathWith() + .property( "parentListAttribute" ) + .containerElement( "", true, null, 0, List.class, 0 ) ) + ); + } + } diff --git a/engine/src/test/java/org/hibernate/validator/test/parameternameprovider/ReflectionParameterNameProviderTest.java b/engine/src/test/java/org/hibernate/validator/test/parameternameprovider/ReflectionParameterNameProviderTest.java index 81a9767e99..603ec4bbc9 100644 --- a/engine/src/test/java/org/hibernate/validator/test/parameternameprovider/ReflectionParameterNameProviderTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/parameternameprovider/ReflectionParameterNameProviderTest.java @@ -28,6 +28,7 @@ public class ReflectionParameterNameProviderTest { private ParameterNameProvider parameterNameProvider; @BeforeClass + @SuppressWarnings("deprecation") public void setup() { parameterNameProvider = new ReflectionParameterNameProvider(); } diff --git a/engine/src/test/java/org/hibernate/validator/test/predefinedscope/PredefinedScopeConstraintDefinitionContributorTest.java b/engine/src/test/java/org/hibernate/validator/test/predefinedscope/PredefinedScopeConstraintDefinitionContributorTest.java new file mode 100644 index 0000000000..5f540bcc50 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/predefinedscope/PredefinedScopeConstraintDefinitionContributorTest.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.test.predefinedscope; + +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.HashSet; +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; + +import org.hibernate.validator.PredefinedScopeHibernateValidator; +import org.hibernate.validator.test.constraintvalidator.MustMatch; +import org.hibernate.validator.test.constraintvalidator.MustNotMatch; +import org.hibernate.validator.testutil.TestForIssue; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * Test related to constraint validator discovery in the predefined scope case. + * + * @author Hardy Ferentschik + * @author Guillaume Smet + */ +@TestForIssue(jiraKey = "HV-1721") +public class PredefinedScopeConstraintDefinitionContributorTest { + + private Validator validator; + + @BeforeMethod + public void setUp() { + validator = getValidator(); + Set> beanMetaDataToInitialize = new HashSet<>(); + beanMetaDataToInitialize.add( Foo.class ); + beanMetaDataToInitialize.add( Quz.class ); + + ValidatorFactory validatorFactory = Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .initializeBeanMetaData( beanMetaDataToInitialize ) + .buildValidatorFactory(); + validator = validatorFactory.getValidator(); + } + + @Test + public void constraint_definitions_can_be_configured_via_service_loader() { + Set> constraintViolations = validator.validate( new Foo() ); + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( MustMatch.class ) + ); + } + + @Test + @TestForIssue(jiraKey = "HV-953") + public void constraints_defined_via_constraint_definition_contributor_can_have_default_message() { + Set> constraintViolations = validator.validate( new Foo() ); + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( MustMatch.class ).withMessage( "MustMatch default message" ) + ); + } + + @Test + @TestForIssue(jiraKey = "HV-953") + public void user_can_override_default_message_of_constraint_definition_contributor() { + Set> constraintViolations = validator.validate( new Quz() ); + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( MustNotMatch.class ).withMessage( "MustNotMatch user message" ) + ); + } + + public class Foo { + // constraint validator defined in service file! + @MustMatch("Foo") + String getFoo() { + return "Bar"; + } + } + + public class Quz { + // constraint validator defined in service file! + @MustNotMatch("Foo") + String getFoo() { + return "Foo"; + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/predefinedscope/PredefinedScopeValidatorFactoryTest.java b/engine/src/test/java/org/hibernate/validator/test/predefinedscope/PredefinedScopeValidatorFactoryTest.java new file mode 100644 index 0000000000..6133fab5e9 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/predefinedscope/PredefinedScopeValidatorFactoryTest.java @@ -0,0 +1,441 @@ +/* + * 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.predefinedscope; + +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; +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.testng.Assert.fail; + +import java.lang.annotation.ElementType; +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Locale; +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.Path; +import javax.validation.Path.Node; +import javax.validation.TraversableResolver; +import javax.validation.Valid; +import javax.validation.Validation; +import javax.validation.ValidationException; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.constraints.Email; +import javax.validation.constraints.NotNull; + +import org.assertj.core.api.Assertions; +import org.hibernate.validator.PredefinedScopeHibernateValidator; +import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer; +import org.hibernate.validator.testutil.TestForIssue; +import org.testng.annotations.Test; + +@TestForIssue(jiraKey = "HV-1667") +public class PredefinedScopeValidatorFactoryTest { + + @Test + public void testValidation() throws NoSuchMethodException, SecurityException { + Validator validator = getValidator(); + + Set> violations = validator.validate( new Bean( "property", "test@example.com" ) ); + assertNoViolations( violations ); + + violations = validator.validate( new Bean( null, "invalid" ) ); + assertThat( violations ).containsOnlyViolations( + violationOf( NotNull.class ).withProperty( "property" ), + violationOf( Email.class ).withProperty( "email" ) ); + + violations = validator.forExecutables() + .validateParameters( new Bean(), Bean.class.getMethod( "setEmail", String.class ), + new Object[]{ "invalid" } ); + assertThat( violations ).containsOnlyViolations( + violationOf( Email.class ) + .withPropertyPath( pathWith().method( "setEmail" ).parameter( "email", 0 ) ) ); + + violations = validator.forExecutables() + .validateReturnValue( new Bean(), Bean.class.getMethod( "getEmail" ), "invalid" ); + assertThat( violations ).containsOnlyViolations( + violationOf( Email.class ) + .withPropertyPath( pathWith().method( "getEmail" ).returnValue() ) ); + + violations = validator.forExecutables() + .validateConstructorParameters( Bean.class.getConstructor( String.class, String.class ), + new Object[]{ null, "invalid" } ); + assertThat( violations ).containsOnlyViolations( + violationOf( NotNull.class ) + .withPropertyPath( pathWith().constructor( Bean.class ).parameter( "property", 0 ) ), + violationOf( Email.class ) + .withPropertyPath( pathWith().constructor( Bean.class ).parameter( "email", 1 ) ) ); + + violations = validator.forExecutables() + .validateConstructorReturnValue( Bean.class.getConstructor( String.class, String.class ), + new Bean( null, "invalid" ) ); + assertThat( violations ).containsOnlyViolations( + violationOf( NotNull.class ) + .withPropertyPath( pathWith().constructor( Bean.class ).returnValue() + .property( "property" ) ), + violationOf( Email.class ) + .withPropertyPath( pathWith().constructor( Bean.class ).returnValue() + .property( "email" ) ) ); + } + + @Test + public void testValidationOnUnknownBean() throws NoSuchMethodException, SecurityException { + Validator validator = getValidator(); + + Set> violations = validator.validate( new UnknownBean() ); + assertNoViolations( violations ); + } + + @Test + public void testValidationOnUnknownBeanMethodParameter() throws NoSuchMethodException, SecurityException { + Validator validator = getValidator(); + + Set> violations = validator.validate( new UnknownBean() ); + assertNoViolations( violations ); + + violations = validator.forExecutables() + .validateParameters( new UnknownBean(), UnknownBean.class.getMethod( "setMethod", String.class ), + new Object[]{ "" } ); + assertNoViolations( violations ); + } + + @Test + public void testValidationOnUnknownBeanMethodReturnValue() throws NoSuchMethodException, SecurityException { + Validator validator = getValidator(); + + Set> violations = validator.forExecutables() + .validateReturnValue( new UnknownBean(), UnknownBean.class.getMethod( "getMethod" ), "" ); + assertNoViolations( violations ); + } + + @Test + public void testValidationOnUnknownBeanConstructorParameters() throws NoSuchMethodException, SecurityException { + Validator validator = getValidator(); + + Set> violations = validator.forExecutables() + .validateConstructorParameters( UnknownBean.class.getConstructor( String.class ), + new Object[]{ "" } ); + assertNoViolations( violations ); + } + + @Test + public void testValidationOnUnknownBeanConstructorReturnValue() throws NoSuchMethodException, SecurityException { + Validator validator = getValidator(); + + Set> violations = validator.forExecutables() + .validateConstructorReturnValue( UnknownBean.class.getConstructor( String.class ), new UnknownBean() ); + assertNoViolations( violations ); + } + + @Test + public void testExistingInitializedLocale() { + Locale defaultLocale = Locale.getDefault(); + + try { + Locale.setDefault( Locale.FRANCE ); + + Validator validator = getValidatorWithInitializedLocale( Locale.FRANCE ); + + Set> violations = validator.validate( new Bean( "", "invalid" ) ); + assertThat( violations ).containsOnlyViolations( + violationOf( Email.class ).withProperty( "email" ).withMessage( "doit être une adresse électronique syntaxiquement correcte" ) ); + } + finally { + Locale.setDefault( defaultLocale ); + } + } + + @Test + public void testUnavailableInitializedLocale() { + Locale defaultLocale = Locale.getDefault(); + + try { + Locale georgianLocale = new Locale( "ka", "GE" ); + + Locale.setDefault( georgianLocale ); + + Validator validator = getValidatorWithInitializedLocale( georgianLocale ); + + Set> violations = validator.validate( new Bean( "", "invalid" ) ); + assertThat( violations ).containsOnlyViolations( + violationOf( Email.class ).withProperty( "email" ).withMessage( "must be a well-formed email address" ) ); + } + finally { + Locale.setDefault( defaultLocale ); + } + } + + @TestForIssue(jiraKey = "HV-1681") + @Test + public void testValidOnUnknownBean() { + Set> violations = getValidator().validate( new AnotherBean() ); + assertNoViolations( violations ); + } + + @Test(expectedExceptions = ValidationException.class, expectedExceptionsMessageRegExp = "HV000250:.*") + public void testUninitializedLocale() { + Locale defaultLocale = Locale.getDefault(); + + try { + Locale.setDefault( Locale.FRANCE ); + + Validator validator = getValidatorWithInitializedLocale( Locale.ENGLISH ); + + Set> violations = validator.validate( new Bean( "", "invalid" ) ); + assertThat( violations ).containsOnlyViolations( + violationOf( Email.class ).withProperty( "email" ).withMessage( "doit être une adresse électronique syntaxiquement correcte" ) ); + } + finally { + Locale.setDefault( defaultLocale ); + } + } + + @Test + public void testBeanMetaDataClassNormalizerNoNormalizer() { + // In this case, as we haven't registered any metadata for the hierarchy, even if we have constraints, + // we won't have any violations. + Set> violations = getValidator().validate( new BeanProxy() ); + assertNoViolations( violations ); + + // Now let's register the metadata for Bean and see how it goes + Set> beanMetaDataToInitialize = new HashSet<>(); + beanMetaDataToInitialize.add( Bean.class ); + + ValidatorFactory validatorFactory = Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .initializeBeanMetaData( beanMetaDataToInitialize ) + .initializeLocales( Collections.singleton( Locale.getDefault() ) ) + .buildValidatorFactory(); + + // As we don't have any metadata for BeanProxy, we consider it is not constrained at all. + violations = validatorFactory.getValidator().validate( new BeanProxy() ); + assertNoViolations( violations ); + } + + @Test + public void testBeanMetaDataClassNormalizer() { + Set> beanMetaDataToInitialize = new HashSet<>(); + beanMetaDataToInitialize.add( Bean.class ); + + ValidatorFactory validatorFactory = Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .initializeBeanMetaData( beanMetaDataToInitialize ) + .initializeLocales( Collections.singleton( Locale.getDefault() ) ) + .beanMetaDataClassNormalizer( new MyProxyInterfaceBeanMetaDataClassNormalizer() ) + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + Set> violations = validator.validate( new BeanProxy() ); + assertThat( violations ).containsOnlyViolations( + violationOf( NotNull.class ).withProperty( "property" ) ); + } + + @Test + public void validatorSpecificTraversableResolver() { + Set> beanMetaDataToInitialize = new HashSet<>(); + beanMetaDataToInitialize.add( Bean.class ); + beanMetaDataToInitialize.add( AnotherBean.class ); + beanMetaDataToInitialize.add( SomeEnum.class ); + + ValidatorFactory validatorFactory = Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .initializeBeanMetaData( beanMetaDataToInitialize ) + .buildValidatorFactory(); + + try { + Validator validator = validatorFactory.usingContext().traversableResolver( new ThrowExceptionTraversableResolver() ) + .getValidator(); + validator.validate( new Bean() ); + fail(); + } + catch (ValidationException e) { + Assertions.assertThat( e ).hasCauseExactlyInstanceOf( ValidatorSpecificTraversableResolverUsedException.class ); + } + } + + @Test + public void variousObjectTypes() { + Set> beanMetaDataToInitialize = new HashSet<>(); + beanMetaDataToInitialize.add( Bean.class ); + beanMetaDataToInitialize.add( AnotherBean.class ); + beanMetaDataToInitialize.add( SomeEnum.class ); + beanMetaDataToInitialize.add( Values.Itr.class ); + + ValidatorFactory validatorFactory = Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .initializeBeanMetaData( beanMetaDataToInitialize ) + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + validator.validate( new Bean() ); + } + + private static Validator getValidator() { + Set> beanMetaDataToInitialize = new HashSet<>(); + beanMetaDataToInitialize.add( Bean.class ); + beanMetaDataToInitialize.add( AnotherBean.class ); + + ValidatorFactory validatorFactory = Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .initializeBeanMetaData( beanMetaDataToInitialize ) + .buildValidatorFactory(); + + return validatorFactory.getValidator(); + } + + private static Validator getValidatorWithInitializedLocale(Locale locale) { + Set> beanMetaDataToInitialize = new HashSet<>(); + beanMetaDataToInitialize.add( Bean.class ); + + ValidatorFactory validatorFactory = Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .initializeBeanMetaData( beanMetaDataToInitialize ) + .initializeLocales( Collections.singleton( locale ) ) + .buildValidatorFactory(); + + return validatorFactory.getValidator(); + } + + private enum SomeEnum { + VALUE; + } + + final class Values extends AbstractCollection implements Collection { + public Iterator iterator() { + return new Itr( null ); + } + + public int size() { + return 0; + } + + public boolean isEmpty() { + return true; + } + + final class Itr implements Iterator { + private final Iterator iterator; + + Itr(final Iterator iterator) { + this.iterator = iterator; + } + + public boolean hasNext() { + return iterator.hasNext(); + } + + public String next() { + return ""; + } + } + } + + private static class Bean { + + @NotNull + private String property; + + @Email + private String email; + + public Bean() { + } + + @Valid + public Bean(@NotNull String property, @Email String email) { + this.property = property; + this.email = email; + } + + @SuppressWarnings("unused") + public String getProperty() { + return property; + } + + public @Email String getEmail() { + return email; + } + + @SuppressWarnings("unused") + public void setEmail(@Email String email) { + this.email = email; + } + } + + private static class AnotherBean { + + @Valid + private final UnknownBean bean; + + + private AnotherBean() { + bean = new UnknownBean(); + } + } + + private static class UnknownBean { + + public UnknownBean() { + } + + @SuppressWarnings("unused") + public UnknownBean(String parameter) { + } + + @SuppressWarnings("unused") + public String getMethod() { + return null; + } + + @SuppressWarnings("unused") + public void setMethod(String parameter) { + } + } + + private interface MyProxyInterface { + } + + private static class MyProxyInterfaceBeanMetaDataClassNormalizer implements BeanMetaDataClassNormalizer { + + @Override + public Class normalize(Class beanClass) { + if ( MyProxyInterface.class.isAssignableFrom( beanClass ) ) { + return beanClass.getSuperclass(); + } + + return beanClass; + } + } + + private static class BeanProxy extends Bean implements MyProxyInterface { + } + + private static class ThrowExceptionTraversableResolver implements TraversableResolver { + + @Override + public boolean isReachable(Object traversableObject, Node traversableProperty, Class rootBeanType, Path pathToTraversableObject, + ElementType elementType) { + throw new ValidatorSpecificTraversableResolverUsedException(); + } + + @Override + public boolean isCascadable(Object traversableObject, Node traversableProperty, Class rootBeanType, Path pathToTraversableObject, + ElementType elementType) { + throw new ValidatorSpecificTraversableResolverUsedException(); + } + } + + private static class ValidatorSpecificTraversableResolverUsedException extends RuntimeException { + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/properties/GetterPropertySelectionStrategyTest.java b/engine/src/test/java/org/hibernate/validator/test/properties/GetterPropertySelectionStrategyTest.java new file mode 100644 index 0000000000..816b82ee7b --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/properties/GetterPropertySelectionStrategyTest.java @@ -0,0 +1,283 @@ +/* + * 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.properties; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.constraints.AssertTrue; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +import org.hibernate.validator.HibernateValidator; +import org.hibernate.validator.HibernateValidatorConfiguration; +import org.hibernate.validator.spi.properties.ConstrainableExecutable; +import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy; +import org.hibernate.validator.testutil.ConstraintViolationAssert; +import org.hibernate.validator.testutil.TestForIssue; +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.Test; + +/** + * @author Marko Bekhta + */ +public class GetterPropertySelectionStrategyTest { + + @Test + public void testGetterPropertySelectionStrategy() throws Exception { + GetterPropertySelectionStrategy strategy = new FooGetterPropertySelectionStrategy(); + + ConstrainableExecutableImpl fooMethod = new ConstrainableExecutableImpl( Foo.class.getDeclaredMethod( "fooMethod" ) ); + ConstrainableExecutableImpl getMethod = new ConstrainableExecutableImpl( Foo.class.getDeclaredMethod( "getMethod" ) ); + + assertThat( strategy.getProperty( fooMethod ) ).isPresent(); + assertThat( strategy.getProperty( getMethod ) ).isNotPresent(); + + assertThat( strategy.getProperty( fooMethod ).get() ).isEqualTo( "method" ); + } + + @Test + public void testConfigureGetterPropertySelectionStrategy() throws Exception { + Validator validator = Validation.byProvider( HibernateValidator.class ) + .configure() + .getterPropertySelectionStrategy( new FooGetterPropertySelectionStrategy() ) + .buildValidatorFactory() + .getValidator(); + Set> violations = validator.validate( new Foo() ); + + ConstraintViolationAssert.assertThat( violations ).containsOnlyViolations( + violationOf( Min.class ) + ); + } + + @Test + public void testConfigureGetterPropertySelectionStrategyWithProperty() throws Exception { + Validator validator = Validation.byProvider( HibernateValidator.class ) + .configure() + .addProperty( HibernateValidatorConfiguration.GETTER_PROPERTY_SELECTION_STRATEGY_CLASSNAME, FooGetterPropertySelectionStrategy.class.getName() ) + .buildValidatorFactory() + .getValidator(); + Set> violations = validator.validate( new Foo() ); + + ConstraintViolationAssert.assertThat( violations ).containsOnlyViolations( + violationOf( Min.class ) + ); + } + + @Test + public void testNoPrefixGetterPropertySelectionStrategy() { + Validator validator = Validation.byProvider( HibernateValidator.class ) + .configure() + .getterPropertySelectionStrategy( new NoPrefixGetterPropertySelectionStrategy() ) + .buildValidatorFactory() + .getValidator(); + Set> violations = validator.validate( new NoPrefixFoo() ); + + ConstraintViolationAssert.assertThat( violations ).containsOnlyViolations( + violationOf( Min.class ).withProperty( "test" ), + violationOf( NotBlank.class ).withProperty( "name" ) + ); + } + + @Test + public void testGetterAndFieldWithSamePropertyNameButDifferentTypes() { + Validator validator = ValidatorUtil.getValidator(); + + Set> violations = validator.validate( new Bar() ); + + ConstraintViolationAssert.assertThat( violations ).containsOnlyViolations( + violationOf( NotEmpty.class ).withProperty( "tags" ), + violationOf( Size.class ).withProperty( "tags" ) + ); + } + + @Test + public void testStrangePropertyNames() { + class StrangeProeprties { + @AssertTrue + public boolean isHasGet() { + return false; + } + + @AssertTrue + public boolean hasIsGet() { + return false; + } + + @NotNull + public String getHasIs() { + return null; + } + } + + Validator validator = ValidatorUtil.getValidator(); + + Set> violations = validator.validate( new StrangeProeprties() ); + + ConstraintViolationAssert.assertThat( violations ).containsOnlyViolations( + violationOf( AssertTrue.class ).withProperty( "hasGet" ), + violationOf( AssertTrue.class ).withProperty( "isGet" ), + violationOf( NotNull.class ).withProperty( "hasIs" ) + ); + } + + @Test + @TestForIssue(jiraKey = "HV-622") + public void testIsGetterMethod() throws Exception { + class Bar { + @NotNull + public String getBar() { + return null; + } + + @NotEmpty + public String getBar(String param) { + return null; + } + } + Validator validator = ValidatorUtil.getValidator(); + + Set> violations = validator.validate( new Bar() ); + + ConstraintViolationAssert.assertThat( violations ).containsOnlyViolations( + violationOf( NotNull.class ).withProperty( "bar" ) + ); + } + + private static class Bar { + + @Size(min = 10, max = 100) + private final String tags = ""; + + @NotEmpty + public List getTags() { + return Arrays.stream( tags.split( "," ) ) + .filter( tag -> !tag.trim().isEmpty() ) + .collect( Collectors.toList() ); + } + } + + private static class Foo { + + @Min(10) + public int fooMethod() { + return 1; + } + + @Max(-10) + public int getMethod() { + return 1; + } + } + + private static class NoPrefixFoo { + + @Min(10) + public int test() { + return 1; + } + + @NotBlank + public String name() { + return ""; + } + + @NotBlank + public String getName() { + return ""; + } + + @AssertTrue + public boolean isTest() { + return false; + } + + @Max(-10) + public int getTest() { + return 1; + } + } + + public static class NoPrefixGetterPropertySelectionStrategy implements GetterPropertySelectionStrategy { + + @Override + public Optional getProperty(ConstrainableExecutable executable) { + if ( executable.getReturnType() == void.class + || executable.getParameterTypes().length > 0 + || executable.getName().startsWith( "is" ) + || executable.getName().startsWith( "get" ) ) { + return Optional.empty(); + } + + return Optional.of( executable.getName() ); + } + + @Override + public Set getGetterMethodNameCandidates(String propertyName) { + return Collections.singleton( propertyName ); + } + } + + public static class FooGetterPropertySelectionStrategy implements GetterPropertySelectionStrategy { + + @Override + public Optional getProperty(ConstrainableExecutable executable) { + if ( executable.getParameterTypes().length > 0 + || !executable.getName().startsWith( "foo" ) ) { + return Optional.empty(); + } + + char[] chars = executable.getName().substring( 3 ).toCharArray(); + chars[0] = Character.toLowerCase( chars[0] ); + return Optional.of( new String( chars ) ); + } + + @Override + public Set getGetterMethodNameCandidates(String propertyName) { + return Collections.singleton( "foo" + Character.toUpperCase( propertyName.charAt( 0 ) ) + propertyName.substring( 1 ) ); + } + } + + private static class ConstrainableExecutableImpl implements ConstrainableExecutable { + + private final Method method; + + private ConstrainableExecutableImpl(Method method) { + this.method = method; + } + + @Override + public Class getReturnType() { + return method.getReturnType(); + } + + @Override + public String getName() { + return method.getName(); + } + + @Override + public Class[] getParameterTypes() { + return method.getParameterTypes(); + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/spi/nodenameprovider/AnnotationPropertyNodeNameProvider.java b/engine/src/test/java/org/hibernate/validator/test/spi/nodenameprovider/AnnotationPropertyNodeNameProvider.java new file mode 100644 index 0000000000..c7215ea880 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/spi/nodenameprovider/AnnotationPropertyNodeNameProvider.java @@ -0,0 +1,90 @@ +/* + * 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.spi.nodenameprovider; + +import java.io.Serializable; +import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.Objects; +import java.util.Optional; + +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.spi.nodenameprovider.JavaBeanProperty; +import org.hibernate.validator.spi.nodenameprovider.Property; +import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider; + +/** + * An example of how a name can be resolved from an annotation + * + * @author Damir Alibegovic + */ +class AnnotationPropertyNodeNameProvider implements PropertyNodeNameProvider, Serializable { + private static final String VALUE = "value"; + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private final Class annotationType; + private final String annotationMemberName; + + AnnotationPropertyNodeNameProvider(Class annotationType) { + this( annotationType, VALUE ); + } + + AnnotationPropertyNodeNameProvider(Class annotationType, String annotationMemberName) { + this.annotationType = Objects.requireNonNull( annotationType ); + this.annotationMemberName = Objects.requireNonNull( annotationMemberName ); + } + + @Override + public String getName(Property property) { + if ( property instanceof JavaBeanProperty ) { + return getJavaBeanPropertyName( (JavaBeanProperty) property ); + } + + return getDefaultName( property ); + } + + private String getJavaBeanPropertyName(JavaBeanProperty property) { + Optional field = getField( property ); + + if ( field.isPresent() && field.get().isAnnotationPresent( annotationType ) ) { + return getAnnotationMemberValue( field.get(), annotationMemberName ) + .orElse( getDefaultName( property ) ); + } + else { + return getDefaultName( property ); + } + } + + private Optional getField(JavaBeanProperty property) { + return Arrays.stream( property.getDeclaringClass().getFields() ) + .peek( field -> field.setAccessible( true ) ) + .filter( field -> property.getName().equals( field.getName() ) ) + .findFirst(); + } + + private Optional getAnnotationMemberValue(Field field, String annotationMemberName) { + Annotation annotation = field.getAnnotation( annotationType ); + + try { + return Optional.of( + (String) annotation.annotationType().getMethod( annotationMemberName ).invoke( annotation ) ); + } + catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + LOG.error( "Unable to get annotation member value", e ); + + return Optional.empty(); + } + } + + private String getDefaultName(Property property) { + return property.getName(); + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/spi/nodenameprovider/PropertyNodeNameProviderTest.java b/engine/src/test/java/org/hibernate/validator/test/spi/nodenameprovider/PropertyNodeNameProviderTest.java new file mode 100644 index 0000000000..ba94821cfe --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/spi/nodenameprovider/PropertyNodeNameProviderTest.java @@ -0,0 +1,241 @@ +/* + * 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.spi.nodenameprovider; + +import static org.testng.Assert.assertEquals; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.Valid; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.constraints.Min; +import javax.validation.constraints.Size; + +import org.hibernate.validator.HibernateValidator; +import org.hibernate.validator.testutil.TestForIssue; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * @author Damir Alibegovic + */ +@TestForIssue(jiraKey = "HV-823") +public class PropertyNodeNameProviderTest { + private static final String INVALID_BRAND_NAME = "BMW"; + private static final String VALID_BRAND_NAME = "Mercedes"; + + private Validator validator; + + @BeforeMethod + public void setUp() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .propertyNodeNameProvider( new AnnotationPropertyNodeNameProvider( PropertyName.class ) ) + .buildValidatorFactory(); + + validator = validatorFactory.getValidator(); + } + + @Test + public void nameIsResolvedFromCustomAnnotationByUsingValidate() { + Car testInstance = new Car( INVALID_BRAND_NAME ); + + Set> violations = validator.validate( testInstance ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "brand.brand_name" ); + } + + @Test + public void nameIsResolvedFromCustomAnnotationByUsingValidateProperty() { + Car testInstance = new Car( INVALID_BRAND_NAME ); + + Set> violations = validator.validateProperty( testInstance, "brand.name" ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "brand.brand_name" ); + } + + @Test + public void nameIsResolvedFromCustomAnnotationByUsingValidateValue() { + Set> violations = validator.validateValue( Brand.class, "name", INVALID_BRAND_NAME ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "brand_name" ); + } + + @Test + public void nameIsResolvedFromCustomAnnotationWithConstraintOnGetter() { + int horsePower = 125; + int speedInRpm = 500; + Airplane testInstance = new Airplane( new Engine( horsePower, speedInRpm ) ); + + Set> violations = validator.validate( testInstance ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "turbojet_engine.speed_in_rpm" ); + } + + @Test + public void propertyCanBeOfTypeMap() { + int horsePower = 0; + int speedInRpm = 1000; + Car testInstance = new Car( VALID_BRAND_NAME ); + testInstance.addComponent( "engine", new Engine( horsePower, speedInRpm ) ); + + Set> violations = validator.validate( testInstance ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "components[engine].horse_power" ); + + } + + @Test + public void defaultValidatorFactoryUsesDefaultPropertyNodeNameProvider() { + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + Validator val = factory.getValidator(); + + Car testInstance = new Car( INVALID_BRAND_NAME ); + + Set> violations = val.validate( testInstance ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "brand.name" ); + } + + @Test + public void defaultProviderUsesDefaultPropertyNodeNameProvider() { + ValidatorFactory validatorFactory = Validation.byDefaultProvider() + .configure() + .buildValidatorFactory(); + Validator val = validatorFactory.getValidator(); + + Car testInstance = new Car( INVALID_BRAND_NAME ); + + Set> violations = val.validate( testInstance ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "brand.name" ); + } + + @Test + public void hibernateValidatorUsesDefaultPropertyNodeProvider() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .buildValidatorFactory(); + Validator val = validatorFactory.getValidator(); + + Car testInstance = new Car( INVALID_BRAND_NAME ); + + Set> violations = val.validate( testInstance ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "brand.name" ); + } + + @Test + public void hibernateValidatorCanUseCustomPropertyNodeNameProvider() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .propertyNodeNameProvider( new AnnotationPropertyNodeNameProvider( PropertyName.class ) ) + .buildValidatorFactory(); + Validator val = validatorFactory.getValidator(); + + Car testInstance = new Car( INVALID_BRAND_NAME ); + + Set> violations = val.validate( testInstance ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "brand.brand_name" ); + } + + @Test + public void hibernateValidatorFallsBackToDefaultPropertyNodeNameProvider() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .propertyNodeNameProvider( null ) + .buildValidatorFactory(); + Validator val = validatorFactory.getValidator(); + + Car testInstance = new Car( INVALID_BRAND_NAME ); + + Set> violations = val.validate( testInstance ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "brand.name" ); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.FIELD, ElementType.METHOD }) + public @interface PropertyName { + String value(); + } + + private class Car { + @PropertyName("components") + public final Map comps = new HashMap<>(); + + @Valid + public final Brand brand; + + Car(String brand) { + this.brand = new Brand( brand ); + } + + public void addComponent(String name, Object component) { + comps.put( name, component ); + } + } + + private class Brand { + @PropertyName("brand_name") + @Size(min = 4) + public final String name; + + Brand(String name) { + this.name = name; + } + } + + private class Engine { + @PropertyName("horse_power") + @Min(1) + public final int horsePower; + + @PropertyName("speed_in_rpm") + public final int speedInRpm; + + public Engine(int horsePower, int speedInRpm) { + this.horsePower = horsePower; + this.speedInRpm = speedInRpm; + } + + @Min(1000) + public int getSpeedInRpm() { + return speedInRpm; + } + } + + private class Airplane { + @Valid + @PropertyName("turbojet_engine") + public final Engine engine; + + public Airplane(Engine engine) { + this.engine = engine; + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/spi/nodenameprovider/jackson/JacksonAnnotationPropertyNodeNameProvider.java b/engine/src/test/java/org/hibernate/validator/test/spi/nodenameprovider/jackson/JacksonAnnotationPropertyNodeNameProvider.java new file mode 100644 index 0000000000..829b5ad5f8 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/spi/nodenameprovider/jackson/JacksonAnnotationPropertyNodeNameProvider.java @@ -0,0 +1,50 @@ +/* + * 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.spi.nodenameprovider.jackson; + +import org.hibernate.validator.spi.nodenameprovider.JavaBeanProperty; +import org.hibernate.validator.spi.nodenameprovider.Property; +import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; + +/** + * An example of how a name can be resolved from a Jackson annotation. + * + * @author Damir Alibegovic + */ +public class JacksonAnnotationPropertyNodeNameProvider implements PropertyNodeNameProvider { + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public String getName(Property property) { + if ( property instanceof JavaBeanProperty ) { + return getJavaBeanPropertyName( (JavaBeanProperty) property ); + } + + return getDefaultName( property ); + } + + private String getJavaBeanPropertyName(JavaBeanProperty property) { + JavaType type = objectMapper.constructType( property.getDeclaringClass() ); + BeanDescription desc = objectMapper.getSerializationConfig().introspect( type ); + + return desc.findProperties() + .stream() + .filter( prop -> prop.getInternalName().equals( property.getName() ) ) + .map( BeanPropertyDefinition::getName ) + .findFirst() + .orElse( property.getName() ); + } + + private String getDefaultName(Property property) { + return property.getName(); + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/spi/nodenameprovider/jackson/JacksonAnnotationPropertyNodeNameProviderTest.java b/engine/src/test/java/org/hibernate/validator/test/spi/nodenameprovider/jackson/JacksonAnnotationPropertyNodeNameProviderTest.java new file mode 100644 index 0000000000..b10f1c3767 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/spi/nodenameprovider/jackson/JacksonAnnotationPropertyNodeNameProviderTest.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.test.spi.nodenameprovider.jackson; + +import static org.testng.Assert.assertEquals; + +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.Valid; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; + +import org.hibernate.validator.HibernateValidator; +import org.hibernate.validator.testutil.TestForIssue; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * @author Damir Alibegovic + */ +@TestForIssue(jiraKey = "HV-823") +public class JacksonAnnotationPropertyNodeNameProviderTest { + private static final int VALID_HORSE_POWER = 150; + private static final int INVALID_HORSE_POWER = 250; + + private Validator validator; + + @BeforeMethod + public void setUp() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .propertyNodeNameProvider( new JacksonAnnotationPropertyNodeNameProvider() ) + .buildValidatorFactory(); + + validator = validatorFactory.getValidator(); + } + + @Test + public void jsonPropertyOnFieldIsUsedForPathResolution() { + Car testInstance = new Car( new Engine( INVALID_HORSE_POWER ) ); + + Set> violations = validator.validate( testInstance ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "engine.horse_power" ); + } + + @Test + public void jsonPropertyOnGetterIsUsedForPathResolution() { + int invalidNumberOfSeats = 0; + Car testInstance = new Car( new Engine( VALID_HORSE_POWER ), invalidNumberOfSeats ); + + Set> violations = validator.validate( testInstance ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "number_of_seats" ); + } + + private class Car { + @Valid + private final Engine engine; + + @Min(1) + private final int numberOfSeats; + + Car(Engine engine) { + this( engine, 4 ); + } + + Car(Engine engine, int numberOfSeats) { + this.engine = engine; + this.numberOfSeats = numberOfSeats; + } + + @JsonProperty("number_of_seats") + public int getNumberOfSeats() { + return numberOfSeats; + } + } + + private class Engine { + @JsonProperty("horse_power") + @Max(200) + private final int horsePower; + + Engine(int horsePower) { + this.horsePower = horsePower; + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/testutils/ConstraintValidatorInitializationHelper.java b/engine/src/test/java/org/hibernate/validator/testutils/ConstraintValidatorInitializationHelper.java index ea3e4b917d..1978dc0c6b 100644 --- a/engine/src/test/java/org/hibernate/validator/testutils/ConstraintValidatorInitializationHelper.java +++ b/engine/src/test/java/org/hibernate/validator/testutils/ConstraintValidatorInitializationHelper.java @@ -8,16 +8,23 @@ import java.lang.annotation.Annotation; import java.time.Duration; +import java.util.Collections; import javax.validation.ClockProvider; import javax.validation.metadata.ConstraintDescriptor; import org.hibernate.validator.constraintvalidation.HibernateConstraintValidator; import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; +import org.hibernate.validator.internal.engine.ConstraintCreationContext; import org.hibernate.validator.internal.engine.DefaultClockProvider; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManagerImpl; import org.hibernate.validator.internal.engine.scripting.DefaultScriptEvaluatorFactory; +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; +import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; import org.hibernate.validator.spi.scripting.ScriptEvaluator; import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; @@ -40,7 +47,7 @@ public static ConstraintDescriptor descriptorFrom(Cons CONSTRAINT_HELPER, null, annotationDescriptor, - null + ConstraintLocationKind.GETTER ); } @@ -65,6 +72,13 @@ public static HibernateConstraintValidatorInitializationContext getDummyConstrai return DUMMY_CONSTRAINT_VALIDATOR_INITIALIZATION_CONTEXT; } + public static ConstraintCreationContext getDummyConstraintCreationContext() { + return new ConstraintCreationContext( new ConstraintHelper(), + new ConstraintValidatorManagerImpl( new ConstraintValidatorFactoryImpl(), getDummyConstraintValidatorInitializationContext() ), + new TypeResolutionHelper(), + new ValueExtractorManager( Collections.emptySet() ) ); + } + public static HibernateConstraintValidatorInitializationContext getConstraintValidatorInitializationContext( ScriptEvaluatorFactory scriptEvaluatorFactory, ClockProvider clockProvider, Duration duration ) { diff --git a/engine/src/test/java/org/hibernate/validator/testutils/ValidatorUtil.java b/engine/src/test/java/org/hibernate/validator/testutils/ValidatorUtil.java index 529e28ae75..061272b05f 100644 --- a/engine/src/test/java/org/hibernate/validator/testutils/ValidatorUtil.java +++ b/engine/src/test/java/org/hibernate/validator/testutils/ValidatorUtil.java @@ -235,6 +235,6 @@ public static T getValidatingProxy(I implementor, Validator exe } public static HibernateConstraintValidatorContext getConstraintValidatorContext() { - return new ConstraintValidatorContextImpl( null, DefaultClockProvider.INSTANCE, null, null, null ); + return new ConstraintValidatorContextImpl( DefaultClockProvider.INSTANCE, null, null, null ); } } diff --git a/engine/src/test/resources/META-INF/services/javax.validation.valueextraction.ValueExtractor b/engine/src/test/resources/META-INF/services/javax.validation.valueextraction.ValueExtractor deleted file mode 100644 index 78eaaae0e1..0000000000 --- a/engine/src/test/resources/META-INF/services/javax.validation.valueextraction.ValueExtractor +++ /dev/null @@ -1 +0,0 @@ -org.hibernate.validator.test.internal.engine.cascaded.GuavaOptionalValueExtractor diff --git a/engine/src/test/resources/org/hibernate/validator/test/internal/engine/constraintvalidation/hv-1589-mapping.xml b/engine/src/test/resources/org/hibernate/validator/test/internal/engine/constraintvalidation/hv-1589-mapping.xml index eb78ab7370..96016dd950 100644 --- a/engine/src/test/resources/org/hibernate/validator/test/internal/engine/constraintvalidation/hv-1589-mapping.xml +++ b/engine/src/test/resources/org/hibernate/validator/test/internal/engine/constraintvalidation/hv-1589-mapping.xml @@ -18,7 +18,7 @@ 5 - 10 + 10 diff --git a/engine/src/test/resources/org/hibernate/validator/test/internal/engine/constraintvalidation/hv-662-mapping.xml b/engine/src/test/resources/org/hibernate/validator/test/internal/engine/constraintvalidation/hv-662-mapping.xml index eb78ab7370..96016dd950 100644 --- a/engine/src/test/resources/org/hibernate/validator/test/internal/engine/constraintvalidation/hv-662-mapping.xml +++ b/engine/src/test/resources/org/hibernate/validator/test/internal/engine/constraintvalidation/hv-662-mapping.xml @@ -18,7 +18,7 @@ 5 - 10 + 10 diff --git a/engine/src/test/resources/org/hibernate/validator/test/internal/engine/methodvalidation/xml/method-validation-mapping.xml b/engine/src/test/resources/org/hibernate/validator/test/internal/engine/methodvalidation/xml/method-validation-mapping.xml index 439487b288..fc903f4809 100644 --- a/engine/src/test/resources/org/hibernate/validator/test/internal/engine/methodvalidation/xml/method-validation-mapping.xml +++ b/engine/src/test/resources/org/hibernate/validator/test/internal/engine/methodvalidation/xml/method-validation-mapping.xml @@ -113,12 +113,12 @@ - + [XML] - must not be null - + [XML] - must not be null diff --git a/engine/src/test/resources/org/hibernate/validator/test/internal/xml/hv-1534-mapping.xml b/engine/src/test/resources/org/hibernate/validator/test/internal/xml/hv-1534-mapping.xml new file mode 100644 index 0000000000..58fac1a395 --- /dev/null +++ b/engine/src/test/resources/org/hibernate/validator/test/internal/xml/hv-1534-mapping.xml @@ -0,0 +1,28 @@ + + + + + org.hibernate.validator.internal.xml + + + + + + + + + + + + + diff --git a/integration/pom.xml b/integration/pom.xml index adb617610a..f4cb25af56 100644 --- a/integration/pom.xml +++ b/integration/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.1.0.Final ../pom.xml @@ -21,10 +21,10 @@ Hibernate Validator WildFly integration tests. - ${project.build.directory}/wildfly-${wildfly.version} + ${project.build.directory}/wildfly-${version.wildfly} ${wildfly.target-dir}/modules/system/layers/base - ${project.build.directory}/wildfly-${wildfly-secondary.version} + ${project.build.directory}/wildfly-${version.wildfly.secondary} ${wildfly-secondary.target-dir}/modules/system/layers/base .. @@ -76,6 +76,18 @@ org.javamoney moneta test + + + javax.annotation + javax.annotation-api + + + + + + jakarta.annotation + jakarta.annotation-api + test org.jboss.arquillian.testng @@ -91,15 +103,37 @@ org.jboss.weld weld-core-impl test + + + org.jboss.spec.javax.interceptor + + jboss-interceptors-api_1.2_spec + + + + javax.enterprise + cdi-api + + + + + jakarta.interceptor + jakarta.interceptor-api + test - org.hibernate.javax.persistence - hibernate-jpa-2.1-api + jakarta.enterprise + jakarta.enterprise.cdi-api test - org.jboss.spec.javax.ejb - jboss-ejb-api_3.2_spec + jakarta.persistence + jakarta.persistence-api + test + + + jakarta.ejb + jakarta.ejb-api test @@ -120,7 +154,7 @@ ${project.groupId} hibernate-validator-modules - wildfly-${wildfly.version}-patch + wildfly-${version.wildfly}-patch zip test @@ -157,6 +191,7 @@ wildfly-current-integration-test integration-test + verify @@ -165,38 +200,13 @@ target/failsafe-reports/failsafe-summary-wildfly-current.xml - - - wildfly-secondary-integration-test - - integration-test - - - - wildfly-secondary - - target/failsafe-reports/failsafe-summary-wildfly-secondary.xml - - - - verify - - verify - - - - target/failsafe-reports/failsafe-summary-wildfly-current.xml - target/failsafe-reports/failsafe-summary-wildfly-secondary.xml - - - maven-dependency-plugin - unpack-wildfly + unpack-wildfly-current pre-integration-test unpack @@ -207,16 +217,7 @@ org.wildfly wildfly-dist - ${wildfly.version} - tar.gz - false - ${project.build.directory} - - - - org.wildfly - wildfly-dist - ${wildfly-secondary.version} + ${version.wildfly} tar.gz false ${project.build.directory} @@ -225,7 +226,7 @@ - copy-patch + copy-patch-current pre-integration-test copy @@ -237,16 +238,7 @@ ${project.groupId} hibernate-validator-modules ${project.version} - wildfly-${wildfly.version}-patch - zip - ${project.build.directory} - - - - ${project.groupId} - hibernate-validator-modules - ${project.version} - wildfly-${wildfly-secondary.version}-patch + wildfly-${version.wildfly}-patch zip ${project.build.directory} @@ -254,7 +246,7 @@ - copy-javamoney + copy-javamoney-current pre-integration-test copy @@ -265,28 +257,15 @@ javax.money money-api - ${javax-money.version} + ${version.javax.money} ${wildfly.modules-dir}/javax/money/api/main/ org.javamoney moneta - ${moneta.version} + ${version.org.javamoney.moneta} ${wildfly.modules-dir}/org/javamoney/moneta/main/ - - - javax.money - money-api - ${javax-money.version} - ${wildfly-secondary.modules-dir}/javax/money/api/main/ - - - org.javamoney - moneta - ${moneta.version} - ${wildfly-secondary.modules-dir}/org/javamoney/moneta/main/ - @@ -312,23 +291,6 @@ - - - copy-wildfly-secondary-resources - pre-integration-test - - copy-resources - - - ${wildfly-secondary.modules-dir} - - - src/test/modules - true - - - - @@ -344,26 +306,10 @@ true - ${project.build.directory}/wildfly-${wildfly.version}/ + ${project.build.directory}/wildfly-${version.wildfly}/ false - patch apply ${project.build.directory}/hibernate-validator-modules-${project.version}-wildfly-${wildfly.version}-patch.zip - - - - - - apply-wildfly-secondary-patch-file - pre-integration-test - - execute-commands - - - true - ${project.build.directory}/wildfly-${wildfly-secondary.version}/ - false - - patch apply ${project.build.directory}/hibernate-validator-modules-${project.version}-wildfly-${wildfly-secondary.version}-patch.zip + patch apply ${project.build.directory}/hibernate-validator-modules-${project.version}-wildfly-${version.wildfly}-patch.zip @@ -379,8 +325,152 @@ - --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED + --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED + + + jdk10- + + (,11) + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + wildfly-secondary-integration-test + + integration-test + verify + + + + wildfly-secondary + + target/failsafe-reports/failsafe-summary-wildfly-secondary.xml + + + + + + maven-dependency-plugin + + + unpack-wildfly-secondary + pre-integration-test + + unpack + + + + + + org.wildfly + wildfly-dist + ${version.wildfly.secondary} + tar.gz + false + ${project.build.directory} + + + + + + copy-patch-secondary + pre-integration-test + + copy + + + + + + ${project.groupId} + hibernate-validator-modules + ${project.version} + wildfly-${version.wildfly.secondary}-patch + zip + ${project.build.directory} + + + + + + copy-javamoney-secondary + pre-integration-test + + copy + + + + + + javax.money + money-api + ${version.javax.money} + ${wildfly-secondary.modules-dir}/javax/money/api/main/ + + + org.javamoney + moneta + ${version.org.javamoney.moneta} + ${wildfly-secondary.modules-dir}/org/javamoney/moneta/main/ + + + + + + + + maven-resources-plugin + + + + copy-wildfly-secondary-resources + pre-integration-test + + copy-resources + + + ${wildfly-secondary.modules-dir} + + + src/test/modules + true + + + + + + + + org.wildfly.plugins + wildfly-maven-plugin + + + + apply-wildfly-secondary-patch-file + pre-integration-test + + execute-commands + + + true + ${project.build.directory}/wildfly-${version.wildfly.secondary}/ + false + + patch apply ${project.build.directory}/hibernate-validator-modules-${project.version}-wildfly-${version.wildfly.secondary}-patch.zip + + + + + + + + diff --git a/integration/src/test/java/org/hibernate/validator/integration/wildfly/MyConstraintMappingContributor.java b/integration/src/test/java/org/hibernate/validator/integration/wildfly/MyConstraintMappingContributor.java index 991438e6a7..fec466f1a9 100644 --- a/integration/src/test/java/org/hibernate/validator/integration/wildfly/MyConstraintMappingContributor.java +++ b/integration/src/test/java/org/hibernate/validator/integration/wildfly/MyConstraintMappingContributor.java @@ -6,8 +6,6 @@ */ package org.hibernate.validator.integration.wildfly; -import java.lang.annotation.ElementType; - import org.hibernate.validator.cfg.defs.NotNullDef; import org.hibernate.validator.spi.cfg.ConstraintMappingContributor; @@ -20,7 +18,7 @@ public class MyConstraintMappingContributor implements ConstraintMappingContribu public void createConstraintMappings(ConstraintMappingBuilder builder) { builder.addConstraintMapping() .type( Broomstick.class ) - .property( "brand", ElementType.FIELD ) + .field( "brand" ) .constraint( new NotNullDef() ); } } diff --git a/integration/src/test/java/org/hibernate/validator/integration/wildfly/jpa/JPATraversableResolverIT.java b/integration/src/test/java/org/hibernate/validator/integration/wildfly/jpa/JPATraversableResolverIT.java new file mode 100644 index 0000000000..a07d7ee8df --- /dev/null +++ b/integration/src/test/java/org/hibernate/validator/integration/wildfly/jpa/JPATraversableResolverIT.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.integration.wildfly.jpa; + +import static org.assertj.core.api.Assertions.assertThat; + +import javax.validation.TraversableResolver; + +import org.hibernate.validator.integration.AbstractArquillianIT; +import org.hibernate.validator.internal.engine.resolver.JPATraversableResolver; +import org.hibernate.validator.internal.engine.resolver.TraversableResolvers; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.asset.Asset; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.descriptor.api.Descriptors; +import org.jboss.shrinkwrap.descriptor.api.persistence20.PersistenceDescriptor; +import org.testng.annotations.Test; + +/** + * Tests that the default {@link TraversableResolver} for a JPA environment is {@code JPATraversableResolver}. + * + * @author Guillaume Smet + */ +public class JPATraversableResolverIT extends AbstractArquillianIT { + + private static final String WAR_FILE_NAME = JPATraversableResolverIT.class.getSimpleName() + ".war"; + + @Deployment + public static Archive createTestArchive() { + return buildTestArchive( WAR_FILE_NAME ) + .addAsResource( persistenceXml(), "META-INF/persistence.xml" ) + .addAsWebInfResource( EmptyAsset.INSTANCE, "beans.xml" ); + } + + private static Asset persistenceXml() { + String persistenceXml = Descriptors.create( PersistenceDescriptor.class ) + .version( "2.0" ) + .createPersistenceUnit() + .name( "default" ) + .jtaDataSource( "java:jboss/datasources/ExampleDS" ) + .up() + .exportAsString(); + return new StringAsset( persistenceXml ); + } + + @Test + public void testDefaultTraversableResolverInJPAEnvironmentIsJPATraversableResolver() throws Exception { + assertThat( TraversableResolvers.getDefault() ).isInstanceOf( JPATraversableResolver.class ); + } +} diff --git a/integration/src/test/java/org/hibernate/validator/integration/wildfly/jpa/WandConstraintMappingContributor.java b/integration/src/test/java/org/hibernate/validator/integration/wildfly/jpa/WandConstraintMappingContributor.java index 02003f8995..43506d5ba0 100644 --- a/integration/src/test/java/org/hibernate/validator/integration/wildfly/jpa/WandConstraintMappingContributor.java +++ b/integration/src/test/java/org/hibernate/validator/integration/wildfly/jpa/WandConstraintMappingContributor.java @@ -6,8 +6,6 @@ */ package org.hibernate.validator.integration.wildfly.jpa; -import java.lang.annotation.ElementType; - import org.hibernate.validator.cfg.defs.SizeDef; import org.hibernate.validator.spi.cfg.ConstraintMappingContributor; @@ -20,7 +18,7 @@ public class WandConstraintMappingContributor implements ConstraintMappingContri public void createConstraintMappings(ConstraintMappingBuilder builder) { builder.addConstraintMapping() .type( Wand.class ) - .property( "brand", ElementType.FIELD ) + .field( "brand" ) .constraint( new SizeDef().min( 5 ) ); } } diff --git a/integration/src/test/modules/javax/money/api/main/module.xml b/integration/src/test/modules/javax/money/api/main/module.xml index 9602e1a952..75fdeff223 100644 --- a/integration/src/test/modules/javax/money/api/main/module.xml +++ b/integration/src/test/modules/javax/money/api/main/module.xml @@ -7,6 +7,6 @@ --> - + diff --git a/integration/src/test/modules/org/javamoney/moneta/main/module.xml b/integration/src/test/modules/org/javamoney/moneta/main/module.xml index 801109908e..710be343b0 100644 --- a/integration/src/test/modules/org/javamoney/moneta/main/module.xml +++ b/integration/src/test/modules/org/javamoney/moneta/main/module.xml @@ -7,7 +7,7 @@ --> - + diff --git a/integration/src/test/resources/arquillian.xml b/integration/src/test/resources/arquillian.xml index 0802cd63a1..9116248ee6 100644 --- a/integration/src/test/resources/arquillian.xml +++ b/integration/src/test/resources/arquillian.xml @@ -22,9 +22,9 @@ REMOTE - ${basedir}/target/wildfly-${wildfly.version} + ${basedir}/target/wildfly-${version.wildfly} - -Dee8.preview.mode=true ${arquillian.javaVmArguments.add-opens} + -Dee8.preview.mode=true ${arquillian.wildfly.jvm.args} - ${project.build.directory}/wildfly-original/wildfly-${wildfly.version} - ${project.build.directory}/wildfly-patched/wildfly-${wildfly.version} + ${project.build.directory}/wildfly-original/wildfly-${version.wildfly} + ${project.build.directory}/wildfly-patched/wildfly-${version.wildfly} ${project.build.directory}/wildfly-current-hv-patch.zip - ${project.build.directory}/wildfly-original/wildfly-${wildfly-secondary.version} - ${project.build.directory}/wildfly-patched/wildfly-${wildfly-secondary.version} + ${project.build.directory}/wildfly-original/wildfly-${version.wildfly.secondary} + ${project.build.directory}/wildfly-patched/wildfly-${version.wildfly.secondary} ${project.build.directory}/wildfly-secondary-hv-patch.zip - - .. @@ -105,13 +100,13 @@ ${wildfly-main.patch-file} zip - wildfly-${wildfly.version}-patch + wildfly-${version.wildfly}-patch ${wildfly-secondary.patch-file} zip - wildfly-${wildfly-secondary.version}-patch + wildfly-${version.wildfly.secondary}-patch @@ -132,7 +127,7 @@ - + ${wildfly-main.patched.target-dir} @@ -148,7 +143,7 @@ - + ${wildfly-secondary.patched.target-dir} @@ -173,7 +168,7 @@ org.wildfly wildfly-dist - ${wildfly.version} + ${version.wildfly} tar.gz false ${project.build.directory}/wildfly-original @@ -181,7 +176,7 @@ org.wildfly wildfly-dist - ${wildfly.version} + ${version.wildfly} tar.gz false ${project.build.directory}/wildfly-patched @@ -190,7 +185,7 @@ org.wildfly wildfly-dist - ${wildfly-secondary.version} + ${version.wildfly.secondary} tar.gz false ${project.build.directory}/wildfly-original @@ -198,7 +193,7 @@ org.wildfly wildfly-dist - ${wildfly-secondary.version} + ${version.wildfly.secondary} tar.gz false ${project.build.directory}/wildfly-patched @@ -217,13 +212,13 @@ - javax.validation - validation-api - ${bv.api.version} + jakarta.validation + jakarta.validation-api + ${version.jakarta.validation-api} false ${wildfly-main.patched.target-dir}/modules/system/layers/base/javax/validation/api/main - validation-api-${bv.api.version}.jar + jakarta.validation-api-${version.jakarta.validation-api}.jar ${project.groupId} @@ -243,13 +238,13 @@ - javax.validation - validation-api - ${bv.api.version} + jakarta.validation + jakarta.validation-api + ${version.jakarta.validation-api} false ${wildfly-secondary.patched.target-dir}/modules/system/layers/base/javax/validation/api/main - validation-api-${bv.api.version}.jar + jakarta.validation-api-${version.jakarta.validation-api}.jar ${project.groupId} @@ -284,7 +279,6 @@ generate-patch - ${patch-gen-maven-plugin.argLine.add-opens} ${wildfly-main.original.target-dir} ${module.xml.targetdir}/wildfly-current-patch.xml ${wildfly-main.patched.target-dir} @@ -298,7 +292,6 @@ generate-patch - ${patch-gen-maven-plugin.argLine.add-opens} ${wildfly-secondary.original.target-dir} ${module.xml.targetdir}/wildfly-secondary-patch.xml ${wildfly-secondary.patched.target-dir} @@ -309,16 +302,4 @@ - - - - jdk9 - - [9,) - - - --add-opens=java.base/java.lang=ALL-UNNAMED - - - diff --git a/modules/src/main/modules/wildfly-current-patch.xml b/modules/src/main/modules/wildfly-current-patch.xml index 3f8f6178f2..5b759320a2 100644 --- a/modules/src/main/modules/wildfly-current-patch.xml +++ b/modules/src/main/modules/wildfly-current-patch.xml @@ -6,11 +6,11 @@ ~ See the license.txt file in the root directory or . --> - wildfly-${wildfly.version}-hibernate-validator-${project.version} - This patch upgrades Hibernate Validator to ${project.version} within a WildFly ${wildfly.version} installation - + wildfly-${version.wildfly}-hibernate-validator-${project.version} + This patch upgrades Hibernate Validator to ${project.version} within a WildFly ${version.wildfly} installation + - This patch upgrades Hibernate Validator to ${project.version} within a WildFly ${wildfly.version} installation + This patch upgrades Hibernate Validator to ${project.version} within a WildFly ${version.wildfly} installation diff --git a/modules/src/main/modules/wildfly-secondary-patch.xml b/modules/src/main/modules/wildfly-secondary-patch.xml index 8692b9b7f8..5700566477 100644 --- a/modules/src/main/modules/wildfly-secondary-patch.xml +++ b/modules/src/main/modules/wildfly-secondary-patch.xml @@ -6,11 +6,11 @@ ~ See the license.txt file in the root directory or . --> - wildfly-${wildfly-secondary.version}-hibernate-validator-${project.version} - This patch upgrades Hibernate Validator to ${project.version} within a WildFly ${wildfly-secondary.version} installation - + wildfly-${version.wildfly.secondary}-hibernate-validator-${project.version} + This patch upgrades Hibernate Validator to ${project.version} within a WildFly ${version.wildfly.secondary} installation + - This patch upgrades Hibernate Validator to ${project.version} within a WildFly ${wildfly-secondary.version} installation + This patch upgrades Hibernate Validator to ${project.version} within a WildFly ${version.wildfly.secondary} installation diff --git a/modules/src/script/setupModules.groovy b/modules/src/script/setupModules.groovy index f4763c3c88..dbc2d2ea48 100644 --- a/modules/src/script/setupModules.groovy +++ b/modules/src/script/setupModules.groovy @@ -17,15 +17,19 @@ def appendDependency(File file, String dependencyToAppend, boolean optional) { file.write( file.text.replaceAll( /<\/dependencies>/, ' \n ' ) ) } -// BV API +def removeDependency(File file, String dependencyToRemove) { + file.write( file.text.replaceAll( //, '' ) ) +} + +// Jakarta Bean Validation API bvModuleXml = new File( wildflyPatchedTargetDir, 'modules/system/layers/base/javax/validation/api/main/module.xml' ) -def bvArtifactName = 'validation-api-' + project.properties['bv.api.version'] + '.jar'; -println "[INFO] Using BV version " + bvArtifactName; +def bvArtifactName = 'jakarta.validation-api-' + project.properties['version.jakarta.validation-api'] + '.jar'; +println "[INFO] Using Jakarta Bean Validation version " + bvArtifactName; processFileInplace( bvModuleXml ) { text -> - text.replaceAll( /validation-api.*jar/, bvArtifactName ) + text.replaceAll( / text.replaceAll( /hibernate-validator.*jar/, hvArtifactName ) } + +removeDependency( hvModuleXml, "org.apache.xerces" ) +appendDependency( hvModuleXml, "javax.xml.stream.api", false ) +appendDependency( hvModuleXml, "javax.api", false ) + appendDependency( hvModuleXml, "javax.money.api", true ) appendDependency( hvModuleXml, "javafx.api", true ) diff --git a/osgi/felixtest/pom.xml b/osgi/felixtest/pom.xml index 7ce49d4fb6..812ae75546 100644 --- a/osgi/felixtest/pom.xml +++ b/osgi/felixtest/pom.xml @@ -10,7 +10,7 @@ org.hibernate.validator hibernate-validator-osgi - 6.0.10-SNAPSHOT + 6.1.0.Final ../pom.xml @@ -28,7 +28,7 @@ org.jboss.arquillian arquillian-bom - ${arquillian.version} + ${version.org.jboss.arquillian} import pom @@ -48,13 +48,13 @@ - javax.validation - validation-api + jakarta.validation + jakarta.validation-api provided - javax.enterprise - cdi-api + jakarta.enterprise + jakarta.enterprise.cdi-api provided @@ -76,7 +76,7 @@ fish.payara.arquillian arquillian-payara-server-4-managed - ${payara-arquillian.version} + ${version.fish.payara.arquillian} test @@ -131,7 +131,7 @@ fish.payara.distributions payara - ${payara.version} + ${version.fish.payara} zip false ${project.build.directory} @@ -165,6 +165,15 @@ hibernate-validator-cdi.jar true + + jakarta.validation + jakarta.validation-api + ${bv.api.version} + jar + ${project.build.directory}/payara5/glassfish/modules + validation-api.jar + true + diff --git a/osgi/integrationtest/pom.xml b/osgi/integrationtest/pom.xml index 869857cb10..416d3debcd 100644 --- a/osgi/integrationtest/pom.xml +++ b/osgi/integrationtest/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-osgi - 6.0.10-SNAPSHOT + 6.1.0.Final ../pom.xml @@ -31,8 +31,8 @@ - javax.validation - validation-api + jakarta.validation + jakarta.validation-api test @@ -64,6 +64,18 @@ org.javamoney moneta test + + + javax.annotation + javax.annotation-api + + + + + + jakarta.annotation + jakarta.annotation-api + test @@ -77,13 +89,13 @@ org.ops4j.pax.exam pax-exam-container-karaf - ${pax.exam.version} + ${version.org.ops4j.pax.exam} test org.apache.karaf apache-karaf - ${apache.karaf.version} + ${version.org.apache.karaf} tar.gz test + + + + org.codehaus.mojo + build-helper-maven-plugin + + + + + + hv-6.1 + + + validator + hv-6.1 + + + + Hibernate Validator + 6.1.0.Alpha4 + javax.validation @@ -202,13 +243,15 @@ + 2.0.1.Final Hibernate Validator - 6.0.8.Final + 6.0.16.Final javax.validation validation-api + ${validation-api.version} ${project.groupId} @@ -218,6 +261,7 @@ org.glassfish javax.el + 3.0.1-b11 log4j @@ -245,7 +289,7 @@ 1.1.0.Final Hibernate Validator - 5.4.2.Final + 5.4.3.Final @@ -261,6 +305,7 @@ org.glassfish javax.el + 3.0.1-b11 log4j diff --git a/performance/src/main/java-bv2/org/hibernate/validator/performance/multilevel/MultiLevelContainerValidation.java b/performance/src/main/java-bv2/org/hibernate/validator/performance/multilevel/MultiLevelContainerValidation.java index 636a4d4205..fb5175b625 100644 --- a/performance/src/main/java-bv2/org/hibernate/validator/performance/multilevel/MultiLevelContainerValidation.java +++ b/performance/src/main/java-bv2/org/hibernate/validator/performance/multilevel/MultiLevelContainerValidation.java @@ -13,6 +13,7 @@ import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.Random; @@ -189,7 +190,7 @@ public EmailAddress(String value) { public static EmailAddress generate() { return new EmailAddress( - String.format( "%s@%s.com", RandomDataGenerator.randomString(), RandomDataGenerator.randomString() ) + String.format( Locale.ROOT, "%s@%s.com", RandomDataGenerator.randomString(), RandomDataGenerator.randomString() ) ); } diff --git a/performance/src/main/java-bv2/org/hibernate/validator/performance/simple/SimpleComposingConstraintValidation.java b/performance/src/main/java-bv2/org/hibernate/validator/performance/simple/SimpleComposingConstraintValidation.java new file mode 100644 index 0000000000..46c21ce1bc --- /dev/null +++ b/performance/src/main/java-bv2/org/hibernate/validator/performance/simple/SimpleComposingConstraintValidation.java @@ -0,0 +1,104 @@ +/* + * 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.performance.simple; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import javax.validation.Constraint; +import javax.validation.ConstraintViolation; +import javax.validation.Payload; +import javax.validation.ReportAsSingleViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +/** + * @author Marko Bekhta + */ +public class SimpleComposingConstraintValidation { + + @State(Scope.Benchmark) + public static class ValidationState { + + public volatile Validator validator; + + { + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + validator = factory.getValidator(); + } + + } + + @Benchmark + @BenchmarkMode(Mode.Throughput) + @OutputTimeUnit(TimeUnit.MILLISECONDS) + @Fork(value = 1) + @Threads(50) + @Warmup(iterations = 10) + @Measurement(iterations = 20) + public void testSimpleComposingConstraintValidation(ValidationState state, Blackhole bh) { + Foo foo = new Foo( "" ); + Set> violations = state.validator.validate( foo ); + bh.consume( violations ); + } + + public static class Foo { + + @ComposingConstraint + private final String foo; + + public Foo(String foo) { + this.foo = foo; + } + } + + @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) + @Retention(RUNTIME) + @Documented + @Constraint(validatedBy = { }) + @ReportAsSingleViolation + @NotNull + @Size(min = 1) + @NotBlank + @NotEmpty + @interface ComposingConstraint { + + String message() default "message"; + + Class[] groups() default { }; + + Class[] payload() default { }; + } +} diff --git a/performance/src/main/java/org/hibernate/validator/performance/simple/SimpleValidation.java b/performance/src/main/java/org/hibernate/validator/performance/simple/SimpleValidation.java index 98671afb86..740318cdd4 100644 --- a/performance/src/main/java/org/hibernate/validator/performance/simple/SimpleValidation.java +++ b/performance/src/main/java/org/hibernate/validator/performance/simple/SimpleValidation.java @@ -6,10 +6,8 @@ */ package org.hibernate.validator.performance.simple; -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Random; import java.util.Set; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import javax.validation.ConstraintViolation; @@ -53,14 +51,23 @@ public class SimpleValidation { @State(Scope.Benchmark) public static class ValidationState { public volatile Validator validator; - public volatile Random random; + public volatile ThreadLocalRandom random; + public volatile Driver[] drivers; { ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); validator = factory.getValidator(); - random = new Random(); + random = ThreadLocalRandom.current(); + + drivers = new Driver[100]; + for ( int i = 0; i < 100; i++ ) { + drivers[i] = new DriverSetup( random ).getDriver(); + } } + public Driver nextDriver() { + return drivers[random.nextInt( 100 )]; + } } @Benchmark @@ -71,13 +78,13 @@ public static class ValidationState { @Warmup(iterations = 10) @Measurement(iterations = 20) public void testSimpleBeanValidation(ValidationState state, Blackhole bh) { - DriverSetup driverSetup = new DriverSetup( state ); - Set> violations = state.validator.validate( driverSetup.getDriver() ); - assertThat( violations ).hasSize( driverSetup.getExpectedViolationCount() ); + Driver driver = state.nextDriver(); + Set> violations = state.validator.validate( driver ); + assert driver.getExpectedViolationCount() == violations.size(); bh.consume( violations ); } - public class Driver { + public static class Driver { @NotNull private String name; @@ -87,10 +94,17 @@ public class Driver { @AssertTrue private boolean hasDrivingLicense; - public Driver(String name, int age, boolean hasDrivingLicense) { + private int expectedViolationCount; + + public Driver(String name, int age, boolean hasDrivingLicense, int expectedViolationCount) { this.name = name; this.age = age; this.hasDrivingLicense = hasDrivingLicense; + this.expectedViolationCount = expectedViolationCount; + } + + public int getExpectedViolationCount() { + return expectedViolationCount; } @Override @@ -105,34 +119,30 @@ public String toString() { } } - private class DriverSetup { + private static class DriverSetup { private int expectedViolationCount; private Driver driver; - public DriverSetup(ValidationState state) { + public DriverSetup(ThreadLocalRandom random) { expectedViolationCount = 0; - String name = names[state.random.nextInt( 10 )]; + String name = names[random.nextInt( 10 )]; if ( name == null ) { expectedViolationCount++; } - int randomAge = state.random.nextInt( 100 ); + int randomAge = random.nextInt( 100 ); if ( randomAge < 18 ) { expectedViolationCount++; } - int rand = state.random.nextInt( 2 ); + int rand = random.nextInt( 2 ); boolean hasLicense = rand == 1; if ( !hasLicense ) { expectedViolationCount++; } - driver = new Driver( name, randomAge, hasLicense ); - } - - public int getExpectedViolationCount() { - return expectedViolationCount; + driver = new Driver( name, randomAge, hasLicense, expectedViolationCount ); } public Driver getDriver() { diff --git a/performance/src/main/java/org/hibernate/validator/performance/statistical/TestEntity.java b/performance/src/main/java/org/hibernate/validator/performance/statistical/TestEntity.java index 7d484f5bd8..9bca4a9df5 100644 --- a/performance/src/main/java/org/hibernate/validator/performance/statistical/TestEntity.java +++ b/performance/src/main/java/org/hibernate/validator/performance/statistical/TestEntity.java @@ -7,9 +7,13 @@ package org.hibernate.validator.performance.statistical; import java.math.BigDecimal; +import java.time.ZoneId; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.TimeZone; + import javax.validation.Valid; import javax.validation.constraints.AssertFalse; import javax.validation.constraints.AssertTrue; @@ -28,7 +32,7 @@ */ public class TestEntity { public static final int MAX_DEPTH = 10; - private static final Calendar cal = GregorianCalendar.getInstance(); + private static final Calendar cal = GregorianCalendar.getInstance( TimeZone.getTimeZone( ZoneId.of( "GMT" ) ), Locale.ROOT ); public TestEntity(int depth) { if ( depth <= MAX_DEPTH ) { diff --git a/performance/src/main/java/org/hibernate/validator/performance/unconstrained/UnconstrainedBeanValidation.java b/performance/src/main/java/org/hibernate/validator/performance/unconstrained/UnconstrainedBeanValidation.java new file mode 100644 index 0000000000..70f8c4c804 --- /dev/null +++ b/performance/src/main/java/org/hibernate/validator/performance/unconstrained/UnconstrainedBeanValidation.java @@ -0,0 +1,127 @@ +/* + * 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.performance.unconstrained; + +import java.util.Set; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +/** + * @author Guillaume Smet + */ +public class UnconstrainedBeanValidation { + + private static final String[] names = { + null, + "Jacob", + "Isabella", + "Ethan", + "Sophia", + "Michael", + "Emma", + "Jayden", + "Olivia", + "William" + }; + + @State(Scope.Benchmark) + public static class ValidationState { + public volatile Validator validator; + public volatile ThreadLocalRandom random; + private volatile Driver[] drivers; + + { + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + validator = factory.getValidator(); + random = ThreadLocalRandom.current(); + + drivers = new Driver[100]; + for ( int i = 0; i < 100; i++ ) { + drivers[i] = new DriverSetup( random ).getDriver(); + } + } + + public Driver nextDriver() { + return drivers[random.nextInt( 100 )]; + } + } + + @Benchmark + @BenchmarkMode(Mode.Throughput) + @OutputTimeUnit(TimeUnit.MILLISECONDS) + @Fork(value = 1) + @Threads(50) + @Warmup(iterations = 10) + @Measurement(iterations = 20) + public void testUnconstrainedBeanValidation(ValidationState state, Blackhole bh) { + Set> violations = state.validator.validate( state.nextDriver() ); + bh.consume( violations ); + } + + public static class Driver { + + private String name; + + private int age; + + private boolean hasDrivingLicense; + + public Driver(String name, int age, boolean hasDrivingLicense) { + this.name = name; + this.age = age; + this.hasDrivingLicense = hasDrivingLicense; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append( "Driver" ); + sb.append( "{name='" ).append( name ).append( '\'' ); + sb.append( ", age=" ).append( age ); + sb.append( ", hasDrivingLicense=" ).append( hasDrivingLicense ); + sb.append( '}' ); + return sb.toString(); + } + } + + private static class DriverSetup { + + private Driver driver; + + public DriverSetup(ThreadLocalRandom random) { + String name = names[random.nextInt( 10 )]; + + int randomAge = random.nextInt( 100 ); + + int rand = random.nextInt( 2 ); + boolean hasLicense = rand == 1; + + driver = new Driver( name, randomAge, hasLicense ); + } + + public Driver getDriver() { + return driver; + } + } +} diff --git a/pom.xml b/pom.xml index bc05e88713..e9f8fbcfb0 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.1.0.Final pom Hibernate Validator Aggregator @@ -86,144 +86,203 @@ test-utils build-config engine - cdi - modules tck-runner annotation-processor - integration performance - osgi - 1.8 - 1.8 + + + 6.0.10.Final + + + + http://beanvalidation.org/2.0/spec/ + http://docs.oracle.com/javase/8/docs/api + http://docs.oracle.com/javase/8/docs/technotes + http://docs.oracle.com/javaee/7/api + http://docs.oracle.com/javase/8/javase-clienttechnologies.htm + http://docs.jboss.org/hibernate/beanvalidation/spec/2.0/api + http://javamoney.github.io/apidocs + org.hibernate.validator org.hibernate.validator.cdi - - org.hibernate.validator.hibernate-validator - org.hibernate.validator.hibernate-validator-cdi - - UTF-8 - UTF-8 - - true + - 2.0.1.Final - 2.0.2.Final + 2.0.2 + 2.0.5 - - 6.0.4.Final - - 2.8 - 3.0.1-b09 - 3.3.2.Final - 2.1.0.Final + 2.8 + 3.0.3 + 3.3.2.Final + 2.1.0.Final - 12.0.0.Final + 18.0.0.Final - 11.0.0.Final + 17.0.1.Final - ${wildfly.version} + ${version.wildfly} - 1.3.4 - 1.8.3 - 2.9.7 - 1.7.22 - 1.0.2.Final + 1.3.4 + 1.8.3 + 2.9.7 + 1.7.22 + 1.2.17 + 2.2.3 - 2.0 - 3.0.3.Final - 2.1.0.Final - 1.0.1.Final + 2.0.1 + 3.1.1.Final + 2.2.0.Final + 3.2.5 + 1.2.4 + 1.3.5 - 1.0.1 - 1.1 - - 1.2 + 1.0.1 + 1.1 + + + 1.2 - - 2.0.1.Alpha5 - 5.0.3 - 1.2.1.Final - 4.0.0.Final + + 11.0.2 - 1.1.11.Final - 6.8 + 1.1.11.Final + 6.8 - 3.8.0 - 4.12 - 3.4 + 3.8.0 + 4.12 + 3.4 + 2.4.12 + 27.1-jre + 4.3.10.RELEASE + 1.0.0.Final + 2.9.10 + 2.9.10 + 1.10.2 - 4.11.0 - 2.5.2 - 4.1.1 - 6.0.0 - 5.Beta1 - 1.0.Beta2 + 4.2.0 + 4.12.0 + 2.5.2 + 6.0.0 + 5.181 + 1.0.Beta3 - 8.1 + + 8.18 - 2.4.12 + - 23.0 + 1.5.6 + 1.0.2.Final + 1.0.3.Final + 9.1.15.0 + 1.6.0-alpha.5 + 1.5.0-alpha.16 - 4.3.10.RELEASE + - - 1.0.1.Final - 1.0.3.Final - 1.5.5 - 9.1.8.0 - 1.6.0-alpha.5 - 1.5.0-alpha.16 + 1.8 + 3.1.0 + 3.0.0 + 3.5.0 + 3.0.0 + 3.0.0 + 3.8.1 + 0.0.6 + 3.0.2 + 1.4.0 + 2.8.2 + 3.0.0-M1 + 2.5 + 1.6 + 2.5.2 + 0.11.0 + 3.0.2 + 1.3.0 + 3.0.1 + 3.0 + 2.5.3 + 3.0.2 + 3.1.0 + 1.2 + 3.0.1 + 2.21.0 + ${version.surefire.plugin} + 1.2.1.Final + 5.0.0.Final + 2.0.1.Final + 5.0.3 - - http://beanvalidation.org/2.0/spec/ - http://docs.oracle.com/javase/8/docs/api - http://docs.oracle.com/javase/8/docs/technotes - http://docs.oracle.com/javaee/7/api - http://docs.oracle.com/javase/8/javase-clienttechnologies.htm - http://docs.jboss.org/hibernate/beanvalidation/spec/2.0/api - http://javamoney.github.io/apidocs + + forbidden-junit.txt + + - 2.20.1 + jboss-releases-repository + https://repository.jboss.org/nexus/service/local/staging/deploy/maven2/ + jboss-snapshots-repository + https://repository.jboss.org/nexus/content/repositories/snapshots/ - - - - + + . + + UTF-8 + UTF-8 + + + true + + 1.8 + 1.8 + ${maven.compiler.target} + ${maven.compiler.source} - java.xml.bind - java.xml.bind + ${maven.compiler.target} + ${maven.compiler.source} + ${maven.compiler.testTarget} + ${maven.compiler.testSource} - - + 10 - - forbidden-junit.txt + + + + + ${surefire.jvm.args.additional} ${surefire.jvm.args.add-opens} ${surefire.jvm.args.illegal-access} + ${surefire.jvm.args.additional} ${surefire.jvm.args.add-opens} ${surefire.jvm.args.illegal-access} - . + + + ${arquillian.wildfly.jvm.args.add-opens} ${arquillian.wildfly.jvm.args.add-modules} + + + ${maven.compiler.argument.source} + 3.3.1 @@ -252,162 +311,167 @@ ${project.groupId} hibernate-validator-modules ${project.version} - wildfly-${wildfly.version}-patch + wildfly-${version.wildfly}-patch zip ${project.groupId} hibernate-validator-modules ${project.version} - wildfly-${wildfly-secondary.version}-patch + wildfly-${version.wildfly.secondary}-patch zip - javax.validation - validation-api - ${bv.api.version} + jakarta.validation + jakarta.validation-api + ${version.jakarta.validation-api} org.jboss.logging jboss-logging - ${jboss.logging.version} + ${version.org.jboss.logging.jboss-logging} org.jboss.logging jboss-logging-processor - ${jboss.logging.processor.version} + ${version.org.jboss.logging.jboss-logging-tools} org.jboss.logging jboss-logging-annotations - ${jboss.logging.processor.version} + ${version.org.jboss.logging.jboss-logging-tools} org.glassfish - javax.el - ${javax.el.version} + jakarta.el + ${version.org.glassfish.jakarta.el} com.fasterxml classmate - ${classmate.version} + ${version.com.fasterxml.classmate} joda-time joda-time - ${joda-time.version} + ${version.joda-time} javax.money money-api - ${javax-money.version} + ${version.javax.money} org.javamoney moneta - ${moneta.version} + ${version.org.javamoney.moneta} + + + net.bytebuddy + byte-buddy + ${version.net.bytebuddy.byte-buddy} org.osgi org.osgi.core - ${osgi-core.version} + ${version.org.osgi.core} org.jsoup jsoup - ${jsoup.version} + ${version.jsoup} log4j log4j - 1.2.17 + ${version.log4j} org.slf4j slf4j-api - ${slf4j.version} + ${version.org.slf4j} org.slf4j slf4j-log4j12 - ${slf4j.version} + ${version.org.slf4j} - org.hibernate.javax.persistence - hibernate-jpa-2.1-api - ${hibernate-jpa-2.1-api.version} + jakarta.persistence + jakarta.persistence-api + ${version.jakarta.persistence-api} junit junit - ${junit.version} + ${version.junit} org.testng testng - ${testng.version} + ${version.org.testng} org.codehaus.groovy groovy-jsr223 - ${groovy.version} + ${version.org.codehaus.groovy} org.easymock easymock - ${easymock.version} + ${version.org.easymock} org.assertj assertj-core - ${assertj-core.version} + ${version.org.assertj.assertj-core} org.jboss.arquillian arquillian-bom - ${arquillian.version} + ${version.org.jboss.arquillian} pom import - javax.annotation - javax.annotation-api - 1.2 + jakarta.annotation + jakarta.annotation-api + ${version.jakarta.annotation-api} - org.jboss.spec.javax.interceptor - jboss-interceptors-api_1.2_spec - 1.0.0.Final + jakarta.interceptor + jakarta.interceptor-api + ${version.jakarta.interceptor-api} - org.jboss.spec.javax.ejb - jboss-ejb-api_3.2_spec - ${jboss-ejb-api_3.2_spec.version} + jakarta.ejb + jakarta.ejb-api + ${version.jakarta.ejb-api} - javax.enterprise - cdi-api - ${cdi-api.version} + jakarta.enterprise + jakarta.enterprise.cdi-api + ${version.jakarta.enterprise.cdi-api} - javax.interceptor - javax.interceptor-api + jakarta.interceptor + jakarta.interceptor-api - javax.el - javax.el-api + jakarta.el + jakarta.el-api org.jboss.weld weld-core-impl - ${weld.version} + ${version.org.jboss.weld.weld} org.wildfly.arquillian wildfly-arquillian-container-managed - ${wildfly-arquillian.version} + ${version.org.wildfly.arquillian} sun.jdk @@ -418,38 +482,46 @@ org.jboss.arquillian.container arquillian-weld-se-embedded-1.1 - 1.0.0.Final + ${version.org.jboss.arquillian.container.arquillian-weld-se-embedded-1.1} + test com.thoughtworks.paranamer paranamer - ${paranamer.version} + ${version.com.thoughtworks.paranamer} com.google.guava guava - ${guava.version} + ${version.com.google.guava} + test org.springframework spring-expression - ${spring-expression.version} + ${version.org.springframework.spring-expression} + test + + + com.fasterxml.jackson.core + jackson-databind + ${version.com.fasterxml.jackson.core.jackson-databind} + test + + + com.fasterxml.jackson.core + jackson-annotations + ${version.com.fasterxml.jackson.core.jackson-annotations} + test - - - org.apache.maven.wagon - wagon-webdav - 1.0-beta-2 - - maven-enforcer-plugin - 3.0.0-M1 + ${version.enforcer.plugin} enforce-java @@ -459,11 +531,21 @@ - [1.8.0-20,) + [${jdk.min.version},) - 3.3.1 + ${maven.min.version} + + + javax.validation:validation-api + org.glassfish:javax.el + javax.annotation:javax.annotation-api + org.jboss.spec.javax.interceptor:jboss-interceptors-api_1.2_spec + javax.enterprise:cdi-api + javax.persistence:javax.persistence-api + + @@ -478,15 +560,15 @@ maven-antrun-plugin - 1.8 + ${version.antrun.plugin} maven-clean-plugin - 3.0.0 + ${version.clean.plugin} maven-jar-plugin - 3.0.2 + ${version.jar.plugin} @@ -501,8 +583,12 @@ maven-compiler-plugin - 3.7.0 + ${version.compiler.plugin} + ${maven.compiler.argument.source} + ${maven.compiler.argument.target} + ${maven.compiler.argument.testSource} + ${maven.compiler.argument.testTarget} -Aorg.jboss.logging.tools.addGeneratedAnnotation=false @@ -511,7 +597,7 @@ maven-checkstyle-plugin - 2.17 + ${version.checkstyle.plugin} ${project.groupId} @@ -532,12 +618,12 @@ org.slf4j jcl-over-slf4j - ${slf4j.version} + ${version.org.slf4j} org.slf4j slf4j-jdk14 - ${slf4j.version} + ${version.org.slf4j} @@ -573,8 +659,9 @@ de.thetaphi forbiddenapis - 2.4.1 + ${version.forbiddenapis.plugin} + ${forbiddenapis.jdk.target} false @@ -606,13 +693,11 @@ verify - + jdk-unsafe jdk-deprecated - jdk-system-out + jdk-non-portable + jdk-internal @@ -624,10 +709,6 @@ verify - jdk-deprecated @@ -637,7 +718,7 @@ com.mycila license-maven-plugin - 3.0 + ${version.license.plugin}

    ${hibernate-validator-parent.path}/build-config/src/main/resources/license.header
    true @@ -675,19 +756,19 @@ maven-surefire-plugin - ${maven-surefire-plugin.version} + ${version.surefire.plugin} once true **/*Test.java - ${maven-surefire-plugin.argLine} + ${surefire.jvm.args} maven-surefire-report-plugin - ${maven-surefire-plugin.version} + ${version.surefire.plugin} generate-test-report @@ -704,22 +785,22 @@ maven-failsafe-plugin - ${maven-surefire-plugin.version} + ${version.failsafe.plugin} - ${maven-surefire-plugin.argLine} ${maven-surefire-plugin.argLine.add-modules} ${maven-surefire-plugin.argLine.add-opens} + ${failsafe.jvm.args} maven-dependency-plugin - 3.0.2 + ${version.dependency.plugin} maven-install-plugin - 2.5.2 + ${version.install.plugin} maven-assembly-plugin - 3.1.0 + ${version.assembly.plugin} - 1.5.0 - - - org.codehaus.mojo - jaxb2-maven-plugin - 2.2 - org.asciidoctor asciidoctor-maven-plugin - ${asciidoctor-maven-plugin.version} + ${version.asciidoctor.plugin} org.jruby jruby-complete - ${jruby.version} + ${version.org.jruby} org.asciidoctor asciidoctorj - ${asciidoctorj.version} + ${version.org.asciidoctor.asciidoctorj} org.asciidoctor asciidoctorj-pdf - ${asciidoctorj-pdf.version} + ${version.org.asciidoctor.asciidoctorj-pdf} org.hibernate.infra hibernate-asciidoctor-extensions - ${hibernate-asciidoctor-extensions.version} + ${version.org.hibernate.infra.hibernate-asciidoctor-extensions} ch.mfrey.maven.plugin copy-maven-plugin - 0.0.6 - - - maven-project-info-reports-plugin - 2.9 + ${version.copy.plugin} org.apache.felix maven-bundle-plugin - 3.5.0 + ${version.bundle.plugin} maven-source-plugin - 3.0.1 + ${version.source.plugin} maven-javadoc-plugin - 3.0.0 + ${version.javadoc.plugin} true true @@ -821,11 +884,11 @@ maven-deploy-plugin - 2.8.2 + ${version.deploy.plugin} maven-resources-plugin - 3.0.2 + ${version.resources.plugin} @@ -838,24 +901,24 @@ org.codehaus.gmavenplus gmavenplus-plugin - 1.6 + ${version.gmavenplus.plugin} org.codehaus.groovy groovy-all - ${groovy.version} + ${version.org.codehaus.groovy} org.apache.servicemix.tooling depends-maven-plugin - 1.4.0 + ${version.depends.plugin} org.codehaus.mojo build-helper-maven-plugin - 1.12 + ${version.buildhelper.plugin} com.github.siom79.japicmp japicmp-maven-plugin - 0.11.0 + ${version.japicmp.plugin} @@ -892,50 +955,43 @@ org.apache.maven.plugins maven-shade-plugin - 3.1.0 + ${version.shade.plugin} org.jboss.as patch-gen-maven-plugin - ${wildfly-patch-gen-maven-plugin.version} + ${version.wildfly-patch-gen.plugin} com.fasterxml.woodstox woodstox-core - ${wildfly-patch-gen-maven-plugin.woodstox.version} + ${version.wildfly-patch-gen.plugin.woodstox} org.wildfly.plugins wildfly-maven-plugin - ${wildfly-maven-plugin.version} + ${version.wildfly.plugin} org.wildfly.core wildfly-patching - ${wildfly-core.version} + ${version.org.wildfly.core} - org.wildfly.core wildfly-cli - ${wildfly-core.version} - - - sun.jdk - jconsole - - + ${version.org.wildfly.core} org.netbeans.tools sigtest-maven-plugin - 1.0 + ${version.sigtest.plugin} @@ -1064,18 +1120,18 @@ scm:git:git@github.com:hibernate/hibernate-validator.git http://github.com/hibernate/hibernate-validator HEAD - + - jboss-releases-repository + ${jboss.releases.repo.id} JBoss Releases Repository - https://repository.jboss.org/nexus/service/local/staging/deploy/maven2/ + ${jboss.releases.repo.url} - jboss-snapshots-repository + ${jboss.snapshots.repo.id} JBoss Snapshots Repository - https://repository.jboss.org/nexus/content/repositories/snapshots/ + ${jboss.snapshots.repo.url} @@ -1087,6 +1143,8 @@ disableDocumentationBuild !true + + (,12) documentation @@ -1099,6 +1157,8 @@ disableDistributionBuild !true + + (,12) distribution @@ -1135,17 +1195,16 @@ - jdk9 + jdk9+ [9,) - --add-modules=${maven-surefire-plugin.jigsaw.modules} - + --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED @@ -1160,7 +1219,7 @@ --add-opens=java.management/javax.management=ALL-UNNAMED --add-opens=java.management/javax.management.openmbean=ALL-UNNAMED --add-opens=java.naming/javax.naming=ALL-UNNAMED - + @@ -1169,18 +1228,59 @@ maven-compiler-plugin true - - -J--add-modules=java.xml.ws.annotation - - maven-surefire-plugin - ${maven-surefire-plugin.version} + org.wildfly.plugins + wildfly-maven-plugin - ${maven-surefire-plugin.argLine} ${maven-surefire-plugin.argLine.add-modules} ${maven-surefire-plugin.argLine.add-opens} + + --add-opens=java.base/java.lang=ALL-UNNAMED + --add-opens=java.base/java.security=ALL-UNNAMED + --add-opens=java.base/java.io=ALL-UNNAMED + + + + + + + + jdk10- + + (,11) + + + osgi + + + + + jdk11- + + (,12) + + + + cdi + modules + integration + + + + jdk11+ + + [11,) + + + + --add-modules=java.se + + + + + org.wildfly.plugins wildfly-maven-plugin @@ -1189,9 +1289,19 @@ --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED + --add-modules=java.se + + org.jboss.as + patch-gen-maven-plugin + + + --add-modules=java.se + + + @@ -1210,7 +1320,7 @@ com.buschmais.jqassistant jqassistant-maven-plugin - 1.3.0 + ${version.jqassistant.plugin} diff --git a/relocation/annotation-processor/pom.xml b/relocation/annotation-processor/pom.xml index c78e4a08d7..f644d1b57d 100644 --- a/relocation/annotation-processor/pom.xml +++ b/relocation/annotation-processor/pom.xml @@ -10,7 +10,7 @@ org.hibernate.validator hibernate-validator-relocation - 6.0.10-SNAPSHOT + 6.1.0.Final org.hibernate diff --git a/relocation/cdi/pom.xml b/relocation/cdi/pom.xml index 5346ffb489..42d5fd83f5 100644 --- a/relocation/cdi/pom.xml +++ b/relocation/cdi/pom.xml @@ -10,7 +10,7 @@ org.hibernate.validator hibernate-validator-relocation - 6.0.10-SNAPSHOT + 6.1.0.Final org.hibernate diff --git a/relocation/engine/pom.xml b/relocation/engine/pom.xml index f8b6393a23..97eef3e08b 100644 --- a/relocation/engine/pom.xml +++ b/relocation/engine/pom.xml @@ -10,7 +10,7 @@ org.hibernate.validator hibernate-validator-relocation - 6.0.10-SNAPSHOT + 6.1.0.Final org.hibernate diff --git a/relocation/karaf-features/pom.xml b/relocation/karaf-features/pom.xml index a237283b0a..6c0cfdb01d 100644 --- a/relocation/karaf-features/pom.xml +++ b/relocation/karaf-features/pom.xml @@ -10,7 +10,7 @@ org.hibernate.validator hibernate-validator-relocation - 6.0.10-SNAPSHOT + 6.1.0.Final org.hibernate diff --git a/relocation/pom.xml b/relocation/pom.xml index f8b8ce054d..c4809bfa57 100644 --- a/relocation/pom.xml +++ b/relocation/pom.xml @@ -10,7 +10,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.1.0.Final hibernate-validator-relocation diff --git a/settings-example.xml b/settings-example.xml deleted file mode 100644 index e96f41de72..0000000000 --- a/settings-example.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - jboss-public-repository - - - - central - Maven Central - http://repo.maven.apache.org/maven2/ - - false - never - - - - jboss-public-repository-group - JBoss Public Maven Repository Group - https://repository.jboss.org/nexus/content/groups/public-jboss/ - default - - true - never - - - true - never - - - - - - - central - Maven Central - http://repo.maven.apache.org/maven2/ - - false - never - - - - jboss-public-repository-group - JBoss Public Maven Repository Group - https://repository.jboss.org/nexus/content/groups/public-jboss/ - default - - true - never - - - true - never - - - - - - - - jboss-public-repository - - - diff --git a/tck-runner/pom.xml b/tck-runner/pom.xml index d637abbde5..3cf0080231 100644 --- a/tck-runner/pom.xml +++ b/tck-runner/pom.xml @@ -11,18 +11,18 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.1.0.Final ../pom.xml hibernate-validator-tck-runner Hibernate Validator TCK Runner - Aggregates dependencies and runs the JSR-380 TCK + Aggregates dependencies and runs the Jakarta Bean Validation TCK ${project.build.directory}/dependency/beanvalidation-tck-tests-suite.xml - ${project.build.directory}/wildfly-${wildfly-tck.version} + ${project.build.directory}/wildfly-${version.wildfly.tck} org.hibernate.validator.HibernateValidator @@ -31,24 +31,20 @@ - javax.validation - validation-api + jakarta.validation + jakarta.validation-api ${project.groupId} hibernate-validator - - ${project.groupId} - hibernate-validator-cdi - org.glassfish - javax.el + jakarta.el - org.hibernate.javax.persistence - hibernate-jpa-2.1-api + jakarta.persistence + jakarta.persistence-api test @@ -58,7 +54,7 @@ org.hibernate.beanvalidation.tck beanvalidation-tck-tests - ${tck.version} + ${version.org.hibernate.beanvalidation.tck} org.jboss.test-audit @@ -105,6 +101,27 @@ org.apache.maven.plugins maven-dependency-plugin + + copy-tck-bv-api-signature-file + generate-test-sources + + unpack + + + + + org.hibernate.beanvalidation.tck + beanvalidation-tck-tests + ${version.org.hibernate.beanvalidation.tck} + jar + true + + + + **/*.sig + ${project.build.directory}/api-signature + + copy-tck-test-suite-file generate-test-sources @@ -176,15 +193,15 @@ LocalSecurityManagerTesting - -DincludeJavaFXTests=true -Djava.security.manager -Djava.security.policy=${project.build.directory}/test-classes/test.policy + -DincludeJavaFXTests=true -Djava.security.manager -Djava.security.policy=${project.build.directory}/test-classes/test.policy - + org.hibernate.beanvalidation.tck beanvalidation-standalone-container-adapter - ${tck.version} + ${version.org.hibernate.beanvalidation.tck} @@ -227,7 +244,7 @@ - -DincludeJavaFXTests=true -Xmx1024m -Djava.util.logging.manager=org.jboss.logmanager.LogManager + -DincludeJavaFXTests=true -Xmx1024m -Djava.util.logging.manager=org.jboss.logmanager.LogManager Servlet 3.0 @@ -236,6 +253,13 @@ wildfly-arquillian-container-managed test + + ${project.groupId} + hibernate-validator-modules + ${project.version} + wildfly-${version.wildfly.tck}-patch + zip + @@ -253,7 +277,7 @@ org.wildfly wildfly-dist - ${wildfly-tck.version} + ${version.wildfly.tck} tar.gz false ${project.build.directory} @@ -273,7 +297,7 @@ ${project.groupId} hibernate-validator-modules ${project.version} - wildfly-${wildfly-tck.version}-patch + wildfly-${version.wildfly.tck}-patch zip ${project.build.directory} @@ -296,7 +320,7 @@ true false - patch apply ${project.build.directory}/hibernate-validator-modules-${project.version}-wildfly-${wildfly-tck.version}-patch.zip + patch apply ${project.build.directory}/hibernate-validator-modules-${project.version}-wildfly-${version.wildfly.tck}-patch.zip @@ -305,6 +329,83 @@ ${wildfly.target-dir} + + + + + + org.codehaus.gmavenplus + gmavenplus-plugin + + + update-standalone-xml + generate-test-resources + + execute + + + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + once + + incontainer + + + + + + + + sigtest + + + + org.netbeans.tools + sigtest-maven-plugin + + + + check + + + + + strictcheck + javax.validation,javax.validation.bootstrap,javax.validation.constraints, + javax.validation.constraintvalidation,javax.validation.executable,javax.validation.groups, + javax.validation.metadata,javax.validation.spi,javax.validation.valueextraction + + ${project.build.directory}/api-signature/validation-api-java8.sig + + + + + + + jdk9+ + + [9,) + + + --add-opens java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED + + + + jdk10- + + (,11) + + + maven-resources-plugin @@ -319,7 +420,7 @@ ${wildfly.target-dir}/modules/system/layers/base/ - src/test/modules + src/test/modules/jdk8 true @@ -327,45 +428,112 @@ - + + + + + jdk11+ + + [11,) + + + + org.openjfx + javafx-base + ${version.org.openjfx} + test + + + + + - org.codehaus.gmavenplus - gmavenplus-plugin + maven-resources-plugin - update-standalone-xml + copy-resources generate-test-resources - execute + copy-resources - - - + ${wildfly.target-dir}/modules/system/layers/base/ + + + src/test/modules/jdk11 + true + + org.apache.maven.plugins - maven-surefire-plugin - - once - - incontainer - - + maven-dependency-plugin + + + copy + generate-test-resources + + copy + + + + + org.openjfx + javafx-base + ${version.org.openjfx} + jar + + + org.openjfx + javafx-base + ${version.org.openjfx} + ${javafx.platform} + jar + + + ${wildfly.target-dir}/modules/system/layers/base/javafx/api/main/ + + + + - jdk9 + linux - [9,) + + linux + + + + linux + + + + macosx + + + mac os x + + + + mac + + + + windows + + + windows + - --add-opens java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED + win diff --git a/tck-runner/src/test/modules/jdk11/javafx/api/main/module.xml b/tck-runner/src/test/modules/jdk11/javafx/api/main/module.xml new file mode 100644 index 0000000000..91344118d8 --- /dev/null +++ b/tck-runner/src/test/modules/jdk11/javafx/api/main/module.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/tck-runner/src/test/modules/javafx/api/main/module.xml b/tck-runner/src/test/modules/jdk8/javafx/api/main/module.xml similarity index 100% rename from tck-runner/src/test/modules/javafx/api/main/module.xml rename to tck-runner/src/test/modules/jdk8/javafx/api/main/module.xml diff --git a/tck-runner/src/test/resources/arquillian.xml b/tck-runner/src/test/resources/arquillian.xml index ce14b1b50c..700b29110d 100644 --- a/tck-runner/src/test/resources/arquillian.xml +++ b/tck-runner/src/test/resources/arquillian.xml @@ -26,7 +26,7 @@ ${wildfly.target-dir} - ${arquillian.javaVmArguments.add-opens} + ${arquillian.wildfly.jvm.args} -Xmx1024m -XX:MaxPermSize=512m ${remote.debug} -Dvalidation.provider=${validation.provider} diff --git a/tck-runner/src/test/resources/test.policy b/tck-runner/src/test/resources/test.policy index cf6722c55e..aae5f1c2d1 100644 --- a/tck-runner/src/test/resources/test.policy +++ b/tck-runner/src/test/resources/test.policy @@ -27,11 +27,10 @@ grant codeBase "file:${localRepository}/org/hibernate/validator/hibernate-valida permission java.lang.reflect.ReflectPermission "suppressAccessChecks"; permission java.lang.RuntimePermission "accessDeclaredMembers"; permission java.lang.RuntimePermission "setContextClassLoader"; + permission java.util.PropertyPermission "org.hibernate.validator.force-disable-javafx-integration", "read"; permission org.hibernate.validator.HibernateValidatorPermission "accessPrivateMembers"; - // JAXB - permission java.util.PropertyPermission "mapAnyUriToUri", "read"; }; // Used during aggregator builds also building "engine", e.g. mvn clean install -pl tck-runner -am @@ -40,11 +39,10 @@ grant codeBase "file:${basedir}/../engine/target/hibernate-validator-${project.v permission java.lang.reflect.ReflectPermission "suppressAccessChecks"; permission java.lang.RuntimePermission "accessDeclaredMembers"; permission java.lang.RuntimePermission "setContextClassLoader"; + permission java.util.PropertyPermission "org.hibernate.validator.force-disable-javafx-integration", "read"; permission org.hibernate.validator.HibernateValidatorPermission "accessPrivateMembers"; - // JAXB - permission java.util.PropertyPermission "mapAnyUriToUri", "read"; }; grant codeBase "file:${localRepository}/com/fasterxml/classmate/-" { @@ -56,11 +54,11 @@ grant codeBase "file:${localRepository}/org/jboss/logging/jboss-logging/-" { permission java.util.PropertyPermission "org.jboss.logging.locale", "read"; }; -/* =================== */ -/* Bean Validation API */ -/* =================== */ +/* =========================== */ +/* Jakarta Bean Validation API */ +/* =========================== */ -grant codeBase "file:${localRepository}/javax/validation/validation-api/-" { +grant codeBase "file:${localRepository}/jakarta/validation/jakarta.validation-api/-" { permission java.io.FilePermission "<>", "read"; // in some tests this property is accessed by the TCK when the API JAR is on the callstack; the TCK doesn't @@ -84,7 +82,7 @@ grant codeBase "file:${localRepository}/org/hibernate/beanvalidation/tck/-" { // tests (which do not use privileged actions for these) grant codeBase "file:${project.build.directory}/classes" { permission java.util.PropertyPermission "validation.provider", "read"; - permission java.io.FilePermission "${localRepository}/org/hibernate/beanvalidation/tck/beanvalidation-tck-tests/${tck.version}/beanvalidation-tck-tests-${tck.version}.jar", "read"; + permission java.io.FilePermission "${localRepository}/org/hibernate/beanvalidation/tck/beanvalidation-tck-tests/${version.org.hibernate.beanvalidation.tck}/beanvalidation-tck-tests-${version.org.hibernate.beanvalidation.tck}.jar", "read"; permission java.util.PropertyPermission "user.language", "write"; permission org.hibernate.validator.HibernateValidatorPermission "accessPrivateMembers"; }; diff --git a/test-utils/pom.xml b/test-utils/pom.xml index c7c34f94ee..412b792a82 100644 --- a/test-utils/pom.xml +++ b/test-utils/pom.xml @@ -10,7 +10,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.1.0.Final hibernate-validator-test-utils @@ -54,8 +54,8 @@ - javax.validation - validation-api + jakarta.validation + jakarta.validation-api log4j diff --git a/test-utils/src/main/java/org/hibernate/validator/testutil/ConstraintViolationAssert.java b/test-utils/src/main/java/org/hibernate/validator/testutil/ConstraintViolationAssert.java index ae8830cd19..cf234f1bc7 100644 --- a/test-utils/src/main/java/org/hibernate/validator/testutil/ConstraintViolationAssert.java +++ b/test-utils/src/main/java/org/hibernate/validator/testutil/ConstraintViolationAssert.java @@ -14,6 +14,7 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Set; import java.util.stream.Collectors; @@ -262,6 +263,21 @@ public ConstraintViolationSetAssert describedAs(String description, Object... ar public void containsOnlyViolations(ViolationExpectation... expectedViolations) { isNotNull(); + List actualViolations = getActualViolationExpectations( expectedViolations ); + + Assertions.assertThat( actualViolations ).containsExactlyInAnyOrder( expectedViolations ); + } + + public void containsOneOfViolations(ViolationExpectation... expectedViolations) { + isNotNull(); + + List actualViolations = getActualViolationExpectations( expectedViolations ); + + Assertions.assertThat( actualViolations ).hasSize( 1 ); + Assertions.assertThat( expectedViolations ).contains( actualViolations.get( 0 ) ); + } + + private List getActualViolationExpectations(ViolationExpectation[] expectedViolations) { List actualViolations = new ArrayList<>(); ViolationExpectationPropertiesToTest referencePropertiesToTest; @@ -272,7 +288,8 @@ public void containsOnlyViolations(ViolationExpectation... expectedViolations) { referencePropertiesToTest = expectedViolations[0].propertiesToTest; for ( ViolationExpectation expectedViolation : expectedViolations ) { if ( !referencePropertiesToTest.equals( expectedViolation.propertiesToTest ) ) { - throw new IllegalArgumentException( String.format( "Expected violations passed in parameter must test the exact same properties but do not: %1$s != %2$s", + throw new IllegalArgumentException( String.format( Locale.ROOT, + "Expected violations passed in parameter must test the exact same properties but do not: %1$s != %2$s", expectedViolations[0], expectedViolation ) ); } } @@ -282,7 +299,7 @@ public void containsOnlyViolations(ViolationExpectation... expectedViolations) { actualViolations.add( new ViolationExpectation( violation, referencePropertiesToTest ) ); } - Assertions.assertThat( actualViolations ).containsExactlyInAnyOrder( expectedViolations ); + return actualViolations; } public void containsOnlyPaths(PathExpectation... paths) { @@ -309,7 +326,7 @@ public void containsPath(PathExpectation expectedPath) { actualPaths.add( actual ); } - fail( String.format( "Didn't find path <%s> in actual paths <%s>.", expectedPath, actualPaths ) ); + fail( String.format( Locale.ROOT, "Didn't find path <%s> in actual paths <%s>.", expectedPath, actualPaths ) ); } public void containsPaths(PathExpectation... expectedPaths) { diff --git a/test-utils/src/main/java/org/hibernate/validator/testutil/DescriptorAssert.java b/test-utils/src/main/java/org/hibernate/validator/testutil/DescriptorAssert.java index 873b52c7e5..0fc8c8f1e1 100644 --- a/test-utils/src/main/java/org/hibernate/validator/testutil/DescriptorAssert.java +++ b/test-utils/src/main/java/org/hibernate/validator/testutil/DescriptorAssert.java @@ -8,11 +8,13 @@ import static org.testng.Assert.fail; -import org.assertj.core.api.IterableAssert; - +import java.util.Locale; import java.util.Set; + import javax.validation.metadata.GroupConversionDescriptor; +import org.assertj.core.api.IterableAssert; + /** * Provides assertion methods for testing {@link javax.validation.metadata.ElementDescriptor} * implementations and collections thereof. @@ -54,7 +56,7 @@ public void containsConversion(Class from, Class to) { } if ( !foundMatchingConversion ) { - fail( String.format( "<%s> does not contain a conversion from <%s> to <%s>.", actual, from, to ) ); + fail( String.format( Locale.ROOT, "<%s> does not contain a conversion from <%s> to <%s>.", actual, from, to ) ); } } }