From 9b26462ecf0c890add80bee8dcc50f6ccb4ae983 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Sat, 10 Mar 2018 14:29:50 +0100 Subject: [PATCH 1/6] HV-1363 Build abstraction over reflection in ConstraintLocation and related code --- .../cfg/context/ConfiguredConstraint.java | 41 ++-- ...nstructorConstraintMappingContextImpl.java | 7 +- ...ParameterConstraintMappingContextImpl.java | 4 +- ...xecutableConstraintMappingContextImpl.java | 31 ++- .../MethodConstraintMappingContextImpl.java | 9 +- ...ParameterConstraintMappingContextImpl.java | 15 +- .../PropertyConstraintMappingContextImpl.java | 41 ++-- ...turnValueConstraintMappingContextImpl.java | 14 +- .../TypeConstraintMappingContextImpl.java | 43 ++-- .../PropertyValidationContext.java | 10 +- .../metadata/aggregated/BeanMetaDataImpl.java | 13 +- .../aggregated/ExecutableMetaData.java | 49 +++-- .../metadata/aggregated/FieldCascadable.java | 116 ----------- .../aggregated/ParameterMetaData.java | 10 +- ...ascadable.java => PropertyCascadable.java} | 38 ++-- .../metadata/aggregated/PropertyMetaData.java | 53 +++-- .../rule/MethodConfigurationRule.java | 8 +- ...ethodMustNotAlterParameterConstraints.java | 4 +- ...GroupConversionForCascadedReturnValue.java | 4 +- ...hodsMustNotDefineParameterConstraints.java | 4 +- ...eMarkedOnceAsCascadedPerHierarchyLine.java | 4 +- ...ethodsMustNotBeReturnValueConstrained.java | 7 +- .../core/AnnotationProcessingOptions.java | 10 +- .../core/AnnotationProcessingOptionsImpl.java | 32 +-- .../descriptor/ConstraintDescriptorImpl.java | 85 +++----- .../location/BeanConstraintLocation.java | 4 +- .../metadata/location/ConstraintLocation.java | 25 +-- .../CrossParameterConstraintLocation.java | 21 +- .../location/ParameterConstraintLocation.java | 27 +-- .../location/PropertyConstraintLocation.java | 88 +++++++++ .../ReturnValueConstraintLocation.java | 33 ++-- .../TypeArgumentConstraintLocation.java | 4 +- .../provider/AnnotationMetaDataProvider.java | 122 ++++++------ .../metadata/raw/ConstrainedElement.java | 2 +- .../metadata/raw/ConstrainedExecutable.java | 50 ++--- .../metadata/raw/ConstrainedField.java | 86 -------- .../metadata/raw/ConstrainedParameter.java | 31 ++- .../metadata/raw/ConstrainedProperty.java | 84 ++++++++ .../internal/properties/Callable.java | 41 ++++ .../internal/properties/Constrainable.java | 27 +++ .../properties/ConstrainableType.java | 14 ++ .../internal/properties/Property.java | 17 ++ .../internal/properties/PropertySelector.java | 17 ++ .../properties/javabean/JavaBean.java | 37 ++++ .../javabean/JavaBeanExecutable.java | 187 ++++++++++++++++++ .../javabean/JavaBeanField.java} | 88 ++++----- .../javabean/JavaBeanGetter.java} | 90 ++++----- .../properties/javabean/package-info.java | 8 + .../internal/util/ExecutableHelper.java | 5 + .../internal/util/ReflectionHelper.java | 1 + .../validator/internal/util/logging/Log.java | 34 ++-- .../formatter/ConstrainableFormatter.java | 39 ++++ .../ConstrainedConstructorStaxBuilder.java | 12 +- .../mapping/ConstrainedFieldStaxBuilder.java | 17 +- .../mapping/ConstrainedGetterStaxBuilder.java | 10 +- .../mapping/ConstrainedMethodStaxBuilder.java | 12 +- .../ConstrainedParameterStaxBuilder.java | 11 +- .../mapping/CrossParameterStaxBuilder.java | 8 +- .../xml/mapping/ReturnValueStaxBuilder.java | 17 +- .../test/cfg/ConstraintMappingTest.java | 12 +- .../metadata/core/MetaConstraintTest.java | 5 +- .../location/ConstraintLocationTest.java | 5 +- .../AnnotationMetaDataProviderTest.java | 21 +- .../AnnotationMetaDataProviderTestBase.java | 23 ++- .../TypeAnnotationMetaDataRetrievalTest.java | 4 +- 65 files changed, 1124 insertions(+), 867 deletions(-) delete mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/FieldCascadable.java rename engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/{GetterCascadable.java => PropertyCascadable.java} (60%) create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/location/PropertyConstraintLocation.java delete mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedField.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedProperty.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/properties/Callable.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/properties/Constrainable.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/properties/ConstrainableType.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/properties/Property.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/properties/PropertySelector.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBean.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanExecutable.java rename engine/src/main/java/org/hibernate/validator/internal/{metadata/location/FieldConstraintLocation.java => properties/javabean/JavaBeanField.java} (53%) rename engine/src/main/java/org/hibernate/validator/internal/{metadata/location/GetterConstraintLocation.java => properties/javabean/JavaBeanGetter.java} (50%) create mode 100644 engine/src/main/java/org/hibernate/validator/internal/properties/javabean/package-info.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/util/logging/formatter/ConstrainableFormatter.java 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..3f47510fca 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 @@ -10,10 +10,6 @@ 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 +20,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.Property; +import org.hibernate.validator.internal.properties.javabean.JavaBeanField; import org.hibernate.validator.internal.util.annotation.AnnotationDescriptor; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; import org.hibernate.validator.internal.util.logging.Log; @@ -58,42 +56,33 @@ static ConfiguredConstraint forType(ConstraintDef( constraint, ConstraintLocation.forClass( beanType ), ElementType.TYPE ); } - 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<>( + static ConfiguredConstraint forProperty(ConstraintDef constraint, Property property) { + return new ConfiguredConstraint<>( constraint, - ConstraintLocation.forGetter( (Method) member ), - ElementType.METHOD - ); - } + ConstraintLocation.forProperty( property ), + property instanceof JavaBeanField ? ElementType.FIELD : ElementType.METHOD + ); } - public static ConfiguredConstraint forParameter(ConstraintDef constraint, Executable executable, int parameterIndex) { + public static ConfiguredConstraint forParameter(ConstraintDef constraint, Callable callable, int parameterIndex) { return new ConfiguredConstraint<>( - constraint, ConstraintLocation.forParameter( executable, parameterIndex ), ExecutableHelper.getElementType( executable ) + constraint, ConstraintLocation.forParameter( callable, parameterIndex ), callable.isConstructor() ? ElementType.CONSTRUCTOR : ElementType.METHOD ); } - public static ConfiguredConstraint forExecutable(ConstraintDef constraint, Executable executable) { + public static ConfiguredConstraint forExecutable(ConstraintDef constraint, Callable callable) { return new ConfiguredConstraint<>( - constraint, ConstraintLocation.forReturnValue( executable ), ExecutableHelper.getElementType( executable ) + constraint, ConstraintLocation.forReturnValue( callable ), callable.isConstructor() ? ElementType.CONSTRUCTOR : ElementType.METHOD ); } - public static ConfiguredConstraint forCrossParameter(ConstraintDef constraint, Executable executable) { + public static ConfiguredConstraint forCrossParameter(ConstraintDef constraint, Callable callable) { return new ConfiguredConstraint<>( - constraint, ConstraintLocation.forCrossParameter( executable ), ExecutableHelper.getElementType( executable ) + constraint, ConstraintLocation.forCrossParameter( callable ), callable.isConstructor() ? ElementType.CONSTRUCTOR : ElementType.METHOD ); } - public static ConfiguredConstraint forTypeArgument(ConstraintDef constraint,ConstraintLocation delegate, TypeVariable typeArgument, Type typeOfAnnotatedElement) { + public static ConfiguredConstraint forTypeArgument(ConstraintDef constraint, ConstraintLocation delegate, TypeVariable typeArgument, Type typeOfAnnotatedElement) { return new ConfiguredConstraint<>( constraint, ConstraintLocation.forTypeArgument( delegate, typeArgument, typeOfAnnotatedElement ), 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..2b71b966a4 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.Callable; /** * 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, Callable 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/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/ExecutableConstraintMappingContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ExecutableConstraintMappingContextImpl.java index 3f2f6d3523..8f6635aaed 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,7 +9,6 @@ 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; @@ -24,7 +23,7 @@ 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.properties.Callable; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; @@ -41,20 +40,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 +61,7 @@ public ParameterConstraintMappingContext parameter(int index) { if ( context != null ) { throw LOG.getParameterHasAlreadyBeConfiguredViaProgrammaticApiException( typeContext.getBeanClass(), - executable, + callable, index ); } @@ -76,7 +75,7 @@ public CrossParameterConstraintMappingContext crossParameter() { if ( crossParameterContext != null ) { throw LOG.getCrossParameterElementHasAlreadyBeConfiguredViaProgrammaticApiException( typeContext.getBeanClass(), - executable + callable ); } @@ -88,7 +87,7 @@ public ReturnValueConstraintMappingContext returnValue() { if ( returnValueContext != null ) { throw LOG.getReturnValueHasAlreadyBeConfiguredViaProgrammaticApiException( typeContext.getBeanClass(), - executable + callable ); } @@ -96,8 +95,8 @@ public ReturnValueConstraintMappingContext returnValue() { return returnValueContext; } - public Executable getExecutable() { - return executable; + public Callable getCallable() { + return callable; } public TypeConstraintMappingContextImpl getTypeContext() { @@ -108,7 +107,7 @@ public ConstrainedElement build(ConstraintHelper constraintHelper, TypeResolutio ValueExtractorManager valueExtractorManager) { return new ConstrainedExecutable( ConfigurationSource.API, - executable, + callable, getParameters( constraintHelper, typeResolutionHelper, valueExtractorManager ), crossParameterContext != null ? crossParameterContext.getConstraints( constraintHelper, typeResolutionHelper, valueExtractorManager ) : Collections.>emptySet(), returnValueContext != null ? returnValueContext.getConstraints( constraintHelper, typeResolutionHelper, valueExtractorManager ) : Collections.>emptySet(), @@ -130,8 +129,8 @@ private List getParameters(ConstraintHelper constraintHelp constrainedParameters.add( new ConstrainedParameter( ConfigurationSource.API, - executable, - ReflectionHelper.typeOf( executable, i ), + callable, + callable.typeOfParameter( i ), i ) ); 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..19e74b27db 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.Callable; /** * 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, Callable 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..a2af84344c 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 @@ -21,7 +21,6 @@ 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; /** @@ -41,7 +40,7 @@ final class ParameterConstraintMappingContextImpl ParameterConstraintMappingContextImpl(ExecutableConstraintMappingContextImpl executableContext, int parameterIndex) { super( executableContext.getTypeContext().getConstraintMapping(), - executableContext.executable.getGenericParameterTypes()[parameterIndex] + executableContext.callable.getGenericParameterTypes()[parameterIndex] ); this.executableContext = executableContext; @@ -58,7 +57,7 @@ public ParameterConstraintMappingContext constraint(ConstraintDef definiti super.addConstraint( ConfiguredConstraint.forParameter( definition, - executableContext.getExecutable(), + executableContext.getCallable(), parameterIndex ) ); @@ -68,7 +67,7 @@ public ParameterConstraintMappingContext constraint(ConstraintDef definiti @Override public ParameterConstraintMappingContext ignoreAnnotations(boolean ignoreAnnotations) { mapping.getAnnotationProcessingOptions().ignoreConstraintAnnotationsOnParameter( - executableContext.getExecutable(), + executableContext.getCallable(), parameterIndex, ignoreAnnotations ); @@ -105,7 +104,7 @@ public ContainerElementConstraintMappingContext containerElementType() { return super.containerElement( this, executableContext.getTypeContext(), - ConstraintLocation.forParameter( executableContext.getExecutable(), parameterIndex ) + ConstraintLocation.forParameter( executableContext.getCallable(), parameterIndex ) ); } @@ -114,7 +113,7 @@ public ContainerElementConstraintMappingContext containerElementType(int index, return super.containerElement( this, executableContext.getTypeContext(), - ConstraintLocation.forParameter( executableContext.getExecutable(), parameterIndex ), + ConstraintLocation.forParameter( executableContext.getCallable(), parameterIndex ), index, nestedIndexes ); @@ -122,11 +121,11 @@ public ContainerElementConstraintMappingContext containerElementType(int index, public ConstrainedParameter build(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager) { - Type parameterType = ReflectionHelper.typeOf( executableContext.getExecutable(), parameterIndex ); + Type parameterType = executableContext.getCallable().typeOfParameter( parameterIndex ); return new ConstrainedParameter( ConfigurationSource.API, - executableContext.getExecutable(), + executableContext.getCallable(), parameterType, parameterIndex, getConstraints( constraintHelper, typeResolutionHelper, valueExtractorManager ), 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/PropertyConstraintMappingContextImpl.java index dcb6681ce5..0d12529b52 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/PropertyConstraintMappingContextImpl.java @@ -7,10 +7,6 @@ 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; @@ -24,8 +20,10 @@ 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.metadata.raw.ConstrainedProperty; +import org.hibernate.validator.internal.properties.Callable; +import org.hibernate.validator.internal.properties.Property; +import org.hibernate.validator.internal.properties.javabean.JavaBeanField; import org.hibernate.validator.internal.util.TypeResolutionHelper; /** @@ -42,19 +40,14 @@ final class PropertyConstraintMappingContextImpl private final TypeConstraintMappingContextImpl typeContext; // either Field or Method - private final Member member; + private final Property property; private final ConstraintLocation location; - PropertyConstraintMappingContextImpl(TypeConstraintMappingContextImpl typeContext, Member member) { - super( typeContext.getConstraintMapping(), ReflectionHelper.typeOf( member ) ); + PropertyConstraintMappingContextImpl(TypeConstraintMappingContextImpl typeContext, Property property) { + 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 = ConstraintLocation.forProperty( property ); } @Override @@ -64,17 +57,17 @@ protected PropertyConstraintMappingContextImpl getThis() { @Override public PropertyConstraintMappingContext constraint(ConstraintDef definition) { - if ( member instanceof Field ) { + if ( property instanceof JavaBeanField ) { super.addConstraint( ConfiguredConstraint.forProperty( - definition, member + definition, property ) ); } else { super.addConstraint( ConfiguredConstraint.forExecutable( - definition, (Method) member + definition, property.as( Callable.class ) ) ); } @@ -88,7 +81,7 @@ public PropertyConstraintMappingContext ignoreAnnotations() { @Override public PropertyConstraintMappingContext ignoreAnnotations(boolean ignoreAnnotations) { - mapping.getAnnotationProcessingOptions().ignoreConstraintAnnotationsOnMember( member, ignoreAnnotations ); + mapping.getAnnotationProcessingOptions().ignoreConstraintAnnotationsOnMember( property, ignoreAnnotations ); return this; } @@ -118,10 +111,10 @@ public ContainerElementConstraintMappingContext containerElementType(int index, } ConstrainedElement build(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager) { - if ( member instanceof Field ) { - return new ConstrainedField( + if ( property instanceof JavaBeanField ) { + return ConstrainedProperty.forField( ConfigurationSource.API, - (Field) member, + property, getConstraints( constraintHelper, typeResolutionHelper, valueExtractorManager ), getTypeArgumentConstraints( constraintHelper, typeResolutionHelper, valueExtractorManager ), getCascadingMetaDataBuilder() @@ -130,7 +123,7 @@ ConstrainedElement build(ConstraintHelper constraintHelper, TypeResolutionHelper else { return new ConstrainedExecutable( ConfigurationSource.API, - (Executable) member, + property.as( Callable.class ), getConstraints( constraintHelper, typeResolutionHelper, valueExtractorManager ), getTypeArgumentConstraints( constraintHelper, typeResolutionHelper, valueExtractorManager ), 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 a9037848ab..b0aa93bdd4 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 @@ -12,7 +12,7 @@ import java.lang.annotation.ElementType; import java.lang.invoke.MethodHandles; import java.lang.reflect.Constructor; -import java.lang.reflect.Member; +import java.lang.reflect.Field; import java.lang.reflect.Method; import java.security.AccessController; import java.security.PrivilegedAction; @@ -32,6 +32,12 @@ 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.Callable; +import org.hibernate.validator.internal.properties.Constrainable; +import org.hibernate.validator.internal.properties.Property; +import org.hibernate.validator.internal.properties.javabean.JavaBeanExecutable; +import org.hibernate.validator.internal.properties.javabean.JavaBeanField; +import org.hibernate.validator.internal.properties.javabean.JavaBeanGetter; import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ReflectionHelper; @@ -63,7 +69,7 @@ public final class TypeConstraintMappingContextImpl extends ConstraintMapping private final Set executableContexts = newHashSet(); private final Set propertyContexts = newHashSet(); - private final Set configuredMembers = newHashSet(); + private final Set configuredMembers = newHashSet(); private List> defaultGroupSequence; private Class> defaultGroupSequenceProviderClass; @@ -115,7 +121,7 @@ public PropertyConstraintMappingContext property(String property, ElementType el Contracts.assertNotNull( elementType, "The element type must not be null." ); Contracts.assertNotEmpty( property, MESSAGES.propertyNameMustNotBeEmpty() ); - Member member = getMember( + Property member = getProperty( beanClass, property, elementType ); @@ -147,15 +153,17 @@ public MethodConstraintMappingContext method(String name, Class... parameterT throw LOG.getBeanDoesNotContainMethodException( beanClass, name, parameterTypes ); } - if ( configuredMembers.contains( method ) ) { - throw LOG.getMethodHasAlreadyBeConfiguredViaProgrammaticApiException( + Callable callable = JavaBeanExecutable.of( method ); + + if ( configuredMembers.contains( callable ) ) { + throw LOG.getMethodHasAlreadyBeenConfiguredViaProgrammaticApiException( beanClass, ExecutableHelper.getExecutableAsString( name, parameterTypes ) ); } - MethodConstraintMappingContextImpl context = new MethodConstraintMappingContextImpl( this, method ); - configuredMembers.add( method ); + MethodConstraintMappingContextImpl context = new MethodConstraintMappingContextImpl( this, callable ); + configuredMembers.add( callable ); executableContexts.add( context ); return context; @@ -172,7 +180,9 @@ public ConstructorConstraintMappingContext constructor(Class... parameterType ); } - if ( configuredMembers.contains( constructor ) ) { + Callable callable = JavaBeanExecutable.of( constructor ); + + if ( configuredMembers.contains( callable ) ) { throw LOG.getConstructorHasAlreadyBeConfiguredViaProgrammaticApiException( beanClass, ExecutableHelper.getExecutableAsString( beanClass.getSimpleName(), parameterTypes ) @@ -181,9 +191,9 @@ public ConstructorConstraintMappingContext constructor(Class... parameterType ConstructorConstraintMappingContextImpl context = new ConstructorConstraintMappingContextImpl( this, - constructor + callable ); - configuredMembers.add( constructor ); + configuredMembers.add( callable ); executableContexts.add( context ); return context; @@ -253,7 +263,7 @@ protected ConstraintType getConstraintType() { * * @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) { + private Property getProperty(Class clazz, String property, ElementType elementType) { Contracts.assertNotNull( clazz, MESSAGES.classCannotBeNull() ); if ( property == null || property.length() == 0 ) { @@ -264,20 +274,21 @@ private Member getMember(Class clazz, String property, ElementType elementTyp throw LOG.getElementTypeHasToBeFieldOrMethodException(); } - Member member = null; if ( ElementType.FIELD.equals( elementType ) ) { - member = run( GetDeclaredField.action( clazz, property ) ); + Field field = run( GetDeclaredField.action( clazz, property ) ); + return field == null ? null : new JavaBeanField( field ); } else { + Method method = null; 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 ) { + method = run( GetMethod.action( clazz, prefix + methodName ) ); + if ( method != null ) { break; } } + return method == null ? null : new JavaBeanGetter( method ); } - return member; } /** 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 index 1a2a1d8a1f..04e37dfecb 100644 --- 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 @@ -23,8 +23,7 @@ import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.core.MetaConstraint; 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.PropertyConstraintLocation; import org.hibernate.validator.internal.metadata.location.TypeArgumentConstraintLocation; /** @@ -72,11 +71,8 @@ private String getPropertyName(ConstraintLocation location) { location = ( (TypeArgumentConstraintLocation) location ).getOuterDelegate(); } - if ( location instanceof FieldConstraintLocation ) { - return ( (FieldConstraintLocation) location ).getPropertyName(); - } - else if ( location instanceof GetterConstraintLocation ) { - return ( (GetterConstraintLocation) location ).getPropertyName(); + if ( location instanceof PropertyConstraintLocation ) { + return ( (PropertyConstraintLocation) location ).getPropertyName(); } return null; 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..faa48f189c 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 @@ -12,8 +12,6 @@ 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; @@ -46,8 +44,9 @@ 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.ConstrainedProperty; import org.hibernate.validator.internal.metadata.raw.ConstrainedType; +import org.hibernate.validator.internal.properties.Callable; import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; @@ -691,8 +690,8 @@ public BuilderDelegate( this.methodValidationConfiguration = methodValidationConfiguration; switch ( constrainedElement.getKind() ) { - case FIELD: - ConstrainedField constrainedField = (ConstrainedField) constrainedElement; + case PROPERTY: + ConstrainedProperty constrainedField = (ConstrainedProperty) constrainedElement; propertyBuilder = new PropertyMetaData.Builder( beanClass, constrainedField, @@ -704,11 +703,11 @@ public BuilderDelegate( case CONSTRUCTOR: case METHOD: ConstrainedExecutable constrainedExecutable = (ConstrainedExecutable) constrainedElement; - Member member = constrainedExecutable.getExecutable(); + Callable member = 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 ( !Modifier.isPrivate( member.getModifiers() ) || beanClass == member.getDeclaringClass() ) { + if ( !member.isPrivate() || beanClass == member.getDeclaringClass() ) { methodBuilder = new ExecutableMetaData.Builder( beanClass, constrainedExecutable, 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 46d1aa7050..d8c2df456c 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; @@ -33,10 +30,10 @@ 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.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; @@ -260,7 +257,7 @@ public static class Builder extends MetaDataBuilder { */ private final ConstrainedElement.ConstrainedElementKind kind; private final Set constrainedExecutables = newHashSet(); - private Executable executable; + private Callable callable; private final boolean isGetterMethod; private final Set> crossParameterConstraints = newHashSet(); private final Set rules; @@ -293,7 +290,7 @@ public Builder( 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(); @@ -306,27 +303,27 @@ 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 isResolvedToSameMethodInHierarchy( executable, candidate ); + return isResolvedToSameMethodInHierarchy( callable, candidate ); } - private boolean isResolvedToSameMethodInHierarchy(Executable first, Executable other) { - if ( first instanceof Constructor || other instanceof Constructor ) { + private boolean isResolvedToSameMethodInHierarchy(Callable first, Callable other) { + if ( first.isConstructor() || other.isConstructor() ) { return first.equals( other ); } - return executableHelper.isResolvedToSameMethodInHierarchy( getBeanClass(), (Method) first, (Method) other ); + return first.isResolvedToSameMethodInHierarchy( executableHelper, getBeanClass(), other ); } - private boolean overrides(Executable first, Executable other) { - if ( first instanceof Constructor || other instanceof Constructor ) { + private boolean overrides(Callable first, Callable other) { + if ( first.isConstructor() || other.isConstructor() ) { return false; } - return executableHelper.overrides( (Method) first, (Method) other ); + return executableHelper.overrides( first, other ); } @Override @@ -334,7 +331,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(); @@ -350,11 +347,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(); } } @@ -365,7 +362,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 ) { @@ -383,17 +380,17 @@ 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 ? callable.getDeclaringClass().getSimpleName() : callable.getName(), + callable.getType(), + callable.getParameterTypes(), kind == ConstrainedElement.ConstrainedElementKind.CONSTRUCTOR ? ElementKind.CONSTRUCTOR : ElementKind.METHOD, - kind == ConstrainedElement.ConstrainedElementKind.CONSTRUCTOR ? Collections.singleton( ExecutableHelper.getSignature( executable ) ) : + kind == ConstrainedElement.ConstrainedElementKind.CONSTRUCTOR ? Collections.singleton( callable.getSignature() ) : CollectionHelper.toImmutableSet( signatures ), adaptOriginsAndImplicitGroups( getDirectConstraints() ), adaptOriginsAndImplicitGroups( getContainerElementConstraints() ), findParameterMetaData(), adaptOriginsAndImplicitGroups( crossParameterConstraints ), - cascadingMetaDataBuilder.build( valueExtractorManager, executable ), + cascadingMetaDataBuilder.build( valueExtractorManager, callable ), isConstrained, isGetterMethod ); @@ -416,7 +413,7 @@ private List findParameterMetaData() { for ( ConstrainedParameter oneParameter : oneExecutable.getAllParameterMetaData() ) { parameterBuilders.add( new ParameterMetaData.Builder( - executable.getDeclaringClass(), + callable.getDeclaringClass(), oneParameter, constraintHelper, typeResolutionHelper, 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 deleted file mode 100644 index 59c433f01c..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/FieldCascadable.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.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; - -/** - * A {@link Cascadable} backed by a field of a Java bean. - * - * @author Gunnar Morling - */ -public class FieldCascadable implements Cascadable { - - private final Field field; - private final Type cascadableType; - private final CascadingMetaData cascadingMetaData; - - FieldCascadable(Field field, CascadingMetaData cascadingMetaData) { - this.field = field; - this.cascadableType = ReflectionHelper.typeOf( field ); - this.cascadingMetaData = cascadingMetaData; - } - - @Override - public ElementType getElementType() { - return ElementType.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 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 ); - } - - @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(); - } - } -} 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..2bd0e5f96a 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 @@ -7,7 +7,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; @@ -24,6 +23,7 @@ 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; @@ -112,7 +112,7 @@ public static class Builder extends MetaDataBuilder { private final ExecutableParameterNameProvider parameterNameProvider; private final Type parameterType; private final int parameterIndex; - private Executable executableForNameRetrieval; + private Callable executableForNameRetrieval; private CascadingMetaDataBuilder cascadingMetaDataBuilder; public Builder(Class beanClass, @@ -157,8 +157,8 @@ public void add(ConstrainedElement constrainedElement) { // 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(); + newConstrainedParameter.getCallable().getDeclaringClass().isAssignableFrom( executableForNameRetrieval.getDeclaringClass() ) ) { + executableForNameRetrieval = newConstrainedParameter.getCallable(); } } @@ -166,7 +166,7 @@ public void add(ConstrainedElement constrainedElement) { public ParameterMetaData build() { return new ParameterMetaData( parameterIndex, - parameterNameProvider.getParameterNames( executableForNameRetrieval ).get( parameterIndex ), + executableForNameRetrieval.getParameterName( parameterNameProvider, parameterIndex ), parameterType, adaptOriginsAndImplicitGroups( getDirectConstraints() ), adaptOriginsAndImplicitGroups( getContainerElementConstraints() ), 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/PropertyCascadable.java similarity index 60% rename from engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/GetterCascadable.java rename to engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/PropertyCascadable.java index 72854cfa39..7afa0fd018 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/PropertyCascadable.java @@ -7,36 +7,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.properties.Property; +import org.hibernate.validator.internal.properties.javabean.JavaBeanField; /** - * A {@link Cascadable} backed by a property getter of a Java bean. + * A {@link Cascadable} backed by a field of a Java bean. * * @author Gunnar Morling + * @author Marko Bekhta */ -public class GetterCascadable implements Cascadable { +public class PropertyCascadable implements Cascadable { - private final Method method; - private final String propertyName; + private final Property property; private final Type cascadableType; private final CascadingMetaData cascadingMetaData; + private final ElementType elementType; - GetterCascadable(Method method, CascadingMetaData cascadingMetaData) { - this.method = method; - this.propertyName = ReflectionHelper.getPropertyName( method ); - this.cascadableType = ReflectionHelper.typeOf( method ); + PropertyCascadable(Property property, CascadingMetaData cascadingMetaData) { + this.property = property; + this.cascadableType = property.getType(); this.cascadingMetaData = cascadingMetaData; + this.elementType = property instanceof JavaBeanField ? ElementType.FIELD : ElementType.METHOD; } @Override public ElementType getElementType() { - return ElementType.METHOD; + return elementType; } @Override @@ -46,12 +47,12 @@ public Type getCascadableType() { @Override public Object getValue(Object parent) { - return ReflectionHelper.getValue( method, parent ); + return property.getValueFrom( parent ); } @Override public void appendTo(PathImpl path) { - path.addPropertyNode( propertyName ); + path.addPropertyNode( property.getPropertyName() ); } @Override @@ -62,13 +63,12 @@ public CascadingMetaData getCascadingMetaData() { public static class Builder implements Cascadable.Builder { private final ValueExtractorManager valueExtractorManager; - private final Method method; + private final Property property; 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) { + public Builder(ValueExtractorManager valueExtractorManager, Property property, CascadingMetaDataBuilder cascadingMetaDataBuilder) { this.valueExtractorManager = valueExtractorManager; - this.method = method; + this.property = property; this.cascadingMetaDataBuilder = cascadingMetaDataBuilder; } @@ -78,8 +78,8 @@ public void mergeCascadingMetaData(CascadingMetaDataBuilder cascadingMetaData) { } @Override - public GetterCascadable build() { - return new GetterCascadable( method, cascadingMetaDataBuilder.build( valueExtractorManager, method ) ); + public PropertyCascadable build() { + return new PropertyCascadable( property, cascadingMetaDataBuilder.build( valueExtractorManager, property ) ); } } } 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..c6de6694fc 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,8 +6,6 @@ */ 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.reflect.Type; import java.security.AccessController; @@ -37,10 +35,12 @@ 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.ConstrainedProperty; 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.Property; 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.stereotypes.Immutable; @@ -151,23 +151,22 @@ public static class Builder extends MetaDataBuilder { private static final EnumSet SUPPORTED_ELEMENT_KINDS = EnumSet.of( ConstrainedElementKind.TYPE, - ConstrainedElementKind.FIELD, + ConstrainedElementKind.PROPERTY, ConstrainedElementKind.METHOD ); 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, + public Builder(Class beanClass, ConstrainedProperty constrainedProperty, ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager) { super( beanClass, constraintHelper, typeResolutionHelper, valueExtractorManager ); - this.propertyName = constrainedField.getField().getName(); - this.propertyType = ReflectionHelper.typeOf( constrainedField.getField() ); - add( constrainedField ); + this.propertyName = constrainedProperty.getProperty().getName(); + this.propertyType = constrainedProperty.getProperty().getType(); + add( constrainedProperty ); } public Builder(Class beanClass, ConstrainedType constrainedType, ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, @@ -183,8 +182,8 @@ public Builder(Class beanClass, ConstrainedExecutable constrainedMethod, Cons ValueExtractorManager valueExtractorManager) { super( beanClass, constraintHelper, typeResolutionHelper, valueExtractorManager ); - 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 ); } @@ -204,36 +203,30 @@ public boolean accepts(ConstrainedElement constrainedElement) { @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 ( constrainedElement.getKind() == ConstrainedElementKind.PROPERTY ) { + Property property = ( (ConstrainedProperty) constrainedElement ).getProperty(); + Cascadable.Builder builder = cascadableBuilders.get( property ); if ( builder == null ) { - builder = new FieldCascadable.Builder( valueExtractorManager, field, constrainedElement.getCascadingMetaDataBuilder() ); - cascadableBuilders.put( field, builder ); + builder = new PropertyCascadable.Builder( valueExtractorManager, property, constrainedElement.getCascadingMetaDataBuilder() ); + cascadableBuilders.put( property, builder ); } else { builder.mergeCascadingMetaData( constrainedElement.getCascadingMetaDataBuilder() ); } } else if ( constrainedElement.getKind() == ConstrainedElementKind.METHOD ) { - Method method = (Method) ( (ConstrainedExecutable) constrainedElement ).getExecutable(); + Callable method = ( (ConstrainedExecutable) constrainedElement ).getCallable(); Cascadable.Builder builder = cascadableBuilders.get( method ); if ( builder == null ) { - builder = new GetterCascadable.Builder( valueExtractorManager, getterAccessibleMethod, constrainedElement.getCascadingMetaDataBuilder() ); + builder = new PropertyCascadable.Builder( valueExtractorManager, method.as( Property.class ), constrainedElement.getCascadingMetaDataBuilder() ); cascadableBuilders.put( method, builder ); } else { @@ -249,7 +242,7 @@ protected Set> adaptConstraints(ConstrainedElement constrained return constraints; } - ConstraintLocation getterConstraintLocation = ConstraintLocation.forGetter( getterAccessibleMethod ); + ConstraintLocation getterConstraintLocation = ConstraintLocation.forProperty( ( (ConstrainedExecutable) constrainedElement ).getCallable().as( Property.class ) ); // convert return value locations into getter locations for usage within this meta-data return constraints.stream() @@ -299,11 +292,11 @@ private MetaConstraint withGetterLocation(ConstraintLocation getterConstraint } private String getPropertyName(ConstrainedElement constrainedElement) { - if ( constrainedElement.getKind() == ConstrainedElementKind.FIELD ) { - return ReflectionHelper.getPropertyName( ( (ConstrainedField) constrainedElement ).getField() ); + if ( constrainedElement.getKind() == ConstrainedElementKind.PROPERTY ) { + return ( (ConstrainedProperty) constrainedElement ).getProperty().getPropertyName(); } else if ( constrainedElement.getKind() == ConstrainedElementKind.METHOD ) { - return ReflectionHelper.getPropertyName( ( (ConstrainedExecutable) constrainedElement ).getExecutable() ); + return ( (ConstrainedExecutable) constrainedElement ).getCallable().as( Property.class ).getPropertyName(); } return null; 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..65580827f8 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 member); - boolean areReturnValueConstraintsIgnoredFor(Member member); + boolean areReturnValueConstraintsIgnoredFor(Constrainable member); - boolean areCrossParameterConstraintsIgnoredFor(Member member); + boolean areCrossParameterConstraintsIgnoredFor(Constrainable member); - boolean areParameterConstraintsIgnoredFor(Member member, int index); + boolean areParameterConstraintsIgnoredFor(Constrainable member, 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..80cc6f3d0a 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,7 +58,7 @@ public class AnnotationProcessingOptionsImpl implements AnnotationProcessingOpti private final Map annotationIgnoresForMethodParameter = newHashMap(); @Override - public boolean areMemberConstraintsIgnoredFor(Member member) { + public boolean areMemberConstraintsIgnoredFor(Constrainable member) { Class clazz = member.getDeclaringClass(); if ( annotationIgnoredForMembers.containsKey( member ) ) { return annotationIgnoredForMembers.get( member ); @@ -69,7 +69,7 @@ public boolean areMemberConstraintsIgnoredFor(Member member) { } @Override - public boolean areReturnValueConstraintsIgnoredFor(Member member) { + public boolean areReturnValueConstraintsIgnoredFor(Constrainable member) { if ( annotationIgnoresForReturnValues.containsKey( member ) ) { return annotationIgnoresForReturnValues.get( member ); } @@ -79,7 +79,7 @@ public boolean areReturnValueConstraintsIgnoredFor(Member member) { } @Override - public boolean areCrossParameterConstraintsIgnoredFor(Member member) { + public boolean areCrossParameterConstraintsIgnoredFor(Constrainable member) { if ( annotationIgnoresForCrossParameter.containsKey( member ) ) { return annotationIgnoresForCrossParameter.get( member ); } @@ -89,7 +89,7 @@ public boolean areCrossParameterConstraintsIgnoredFor(Member member) { } @Override - public boolean areParameterConstraintsIgnoredFor(Member member, int index) { + public boolean areParameterConstraintsIgnoredFor(Constrainable member, int index) { ExecutableParameterKey key = new ExecutableParameterKey( member, index ); if ( annotationIgnoresForMethodParameter.containsKey( key ) ) { return annotationIgnoresForMethodParameter.get( key ); @@ -138,19 +138,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,10 +164,10 @@ private boolean areAllConstraintAnnotationsIgnoredFor(Class clazz) { } public class ExecutableParameterKey { - private final Member member; + private final Constrainable member; private final int index; - public ExecutableParameterKey(Member member, int index) { + public ExecutableParameterKey(Constrainable member, int index) { this.member = member; this.index = index; } 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..17088c88c7 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 @@ -17,10 +17,6 @@ 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 +45,9 @@ 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.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; @@ -164,7 +163,7 @@ public class ConstraintDescriptorImpl implements Constrain private final int hashCode; public ConstraintDescriptorImpl(ConstraintHelper constraintHelper, - Member member, + Constrainable constrainable, ConstraintAnnotationDescriptor annotationDescriptor, ElementType type, Class implicitGroup, @@ -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,13 @@ public ConstraintDescriptorImpl(ConstraintHelper constraintHelper, this.constraintType = determineConstraintType( annotationDescriptor.getType(), - member, + constrainable, type, !genericValidatorDescriptors.isEmpty(), !crossParameterValidatorDescriptors.isEmpty(), externalConstraintType ); - this.composingConstraints = parseComposingConstraints( constraintHelper, member, constraintType ); + this.composingConstraints = parseComposingConstraints( constraintHelper, constrainable, constraintType ); this.compositionType = parseCompositionType( constraintHelper ); validateComposingConstraintTypes(); @@ -227,18 +226,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 ); + this( constraintHelper, constrainable, annotationDescriptor, type, null, ConstraintOrigin.DEFINED_LOCALLY, null ); } public ConstraintDescriptorImpl(ConstraintHelper constraintHelper, - Member member, + Constrainable constrainable, ConstraintAnnotationDescriptor annotationDescriptor, ElementType type, ConstraintType constraintType) { - this( constraintHelper, member, annotationDescriptor, type, null, ConstraintOrigin.DEFINED_LOCALLY, constraintType ); + this( constraintHelper, constrainable, annotationDescriptor, type, null, ConstraintOrigin.DEFINED_LOCALLY, constraintType ); } public ConstraintAnnotationDescriptor getAnnotationDescriptor() { @@ -389,7 +388,7 @@ public String toString() { * specify the target explicitly). * * - * @param member The annotated member + * @param constrainable The annotated member * @param elementType The type of the annotated element * @param hasGenericValidators Whether the constraint has at least one generic validator or * not @@ -400,7 +399,7 @@ public String toString() { * @return The type of this constraint */ private ConstraintType determineConstraintType(Class constraintAnnotationType, - Member member, + Constrainable constrainable, ElementType elementType, boolean hasGenericValidators, boolean hasCrossParameterValidator, @@ -453,9 +452,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 +472,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 +498,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,35 +533,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; } @@ -648,7 +619,7 @@ private void ensureAttributeIsOverridable(Method m, OverridesAttribute overrides } } - private Set> parseComposingConstraints(ConstraintHelper constraintHelper, Member member, + private Set> parseComposingConstraints(ConstraintHelper constraintHelper, Constrainable member, ConstraintType constraintType) { Set> composingConstraintsSet = newHashSet(); Map> overrideParameters = parseOverrideParameters(); @@ -727,7 +698,7 @@ private CompositionType parseCompositionType(ConstraintHelper constraintHelper) private ConstraintDescriptorImpl createComposingConstraintDescriptor( ConstraintHelper constraintHelper, - Member member, + Constrainable member, Map> overrideParameters, int index, U constraintAnnotation, 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..81b2d8a0fd 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; @@ -47,7 +47,7 @@ public Class getDeclaringClass() { } @Override - public Member getMember() { + public Constrainable getMember() { return null; } 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..95aeb00ae2 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,14 +6,13 @@ */ 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.reflect.Type; import java.lang.reflect.TypeVariable; 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.properties.Property; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; /** @@ -39,27 +38,23 @@ static ConstraintLocation forClass(Class declaringClass) { return new BeanConstraintLocation( declaringClass ); } - static ConstraintLocation forField(Field field) { - return new FieldConstraintLocation( field ); - } - - static ConstraintLocation forGetter(Method getter) { - return new GetterConstraintLocation( getter ); + static ConstraintLocation forProperty(Property property) { + return new PropertyConstraintLocation( property ); } static ConstraintLocation forTypeArgument(ConstraintLocation delegate, TypeVariable typeParameter, Type typeOfAnnotatedElement) { return new TypeArgumentConstraintLocation( delegate, typeParameter, typeOfAnnotatedElement ); } - static ConstraintLocation forReturnValue(Executable executable) { + static ConstraintLocation forReturnValue(Callable executable) { return new ReturnValueConstraintLocation( executable ); } - static ConstraintLocation forCrossParameter(Executable executable) { + static ConstraintLocation forCrossParameter(Callable executable) { return new CrossParameterConstraintLocation( executable ); } - static ConstraintLocation forParameter(Executable executable, int index) { + static ConstraintLocation forParameter(Callable executable, int index) { return new ParameterConstraintLocation( executable, index ); } @@ -73,7 +68,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 getMember(); /** * Returns the type to be used when resolving constraint validators for constraints at this location. Note that this @@ -91,7 +86,7 @@ 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 PropertyConstraintLocation} but an * object array for a {@link ParameterConstraintLocation}. */ Object getValue(Object parent); 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..4c345ff609 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,10 @@ */ 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.Constrainable; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; /** @@ -21,9 +20,9 @@ */ class CrossParameterConstraintLocation implements ConstraintLocation { - private final Executable executable; + private final Constrainable executable; - CrossParameterConstraintLocation(Executable executable) { + CrossParameterConstraintLocation(Constrainable executable) { this.executable = executable; } @@ -33,7 +32,7 @@ public Class getDeclaringClass() { } @Override - public Member getMember() { + public Constrainable getMember() { return executable; } @@ -59,10 +58,7 @@ public String toString() { @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ( ( executable == null ) ? 0 : executable.hashCode() ); - return result; + return executable.hashCode(); } @Override @@ -77,12 +73,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 ( !executable.equals( other.executable ) ) { return false; } 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..80e2060820 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; @@ -22,14 +22,14 @@ */ public class ParameterConstraintLocation implements ConstraintLocation { - private final Executable executable; + private final Callable executable; private final int index; private final Type typeForValidatorResolution; - ParameterConstraintLocation(Executable executable, int index) { + public ParameterConstraintLocation(Callable executable, int index) { this.executable = executable; this.index = index; - this.typeForValidatorResolution = ReflectionHelper.boxedType( ReflectionHelper.typeOf( executable, index ) ); + this.typeForValidatorResolution = ReflectionHelper.boxedType( executable.typeOfParameter( index ) ); } @Override @@ -38,7 +38,7 @@ public Class getDeclaringClass() { } @Override - public Member getMember() { + public Constrainable getMember() { return executable; } @@ -53,8 +53,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( executable.getParameterName( parameterNameProvider, index ), index ); } @Override @@ -64,15 +63,14 @@ public Object getValue(Object parent) { @Override public String toString() { - return "ParameterConstraintLocation [executable=" + executable + ", index=" + index - + ", typeForValidatorResolution=" + typeForValidatorResolution + "]"; + return "ParameterConstraintLocation [executable=" + executable + ", index=" + index + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ( ( executable == null ) ? 0 : executable.hashCode() ); + result = prime * result + executable.hashCode(); result = prime * result + index; return result; } @@ -89,12 +87,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 ( !executable.equals( other.executable ) ) { return false; } if ( index != other.index ) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/PropertyConstraintLocation.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/PropertyConstraintLocation.java new file mode 100644 index 0000000000..0eb4c5ca03 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/PropertyConstraintLocation.java @@ -0,0 +1,88 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.metadata.location; + +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.properties.Property; +import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; + +/** + * Property constraint location. + * + * @author Marko Bekhta + */ +public class PropertyConstraintLocation implements ConstraintLocation { + + /** + * The member the constraint was defined on. + */ + private final Property property; + + PropertyConstraintLocation(Property property) { + this.property = property; + } + + @Override + public Class getDeclaringClass() { + return property.getDeclaringClass(); + } + + @Override + public Constrainable getMember() { + 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.getPropertyName() ); + } + + @Override + public Object getValue(Object parent) { + return property.getValueFrom( parent ); + } + + @Override + public String toString() { + return "PropertyConstraintLocation [property=" + property + "]"; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + + PropertyConstraintLocation that = (PropertyConstraintLocation) 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/ReturnValueConstraintLocation.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ReturnValueConstraintLocation.java index b6ac18e2ca..f0526407c8 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,41 @@ */ 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 */ 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 ) ); + ReturnValueConstraintLocation(Callable executable) { + this.callable = executable; } @Override public Class getDeclaringClass() { - return executable.getDeclaringClass(); + return callable.getDeclaringClass(); } @Override - public Member getMember() { - return executable; + public Constrainable getMember() { + return callable; } @Override public Type getTypeForValidatorResolution() { - return typeForValidatorResolution; + return callable.getTypeForValidatorResolution(); } @Override @@ -57,14 +55,14 @@ public Object getValue(Object parent) { @Override public String toString() { - return "ReturnValueConstraintLocation [executable=" + executable + "]"; + return "ReturnValueConstraintLocation [executable=" + 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 +78,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..8d518cc347 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,7 +52,7 @@ public Class getDeclaringClass() { } @Override - public Member getMember() { + public Constrainable getMember() { return delegate.getMember(); } 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..605f4bc95e 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 @@ -60,11 +60,15 @@ 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.ConstrainedParameter; +import org.hibernate.validator.internal.metadata.raw.ConstrainedProperty; 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.Property; +import org.hibernate.validator.internal.properties.javabean.JavaBeanExecutable; +import org.hibernate.validator.internal.properties.javabean.JavaBeanField; 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; @@ -214,45 +218,46 @@ private Set getFieldMetaData(Class beanClass) { Set propertyMetaData = newHashSet(); for ( Field field : run( GetDeclaredFields.action( beanClass ) ) ) { + Property property = new JavaBeanField( field ); // HV-172 if ( Modifier.isStatic( field.getModifiers() ) || - annotationProcessingOptions.areMemberConstraintsIgnoredFor( field ) || + annotationProcessingOptions.areMemberConstraintsIgnoredFor( property ) || field.isSynthetic() ) { continue; } - propertyMetaData.add( findPropertyMetaData( field ) ); + propertyMetaData.add( findPropertyMetaData( field, property ) ); } return propertyMetaData; } - private ConstrainedField findPropertyMetaData(Field field) { + private ConstrainedProperty findPropertyMetaData(Field field, Property property) { Set> constraints = convertToMetaConstraints( - findConstraints( field, ElementType.FIELD ), - field + findConstraints( field, ElementType.FIELD, property ), + property ); CascadingMetaDataBuilder cascadingMetaDataBuilder = findCascadingMetaData( field ); - Set> typeArgumentsConstraints = findTypeAnnotationConstraints( field ); + Set> typeArgumentsConstraints = findTypeAnnotationConstraints( field, property ); - return new ConstrainedField( + return ConstrainedProperty.forField( ConfigurationSource.ANNOTATION, - field, + property, constraints, typeArgumentsConstraints, cascadingMetaDataBuilder ); } - private Set> convertToMetaConstraints(List> constraintDescriptors, Field field) { + private Set> convertToMetaConstraints(List> constraintDescriptors, Property property) { if ( constraintDescriptors.isEmpty() ) { return Collections.emptySet(); } Set> constraints = newHashSet(); - ConstraintLocation location = ConstraintLocation.forField( field ); + ConstraintLocation location = ConstraintLocation.forProperty( property ); for ( ConstraintDescriptorImpl constraintDescription : constraintDescriptors ) { constraints.add( MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintDescription, location ) ); @@ -297,20 +302,23 @@ private Set getMetaData(Executable[] executableElements) * given element. */ private ConstrainedExecutable findExecutableMetaData(Executable executable) { - List parameterConstraints = getParameterMetaData( executable ); + Callable callable = JavaBeanExecutable.of( executable ); + List parameterConstraints = getParameterMetaData( executable, callable ); - Map>> executableConstraints = findConstraints( executable, ExecutableHelper.getElementType( executable ) ) - .stream() - .collect( Collectors.groupingBy( ConstraintDescriptorImpl::getConstraintType ) ); + Map>> executableConstraints = findConstraints( + executable, + callable.isConstructor() ? ElementType.CONSTRUCTOR : ElementType.METHOD, + callable + ).stream().collect( Collectors.groupingBy( ConstraintDescriptorImpl::getConstraintType ) ); Set> crossParameterConstraints; - if ( annotationProcessingOptions.areCrossParameterConstraintsIgnoredFor( executable ) ) { + if ( annotationProcessingOptions.areCrossParameterConstraintsIgnoredFor( callable ) ) { crossParameterConstraints = Collections.emptySet(); } else { crossParameterConstraints = convertToMetaConstraints( executableConstraints.get( ConstraintType.CROSS_PARAMETER ), - executable + callable ); } @@ -318,7 +326,7 @@ private ConstrainedExecutable findExecutableMetaData(Executable executable) { Set> typeArgumentsConstraints; CascadingMetaDataBuilder cascadingMetaDataBuilder; - if ( annotationProcessingOptions.areReturnValueConstraintsIgnoredFor( executable ) ) { + if ( annotationProcessingOptions.areReturnValueConstraintsIgnoredFor( callable ) ) { returnValueConstraints = Collections.emptySet(); typeArgumentsConstraints = Collections.emptySet(); cascadingMetaDataBuilder = CascadingMetaDataBuilder.nonCascading(); @@ -326,17 +334,17 @@ private ConstrainedExecutable findExecutableMetaData(Executable executable) { else { AnnotatedType annotatedReturnType = executable.getAnnotatedReturnType(); - typeArgumentsConstraints = findTypeAnnotationConstraints( executable, annotatedReturnType ); + typeArgumentsConstraints = findTypeAnnotationConstraints( executable, callable, annotatedReturnType ); returnValueConstraints = convertToMetaConstraints( executableConstraints.get( ConstraintType.GENERIC ), - executable + callable ); cascadingMetaDataBuilder = findCascadingMetaData( executable, annotatedReturnType ); } return new ConstrainedExecutable( ConfigurationSource.ANNOTATION, - executable, + callable, parameterConstraints, crossParameterConstraints, returnValueConstraints, @@ -345,15 +353,15 @@ private ConstrainedExecutable findExecutableMetaData(Executable executable) { ); } - private Set> convertToMetaConstraints(List> constraintsDescriptors, Executable executable) { + private Set> convertToMetaConstraints(List> constraintsDescriptors, Callable callable) { if ( constraintsDescriptors == null ) { return Collections.emptySet(); } Set> constraints = newHashSet(); - 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; @@ -369,9 +377,10 @@ private Set> convertToMetaConstraints(List getParameterMetaData(Executable executable) { + private List getParameterMetaData(Executable executable, Callable callable) { if ( executable.getParameterCount() == 0 ) { return Collections.emptyList(); } @@ -393,12 +402,12 @@ private List getParameterMetaData(Executable executable) { Set> parameterConstraints = newHashSet(); - if ( annotationProcessingOptions.areParameterConstraintsIgnoredFor( executable, i ) ) { + if ( annotationProcessingOptions.areParameterConstraintsIgnoredFor( callable, i ) ) { Type type = ReflectionHelper.typeOf( executable, i ); metaData.add( new ConstrainedParameter( ConfigurationSource.ANNOTATION, - executable, + callable, type, i, parameterConstraints, @@ -410,12 +419,12 @@ private List getParameterMetaData(Executable executable) { continue; } - ConstraintLocation location = ConstraintLocation.forParameter( executable, i ); + ConstraintLocation location = ConstraintLocation.forParameter( callable, i ); for ( Annotation parameterAnnotation : parameterAnnotations ) { // collect constraints if this annotation is a constraint annotation List> constraints = findConstraintAnnotations( - executable, parameterAnnotation, ElementType.PARAMETER + callable, parameterAnnotation, ElementType.PARAMETER ); for ( ConstraintDescriptorImpl constraintDescriptorImpl : constraints ) { parameterConstraints.add( @@ -426,13 +435,13 @@ private List getParameterMetaData(Executable executable) { AnnotatedType parameterAnnotatedType = parameter.getAnnotatedType(); - Set> typeArgumentsConstraints = findTypeAnnotationConstraintsForExecutableParameter( executable, i, parameterAnnotatedType ); + Set> typeArgumentsConstraints = findTypeAnnotationConstraintsForExecutableParameter( executable, callable, i, parameterAnnotatedType ); CascadingMetaDataBuilder cascadingMetaData = findCascadingMetaData( executable, parameters, i, parameterAnnotatedType ); metaData.add( new ConstrainedParameter( ConfigurationSource.ANNOTATION, - executable, + callable, ReflectionHelper.typeOf( executable, i ), i, parameterConstraints, @@ -455,10 +464,10 @@ private List getParameterMetaData(Executable executable) { * * @return A list of constraint descriptors for all constraint specified for the given member. */ - private List> findConstraints(Member member, ElementType type) { + private List> findConstraints(Member member, ElementType type, Constrainable constrainable) { List> metaData = newArrayList(); for ( Annotation annotation : ( (AccessibleObject) member ).getDeclaredAnnotations() ) { - metaData.addAll( findConstraintAnnotations( member, annotation, type ) ); + metaData.addAll( findConstraintAnnotations( constrainable, annotation, type ) ); } return metaData; @@ -483,7 +492,7 @@ private List> findClassLevelConstraints(Class bea /** * 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 member 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,7 +500,8 @@ 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) { @@ -512,7 +522,7 @@ else if ( constraintHelper.isMultiValueConstraint( annotationType ) ) { } return constraints.stream() - .map( c -> buildConstraintDescriptor( member, c, type ) ) + .map( c -> buildConstraintDescriptor( constrainable, c, type ) ) .collect( Collectors.toList() ); } @@ -549,12 +559,12 @@ private Map, Class> getGroupConversions(ConvertGroup groupConversion return groupConversions; } - private ConstraintDescriptorImpl buildConstraintDescriptor(Member member, + private ConstraintDescriptorImpl buildConstraintDescriptor(Constrainable constrainable, A annotation, ElementType type) { return new ConstraintDescriptorImpl<>( constraintHelper, - member, + constrainable, new ConstraintAnnotationDescriptor<>( annotation ), type ); @@ -573,22 +583,22 @@ private T run(PrivilegedAction action) { /** * Finds type arguments constraints for fields. */ - protected Set> findTypeAnnotationConstraints(Field field) { + protected Set> findTypeAnnotationConstraints(Field field, Property property) { return findTypeArgumentsConstraints( - field, - new TypeArgumentFieldLocation( field ), - field.getAnnotatedType() + property, + new TypeArgumentFieldLocation( field ), + field.getAnnotatedType() ); } /** * Finds type arguments constraints for method return values. */ - protected Set> findTypeAnnotationConstraints(Executable executable, AnnotatedType annotatedReturnType) { + protected Set> findTypeAnnotationConstraints(Executable executable, Callable callable, AnnotatedType annotatedReturnType) { return findTypeArgumentsConstraints( - executable, - new TypeArgumentReturnValueLocation( executable ), - annotatedReturnType + callable, + new TypeArgumentReturnValueLocation( executable ), + annotatedReturnType ); } @@ -707,10 +717,10 @@ else if ( annotatedType instanceof AnnotatedParameterizedType ) { * * @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(Executable executable, Callable callable, int i, AnnotatedType parameterAnnotatedType) { try { return findTypeArgumentsConstraints( - executable, + callable, new TypeArgumentExecutableParameterLocation( executable, i ), parameterAnnotatedType ); @@ -721,7 +731,7 @@ protected Set> findTypeAnnotationConstraintsForExecutableParam } } - private Set> findTypeArgumentsConstraints(Member member, TypeArgumentLocation location, AnnotatedType annotatedType) { + private Set> findTypeArgumentsConstraints(Constrainable member, TypeArgumentLocation location, AnnotatedType annotatedType) { // HV-1428 Container element support is disabled for arrays if ( !(annotatedType instanceof AnnotatedParameterizedType) ) { return Collections.emptySet(); @@ -773,9 +783,9 @@ 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) { + private Set> findTypeUseConstraints(Constrainable constrainable, AnnotatedType typeArgument, TypeVariable typeVariable, TypeArgumentLocation location, Type type) { Set> constraints = Arrays.stream( typeArgument.getAnnotations() ) - .flatMap( a -> findConstraintAnnotations( member, a, ElementType.TYPE_USE ).stream() ) + .flatMap( a -> findConstraintAnnotations( constrainable, a, ElementType.TYPE_USE ).stream() ) .map( d -> createTypeArgumentMetaConstraint( d, location, typeVariable, type ) ) .collect( Collectors.toSet() ); @@ -801,7 +811,7 @@ private CascadingMetaDataBuilder getCascadingMetaData(Type type, AnnotatedElemen * 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#forProperty(Property)} tries to make the {@code Member} accessible * which might not be possible (for instance for {@code java.util} classes). */ private interface TypeArgumentLocation { @@ -820,7 +830,7 @@ private TypeArgumentExecutableParameterLocation(Executable executable, int index @Override public ConstraintLocation toConstraintLocation() { - return ConstraintLocation.forParameter( executable, index ); + return ConstraintLocation.forParameter( JavaBeanExecutable.of( executable ), index ); } } @@ -833,7 +843,7 @@ private TypeArgumentFieldLocation(Field field) { @Override public ConstraintLocation toConstraintLocation() { - return ConstraintLocation.forField( field ); + return ConstraintLocation.forProperty( new JavaBeanField( field ) ); } } @@ -846,7 +856,7 @@ private TypeArgumentReturnValueLocation(Executable executable) { @Override public ConstraintLocation toConstraintLocation() { - return ConstraintLocation.forReturnValue( executable ); + return ConstraintLocation.forReturnValue( JavaBeanExecutable.of( executable ) ); } } 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..30adba95dd 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 @@ -47,7 +47,7 @@ public interface ConstrainedElement extends Iterable> { * @author Gunnar Morling */ enum ConstrainedElementKind { - TYPE, FIELD, CONSTRUCTOR, METHOD, PARAMETER + TYPE, CONSTRUCTOR, METHOD, PARAMETER, PROPERTY } /** 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..f7d6b0c63f 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,9 @@ 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.properties.Property; 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 +38,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. @@ -59,7 +57,7 @@ public class ConstrainedExecutable extends AbstractConstrainedElement { * 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 +65,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 +84,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 +96,7 @@ public ConstrainedExecutable( */ public ConstrainedExecutable( ConfigurationSource source, - Executable executable, + Callable callable, List parameterMetaData, Set> crossParameterConstraints, Set> returnValueConstraints, @@ -106,18 +104,18 @@ public ConstrainedExecutable( CascadingMetaDataBuilder cascadingMetaDataBuilder) { super( source, - ( executable instanceof Constructor ) ? ConstrainedElementKind.CONSTRUCTOR : ConstrainedElementKind.METHOD, + callable.isConstructor() ? ConstrainedElementKind.CONSTRUCTOR : ConstrainedElementKind.METHOD, returnValueConstraints, typeArgumentConstraints, cascadingMetaDataBuilder ); - this.executable = executable; + this.callable = callable; - if ( parameterMetaData.size() != executable.getParameterTypes().length ) { + if ( parameterMetaData.size() != callable.getParameterTypes().length ) { throw LOG.getInvalidLengthOfParameterMetaDataListException( - executable, - executable.getParameterTypes().length, + callable, + callable.getParameterTypes().length, parameterMetaData.size() ); } @@ -125,7 +123,7 @@ public ConstrainedExecutable( this.crossParameterConstraints = CollectionHelper.toImmutableSet( crossParameterConstraints ); this.parameterMetaData = CollectionHelper.toImmutableList( parameterMetaData ); this.hasParameterConstraints = hasParameterConstraints( parameterMetaData ) || !crossParameterConstraints.isEmpty(); - this.isGetterMethod = ReflectionHelper.isGetterMethod( executable ); + this.isGetterMethod = callable instanceof Property; } /** @@ -142,7 +140,7 @@ public ConstrainedExecutable( public ConstrainedParameter getParameterMetaData(int parameterIndex) { if ( parameterIndex < 0 || parameterIndex > parameterMetaData.size() - 1 ) { throw LOG.getInvalidExecutableParameterIndexException( - executable, + callable, parameterIndex ); } @@ -202,13 +200,13 @@ 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 +282,7 @@ public ConstrainedExecutable merge(ConstrainedExecutable other) { return new ConstrainedExecutable( mergedSource, - executable, + callable, mergedParameterMetaData, mergedCrossParameterConstraints, mergedReturnValueConstraints, @@ -307,8 +305,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 +321,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 deleted file mode 100644 index cc889ae089..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedField.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Hibernate Validator, declare and validate application constraints - * - * License: Apache License, Version 2.0 - * See the license.txt file in the root directory or . - */ -package org.hibernate.validator.internal.metadata.raw; - -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; - -/** - * Represents a field of a Java type and all its associated meta-data relevant - * in the context of bean validation, for instance its constraints. - * - * @author Gunnar Morling - * @author Guillaume Smet - */ -public class ConstrainedField extends AbstractConstrainedElement { - - private final Field field; - - /** - * Creates a new field meta data object. - * - * @param source The source of meta data. - * @param field The represented field. - * @param constraints The constraints of the represented field, if any. - * @param typeArgumentConstraints Type arguments constraints, if any. - * @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) { - - 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 ) + "]"; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ( ( field == null ) ? 0 : field.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; - } - ConstrainedField other = (ConstrainedField) obj; - if ( field == null ) { - if ( other.field != null ) { - return false; - } - } - else if ( !field.equals( other.field ) ) { - return false; - } - return true; - } -} 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/metadata/raw/ConstrainedProperty.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedProperty.java new file mode 100644 index 0000000000..d57628c29b --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedProperty.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.metadata.raw; + +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.properties.Property; + +/** + * Represents a property of a Java type (either field or getter) and all its associated meta-data relevant + * in the context of bean validation, for instance its constraints. + * + * @author Marko Bekhta + */ +public class ConstrainedProperty extends AbstractConstrainedElement { + + private final Property property; + + /** + * Creates a new field meta data object. + * + * @param source The source of meta data. + * @param property The represented property. + * @param constraints The constraints of the represented field, if any. + * @param typeArgumentConstraints Type arguments constraints, if any. + * @param cascadingMetaDataBuilder The cascaded validation metadata for this element and its container elements. + */ + private ConstrainedProperty(ConfigurationSource source, + Property property, + Set> constraints, + Set> typeArgumentConstraints, + CascadingMetaDataBuilder cascadingMetaDataBuilder) { + + super( source, ConstrainedElementKind.PROPERTY, constraints, typeArgumentConstraints, cascadingMetaDataBuilder ); + + this.property = property; + } + + public static ConstrainedProperty forField(ConfigurationSource source, + Property property, + Set> constraints, + Set> typeArgumentConstraints, + CascadingMetaDataBuilder cascadingMetaDataBuilder) { + return new ConstrainedProperty( source, property, constraints, typeArgumentConstraints, cascadingMetaDataBuilder ); + } + + public Property getProperty() { + return property; + } + + @Override + public String toString() { + return "ConstrainedProperty [property=" + property.getName() + "]"; + } + + @Override public int hashCode() { + int result = super.hashCode(); + result = 31 * result + this.property.hashCode(); + return result; + } + + @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; + } + + ConstrainedProperty that = (ConstrainedProperty) o; + + return this.property.equals( that.property ); + } +} 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..d1b2c7f354 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/Callable.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.internal.properties; + +import java.lang.reflect.Type; + +import org.hibernate.validator.internal.util.ExecutableHelper; +import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; + +/** + * @author Marko Bekhta + */ +public interface Callable extends Constrainable { + + boolean hasReturnValue(); + + boolean hasParameters(); + + Class[] getParameterTypes(); + + Type[] getGenericParameterTypes(); + + String getParameterName(ExecutableParameterNameProvider parameterNameProvider, int parameterIndex); + + boolean isPrivate(); + + boolean isConstructor(); + + String getSignature(); + + Type typeOfParameter(int parameterIndex); + + 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..45dd703b92 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/Constrainable.java @@ -0,0 +1,27 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.properties; + +import java.lang.reflect.Type; + +/** + * @author Marko Bekhta + */ +public interface Constrainable { + + String getName(); + + Class getDeclaringClass(); + + Type getTypeForValidatorResolution(); + + Type getType(); + + default T as(Class clazz) { + return ( (T) this ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/ConstrainableType.java b/engine/src/main/java/org/hibernate/validator/internal/properties/ConstrainableType.java new file mode 100644 index 0000000000..01356e09de --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/ConstrainableType.java @@ -0,0 +1,14 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.properties; + +/** + * @author Marko Bekhta + */ +public interface ConstrainableType { + +} 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..f6f1d4c288 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/Property.java @@ -0,0 +1,17 @@ +/* + * 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 { + + Object getValueFrom(Object bean); + + String getPropertyName(); +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/PropertySelector.java b/engine/src/main/java/org/hibernate/validator/internal/properties/PropertySelector.java new file mode 100644 index 0000000000..0fcd264f50 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/PropertySelector.java @@ -0,0 +1,17 @@ +/* + * 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.util.stream.Stream; + +/** + * @author Marko Bekhta + */ +public interface PropertySelector { + + Stream getProperties(T type); +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBean.java b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBean.java new file mode 100644 index 0000000000..ff9dd436dc --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBean.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.util.Arrays; +import java.util.stream.Stream; + +import org.hibernate.validator.internal.properties.Property; +import org.hibernate.validator.internal.properties.ConstrainableType; +import org.hibernate.validator.internal.util.ReflectionHelper; + +/** + * @author Marko Bekhta + */ +public class JavaBean implements ConstrainableType { + + private final Class clazz; + + public JavaBean(Class clazz) { + this.clazz = clazz; + } + + public Stream getFieldProperties() { + return Arrays.stream( clazz.getDeclaredFields() ) + .map( JavaBeanField::new ); + } + + public Stream getGetterProperties() { + return Arrays.stream( clazz.getDeclaredMethods() ) + .filter( ReflectionHelper::isGetterMethod ) + .map( JavaBeanGetter::new ); + } +} 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..10ba6f195f --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanExecutable.java @@ -0,0 +1,187 @@ +/* + * 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.Executable; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; + +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.ReflectionHelper; +import org.hibernate.validator.internal.util.TypeHelper; + +/** + * @author Marko Bekhta + */ +public class JavaBeanExecutable implements Callable { + + protected final Executable executable; + private final Type typeForValidatorResolution; + private final String name; + private final boolean hasParameters; + private final boolean hasReturnValue; + private final Type type; + + JavaBeanExecutable(Executable executable) { + this.executable = executable; + this.name = executable.getName(); + this.type = ReflectionHelper.typeOf( executable ); + this.typeForValidatorResolution = ReflectionHelper.boxedType( type ); + this.hasParameters = executable.getParameterTypes().length > 0; + this.hasReturnValue = hasReturnValue( executable ); + } + + public static JavaBeanExecutable of(Executable executable) { + if ( ReflectionHelper.isGetterMethod( executable ) ) { + return new JavaBeanGetter( (Method) executable ); + } + else { + return new JavaBeanExecutable( executable ); + } + } + + @Override + public boolean hasReturnValue() { + return hasReturnValue; + } + + @Override + public boolean hasParameters() { + return hasParameters; + } + + @Override + public String getName() { + return name; + } + + @Override + public Class getDeclaringClass() { + return executable.getDeclaringClass(); + } + + @Override + public Type getTypeForValidatorResolution() { + return typeForValidatorResolution; + } + + @Override + public Type getType() { + return type; + } + + @Override + public Class[] getParameterTypes() { + return executable.getParameterTypes(); + } + + @Override + public Type[] getGenericParameterTypes() { + return executable.getGenericParameterTypes(); + } + + @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 boolean isConstructor() { + return executable instanceof Constructor; + } + + @Override + public String getSignature() { + return ExecutableHelper.getSignature( executable ); + } + + @Override + public Type typeOfParameter(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; + } + + @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 boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || this.getClass() != o.getClass() ) { + return false; + } + + JavaBeanExecutable that = (JavaBeanExecutable) o; + + if ( this.hasParameters != that.hasParameters ) { + return false; + } + if ( this.hasReturnValue != that.hasReturnValue ) { + return false; + } + if ( !this.executable.equals( that.executable ) ) { + return false; + } + if ( !this.typeForValidatorResolution.equals( that.typeForValidatorResolution ) ) { + return false; + } + if ( !this.name.equals( that.name ) ) { + 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.name.hashCode(); + result = 31 * result + ( this.hasParameters ? 1 : 0 ); + result = 31 * result + ( this.hasReturnValue ? 1 : 0 ); + result = 31 * result + this.type.hashCode(); + return result; + } + + private boolean hasReturnValue(Executable executable) { + if ( executable instanceof Constructor ) { + return true; + } + else { + return ( (Method) executable ).getGenericReturnType() != void.class; + } + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/FieldConstraintLocation.java b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanField.java similarity index 53% rename from engine/src/main/java/org/hibernate/validator/internal/metadata/location/FieldConstraintLocation.java rename to engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanField.java index 5b88190ba6..64160b052e 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/FieldConstraintLocation.java +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanField.java @@ -4,52 +4,38 @@ * License: Apache License, Version 2.0 * See the license.txt file in the root directory or . */ -package org.hibernate.validator.internal.metadata.location; +package org.hibernate.validator.internal.properties.javabean; 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.properties.Property; import org.hibernate.validator.internal.util.ReflectionHelper; -import org.hibernate.validator.internal.util.StringHelper; import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredField; /** - * Field constraint location. - * - * @author Hardy Ferentschik - * @author Gunnar Morling + * @author Marko Bekhta */ -public class FieldConstraintLocation implements ConstraintLocation { +public class JavaBeanField implements Property { - /** - * 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 String name; private final Type typeForValidatorResolution; + private final Type type; + public JavaBeanField(Field field) { + this.field = getAccessible( field ); + this.name = field.getName(); + this.type = ReflectionHelper.typeOf( field ); + this.typeForValidatorResolution = ReflectionHelper.boxedType( this.type ); + } - FieldConstraintLocation(Field field) { - this.field = field; - this.accessibleField = getAccessible( field ); - this.propertyName = ReflectionHelper.getPropertyName( field ); - this.typeForValidatorResolution = ReflectionHelper.boxedType( ReflectionHelper.typeOf( field ) ); + @Override + public String getName() { + return name; } @Override @@ -58,12 +44,8 @@ public Class getDeclaringClass() { } @Override - public Member getMember() { - return field; - } - - public String getPropertyName() { - return propertyName; + public Type getType() { + return type; } @Override @@ -72,19 +54,13 @@ public Type getTypeForValidatorResolution() { } @Override - public void appendTo(ExecutableParameterNameProvider parameterNameProvider, PathImpl path) { - path.addPropertyNode( propertyName ); + public Object getValueFrom(Object bean) { + return ReflectionHelper.getValue( field, bean ); } @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 ) + "]"; + public String getPropertyName() { + return getName(); } @Override @@ -92,26 +68,30 @@ public boolean equals(Object o) { if ( this == o ) { return true; } - if ( o == null || getClass() != o.getClass() ) { + if ( o == null || this.getClass() != o.getClass() ) { return false; } - FieldConstraintLocation that = (FieldConstraintLocation) o; + JavaBeanField that = (JavaBeanField) o; - if ( field != null ? !field.equals( that.field ) : that.field != null ) { + if ( !this.field.equals( that.field ) ) { return false; } - if ( !typeForValidatorResolution.equals( that.typeForValidatorResolution ) ) { + if ( !this.name.equals( that.name ) ) { return false; } - - return true; + if ( !this.typeForValidatorResolution.equals( that.typeForValidatorResolution ) ) { + return false; + } + return this.type.equals( that.type ); } @Override public int hashCode() { - int result = field != null ? field.hashCode() : 0; - result = 31 * result + typeForValidatorResolution.hashCode(); + int result = this.field.hashCode(); + result = 31 * result + this.name.hashCode(); + result = 31 * result + this.typeForValidatorResolution.hashCode(); + result = 31 * result + this.type.hashCode(); return result; } @@ -136,7 +116,7 @@ private static Field getAccessible(Field original) { /** * 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. */ diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/GetterConstraintLocation.java b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanGetter.java similarity index 50% rename from engine/src/main/java/org/hibernate/validator/internal/metadata/location/GetterConstraintLocation.java rename to engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanGetter.java index 0e59e27cf4..4356530af5 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/GetterConstraintLocation.java +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanGetter.java @@ -4,7 +4,7 @@ * License: Apache License, Version 2.0 * See the license.txt file in the root directory or . */ -package org.hibernate.validator.internal.metadata.location; +package org.hibernate.validator.internal.properties.javabean; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Method; @@ -13,78 +13,60 @@ import java.security.PrivilegedAction; import org.hibernate.validator.HibernateValidatorPermission; -import org.hibernate.validator.internal.engine.path.PathImpl; +import org.hibernate.validator.internal.properties.Property; 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; /** - * Getter method 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; +public class JavaBeanGetter extends JavaBeanExecutable implements Property { - /** - * 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; + private static final Class[] PARAMETER_TYPES = new Class[0]; + private final String name; - GetterConstraintLocation(Method method) { - this.method = method; - this.accessibleMethod = getAccessible( method ); - this.propertyName = ReflectionHelper.getPropertyName( method ); - this.typeForValidatorResolution = ReflectionHelper.boxedType( ReflectionHelper.typeOf( method ) ); + public JavaBeanGetter(Method method) { + super( getAccessible( method ) ); + this.name = ReflectionHelper.getPropertyName( method ); } @Override - public Class getDeclaringClass() { - return method.getDeclaringClass(); + public Object getValueFrom(Object bean) { + return ReflectionHelper.getValue( (Method) executable, bean ); } @Override - public Method getMember() { - return method; + public String getPropertyName() { + return name; } - public String getPropertyName() { - return propertyName; + @Override + public boolean hasReturnValue() { + // getters should always have a return value + return true; } @Override - public Type getTypeForValidatorResolution() { - return typeForValidatorResolution; + public boolean hasParameters() { + // getters should never have parameters + return false; } @Override - public void appendTo(ExecutableParameterNameProvider parameterNameProvider, PathImpl path) { - path.addPropertyNode( propertyName ); + public Class[] getParameterTypes() { + return PARAMETER_TYPES; } @Override - public Object getValue(Object parent) { - return ReflectionHelper.getValue( accessibleMethod, parent ); + public Type[] getGenericParameterTypes() { + return PARAMETER_TYPES; } @Override - public String toString() { - return "GetterConstraintLocation [method=" + StringHelper.toShortString( method ) + ", typeForValidatorResolution=" - + StringHelper.toShortString( typeForValidatorResolution ) + "]"; + public String getParameterName(ExecutableParameterNameProvider parameterNameProvider, int parameterIndex) { + throw new IllegalStateException( "Getters cannot have parameters" ); } @Override @@ -92,26 +74,22 @@ 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 ) { + if ( o == null || this.getClass() != o.getClass() ) { return false; } - if ( !typeForValidatorResolution.equals( that.typeForValidatorResolution ) ) { + if ( !super.equals( o ) ) { return false; } - return true; + JavaBeanGetter that = (JavaBeanGetter) o; + + return this.name.equals( that.name ); } @Override public int hashCode() { - int result = method.hashCode(); - result = 31 * result + typeForValidatorResolution.hashCode(); + int result = super.hashCode(); + result = 31 * result + this.name.hashCode(); return result; } @@ -137,7 +115,7 @@ private static Method getAccessible(Method original) { /** * 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. */ 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/ExecutableHelper.java b/engine/src/main/java/org/hibernate/validator/internal/util/ExecutableHelper.java index 0ae6378a5a..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,7 @@ 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; @@ -46,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}. * 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..d8722e0027 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 @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; +import org.hibernate.validator.internal.properties.Callable; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; 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 d0cba30970..40b759d040 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 @@ -48,10 +48,13 @@ 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.properties.Callable; +import org.hibernate.validator.internal.properties.Constrainable; 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; +import org.hibernate.validator.internal.util.logging.formatter.ConstrainableFormatter; import org.hibernate.validator.internal.util.logging.formatter.DurationFormatter; import org.hibernate.validator.internal.util.logging.formatter.ExecutableFormatter; import org.hibernate.validator.internal.util.logging.formatter.ObjectArrayFormatter; @@ -248,14 +251,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(@FormatWith(ConstrainableFormatter.class) 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(@FormatWith(ConstrainableFormatter.class) Callable callable, int nbParameters, int listSize); @Message(id = 63, value = "Unable to instantiate %s.") ValidationException getUnableToInstantiateException(@FormatWith(ClassObjectFormatter.class) Class clazz, @Cause Exception e); @@ -461,11 +463,11 @@ 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(@FormatWith(ConstrainableFormatter.class) Callable executable1, @FormatWith(ConstrainableFormatter.class) Callable executable2); @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(@FormatWith(ConstrainableFormatter.class) 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, @@ -500,7 +502,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, @FormatWith(ConstrainableFormatter.class) Constrainable executable); @Message(id = 143, value = "Cross parameter constraint %1$s is illegally placed on class level.") @@ -509,7 +511,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); + @FormatWith(ConstrainableFormatter.class) Constrainable field); @Message(id = 146, value = "No parameter nodes may be added since path %s doesn't refer to a cross-parameter constraint.") @@ -535,11 +537,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(@FormatWith(ConstrainableFormatter.class) Callable superMethod, @FormatWith(ConstrainableFormatter.class) 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(@FormatWith(ConstrainableFormatter.class) Callable method1, @FormatWith(ConstrainableFormatter.class) Callable method2); @Message(id = 153, value = "The constraint %1$s used ConstraintTarget#%2$s but is not specified on a method or constructor.") @@ -581,7 +583,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(@FormatWith(ConstrainableFormatter.class) Callable method1, @FormatWith(ConstrainableFormatter.class) Callable method2); @Message(id = 162, value = "The validated type %1$s does not specify the constructor/method: %2$s") @@ -625,15 +627,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, @FormatWith(ConstrainableFormatter.class) 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, @FormatWith(ConstrainableFormatter.class) Callable callable); @Message(id = 176, value = "Constructor %2$s of type %1$s is configured more than once via the programmatic constraint declaration API.") @@ -641,7 +643,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, @FormatWith(ConstrainableFormatter.class) Callable callable); @Message(id = 178, value = "Multiplier cannot be negative: %d.") IllegalArgumentException getMultiplierCannotBeNegativeException(int multiplier); @@ -714,7 +716,7 @@ 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(@FormatWith(ConstrainableFormatter.class) 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); diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/logging/formatter/ConstrainableFormatter.java b/engine/src/main/java/org/hibernate/validator/internal/util/logging/formatter/ConstrainableFormatter.java new file mode 100644 index 0000000000..d5f8ea6f1a --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/util/logging/formatter/ConstrainableFormatter.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.internal.util.logging.formatter; + +import org.hibernate.validator.internal.properties.Callable; +import org.hibernate.validator.internal.properties.Constrainable; +import org.hibernate.validator.internal.util.ExecutableHelper; + +/** + * Used with JBoss Logging to display executables in log messages. + * + * @author Marko Bekhta + */ +public class ConstrainableFormatter { + + private final String stringRepresentation; + + public ConstrainableFormatter(Constrainable constrainable) { + String name = constrainable.getName(); + if ( constrainable instanceof Callable ) { + name = constrainable.getDeclaringClass().getSimpleName() + "#" + name; + Class[] parameterTypes = ( (Callable) constrainable ).getParameterTypes(); + + this.stringRepresentation = ExecutableHelper.getExecutableAsString( name, parameterTypes ); + } + else { + this.stringRepresentation = name; + } + } + + @Override + public String toString() { + return stringRepresentation; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedConstructorStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedConstructorStaxBuilder.java index d29312b7b8..1807e03322 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedConstructorStaxBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedConstructorStaxBuilder.java @@ -26,6 +26,7 @@ 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.JavaBeanExecutable; import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.logging.Log; @@ -84,6 +85,7 @@ ConstrainedExecutable build(Class beanClass, List> alreadyProc parameterTypes ); } + JavaBeanExecutable executable = JavaBeanExecutable.of( constructor ); if ( alreadyProcessedConstructors.contains( constructor ) ) { throw LOG.getConstructorIsDefinedTwiceInMappingXmlForBeanException( constructor, beanClass ); @@ -95,7 +97,7 @@ ConstrainedExecutable build(Class beanClass, List> alreadyProc // ignore annotations if ( ignoreAnnotations.isPresent() ) { annotationProcessingOptions.ignoreConstraintAnnotationsOnMember( - constructor, + executable, ignoreAnnotations.get() ); } @@ -103,21 +105,21 @@ ConstrainedExecutable build(Class beanClass, List> alreadyProc List constrainedParameters = CollectionHelper.newArrayList( constrainedParameterStaxBuilders.size() ); for ( int index = 0; index < constrainedParameterStaxBuilders.size(); index++ ) { ConstrainedParameterStaxBuilder builder = constrainedParameterStaxBuilders.get( index ); - constrainedParameters.add( builder.build( constructor, index ) ); + constrainedParameters.add( builder.build( executable, index ) ); } Set> crossParameterConstraints = getCrossParameterStaxBuilder() - .map( builder -> builder.build( constructor ) ).orElse( Collections.emptySet() ); + .map( builder -> builder.build( executable ) ).orElse( Collections.emptySet() ); // parse the return value Set> returnValueConstraints = new HashSet<>(); Set> returnValueTypeArgumentConstraints = new HashSet<>(); - CascadingMetaDataBuilder cascadingMetaDataBuilder = getReturnValueStaxBuilder().map( builder -> builder.build( constructor, returnValueConstraints, returnValueTypeArgumentConstraints ) ) + CascadingMetaDataBuilder cascadingMetaDataBuilder = getReturnValueStaxBuilder().map( builder -> builder.build( executable, returnValueConstraints, returnValueTypeArgumentConstraints ) ) .orElse( CascadingMetaDataBuilder.nonCascading() ); return new ConstrainedExecutable( ConfigurationSource.XML, - constructor, + executable, constrainedParameters, crossParameterConstraints, returnValueConstraints, 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 index 8b2a8a97be..856f7b3476 100644 --- 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 @@ -23,7 +23,8 @@ 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.metadata.raw.ConstrainedProperty; +import org.hibernate.validator.internal.properties.javabean.JavaBeanField; import org.hibernate.validator.internal.util.ReflectionHelper; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.logging.Log; @@ -61,7 +62,7 @@ protected String getAcceptableQName() { return FIELD_QNAME_LOCAL_PART; } - ConstrainedField build(Class beanClass, List alreadyProcessedFieldNames) { + ConstrainedProperty build(Class beanClass, List alreadyProcessedFieldNames) { if ( alreadyProcessedFieldNames.contains( mainAttributeValue ) ) { throw LOG.getIsDefinedTwiceInMappingXmlForBeanException( mainAttributeValue, beanClass ); } @@ -69,17 +70,19 @@ ConstrainedField build(Class beanClass, List alreadyProcessedFieldNam alreadyProcessedFieldNames.add( mainAttributeValue ); } Field field = findField( beanClass, mainAttributeValue ); - ConstraintLocation constraintLocation = ConstraintLocation.forField( field ); + JavaBeanField property = new JavaBeanField( field ); + ConstraintLocation constraintLocation = ConstraintLocation.forProperty( property ); + Set> metaConstraints = constraintTypeStaxBuilders.stream() .map( builder -> builder.build( constraintLocation, java.lang.annotation.ElementType.FIELD, null ) ) .collect( Collectors.toSet() ); ContainerElementTypeConfiguration containerElementTypeConfiguration = getContainerElementTypeConfiguration( - ReflectionHelper.typeOf( field ), constraintLocation ); + property.getType(), constraintLocation ); - ConstrainedField constrainedField = new ConstrainedField( + ConstrainedProperty constrainedField = ConstrainedProperty.forField( ConfigurationSource.XML, - field, + property, metaConstraints, containerElementTypeConfiguration.getMetaConstraints(), getCascadingMetaData( containerElementTypeConfiguration.getTypeParametersCascadingMetaData(), ReflectionHelper.typeOf( field ) ) @@ -88,7 +91,7 @@ ConstrainedField build(Class beanClass, List alreadyProcessedFieldNam // ignore annotations if ( ignoreAnnotations.isPresent() ) { annotationProcessingOptions.ignoreConstraintAnnotationsOnMember( - field, + property, ignoreAnnotations.get() ); } 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 index 92ee72d0de..390efd5129 100644 --- 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 @@ -26,6 +26,9 @@ 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.Callable; +import org.hibernate.validator.internal.properties.Property; +import org.hibernate.validator.internal.properties.javabean.JavaBeanGetter; import org.hibernate.validator.internal.util.ReflectionHelper; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.logging.Log; @@ -71,7 +74,8 @@ ConstrainedExecutable build(Class beanClass, List alreadyProcessedGet alreadyProcessedGetterNames.add( mainAttributeValue ); } Method getter = findGetter( beanClass, mainAttributeValue ); - ConstraintLocation constraintLocation = ConstraintLocation.forGetter( getter ); + Property property = new JavaBeanGetter( getter ); + ConstraintLocation constraintLocation = ConstraintLocation.forProperty( property ); Set> metaConstraints = constraintTypeStaxBuilders.stream() .map( builder -> builder.build( constraintLocation, java.lang.annotation.ElementType.METHOD, null ) ) @@ -82,7 +86,7 @@ ConstrainedExecutable build(Class beanClass, List alreadyProcessedGet ConstrainedExecutable constrainedGetter = new ConstrainedExecutable( ConfigurationSource.XML, - getter, + property.as( Callable.class ), Collections.emptyList(), Collections.>emptySet(), metaConstraints, @@ -93,7 +97,7 @@ ConstrainedExecutable build(Class beanClass, List alreadyProcessedGet // ignore annotations if ( ignoreAnnotations.isPresent() ) { annotationProcessingOptions.ignoreConstraintAnnotationsOnMember( - getter, + property, ignoreAnnotations.get() ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedMethodStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedMethodStaxBuilder.java index ede789a730..f1876750c4 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedMethodStaxBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedMethodStaxBuilder.java @@ -26,6 +26,7 @@ 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.JavaBeanExecutable; import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.logging.Log; @@ -96,11 +97,12 @@ ConstrainedExecutable build(Class beanClass, List alreadyProcessedMet else { alreadyProcessedMethods.add( method ); } + JavaBeanExecutable executable = JavaBeanExecutable.of( method ); // ignore annotations if ( ignoreAnnotations.isPresent() ) { annotationProcessingOptions.ignoreConstraintAnnotationsOnMember( - method, + executable, ignoreAnnotations.get() ); } @@ -108,21 +110,21 @@ ConstrainedExecutable build(Class beanClass, List alreadyProcessedMet List constrainedParameters = CollectionHelper.newArrayList( constrainedParameterStaxBuilders.size() ); for ( int index = 0; index < constrainedParameterStaxBuilders.size(); index++ ) { ConstrainedParameterStaxBuilder builder = constrainedParameterStaxBuilders.get( index ); - constrainedParameters.add( builder.build( method, index ) ); + constrainedParameters.add( builder.build( executable, index ) ); } Set> crossParameterConstraints = getCrossParameterStaxBuilder() - .map( builder -> builder.build( method ) ).orElse( Collections.emptySet() ); + .map( builder -> builder.build( executable ) ).orElse( Collections.emptySet() ); // parse the return value Set> returnValueConstraints = new HashSet<>(); Set> returnValueTypeArgumentConstraints = new HashSet<>(); - CascadingMetaDataBuilder cascadingMetaDataBuilder = getReturnValueStaxBuilder().map( builder -> builder.build( method, returnValueConstraints, returnValueTypeArgumentConstraints ) ) + CascadingMetaDataBuilder cascadingMetaDataBuilder = getReturnValueStaxBuilder().map( builder -> builder.build( executable, returnValueConstraints, returnValueTypeArgumentConstraints ) ) .orElse( CascadingMetaDataBuilder.nonCascading() ); return new ConstrainedExecutable( ConfigurationSource.XML, - method, + executable, constrainedParameters, crossParameterConstraints, returnValueConstraints, 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 index cf5a77d690..3fc83dbdc1 100644 --- 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 @@ -23,6 +23,7 @@ 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.properties.Callable; import org.hibernate.validator.internal.util.ReflectionHelper; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.logging.Log; @@ -68,10 +69,10 @@ public Class getParameterType(Class beanClass) { } } - ConstrainedParameter build(Executable executable, int index) { + ConstrainedParameter build(Callable callable, int index) { - ConstraintLocation constraintLocation = ConstraintLocation.forParameter( executable, index ); - Type type = ReflectionHelper.typeOf( executable, index ); + ConstraintLocation constraintLocation = ConstraintLocation.forParameter( callable, index ); + Type type = callable.typeOfParameter( index ); Set> metaConstraints = constraintTypeStaxBuilders.stream() .map( builder -> builder.build( constraintLocation, java.lang.annotation.ElementType.PARAMETER, null ) ) @@ -82,7 +83,7 @@ ConstrainedParameter build(Executable executable, int index) { // ignore annotations if ( ignoreAnnotations.isPresent() ) { annotationProcessingOptions.ignoreConstraintAnnotationsOnParameter( - executable, + callable, index, ignoreAnnotations.get() ); @@ -90,7 +91,7 @@ ConstrainedParameter build(Executable executable, int index) { ConstrainedParameter constrainedParameter = new ConstrainedParameter( ConfigurationSource.XML, - executable, + callable, type, index, metaConstraints, 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 index 30397e1009..ce40f9625f 100644 --- 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 @@ -6,7 +6,6 @@ */ package org.hibernate.validator.internal.xml.mapping; -import java.lang.reflect.Executable; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -24,6 +23,7 @@ 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.properties.Callable; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.xml.AbstractStaxBuilder; @@ -83,9 +83,9 @@ private ConstraintTypeStaxBuilder getNewConstraintTypeStaxBuilder() { return new ConstraintTypeStaxBuilder( classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, defaultPackageStaxBuilder ); } - Set> build(Executable executable) { + Set> build(Callable callable) { - ConstraintLocation constraintLocation = ConstraintLocation.forCrossParameter( executable ); + ConstraintLocation constraintLocation = ConstraintLocation.forCrossParameter( callable ); Set> crossParameterConstraints = constraintTypeStaxBuilders.stream() .map( builder -> builder.build( constraintLocation, java.lang.annotation.ElementType.METHOD, ConstraintType.CROSS_PARAMETER ) ) @@ -94,7 +94,7 @@ Set> build(Executable executable) { // ignore annotations if ( ignoreAnnotations.isPresent() ) { annotationProcessingOptions.ignoreConstraintAnnotationsForCrossParameterConstraint( - executable, + callable, ignoreAnnotations.get() ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ReturnValueStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ReturnValueStaxBuilder.java index 85cae903e2..b9458a5fdf 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ReturnValueStaxBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ReturnValueStaxBuilder.java @@ -6,7 +6,7 @@ */ package org.hibernate.validator.internal.xml.mapping; -import java.lang.reflect.Executable; +import java.lang.annotation.ElementType; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -20,8 +20,7 @@ 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.util.ExecutableHelper; -import org.hibernate.validator.internal.util.ReflectionHelper; +import org.hibernate.validator.internal.properties.Callable; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.xml.mapping.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration; @@ -51,27 +50,27 @@ protected String getAcceptableQName() { } CascadingMetaDataBuilder build( - Executable executable, + Callable callable, Set> returnValueConstraints, Set> returnValueTypeArgumentConstraints) { - ConstraintLocation constraintLocation = ConstraintLocation.forReturnValue( executable ); + ConstraintLocation constraintLocation = ConstraintLocation.forReturnValue( callable ); returnValueConstraints.addAll( constraintTypeStaxBuilders.stream() - .map( builder -> builder.build( constraintLocation, ExecutableHelper.getElementType( executable ), ConstraintDescriptorImpl.ConstraintType.GENERIC ) ) + .map( builder -> builder.build( constraintLocation, callable.isConstructor() ? ElementType.CONSTRUCTOR : ElementType.METHOD, ConstraintDescriptorImpl.ConstraintType.GENERIC ) ) .collect( Collectors.toSet() ) ); - ContainerElementTypeConfiguration containerElementTypeConfiguration = getContainerElementTypeConfiguration( ReflectionHelper.typeOf( executable ), constraintLocation ); + ContainerElementTypeConfiguration containerElementTypeConfiguration = getContainerElementTypeConfiguration( callable.getType(), constraintLocation ); returnValueTypeArgumentConstraints.addAll( containerElementTypeConfiguration.getMetaConstraints() ); // ignore annotations if ( ignoreAnnotations.isPresent() ) { annotationProcessingOptions.ignoreConstraintAnnotationsForReturnValue( - executable, + callable, ignoreAnnotations.get() ); } - return getCascadingMetaData( containerElementTypeConfiguration.getTypeParametersCascadingMetaData(), ReflectionHelper.typeOf( executable ) ); + return getCascadingMetaData( containerElementTypeConfiguration.getTypeParametersCascadingMetaData(), callable.getType() ); } } 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..9417c0c30b 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 @@ -56,7 +56,7 @@ 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.ConstrainedProperty; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider; import org.hibernate.validator.testutils.ValidatorUtil; @@ -553,11 +553,11 @@ private BeanConfiguration getBeanConfiguration(Class type) { return null; } - private ConstrainedField getConstrainedField(BeanConfiguration beanConfiguration, String fieldName) { + private ConstrainedProperty getConstrainedField(BeanConfiguration beanConfiguration, String fieldName) { for ( ConstrainedElement constrainedElement : beanConfiguration.getConstrainedElements() ) { - if ( constrainedElement.getKind() == ConstrainedElementKind.FIELD && - ( (ConstrainedField) constrainedElement ).getField().getName().equals( fieldName ) ) { - return (ConstrainedField) constrainedElement; + if ( constrainedElement.getKind() == ConstrainedElementKind.PROPERTY && + ( (ConstrainedProperty) constrainedElement ).getProperty().getPropertyName().equals( fieldName ) ) { + return (ConstrainedProperty) constrainedElement; } } @@ -567,7 +567,7 @@ 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 ) ) { + ( (ConstrainedExecutable) constrainedElement ).getCallable().getName().equals( executableName ) ) { return (ConstrainedExecutable) constrainedElement; } } 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..16fc4e3062 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 @@ -20,6 +20,7 @@ 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.properties.javabean.JavaBeanExecutable; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; import org.hibernate.validator.testutil.TestForIssue; @@ -49,14 +50,14 @@ public void setUp() throws Exception { @TestForIssue(jiraKey = "HV-930") public void two_meta_constraints_for_the_same_constraint_should_be_equal() throws Exception { ConstraintDescriptorImpl constraintDescriptor1 = new ConstraintDescriptorImpl<>( - constraintHelper, barMethod, constraintAnnotationDescriptor, METHOD + constraintHelper, JavaBeanExecutable.of( barMethod ), constraintAnnotationDescriptor, METHOD ); ConstraintLocation location1 = ConstraintLocation.forClass( Foo.class ); MetaConstraint metaConstraint1 = MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintDescriptor1, location1 ); ConstraintDescriptorImpl constraintDescriptor2 = new ConstraintDescriptorImpl<>( - constraintHelper, barMethod, constraintAnnotationDescriptor, METHOD + constraintHelper, JavaBeanExecutable.of( barMethod ), constraintAnnotationDescriptor, METHOD ); ConstraintLocation location2 = ConstraintLocation.forClass( Foo.class ); MetaConstraint metaConstraint2 = MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintDescriptor2, location2 ); 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..acf7934c4b 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.forProperty( new JavaBeanGetter( getter ) ); + ConstraintLocation location2 = ConstraintLocation.forProperty( new JavaBeanGetter( getter ) ); 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..be827dcde9 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 @@ -38,8 +38,9 @@ import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; 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.ConstrainedProperty; import org.hibernate.validator.internal.metadata.raw.ConstrainedType; +import org.hibernate.validator.internal.properties.javabean.JavaBeanExecutable; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.testutil.TestForIssue; import org.joda.time.DateMidnight; @@ -101,11 +102,13 @@ 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( + JavaBeanExecutable.of( + Calendar.class.getMethod( + "createEvent", + DateMidnight.class, + DateMidnight.class + ) ) ); @@ -129,7 +132,7 @@ public void configurationsHaveAnnotationSource() { public void noGroupConversionOnField() throws Exception { //when BeanConfiguration beanConfiguration = provider.getBeanConfiguration( User.class ); - ConstrainedField field = findConstrainedField( beanConfiguration, User.class, "mail" ); + ConstrainedProperty field = findConstrainedField( beanConfiguration, User.class, "mail" ); //then assertThat( field.getCascadingMetaDataBuilder().getGroupConversions() ).isEmpty(); @@ -139,7 +142,7 @@ public void noGroupConversionOnField() throws Exception { public void singleGroupConversionOnField() throws Exception { //when BeanConfiguration beanConfiguration = provider.getBeanConfiguration( User.class ); - ConstrainedField field = findConstrainedField( beanConfiguration, User.class, "phone" ); + ConstrainedProperty field = findConstrainedField( beanConfiguration, User.class, "phone" ); //then Map, Class> expected = newHashMap(); @@ -152,7 +155,7 @@ public void singleGroupConversionOnField() throws Exception { public void multipleGroupConversionsOnField() throws Exception { //when BeanConfiguration beanConfiguration = provider.getBeanConfiguration( User.class ); - ConstrainedField field = findConstrainedField( beanConfiguration, User.class, "address" ); + ConstrainedProperty field = findConstrainedField( beanConfiguration, User.class, "address" ); //then Map, Class> expected = newHashMap(); 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..14b8b4b600 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 @@ -14,18 +14,21 @@ 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.ConstrainedProperty; import org.hibernate.validator.internal.metadata.raw.ConstrainedType; +import org.hibernate.validator.internal.properties.Constrainable; +import org.hibernate.validator.internal.properties.javabean.JavaBeanExecutable; +import org.hibernate.validator.internal.properties.javabean.JavaBeanField; /** * @author Gunnar Morling */ public abstract class AnnotationMetaDataProviderTestBase { - protected ConstrainedField findConstrainedField(BeanConfiguration beanConfiguration, + protected ConstrainedProperty findConstrainedField(BeanConfiguration beanConfiguration, Class clazz, String fieldName) throws Exception { - return (ConstrainedField) findConstrainedElement( beanConfiguration, clazz.getDeclaredField( fieldName ) ); + return (ConstrainedProperty) findConstrainedElement( beanConfiguration, clazz.getDeclaredField( fieldName ) ); } protected ConstrainedExecutable findConstrainedMethod(BeanConfiguration beanConfiguration, @@ -59,14 +62,22 @@ protected ConstrainedType findConstrainedType(BeanConfiguration beanConfi protected ConstrainedElement findConstrainedElement(BeanConfiguration beanConfiguration, Member member) { + Constrainable constrainable; + if ( member instanceof Field ) { + constrainable = new JavaBeanField( (Field) member ); + } + else { + constrainable = JavaBeanExecutable.of( (Executable) member ); + } + 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 ConstrainedProperty ) { + if ( constrainable.equals( ( (ConstrainedProperty) constrainedElement ).getProperty() ) ) { 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..38078e2804 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 @@ -25,8 +25,8 @@ 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.metadata.raw.ConstrainedProperty; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -54,7 +54,7 @@ public void setup() { public void testFieldTypeArgument() throws Exception { BeanConfiguration beanConfiguration = provider.getBeanConfiguration( A.class ); - ConstrainedField field = findConstrainedField( beanConfiguration, A.class, "names" ); + ConstrainedProperty field = findConstrainedField( beanConfiguration, A.class, "names" ); assertThat( field.getTypeArgumentConstraints().size() ).isEqualTo( 2 ); assertThat( getAnnotationsTypes( field.getTypeArgumentConstraints() ) ).contains( NotNull.class, NotBlank.class From dd51f80e329ce8f277246dc59b980c2e2448c43c Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Mon, 7 May 2018 20:04:49 +0200 Subject: [PATCH 2/6] fixup! HV-1363 Build abstraction over reflection in ConstraintLocation and related code --- .../ExecutableConstraintMappingContextImpl.java | 2 +- .../ParameterConstraintMappingContextImpl.java | 2 +- .../TypeConstraintMappingContextImpl.java | 6 +++--- .../engine/ConstraintViolationImpl.java | 7 ------- .../location/ParameterConstraintLocation.java | 2 +- .../location/ReturnValueConstraintLocation.java | 4 ++-- .../metadata/raw/ConstrainedProperty.java | 2 +- .../validator/internal/properties/Callable.java | 2 +- .../internal/properties/Constrainable.java | 1 + .../properties/javabean/JavaBeanExecutable.java | 2 +- .../properties/javabean/JavaBeanGetter.java | 2 +- .../internal/util/ReflectionHelper.java | 1 - .../ConstrainedParameterStaxBuilder.java | 4 +--- .../internal/engine/failfast/FailFastTest.java | 10 ++++++---- .../testutil/ConstraintViolationAssert.java | 17 ++++++++++++++++- 15 files changed, 36 insertions(+), 28 deletions(-) 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 8f6635aaed..9cb9d826a2 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 @@ -130,7 +130,7 @@ private List getParameters(ConstraintHelper constraintHelp new ConstrainedParameter( ConfigurationSource.API, callable, - callable.typeOfParameter( i ), + callable.getTypeOfParameter( i ), i ) ); 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 a2af84344c..1f8bc55dc5 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 @@ -121,7 +121,7 @@ public ContainerElementConstraintMappingContext containerElementType(int index, public ConstrainedParameter build(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager) { - Type parameterType = executableContext.getCallable().typeOfParameter( parameterIndex ); + Type parameterType = executableContext.getCallable().getTypeOfParameter( parameterIndex ); return new ConstrainedParameter( ConfigurationSource.API, 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 b0aa93bdd4..ffd42b5307 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 @@ -255,13 +255,13 @@ protected ConstraintType getConstraintType() { } /** - * Returns the member with the given name and type. + * Returns the property with the given name and type. * - * @param clazz The class from which to retrieve the member. Cannot be {@code null}. + * @param clazz The class from which to retrieve the property. 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. + * @return the property which matches the name and type or {@code null} if no such property exists. */ private Property getProperty(Class clazz, String property, ElementType elementType) { Contracts.assertNotNull( clazz, MESSAGES.classCannotBeNull() ); 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..59e3ee04c9 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 @@ -39,7 +39,6 @@ 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; @@ -161,7 +160,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 +294,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 +325,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/metadata/location/ParameterConstraintLocation.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ParameterConstraintLocation.java index 80e2060820..8a72f94b26 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 @@ -29,7 +29,7 @@ public class ParameterConstraintLocation implements ConstraintLocation { public ParameterConstraintLocation(Callable executable, int index) { this.executable = executable; this.index = index; - this.typeForValidatorResolution = ReflectionHelper.boxedType( executable.typeOfParameter( index ) ); + this.typeForValidatorResolution = ReflectionHelper.boxedType( executable.getTypeOfParameter( index ) ); } @Override 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 f0526407c8..f75c4289fa 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 @@ -24,8 +24,8 @@ class ReturnValueConstraintLocation implements ConstraintLocation { private final Callable callable; - ReturnValueConstraintLocation(Callable executable) { - this.callable = executable; + ReturnValueConstraintLocation(Callable callable) { + this.callable = callable; } @Override diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedProperty.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedProperty.java index d57628c29b..adc64608a1 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedProperty.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedProperty.java @@ -13,7 +13,7 @@ import org.hibernate.validator.internal.properties.Property; /** - * Represents a property of a Java type (either field or getter) and all its associated meta-data relevant + * Represents a property of a Java type (either field or getter) and all its associated metadata relevant * in the context of bean validation, for instance its constraints. * * @author Marko Bekhta 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 index d1b2c7f354..b25586c4b1 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/properties/Callable.java +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/Callable.java @@ -32,7 +32,7 @@ public interface Callable extends Constrainable { String getSignature(); - Type typeOfParameter(int parameterIndex); + Type getTypeOfParameter(int parameterIndex); boolean overrides(ExecutableHelper executableHelper, 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 index 45dd703b92..a4ab094455 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/properties/Constrainable.java +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/Constrainable.java @@ -21,6 +21,7 @@ public interface Constrainable { Type getType(); + @SuppressWarnings("unchecked") default T as(Class clazz) { return ( (T) this ); } 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 index 10ba6f195f..afe1e9211a 100644 --- 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 @@ -110,7 +110,7 @@ public String getSignature() { } @Override - public Type typeOfParameter(int parameterIndex) { + public Type getTypeOfParameter(int parameterIndex) { Type[] genericParameterTypes = executable.getGenericParameterTypes(); // getGenericParameterTypes() doesn't return synthetic parameters; in this case fall back to getParameterTypes() 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 index 4356530af5..eadbecfe21 100644 --- 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 @@ -23,7 +23,7 @@ */ public class JavaBeanGetter extends JavaBeanExecutable implements Property { - private static final Class[] PARAMETER_TYPES = new Class[0]; + private static final Class[] PARAMETER_TYPES = new Class[0]; private final String name; 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 d8722e0027..f964a54d6a 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 @@ -24,7 +24,6 @@ import java.util.List; import java.util.Map; -import org.hibernate.validator.internal.properties.Callable; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; 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 index 3fc83dbdc1..5344231f1c 100644 --- 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 @@ -7,7 +7,6 @@ package org.hibernate.validator.internal.xml.mapping; import java.lang.invoke.MethodHandles; -import java.lang.reflect.Executable; import java.lang.reflect.Type; import java.util.Optional; import java.util.Set; @@ -24,7 +23,6 @@ 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.ReflectionHelper; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; @@ -72,7 +70,7 @@ public Class getParameterType(Class beanClass) { ConstrainedParameter build(Callable callable, int index) { ConstraintLocation constraintLocation = ConstraintLocation.forParameter( callable, index ); - Type type = callable.typeOfParameter( index ); + Type type = callable.getTypeOfParameter( index ); Set> metaConstraints = constraintTypeStaxBuilders.stream() .map( builder -> builder.build( constraintLocation, java.lang.annotation.ElementType.PARAMETER, null ) ) 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..29721e713f 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 @@ -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/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..acc4129e99 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 @@ -262,6 +262,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; @@ -282,7 +297,7 @@ public void containsOnlyViolations(ViolationExpectation... expectedViolations) { actualViolations.add( new ViolationExpectation( violation, referencePropertiesToTest ) ); } - Assertions.assertThat( actualViolations ).containsExactlyInAnyOrder( expectedViolations ); + return actualViolations; } public void containsOnlyPaths(PathExpectation... paths) { From e34276b44c2ec9032f1a29ac6b1a163617a0edb6 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Sun, 11 Mar 2018 21:18:40 +0100 Subject: [PATCH 3/6] HV-1363 Moved getter detection logic to separate class --- .../cfg/context/DefaultConstraintMapping.java | 3 +- .../TypeConstraintMappingContextImpl.java | 26 +++-- .../internal/engine/ValidatorFactoryImpl.java | 10 +- .../metadata/BeanMetaDataManager.java | 3 + .../provider/AnnotationMetaDataProvider.java | 24 +++-- .../provider/XmlMetaDataProvider.java | 4 +- .../DefaultGetterPropertyMatcher.java | 98 +++++++++++++++++++ .../properties/javabean/JavaBean.java | 14 ++- .../javabean/JavaBeanExecutable.java | 8 +- .../properties/javabean/JavaBeanGetter.java | 7 +- .../internal/util/ReflectionHelper.java | 43 ++------ .../GetMethodFromPropertyName.java | 24 ++--- .../internal/xml/mapping/BeanStaxBuilder.java | 11 ++- .../ConstrainedConstructorStaxBuilder.java | 5 +- .../mapping/ConstrainedGetterStaxBuilder.java | 21 ++-- .../mapping/ConstrainedMethodStaxBuilder.java | 5 +- .../ConstraintMappingsStaxBuilder.java | 7 +- .../xml/mapping/MappingXmlParser.java | 8 +- .../properties/ExecutableProperty.java | 29 ++++++ .../properties/GetterPropertyMatcher.java | 23 +++++ .../internal/engine/path/PathImplTest.java | 2 + .../metadata/BeanMetaDataManagerTest.java | 2 + .../aggregated/ExecutableMetaDataTest.java | 2 + .../aggregated/ParameterMetaDataTest.java | 3 + .../aggregated/PropertyMetaDataTest.java | 2 + .../metadata/core/MetaConstraintTest.java | 7 +- .../location/ConstraintLocationTest.java | 4 +- .../AnnotationMetaDataProviderTest.java | 3 + .../AnnotationMetaDataProviderTestBase.java | 3 +- .../TypeAnnotationMetaDataRetrievalTest.java | 2 + .../internal/util/ReflectionHelperTest.java | 12 +-- .../internal/xml/MappingXmlParserTest.java | 3 +- 32 files changed, 298 insertions(+), 120 deletions(-) create mode 100644 engine/src/main/java/org/hibernate/validator/internal/properties/DefaultGetterPropertyMatcher.java create mode 100644 engine/src/main/java/org/hibernate/validator/properties/ExecutableProperty.java create mode 100644 engine/src/main/java/org/hibernate/validator/properties/GetterPropertyMatcher.java 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..592ac04a6d 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 @@ -24,6 +24,7 @@ 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.DefaultGetterPropertyMatcher; import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.logging.Log; @@ -62,7 +63,7 @@ public final TypeConstraintMappingContext type(Class type) { throw LOG.getBeanClassHasAlreadyBeConfiguredViaProgrammaticApiException( type ); } - TypeConstraintMappingContextImpl typeContext = new TypeConstraintMappingContextImpl<>( this, type ); + TypeConstraintMappingContextImpl typeContext = new TypeConstraintMappingContextImpl<>( this, type, new DefaultGetterPropertyMatcher() ); typeContexts.add( typeContext ); configuredTypes.add( type ); 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 ffd42b5307..b94b5f1cce 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 @@ -37,18 +37,17 @@ import org.hibernate.validator.internal.properties.Property; import org.hibernate.validator.internal.properties.javabean.JavaBeanExecutable; import org.hibernate.validator.internal.properties.javabean.JavaBeanField; -import org.hibernate.validator.internal.properties.javabean.JavaBeanGetter; 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.GetMethodFromPropertyName; import org.hibernate.validator.internal.util.privilegedactions.NewInstance; +import org.hibernate.validator.properties.GetterPropertyMatcher; import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider; /** @@ -74,9 +73,12 @@ public final class TypeConstraintMappingContextImpl extends ConstraintMapping private List> defaultGroupSequence; private Class> defaultGroupSequenceProviderClass; - TypeConstraintMappingContextImpl(DefaultConstraintMapping mapping, Class beanClass) { + private final GetterPropertyMatcher getterPropertyMatcher; + + TypeConstraintMappingContextImpl(DefaultConstraintMapping mapping, Class beanClass, GetterPropertyMatcher getterPropertyMatcher) { super( mapping ); this.beanClass = beanClass; + this.getterPropertyMatcher = getterPropertyMatcher; mapping.getAnnotationProcessingOptions().ignoreAnnotationConstraintForClass( beanClass, Boolean.FALSE ); } @@ -153,7 +155,7 @@ public MethodConstraintMappingContext method(String name, Class... parameterT throw LOG.getBeanDoesNotContainMethodException( beanClass, name, parameterTypes ); } - Callable callable = JavaBeanExecutable.of( method ); + Callable callable = JavaBeanExecutable.of( getterPropertyMatcher, method ); if ( configuredMembers.contains( callable ) ) { throw LOG.getMethodHasAlreadyBeenConfiguredViaProgrammaticApiException( @@ -180,7 +182,7 @@ public ConstructorConstraintMappingContext constructor(Class... parameterType ); } - Callable callable = JavaBeanExecutable.of( constructor ); + Callable callable = JavaBeanExecutable.of( getterPropertyMatcher, constructor ); if ( configuredMembers.contains( callable ) ) { throw LOG.getConstructorHasAlreadyBeConfiguredViaProgrammaticApiException( @@ -279,15 +281,9 @@ private Property getProperty(Class clazz, String property, ElementType elemen return field == null ? null : new JavaBeanField( field ); } else { - Method method = null; - String methodName = property.substring( 0, 1 ).toUpperCase() + property.substring( 1 ); - for ( String prefix : ReflectionHelper.PROPERTY_ACCESSOR_PREFIXES ) { - method = run( GetMethod.action( clazz, prefix + methodName ) ); - if ( method != null ) { - break; - } - } - return method == null ? null : new JavaBeanGetter( method ); + Method method = GetMethodFromPropertyName.action( clazz, getterPropertyMatcher, property ).run(); + + return method == null ? null : JavaBeanExecutable.of( getterPropertyMatcher, method ).as( Property.class ); } } 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 0297b1d599..48f8cec951 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 @@ -47,6 +47,7 @@ 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.DefaultGetterPropertyMatcher; import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; @@ -59,6 +60,7 @@ 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.properties.GetterPropertyMatcher; import org.hibernate.validator.spi.cfg.ConstraintMappingContributor; import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; @@ -132,12 +134,15 @@ public class ValidatorFactoryImpl implements HibernateValidatorFactory { private final ValueExtractorManager valueExtractorManager; + private final GetterPropertyMatcher getterPropertyMatcher; + private final ValidationOrderGenerator validationOrderGenerator; public ValidatorFactoryImpl(ConfigurationState configurationState) { ClassLoader externalClassLoader = getExternalClassLoader( configurationState ); this.valueExtractorManager = new ValueExtractorManager( configurationState.getValueExtractors() ); + this.getterPropertyMatcher = new DefaultGetterPropertyMatcher(); this.beanMetaDataManagers = new ConcurrentHashMap<>(); this.constraintHelper = new ConstraintHelper(); this.typeResolutionHelper = new TypeResolutionHelper(); @@ -154,7 +159,7 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) { } else { this.xmlMetaDataProvider = new XmlMetaDataProvider( - constraintHelper, typeResolutionHelper, valueExtractorManager, configurationState.getMappingStreams(), externalClassLoader + constraintHelper, typeResolutionHelper, valueExtractorManager, getterPropertyMatcher, configurationState.getMappingStreams(), externalClassLoader ); } @@ -347,11 +352,12 @@ Validator createValidator(ConstraintValidatorFactory constraintValidatorFactory, typeResolutionHelper, validatorFactoryScopedContext.getParameterNameProvider(), valueExtractorManager, + getterPropertyMatcher, validationOrderGenerator, buildDataProviders(), methodValidationConfiguration ) - ); + ); return new ValidatorImpl( constraintValidatorFactory, 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 197a3232c4..0c789a8d4d 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 @@ -37,6 +37,7 @@ import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.classhierarchy.ClassHierarchyHelper; import org.hibernate.validator.internal.util.stereotypes.Immutable; +import org.hibernate.validator.properties.GetterPropertyMatcher; /** * This manager is in charge of providing all constraint related meta data @@ -121,6 +122,7 @@ public BeanMetaDataManager(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ExecutableParameterNameProvider parameterNameProvider, ValueExtractorManager valueExtractorManager, + GetterPropertyMatcher getterPropertyMatcher, ValidationOrderGenerator validationOrderGenerator, List optionalMetaDataProviders, MethodValidationConfiguration methodValidationConfiguration) { @@ -147,6 +149,7 @@ public BeanMetaDataManager(ConstraintHelper constraintHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, + getterPropertyMatcher, annotationProcessingOptions ); List tmpMetaDataProviders = new ArrayList<>( optionalMetaDataProviders.size() + 1 ); 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 605f4bc95e..65d54278fa 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 @@ -79,6 +79,7 @@ import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredMethods; import org.hibernate.validator.internal.util.privilegedactions.GetMethods; import org.hibernate.validator.internal.util.privilegedactions.NewInstance; +import org.hibernate.validator.properties.GetterPropertyMatcher; import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider; /** @@ -98,16 +99,19 @@ public class AnnotationMetaDataProvider implements MetaDataProvider { private final TypeResolutionHelper typeResolutionHelper; private final AnnotationProcessingOptions annotationProcessingOptions; private final ValueExtractorManager valueExtractorManager; + private final GetterPropertyMatcher getterPropertyMatcher; private final BeanConfiguration objectBeanConfiguration; public AnnotationMetaDataProvider(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, + GetterPropertyMatcher getterPropertyMatcher, AnnotationProcessingOptions annotationProcessingOptions) { this.constraintHelper = constraintHelper; this.typeResolutionHelper = typeResolutionHelper; this.valueExtractorManager = valueExtractorManager; + this.getterPropertyMatcher = getterPropertyMatcher; this.annotationProcessingOptions = annotationProcessingOptions; this.objectBeanConfiguration = retrieveBeanConfiguration( Object.class ); @@ -302,7 +306,7 @@ private Set getMetaData(Executable[] executableElements) * given element. */ private ConstrainedExecutable findExecutableMetaData(Executable executable) { - Callable callable = JavaBeanExecutable.of( executable ); + Callable callable = JavaBeanExecutable.of( getterPropertyMatcher, executable ); List parameterConstraints = getParameterMetaData( executable, callable ); Map>> executableConstraints = findConstraints( @@ -797,7 +801,7 @@ private Set> findTypeUseConstraints(Constrainable constrainabl */ private MetaConstraint createTypeArgumentMetaConstraint(ConstraintDescriptorImpl descriptor, TypeArgumentLocation location, TypeVariable typeVariable, Type type) { - ConstraintLocation constraintLocation = ConstraintLocation.forTypeArgument( location.toConstraintLocation(), typeVariable, type ); + ConstraintLocation constraintLocation = ConstraintLocation.forTypeArgument( location.toConstraintLocation( getterPropertyMatcher ), typeVariable, type ); return MetaConstraints.create( typeResolutionHelper, valueExtractorManager, descriptor, constraintLocation ); } @@ -815,7 +819,7 @@ private CascadingMetaDataBuilder getCascadingMetaData(Type type, AnnotatedElemen * which might not be possible (for instance for {@code java.util} classes). */ private interface TypeArgumentLocation { - ConstraintLocation toConstraintLocation(); + ConstraintLocation toConstraintLocation(GetterPropertyMatcher getterPropertyMatcher); } private static class TypeArgumentExecutableParameterLocation implements TypeArgumentLocation { @@ -829,8 +833,8 @@ private TypeArgumentExecutableParameterLocation(Executable executable, int index } @Override - public ConstraintLocation toConstraintLocation() { - return ConstraintLocation.forParameter( JavaBeanExecutable.of( executable ), index ); + public ConstraintLocation toConstraintLocation(GetterPropertyMatcher getterPropertyMatcher) { + return ConstraintLocation.forParameter( JavaBeanExecutable.of( getterPropertyMatcher, executable ), index ); } } @@ -842,7 +846,7 @@ private TypeArgumentFieldLocation(Field field) { } @Override - public ConstraintLocation toConstraintLocation() { + public ConstraintLocation toConstraintLocation(GetterPropertyMatcher getterPropertyMatcher) { return ConstraintLocation.forProperty( new JavaBeanField( field ) ); } } @@ -855,8 +859,8 @@ private TypeArgumentReturnValueLocation(Executable executable) { } @Override - public ConstraintLocation toConstraintLocation() { - return ConstraintLocation.forReturnValue( JavaBeanExecutable.of( executable ) ); + public ConstraintLocation toConstraintLocation(GetterPropertyMatcher getterPropertyMatcher) { + return ConstraintLocation.forReturnValue( JavaBeanExecutable.of( getterPropertyMatcher, executable ) ); } } @@ -872,8 +876,8 @@ private NestedTypeArgumentLocation(TypeArgumentLocation parentLocation, TypeVari } @Override - public ConstraintLocation toConstraintLocation() { - return ConstraintLocation.forTypeArgument( parentLocation.toConstraintLocation(), typeParameter, typeOfAnnotatedElement ); + public ConstraintLocation toConstraintLocation(GetterPropertyMatcher getterPropertyMatcher) { + return ConstraintLocation.forTypeArgument( parentLocation.toConstraintLocation( getterPropertyMatcher ), typeParameter, typeOfAnnotatedElement ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/XmlMetaDataProvider.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/XmlMetaDataProvider.java index 9cef432d79..0b67d88f4a 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/XmlMetaDataProvider.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/XmlMetaDataProvider.java @@ -21,6 +21,7 @@ import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.stereotypes.Immutable; import org.hibernate.validator.internal.xml.mapping.MappingXmlParser; +import org.hibernate.validator.properties.GetterPropertyMatcher; /** * A {@link MetaDataProvider} providing constraint related meta data based on @@ -40,11 +41,12 @@ public class XmlMetaDataProvider implements MetaDataProvider { public XmlMetaDataProvider(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, + GetterPropertyMatcher getterPropertyMatcher, Set mappingStreams, ClassLoader externalClassLoader) { MappingXmlParser mappingParser = new MappingXmlParser( constraintHelper, typeResolutionHelper, valueExtractorManager, - externalClassLoader ); + getterPropertyMatcher, externalClassLoader ); mappingParser.parse( mappingStreams ); configuredBeans = CollectionHelper.toImmutableMap( createBeanConfigurations( mappingParser ) ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/DefaultGetterPropertyMatcher.java b/engine/src/main/java/org/hibernate/validator/internal/properties/DefaultGetterPropertyMatcher.java new file mode 100644 index 0000000000..052e0f77ce --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/DefaultGetterPropertyMatcher.java @@ -0,0 +1,98 @@ +/* + * 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.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +import org.hibernate.validator.internal.util.StringHelper; +import org.hibernate.validator.properties.GetterPropertyMatcher; + +/** + * @author Marko Bekhta + */ +public class DefaultGetterPropertyMatcher implements GetterPropertyMatcher { + + 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 + }; + + /** + * 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 JavaBeans spec).
  • + *
+ * + * @param executable The executable of interest. + * + * @return {@code true}, if the given executable is a JavaBean getter method, + * {@code false} otherwise. + */ + @Override + public boolean isProperty(Executable executable) { + if ( executable instanceof Constructor ) { + return false; + } + if ( executable.getParameterTypes().length != 0 ) { + return false; + } + Method method = (Method) executable; + + 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; + } + + @Override + public String getPropertyName(Method method) { + String name = null; + String methodName = method.getName(); + for ( String prefix : PROPERTY_ACCESSOR_PREFIXES ) { + if ( methodName.startsWith( prefix ) ) { + name = StringHelper.decapitalize( methodName.substring( prefix.length() ) ); + } + } + return name; + } + + @Override + public Set getPossibleMethodNamesForProperty(String propertyName) { + char[] chars = propertyName.toCharArray(); + chars[0] = Character.toUpperCase( chars[0] ); + String propertyMethodEnding = new String( chars ); + return Arrays.stream( PROPERTY_ACCESSOR_PREFIXES ) + .map( prefix -> prefix + propertyMethodEnding ) + .collect( Collectors.toSet() ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBean.java b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBean.java index ff9dd436dc..bce7714116 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBean.java +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBean.java @@ -9,18 +9,21 @@ import java.util.Arrays; import java.util.stream.Stream; -import org.hibernate.validator.internal.properties.Property; import org.hibernate.validator.internal.properties.ConstrainableType; -import org.hibernate.validator.internal.util.ReflectionHelper; +import org.hibernate.validator.internal.properties.Property; +import org.hibernate.validator.properties.GetterPropertyMatcher; /** * @author Marko Bekhta */ public class JavaBean implements ConstrainableType { + private final GetterPropertyMatcher getterPropertyMatcher; + private final Class clazz; - public JavaBean(Class clazz) { + public JavaBean(GetterPropertyMatcher getterPropertyMatcher, Class clazz) { + this.getterPropertyMatcher = getterPropertyMatcher; this.clazz = clazz; } @@ -31,7 +34,8 @@ public Stream getFieldProperties() { public Stream getGetterProperties() { return Arrays.stream( clazz.getDeclaredMethods() ) - .filter( ReflectionHelper::isGetterMethod ) - .map( JavaBeanGetter::new ); + .filter( getterPropertyMatcher::isProperty ) + .map( m -> new JavaBeanGetter( m, getterPropertyMatcher.getPropertyName( m ) ) ); } + } 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 index afe1e9211a..960e9c7c1f 100644 --- 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 @@ -18,6 +18,7 @@ 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.properties.GetterPropertyMatcher; /** * @author Marko Bekhta @@ -40,9 +41,10 @@ public class JavaBeanExecutable implements Callable { this.hasReturnValue = hasReturnValue( executable ); } - public static JavaBeanExecutable of(Executable executable) { - if ( ReflectionHelper.isGetterMethod( executable ) ) { - return new JavaBeanGetter( (Method) executable ); + public static JavaBeanExecutable of(GetterPropertyMatcher getterPropertyMatcher, Executable executable) { + if ( getterPropertyMatcher.isProperty( executable ) ) { + Method method = (Method) executable; + return new JavaBeanGetter( method, getterPropertyMatcher.getPropertyName( method ) ); } else { return new JavaBeanExecutable( executable ); 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 index eadbecfe21..70611dab1e 100644 --- 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 @@ -17,19 +17,20 @@ import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.ReflectionHelper; import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredMethod; +import org.hibernate.validator.properties.ExecutableProperty; /** * @author Marko Bekhta */ -public class JavaBeanGetter extends JavaBeanExecutable implements Property { +public class JavaBeanGetter extends JavaBeanExecutable implements Property, ExecutableProperty { private static final Class[] PARAMETER_TYPES = new Class[0]; private final String name; - public JavaBeanGetter(Method method) { + public JavaBeanGetter(Method method, String name) { super( getAccessible( method ) ); - this.name = ReflectionHelper.getPropertyName( method ); + this.name = name; } @Override 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..cdcfdcc510 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 @@ -6,6 +6,7 @@ */ package org.hibernate.validator.internal.util; +import static org.hibernate.validator.internal.properties.DefaultGetterPropertyMatcher.PROPERTY_ACCESSOR_PREFIXES; import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; import java.lang.invoke.MethodHandles; @@ -24,8 +25,10 @@ import java.util.List; import java.util.Map; +import org.hibernate.validator.internal.properties.DefaultGetterPropertyMatcher; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.properties.GetterPropertyMatcher; /** * Some reflection utility methods. Where necessary calls will be performed as {@code PrivilegedAction} which is necessary @@ -38,15 +41,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; @@ -113,6 +107,7 @@ private ReflectionHelper() { * member is neither a field nor a getter method according to the * JavaBeans standard. */ + @Deprecated public static String getPropertyName(Member member) { String name = null; @@ -131,6 +126,8 @@ public static String getPropertyName(Member member) { return name; } + private static final GetterPropertyMatcher PROPERTY_FILTER = new DefaultGetterPropertyMatcher(); + /** * Checks whether the given executable is a valid JavaBeans getter method, which * is the case if @@ -147,33 +144,9 @@ public static String getPropertyName(Member member) { * @return {@code true}, if the given executable is a JavaBeans getter method, * {@code false} otherwise. */ + @Deprecated 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; + return PROPERTY_FILTER.isProperty( executable ); } /** 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 index 359f617837..1013569663 100644 --- 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 @@ -9,41 +9,41 @@ import java.lang.reflect.Method; import java.security.PrivilegedAction; +import org.hibernate.validator.properties.GetterPropertyMatcher; + /** * 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 + * @author Marko Bekhta */ public final class GetMethodFromPropertyName implements PrivilegedAction { private final Class clazz; + private final GetterPropertyMatcher getterPropertyMatcher; private final String property; - public static GetMethodFromPropertyName action(Class clazz, String property) { - return new GetMethodFromPropertyName( clazz, property ); + public static GetMethodFromPropertyName action(Class clazz, GetterPropertyMatcher getterPropertyMatcher, String property) { + return new GetMethodFromPropertyName( clazz, getterPropertyMatcher, property ); } - private GetMethodFromPropertyName(Class clazz, String property) { + private GetMethodFromPropertyName(Class clazz, GetterPropertyMatcher getterPropertyMatcher, String property) { this.clazz = clazz; + this.getterPropertyMatcher = getterPropertyMatcher; this.property = property; } @Override public Method run() { - try { - char[] string = property.toCharArray(); - string[0] = Character.toUpperCase( string[0] ); - String fullMethodName = new String( string ); + for ( String possibleName : getterPropertyMatcher.getPossibleMethodNamesForProperty( property ) ) { try { - return clazz.getMethod( "get" + fullMethodName ); + return clazz.getMethod( possibleName ); } catch (NoSuchMethodException e) { - return clazz.getMethod( "is" + fullMethodName ); + // silently ignore the exception } } - catch (NoSuchMethodException e) { - return null; - } + return null; } } 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 index e551a46485..892dcb89f8 100644 --- 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 @@ -33,6 +33,7 @@ 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.properties.GetterPropertyMatcher; /** * Builder for definition of all bean constraints. @@ -53,6 +54,7 @@ class BeanStaxBuilder extends AbstractStaxBuilder { private final ValueExtractorManager valueExtractorManager; private final DefaultPackageStaxBuilder defaultPackageStaxBuilder; private final AnnotationProcessingOptionsImpl annotationProcessingOptions; + private final GetterPropertyMatcher getterPropertyMatcher; private final Map, List>> defaultSequences; protected String className; @@ -66,7 +68,7 @@ class BeanStaxBuilder extends AbstractStaxBuilder { BeanStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, DefaultPackageStaxBuilder defaultPackageStaxBuilder, AnnotationProcessingOptionsImpl annotationProcessingOptions, - Map, List>> defaultSequences) { + GetterPropertyMatcher getterPropertyMatcher, Map, List>> defaultSequences) { this.classLoadingHelper = classLoadingHelper; this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; this.constraintHelper = constraintHelper; @@ -74,6 +76,7 @@ class BeanStaxBuilder extends AbstractStaxBuilder { this.valueExtractorManager = valueExtractorManager; this.annotationProcessingOptions = annotationProcessingOptions; + this.getterPropertyMatcher = getterPropertyMatcher; this.defaultSequences = defaultSequences; this.constrainedFieldStaxBuilders = new ArrayList<>(); @@ -174,7 +177,7 @@ void build(Set> processedClasses, Map, Set constrainedElementsByType, beanClass, constrainedGetterStaxBuilders.stream() - .map( builder -> builder.build( beanClass, alreadyProcessedGetterNames ) ) + .map( builder -> builder.build( beanClass, getterPropertyMatcher, alreadyProcessedGetterNames ) ) .collect( Collectors.toList() ) ); @@ -183,7 +186,7 @@ void build(Set> processedClasses, Map, Set constrainedElementsByType, beanClass, constrainedMethodStaxBuilders.stream() - .map( builder -> builder.build( beanClass, alreadyProcessedMethods ) ) + .map( builder -> builder.build( beanClass, getterPropertyMatcher, alreadyProcessedMethods ) ) .collect( Collectors.toList() ) ); @@ -192,7 +195,7 @@ void build(Set> processedClasses, Map, Set constrainedElementsByType, beanClass, constrainedConstructorStaxBuilders.stream() - .map( builder -> builder.build( beanClass, alreadyProcessedConstructors ) ) + .map( builder -> builder.build( beanClass, getterPropertyMatcher, alreadyProcessedConstructors ) ) .collect( Collectors.toList() ) ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedConstructorStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedConstructorStaxBuilder.java index 1807e03322..593b0ec9bb 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedConstructorStaxBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedConstructorStaxBuilder.java @@ -32,6 +32,7 @@ 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.properties.GetterPropertyMatcher; /** * Builder for constrained constructors. @@ -67,7 +68,7 @@ public String getMethodName() { return mainAttributeValue; } - ConstrainedExecutable build(Class beanClass, List> alreadyProcessedConstructors) { + ConstrainedExecutable build(Class beanClass, GetterPropertyMatcher getterPropertyMatcher, List> alreadyProcessedConstructors) { Class[] parameterTypes = constrainedParameterStaxBuilders.stream() .map( builder -> builder.getParameterType( beanClass ) ) .toArray( Class[]::new ); @@ -85,7 +86,7 @@ ConstrainedExecutable build(Class beanClass, List> alreadyProc parameterTypes ); } - JavaBeanExecutable executable = JavaBeanExecutable.of( constructor ); + JavaBeanExecutable executable = JavaBeanExecutable.of( getterPropertyMatcher, constructor ); if ( alreadyProcessedConstructors.contains( constructor ) ) { throw LOG.getConstructorIsDefinedTwiceInMappingXmlForBeanException( constructor, beanClass ); 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 index 390efd5129..98692881a1 100644 --- 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 @@ -35,6 +35,7 @@ import org.hibernate.validator.internal.util.logging.LoggerFactory; import org.hibernate.validator.internal.util.privilegedactions.GetMethodFromPropertyName; import org.hibernate.validator.internal.xml.mapping.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration; +import org.hibernate.validator.properties.GetterPropertyMatcher; /** * Builder for constrained getters. @@ -66,15 +67,19 @@ protected String getAcceptableQName() { return GETTER_QNAME_LOCAL_PART; } - ConstrainedExecutable build(Class beanClass, List alreadyProcessedGetterNames) { - if ( alreadyProcessedGetterNames.contains( mainAttributeValue ) ) { - throw LOG.getIsDefinedTwiceInMappingXmlForBeanException( mainAttributeValue, beanClass ); + private String getPropertyName(){ + return mainAttributeValue; + } + + ConstrainedExecutable build(Class beanClass, GetterPropertyMatcher getterPropertyMatcher, List alreadyProcessedGetterNames) { + if ( alreadyProcessedGetterNames.contains( getPropertyName() ) ) { + throw LOG.getIsDefinedTwiceInMappingXmlForBeanException( getPropertyName(), beanClass ); } else { - alreadyProcessedGetterNames.add( mainAttributeValue ); + alreadyProcessedGetterNames.add( getPropertyName() ); } - Method getter = findGetter( beanClass, mainAttributeValue ); - Property property = new JavaBeanGetter( getter ); + Method getter = findGetter( beanClass, getterPropertyMatcher, getPropertyName() ); + Property property = new JavaBeanGetter( getter, getPropertyName() ); ConstraintLocation constraintLocation = ConstraintLocation.forProperty( property ); Set> metaConstraints = constraintTypeStaxBuilders.stream() @@ -105,8 +110,8 @@ ConstrainedExecutable build(Class beanClass, List alreadyProcessedGet return constrainedGetter; } - private static Method findGetter(Class beanClass, String getterName) { - final Method method = run( GetMethodFromPropertyName.action( beanClass, getterName ) ); + private static Method findGetter(Class beanClass, GetterPropertyMatcher getterPropertyMatcher, String getterName) { + final Method method = run( GetMethodFromPropertyName.action( beanClass, getterPropertyMatcher, getterName ) ); if ( method == null ) { throw 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 index f1876750c4..8931cecf6b 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedMethodStaxBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedMethodStaxBuilder.java @@ -32,6 +32,7 @@ import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredMethod; +import org.hibernate.validator.properties.GetterPropertyMatcher; /** * Builder for constrained methods. @@ -68,7 +69,7 @@ public String getMethodName() { return mainAttributeValue; } - ConstrainedExecutable build(Class beanClass, List alreadyProcessedMethods) { + ConstrainedExecutable build(Class beanClass, GetterPropertyMatcher getterPropertyMatcher, List alreadyProcessedMethods) { Class[] parameterTypes = constrainedParameterStaxBuilders.stream() .map( builder -> builder.getParameterType( beanClass ) ) .toArray( Class[]::new ); @@ -97,7 +98,7 @@ ConstrainedExecutable build(Class beanClass, List alreadyProcessedMet else { alreadyProcessedMethods.add( method ); } - JavaBeanExecutable executable = JavaBeanExecutable.of( method ); + JavaBeanExecutable executable = JavaBeanExecutable.of( getterPropertyMatcher, method ); // ignore annotations if ( ignoreAnnotations.isPresent() ) { 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 index 50ac58eeb8..6c2a8f8ae5 100644 --- 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 @@ -21,6 +21,7 @@ import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.xml.AbstractStaxBuilder; +import org.hibernate.validator.properties.GetterPropertyMatcher; /** * Top level builder for constraint mappings. Reads the whole mapping file and builds the constraint definitions defined @@ -37,6 +38,7 @@ class ConstraintMappingsStaxBuilder extends AbstractStaxBuilder { private final TypeResolutionHelper typeResolutionHelper; private final ValueExtractorManager valueExtractorManager; private final AnnotationProcessingOptionsImpl annotationProcessingOptions; + private final GetterPropertyMatcher getterPropertyMatcher; private final Map, List>> defaultSequences; private final DefaultPackageStaxBuilder defaultPackageStaxBuilder; @@ -44,12 +46,13 @@ class ConstraintMappingsStaxBuilder extends AbstractStaxBuilder { private final List constraintDefinitionStaxBuilders; public ConstraintMappingsStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, - AnnotationProcessingOptionsImpl annotationProcessingOptions, Map, List>> defaultSequences) { + AnnotationProcessingOptionsImpl annotationProcessingOptions, GetterPropertyMatcher getterPropertyMatcher, Map, List>> defaultSequences) { this.classLoadingHelper = classLoadingHelper; this.constraintHelper = constraintHelper; this.typeResolutionHelper = typeResolutionHelper; this.valueExtractorManager = valueExtractorManager; this.annotationProcessingOptions = annotationProcessingOptions; + this.getterPropertyMatcher = getterPropertyMatcher; this.defaultSequences = defaultSequences; this.defaultPackageStaxBuilder = new DefaultPackageStaxBuilder(); @@ -82,7 +85,7 @@ else if ( constraintDefinitionStaxBuilder.process( xmlEventReader, xmlEvent ) ) } private BeanStaxBuilder getNewBeanStaxBuilder() { - return new BeanStaxBuilder( classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, defaultPackageStaxBuilder, annotationProcessingOptions, defaultSequences ); + return new BeanStaxBuilder( classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, defaultPackageStaxBuilder, annotationProcessingOptions, getterPropertyMatcher, defaultSequences ); } private ConstraintDefinitionStaxBuilder getNewConstraintDefinitionStaxBuilder() { 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 index 75cbb1bd59..b4af6a83c7 100644 --- 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 @@ -38,6 +38,8 @@ import org.hibernate.validator.internal.util.privilegedactions.SetContextClassLoader; import org.hibernate.validator.internal.xml.CloseIgnoringInputStream; import org.hibernate.validator.internal.xml.XmlParserHelper; +import org.hibernate.validator.properties.GetterPropertyMatcher; + import org.xml.sax.SAXException; /** @@ -55,6 +57,7 @@ public class MappingXmlParser { private final TypeResolutionHelper typeResolutionHelper; private final ValueExtractorManager valueExtractorManager; private final AnnotationProcessingOptionsImpl annotationProcessingOptions; + private final GetterPropertyMatcher getterPropertyMatcher; private final Map, List>> defaultSequences; private final Map, Set> constrainedElements; @@ -75,11 +78,12 @@ private static Map getSchemasByVersion() { } public MappingXmlParser(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, - ClassLoader externalClassLoader) { + GetterPropertyMatcher getterPropertyMatcher, ClassLoader externalClassLoader) { this.constraintHelper = constraintHelper; this.typeResolutionHelper = typeResolutionHelper; this.valueExtractorManager = valueExtractorManager; this.annotationProcessingOptions = new AnnotationProcessingOptionsImpl(); + this.getterPropertyMatcher = getterPropertyMatcher; this.defaultSequences = newHashMap(); this.constrainedElements = newHashMap(); this.xmlParserHelper = new XmlParserHelper(); @@ -120,7 +124,7 @@ public final void parse(Set mappingStreams) { ConstraintMappingsStaxBuilder constraintMappingsStaxBuilder = new ConstraintMappingsStaxBuilder( classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, - annotationProcessingOptions, defaultSequences + annotationProcessingOptions, getterPropertyMatcher, defaultSequences ); xmlEventReader = xmlParserHelper.createXmlEventReader( "constraint mapping file", new CloseIgnoringInputStream( in ) ); diff --git a/engine/src/main/java/org/hibernate/validator/properties/ExecutableProperty.java b/engine/src/main/java/org/hibernate/validator/properties/ExecutableProperty.java new file mode 100644 index 0000000000..b04e35d671 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/properties/ExecutableProperty.java @@ -0,0 +1,29 @@ +/* + * 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.properties; + +import java.lang.reflect.Type; + +/** + * A class to be used for filtering properties. + * + * @author Marko Bekhta + */ +public interface ExecutableProperty { + + /** + * @return a return type of the method. + */ + Type getType(); + + /** + * @return full name of the method. + */ + String getName(); + + Type[] getParameterTypes(); +} diff --git a/engine/src/main/java/org/hibernate/validator/properties/GetterPropertyMatcher.java b/engine/src/main/java/org/hibernate/validator/properties/GetterPropertyMatcher.java new file mode 100644 index 0000000000..9231c5c5d9 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/properties/GetterPropertyMatcher.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.properties; + +import java.lang.reflect.Executable; +import java.lang.reflect.Method; +import java.util.Set; + +/** + * @author Marko Bekhta + */ +public interface GetterPropertyMatcher { + + boolean isProperty(Executable executable); + + String getPropertyName(Method method); + + Set getPossibleMethodNamesForProperty(String propertyName); +} 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..68c669b1e3 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 @@ -36,6 +36,7 @@ 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.DefaultGetterPropertyMatcher; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.TypeResolutionHelper; @@ -197,6 +198,7 @@ public void testCreationOfExecutablePath() throws Exception { new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), new ValueExtractorManager( Collections.emptySet() ), + new DefaultGetterPropertyMatcher(), new ValidationOrderGenerator(), Collections.emptyList(), new MethodValidationConfiguration.Builder().build() 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..c498b109fd 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 @@ -29,6 +29,7 @@ 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.DefaultGetterPropertyMatcher; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.TypeResolutionHelper; @@ -56,6 +57,7 @@ public void setUpBeanMetaDataManager() { new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), new ValueExtractorManager( Collections.emptySet() ), + new DefaultGetterPropertyMatcher(), new ValidationOrderGenerator(), Collections.emptyList(), new MethodValidationConfiguration.Builder().build() diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java index e1a80396f4..2dfaa4e998 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 @@ -31,6 +31,7 @@ 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.DefaultGetterPropertyMatcher; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.TypeResolutionHelper; @@ -61,6 +62,7 @@ public void setupBeanMetaData() { new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), new ValueExtractorManager( Collections.emptySet() ), + new DefaultGetterPropertyMatcher(), new ValidationOrderGenerator(), Collections.emptyList(), new MethodValidationConfiguration.Builder().build() diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ParameterMetaDataTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ParameterMetaDataTest.java index bccf0c208b..77a14b20fe 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 @@ -33,6 +33,7 @@ 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.DefaultGetterPropertyMatcher; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.TypeResolutionHelper; @@ -61,6 +62,7 @@ public void setupBeanMetaData() { new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), new ValueExtractorManager( Collections.emptySet() ), + new DefaultGetterPropertyMatcher(), new ValidationOrderGenerator(), Collections.emptyList(), new MethodValidationConfiguration.Builder().build() @@ -131,6 +133,7 @@ public void parameterNameInInheritanceHierarchy() throws Exception { new TypeResolutionHelper(), new ExecutableParameterNameProvider( new SkewedParameterNameProvider() ), new ValueExtractorManager( Collections.emptySet() ), + new DefaultGetterPropertyMatcher(), 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..47f6ee87cd 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 @@ -24,6 +24,7 @@ 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.DefaultGetterPropertyMatcher; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.TypeResolutionHelper; @@ -45,6 +46,7 @@ public void setupBeanMetaDataManager() { new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), new ValueExtractorManager( Collections.emptySet() ), + new DefaultGetterPropertyMatcher(), new ValidationOrderGenerator(), Collections.emptyList(), new MethodValidationConfiguration.Builder().build() 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 16fc4e3062..088552b0b1 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 @@ -20,6 +20,7 @@ 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.properties.DefaultGetterPropertyMatcher; import org.hibernate.validator.internal.properties.javabean.JavaBeanExecutable; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; @@ -49,15 +50,15 @@ public void setUp() throws Exception { @Test @TestForIssue(jiraKey = "HV-930") public void two_meta_constraints_for_the_same_constraint_should_be_equal() throws Exception { + DefaultGetterPropertyMatcher propertyFilter = new DefaultGetterPropertyMatcher(); ConstraintDescriptorImpl constraintDescriptor1 = new ConstraintDescriptorImpl<>( - constraintHelper, JavaBeanExecutable.of( barMethod ), constraintAnnotationDescriptor, METHOD + constraintHelper, JavaBeanExecutable.of( propertyFilter, barMethod ), constraintAnnotationDescriptor, METHOD ); ConstraintLocation location1 = ConstraintLocation.forClass( Foo.class ); MetaConstraint metaConstraint1 = MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintDescriptor1, location1 ); - ConstraintDescriptorImpl constraintDescriptor2 = new ConstraintDescriptorImpl<>( - constraintHelper, JavaBeanExecutable.of( barMethod ), constraintAnnotationDescriptor, METHOD + constraintHelper, JavaBeanExecutable.of( propertyFilter, barMethod ), constraintAnnotationDescriptor, METHOD ); ConstraintLocation location2 = ConstraintLocation.forClass( Foo.class ); MetaConstraint metaConstraint2 = MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintDescriptor2, location2 ); 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 acf7934c4b..7c3fdd7fb6 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 @@ -33,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.forProperty( new JavaBeanGetter( getter ) ); - ConstraintLocation location2 = ConstraintLocation.forProperty( new JavaBeanGetter( getter ) ); + ConstraintLocation location1 = ConstraintLocation.forProperty( new JavaBeanGetter( getter, "bar" ) ); + ConstraintLocation location2 = ConstraintLocation.forProperty( new JavaBeanGetter( getter, "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 be827dcde9..7904192084 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 @@ -40,6 +40,7 @@ import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable; import org.hibernate.validator.internal.metadata.raw.ConstrainedProperty; import org.hibernate.validator.internal.metadata.raw.ConstrainedType; +import org.hibernate.validator.internal.properties.DefaultGetterPropertyMatcher; import org.hibernate.validator.internal.properties.javabean.JavaBeanExecutable; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.testutil.TestForIssue; @@ -62,6 +63,7 @@ public void setUpProvider() { new ConstraintHelper(), new TypeResolutionHelper(), new ValueExtractorManager( Collections.emptySet() ), + new DefaultGetterPropertyMatcher(), new AnnotationProcessingOptionsImpl() ); } @@ -104,6 +106,7 @@ public void testGetCrossParameterMetaData() throws Exception { assertThat( createEvent.getCallable() ).isEqualTo( JavaBeanExecutable.of( + new DefaultGetterPropertyMatcher(), Calendar.class.getMethod( "createEvent", DateMidnight.class, 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 14b8b4b600..4e9f0eb8d6 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 @@ -17,6 +17,7 @@ import org.hibernate.validator.internal.metadata.raw.ConstrainedProperty; import org.hibernate.validator.internal.metadata.raw.ConstrainedType; import org.hibernate.validator.internal.properties.Constrainable; +import org.hibernate.validator.internal.properties.DefaultGetterPropertyMatcher; import org.hibernate.validator.internal.properties.javabean.JavaBeanExecutable; import org.hibernate.validator.internal.properties.javabean.JavaBeanField; @@ -67,7 +68,7 @@ protected ConstrainedElement findConstrainedElement(BeanConfiguration beanCon constrainable = new JavaBeanField( (Field) member ); } else { - constrainable = JavaBeanExecutable.of( (Executable) member ); + constrainable = JavaBeanExecutable.of( new DefaultGetterPropertyMatcher(), (Executable) member ); } for ( ConstrainedElement constrainedElement : beanConfiguration.getConstrainedElements() ) { 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 38078e2804..6babf4dc8c 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 @@ -27,6 +27,7 @@ import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable; import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter; import org.hibernate.validator.internal.metadata.raw.ConstrainedProperty; +import org.hibernate.validator.internal.properties.DefaultGetterPropertyMatcher; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -46,6 +47,7 @@ public void setup() { new ConstraintHelper(), new TypeResolutionHelper(), new ValueExtractorManager( Collections.emptySet() ), + new DefaultGetterPropertyMatcher(), new AnnotationProcessingOptionsImpl() ); } 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..73da50013d 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; @@ -164,11 +163,12 @@ public void testGetIndexedValueForNull() { @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 ) ); + //TODO: move tests to correct place +// 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) { 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 0ef2a724f0..64f7bfece8 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 @@ -25,6 +25,7 @@ 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.properties.DefaultGetterPropertyMatcher; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.xml.mapping.MappingXmlParser; import org.hibernate.validator.testutil.TestForIssue; @@ -43,7 +44,7 @@ public class MappingXmlParserTest { public void setupParserHelper() { constraintHelper = new ConstraintHelper(); xmlMappingParser = new MappingXmlParser( - constraintHelper, new TypeResolutionHelper(), new ValueExtractorManager( Collections.emptySet() ), null + constraintHelper, new TypeResolutionHelper(), new ValueExtractorManager( Collections.emptySet() ), new DefaultGetterPropertyMatcher(), null ); } From c63b86e15c780caaae83537390d3a5dd99ba28cf Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Sun, 18 Mar 2018 20:35:18 +0100 Subject: [PATCH 4/6] HV-1363 Reworked the property filtering logic --- .../validator/cdi/ValidationExtension.java | 3 +- .../TypeConstraintMappingContextImpl.java | 59 +++---- .../internal/engine/ValidatorFactoryImpl.java | 2 +- .../metadata/BeanMetaDataManager.java | 2 +- .../provider/AnnotationMetaDataProvider.java | 108 ++++++------- .../provider/XmlMetaDataProvider.java | 2 +- .../properties/ConstrainableType.java | 2 +- .../DefaultGetterPropertyMatcher.java | 40 ++--- .../properties/javabean/JavaBean.java | 145 ++++++++++++++++-- .../javabean/JavaBeanExecutable.java | 11 -- .../properties/javabean/JavaBeanGetter.java | 3 +- .../internal/util/ReflectionHelper.java | 25 --- .../validator/internal/util/logging/Log.java | 39 +++-- .../ConstrainableTypeObjectFormatter.java | 29 ++++ .../GetDeclaredFieldValueMethodHandle.java | 61 ++++++++ .../GetJavaBeanExecutableProperty.java | 59 +++++++ .../GetJavaBeanFieldProperty.java | 59 +++++++ .../GetMethodFromPropertyName.java | 49 ------ .../internal/xml/mapping/BeanStaxBuilder.java | 20 +-- .../ConstrainedConstructorStaxBuilder.java | 50 ++---- .../mapping/ConstrainedFieldStaxBuilder.java | 36 ++--- .../mapping/ConstrainedGetterStaxBuilder.java | 41 ++--- .../mapping/ConstrainedMethodStaxBuilder.java | 56 ++----- .../ConstrainedParameterStaxBuilder.java | 5 +- .../ConstraintMappingsStaxBuilder.java | 2 +- .../xml/mapping/MappingXmlParser.java | 2 +- .../properties/ExecutableProperty.java | 29 ---- .../properties/GetterPropertyMatcher.java | 23 --- .../properties/ConstrainableExecutable.java | 34 ++++ .../spi/properties/GetterPropertyMatcher.java | 56 +++++++ .../spi/properties/package-info.java | 14 ++ .../metadata/core/MetaConstraintTest.java | 6 +- .../AnnotationMetaDataProviderTest.java | 4 +- .../AnnotationMetaDataProviderTestBase.java | 4 +- 34 files changed, 630 insertions(+), 450 deletions(-) create mode 100644 engine/src/main/java/org/hibernate/validator/internal/util/logging/formatter/ConstrainableTypeObjectFormatter.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetDeclaredFieldValueMethodHandle.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetJavaBeanExecutableProperty.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetJavaBeanFieldProperty.java delete mode 100644 engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetMethodFromPropertyName.java delete mode 100644 engine/src/main/java/org/hibernate/validator/properties/ExecutableProperty.java delete mode 100644 engine/src/main/java/org/hibernate/validator/properties/GetterPropertyMatcher.java create mode 100644 engine/src/main/java/org/hibernate/validator/spi/properties/ConstrainableExecutable.java create mode 100644 engine/src/main/java/org/hibernate/validator/spi/properties/GetterPropertyMatcher.java create mode 100644 engine/src/main/java/org/hibernate/validator/spi/properties/package-info.java 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 cc092fe17b..ea0f4c52f6 100644 --- a/cdi/src/main/java/org/hibernate/validator/cdi/ValidationExtension.java +++ b/cdi/src/main/java/org/hibernate/validator/cdi/ValidationExtension.java @@ -263,7 +263,8 @@ private void determineConstrainedMethods(AnnotatedType type, BeanDescript for ( AnnotatedMethod annotatedMethod : type.getMethods() ) { Method method = annotatedMethod.getJavaMember(); - boolean isGetter = ReflectionHelper.isGetterMethod( method ); + //TODO: need to update the getter logic here: + boolean isGetter = false/*ReflectionHelper.isGetterMethod( method )*/; // obtain @ValidateOnExecution from the top-most method in the hierarchy Method methodForExecutableTypeRetrieval = replaceWithOverriddenOrInterfaceMethod( method, overriddenAndImplementedMethods ); 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 b94b5f1cce..b576ff1798 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.Field; -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; @@ -35,20 +33,15 @@ 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.properties.javabean.JavaBeanExecutable; -import org.hibernate.validator.internal.properties.javabean.JavaBeanField; +import org.hibernate.validator.internal.properties.javabean.JavaBean; import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.ExecutableHelper; 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.GetMethodFromPropertyName; import org.hibernate.validator.internal.util.privilegedactions.NewInstance; -import org.hibernate.validator.properties.GetterPropertyMatcher; import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider; +import org.hibernate.validator.spi.properties.GetterPropertyMatcher; /** * Constraint mapping creational context which allows to configure the class-level constraints for one bean. @@ -58,6 +51,7 @@ * @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 { @@ -65,6 +59,7 @@ public final class TypeConstraintMappingContextImpl extends ConstraintMapping private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); private final Class beanClass; + private final JavaBean javaBean; private final Set executableContexts = newHashSet(); private final Set propertyContexts = newHashSet(); @@ -73,12 +68,10 @@ public final class TypeConstraintMappingContextImpl extends ConstraintMapping private List> defaultGroupSequence; private Class> defaultGroupSequenceProviderClass; - private final GetterPropertyMatcher getterPropertyMatcher; - TypeConstraintMappingContextImpl(DefaultConstraintMapping mapping, Class beanClass, GetterPropertyMatcher getterPropertyMatcher) { super( mapping ); this.beanClass = beanClass; - this.getterPropertyMatcher = getterPropertyMatcher; + this.javaBean = new JavaBean( getterPropertyMatcher, beanClass ); mapping.getAnnotationProcessingOptions().ignoreAnnotationConstraintForClass( beanClass, Boolean.FALSE ); } @@ -123,24 +116,24 @@ public PropertyConstraintMappingContext property(String property, ElementType el Contracts.assertNotNull( elementType, "The element type must not be null." ); Contracts.assertNotEmpty( property, MESSAGES.propertyNameMustNotBeEmpty() ); - Property member = getProperty( + Optional member = getProperty( beanClass, property, elementType ); - if ( member == null || member.getDeclaringClass() != beanClass ) { + if ( !member.isPresent() || member.get().getDeclaringClass() != beanClass ) { throw LOG.getUnableToFindPropertyWithAccessException( beanClass, property, elementType ); } - - if ( configuredMembers.contains( member ) ) { + Property constrainable = member.get(); + if ( configuredMembers.contains( constrainable ) ) { throw LOG.getPropertyHasAlreadyBeConfiguredViaProgrammaticApiException( beanClass, property ); } PropertyConstraintMappingContextImpl context = new PropertyConstraintMappingContextImpl( this, - member + constrainable ); - configuredMembers.add( member ); + configuredMembers.add( constrainable ); propertyContexts.add( context ); return context; } @@ -149,14 +142,13 @@ 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 method = javaBean.getCallableByNameAndParameters( name, parameterTypes ); - if ( method == null || method.getDeclaringClass() != beanClass ) { - throw LOG.getBeanDoesNotContainMethodException( beanClass, name, parameterTypes ); + if ( !method.isPresent() || method.get().getDeclaringClass() != beanClass ) { + throw LOG.getBeanDoesNotContainMethodException( javaBean, name, parameterTypes ); } - Callable callable = JavaBeanExecutable.of( getterPropertyMatcher, method ); - + Callable callable = method.get(); if ( configuredMembers.contains( callable ) ) { throw LOG.getMethodHasAlreadyBeenConfiguredViaProgrammaticApiException( beanClass, @@ -173,16 +165,16 @@ public MethodConstraintMappingContext method(String name, Class... parameterT @Override public ConstructorConstraintMappingContext constructor(Class... parameterTypes) { - Constructor constructor = run( GetDeclaredConstructor.action( beanClass, parameterTypes ) ); + Optional constructor = javaBean.getConstructorByParameters( parameterTypes ); - if ( constructor == null || constructor.getDeclaringClass() != beanClass ) { + if ( !constructor.isPresent() || constructor.get().getDeclaringClass() != beanClass ) { throw LOG.getBeanDoesNotContainConstructorException( - beanClass, + javaBean, parameterTypes ); } - Callable callable = JavaBeanExecutable.of( getterPropertyMatcher, constructor ); + Callable callable = constructor.get(); if ( configuredMembers.contains( callable ) ) { throw LOG.getConstructorHasAlreadyBeConfiguredViaProgrammaticApiException( @@ -263,9 +255,9 @@ protected ConstraintType getConstraintType() { * @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 property which matches the name and type or {@code null} if no such property exists. + * @return the property which matches the name and type or {@link Optional#empty()} if no such property exists. */ - private Property getProperty(Class clazz, String property, ElementType elementType) { + private Optional getProperty(Class clazz, String property, ElementType elementType) { Contracts.assertNotNull( clazz, MESSAGES.classCannotBeNull() ); if ( property == null || property.length() == 0 ) { @@ -277,13 +269,10 @@ private Property getProperty(Class clazz, String property, ElementType elemen } if ( ElementType.FIELD.equals( elementType ) ) { - Field field = run( GetDeclaredField.action( clazz, property ) ); - return field == null ? null : new JavaBeanField( field ); + return javaBean.getFieldPropertyByName( property ); } else { - Method method = GetMethodFromPropertyName.action( clazz, getterPropertyMatcher, property ).run(); - - return method == null ? null : JavaBeanExecutable.of( getterPropertyMatcher, method ).as( Property.class ); + return javaBean.getCallablePropertyByName( property ); } } 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 48f8cec951..a7750ebe44 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 @@ -60,7 +60,7 @@ 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.properties.GetterPropertyMatcher; +import org.hibernate.validator.spi.properties.GetterPropertyMatcher; import org.hibernate.validator.spi.cfg.ConstraintMappingContributor; import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; 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 0c789a8d4d..96bfc6214f 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 @@ -37,7 +37,7 @@ import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.classhierarchy.ClassHierarchyHelper; import org.hibernate.validator.internal.util.stereotypes.Immutable; -import org.hibernate.validator.properties.GetterPropertyMatcher; +import org.hibernate.validator.spi.properties.GetterPropertyMatcher; /** * This manager is in charge of providing all constraint related meta data 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 65d54278fa..012786c1d4 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 @@ -24,7 +24,6 @@ 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; @@ -37,8 +36,10 @@ import java.util.HashSet; 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.validation.GroupSequence; import javax.validation.Valid; @@ -66,6 +67,7 @@ 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.properties.javabean.JavaBean; import org.hibernate.validator.internal.properties.javabean.JavaBeanExecutable; import org.hibernate.validator.internal.properties.javabean.JavaBeanField; import org.hibernate.validator.internal.util.CollectionHelper; @@ -74,12 +76,11 @@ 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.GetDeclaredConstructors; -import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredFields; -import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredMethods; +import org.hibernate.validator.internal.util.privilegedactions.GetJavaBeanExecutableProperty; +import org.hibernate.validator.internal.util.privilegedactions.GetJavaBeanFieldProperty; import org.hibernate.validator.internal.util.privilegedactions.GetMethods; import org.hibernate.validator.internal.util.privilegedactions.NewInstance; -import org.hibernate.validator.properties.GetterPropertyMatcher; +import org.hibernate.validator.spi.properties.GetterPropertyMatcher; import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider; /** @@ -138,23 +139,33 @@ public BeanConfiguration getBeanConfiguration(Class beanClass) { * @return Retrieves constraint related meta data from the annotations of the given type. */ private BeanConfiguration retrieveBeanConfiguration(Class beanClass) { - Set constrainedElements = getFieldMetaData( beanClass ); - constrainedElements.addAll( getMethodMetaData( beanClass ) ); - constrainedElements.addAll( getConstructorMetaData( beanClass ) ); + JavaBean javaBean = new JavaBean( getterPropertyMatcher, beanClass ); + Stream classLevelStream; //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 = + classLevelStream = Stream.of( new ConstrainedType( ConfigurationSource.ANNOTATION, beanClass, classLevelConstraints - ); - constrainedElements.add( classLevelMetaData ); + ) + ); + } + else { + classLevelStream = Stream.empty(); } + Set constrainedElements = Stream.concat( + classLevelStream, + javaBean.getAllConstrainables() + .map( this::toConstrainedElement ) + .filter( Optional::isPresent ) + .map( Optional::get ) + ).collect( Collectors.toSet() ); + return new BeanConfiguration<>( ConfigurationSource.ANNOTATION, beanClass, @@ -164,6 +175,19 @@ private BeanConfiguration retrieveBeanConfiguration(Class beanClass) { ); } + private Optional toConstrainedElement(Constrainable constrainable) { + if ( constrainable instanceof JavaBeanField ) { + if ( annotationProcessingOptions.areMemberConstraintsIgnoredFor( constrainable ) ) { + return Optional.empty(); + } + return Optional.of( findPropertyMetaData( constrainable.as( Property.class ) ) ); + } + else if ( constrainable instanceof JavaBeanExecutable ) { + return Optional.of( findExecutableMetaData( constrainable.as( Callable.class ) ) ); + } + throw new IllegalStateException( String.format( "Received unknown constrainable '%s' of type %s", constrainable.getName(), constrainable.getClass() ) ); + } + private List> getDefaultGroupSequence(Class beanClass) { GroupSequence groupSequenceAnnotation = beanClass.getAnnotation( GroupSequence.class ); return groupSequenceAnnotation != null ? Arrays.asList( groupSequenceAnnotation.value() ) : null; @@ -218,25 +242,8 @@ private Set> getClassLevelConstraints(Class clazz) { return classLevelConstraints; } - private Set getFieldMetaData(Class beanClass) { - Set propertyMetaData = newHashSet(); - - for ( Field field : run( GetDeclaredFields.action( beanClass ) ) ) { - Property property = new JavaBeanField( field ); - // HV-172 - if ( Modifier.isStatic( field.getModifiers() ) || - annotationProcessingOptions.areMemberConstraintsIgnoredFor( property ) || - field.isSynthetic() ) { - - continue; - } - - propertyMetaData.add( findPropertyMetaData( field, property ) ); - } - return propertyMetaData; - } - - private ConstrainedProperty findPropertyMetaData(Field field, Property property) { + private ConstrainedProperty findPropertyMetaData(Property property) { + Field field = run( GetJavaBeanFieldProperty.action( property.as( JavaBeanField.class ) ) ); Set> constraints = convertToMetaConstraints( findConstraints( field, ElementType.FIELD, property ), property @@ -269,44 +276,17 @@ private Set> convertToMetaConstraints(List getConstructorMetaData(Class clazz) { - Executable[] declaredConstructors = run( GetDeclaredConstructors.action( clazz ) ); - - return getMetaData( declaredConstructors ); - } - - private Set getMethodMetaData(Class clazz) { - Executable[] declaredMethods = run( GetDeclaredMethods.action( clazz ) ); - - return getMetaData( declaredMethods ); - } - - private Set getMetaData(Executable[] executableElements) { - Set executableMetaData = newHashSet(); - - for ( Executable executable : executableElements ) { - // HV-172; ignoring synthetic methods (inserted by the compiler), as they can't have any constraints - // anyway and possibly hide the actual method with the same signature in the built meta model - if ( Modifier.isStatic( executable.getModifiers() ) || executable.isSynthetic() ) { - continue; - } - - executableMetaData.add( findExecutableMetaData( executable ) ); - } - - return executableMetaData; - } - /** * Finds all constraint annotations defined for the given method or constructor. * - * @param executable The executable element to check for constraints annotations. + * @param callable The executable element to check for constraints annotations. * * @return A meta data object describing the constraints specified for the - * given element. + * given element. */ - private ConstrainedExecutable findExecutableMetaData(Executable executable) { - Callable callable = JavaBeanExecutable.of( getterPropertyMatcher, executable ); + private ConstrainedExecutable findExecutableMetaData(Callable callable) { + Executable executable = run( GetJavaBeanExecutableProperty.action( callable.as( JavaBeanExecutable.class ) ) ); + List parameterConstraints = getParameterMetaData( executable, callable ); Map>> executableConstraints = findConstraints( @@ -834,7 +814,7 @@ private TypeArgumentExecutableParameterLocation(Executable executable, int index @Override public ConstraintLocation toConstraintLocation(GetterPropertyMatcher getterPropertyMatcher) { - return ConstraintLocation.forParameter( JavaBeanExecutable.of( getterPropertyMatcher, executable ), index ); + return ConstraintLocation.forParameter( JavaBean.toJavaBeanExecutable( getterPropertyMatcher, executable ), index ); } } @@ -860,7 +840,7 @@ private TypeArgumentReturnValueLocation(Executable executable) { @Override public ConstraintLocation toConstraintLocation(GetterPropertyMatcher getterPropertyMatcher) { - return ConstraintLocation.forReturnValue( JavaBeanExecutable.of( getterPropertyMatcher, executable ) ); + return ConstraintLocation.forReturnValue( JavaBean.toJavaBeanExecutable( getterPropertyMatcher, executable ) ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/XmlMetaDataProvider.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/XmlMetaDataProvider.java index 0b67d88f4a..5ed12c11f4 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/XmlMetaDataProvider.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/XmlMetaDataProvider.java @@ -21,7 +21,7 @@ import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.stereotypes.Immutable; import org.hibernate.validator.internal.xml.mapping.MappingXmlParser; -import org.hibernate.validator.properties.GetterPropertyMatcher; +import org.hibernate.validator.spi.properties.GetterPropertyMatcher; /** * A {@link MetaDataProvider} providing constraint related meta data based on diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/ConstrainableType.java b/engine/src/main/java/org/hibernate/validator/internal/properties/ConstrainableType.java index 01356e09de..43277ad1cd 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/properties/ConstrainableType.java +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/ConstrainableType.java @@ -10,5 +10,5 @@ * @author Marko Bekhta */ public interface ConstrainableType { - + String getName(); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/DefaultGetterPropertyMatcher.java b/engine/src/main/java/org/hibernate/validator/internal/properties/DefaultGetterPropertyMatcher.java index 052e0f77ce..cb437d5cd5 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/properties/DefaultGetterPropertyMatcher.java +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/DefaultGetterPropertyMatcher.java @@ -6,15 +6,9 @@ */ package org.hibernate.validator.internal.properties; -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Set; -import java.util.stream.Collectors; - import org.hibernate.validator.internal.util.StringHelper; -import org.hibernate.validator.properties.GetterPropertyMatcher; +import org.hibernate.validator.spi.properties.ConstrainableExecutable; +import org.hibernate.validator.spi.properties.GetterPropertyMatcher; /** * @author Marko Bekhta @@ -38,7 +32,7 @@ public class DefaultGetterPropertyMatcher implements GetterPropertyMatcher { *
  • 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).
  • + * {@code boolean} (HV-specific, not mandated by the JavaBeans spec). * * * @param executable The executable of interest. @@ -47,27 +41,23 @@ public class DefaultGetterPropertyMatcher implements GetterPropertyMatcher { * {@code false} otherwise. */ @Override - public boolean isProperty(Executable executable) { - if ( executable instanceof Constructor ) { - return false; - } + public boolean isProperty(ConstrainableExecutable executable) { if ( executable.getParameterTypes().length != 0 ) { return false; } - Method method = (Method) executable; - String methodName = method.getName(); + String methodName = executable.getName(); // get() - if ( methodName.startsWith( PROPERTY_ACCESSOR_PREFIX_GET ) && method.getReturnType() != void.class ) { + if ( methodName.startsWith( PROPERTY_ACCESSOR_PREFIX_GET ) && executable.getReturnType() != void.class ) { return true; } //boolean is() - else if ( methodName.startsWith( PROPERTY_ACCESSOR_PREFIX_IS ) && method.getReturnType() == boolean.class ) { + else if ( methodName.startsWith( PROPERTY_ACCESSOR_PREFIX_IS ) && executable.getReturnType() == boolean.class ) { return true; } //boolean has() - else if ( methodName.startsWith( PROPERTY_ACCESSOR_PREFIX_HAS ) && method.getReturnType() == boolean.class ) { + else if ( methodName.startsWith( PROPERTY_ACCESSOR_PREFIX_HAS ) && executable.getReturnType() == boolean.class ) { return true; } @@ -75,9 +65,9 @@ else if ( methodName.startsWith( PROPERTY_ACCESSOR_PREFIX_HAS ) && method.getRet } @Override - public String getPropertyName(Method method) { + public String getPropertyName(ConstrainableExecutable executable) { String name = null; - String methodName = method.getName(); + String methodName = executable.getName(); for ( String prefix : PROPERTY_ACCESSOR_PREFIXES ) { if ( methodName.startsWith( prefix ) ) { name = StringHelper.decapitalize( methodName.substring( prefix.length() ) ); @@ -85,14 +75,4 @@ public String getPropertyName(Method method) { } return name; } - - @Override - public Set getPossibleMethodNamesForProperty(String propertyName) { - char[] chars = propertyName.toCharArray(); - chars[0] = Character.toUpperCase( chars[0] ); - String propertyMethodEnding = new String( chars ); - return Arrays.stream( PROPERTY_ACCESSOR_PREFIXES ) - .map( prefix -> prefix + propertyMethodEnding ) - .collect( Collectors.toSet() ); - } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBean.java b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBean.java index bce7714116..5064cade0c 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBean.java +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBean.java @@ -6,36 +6,155 @@ */ package org.hibernate.validator.internal.properties.javabean; +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; import java.util.stream.Stream; +import org.hibernate.validator.internal.properties.Callable; +import org.hibernate.validator.internal.properties.Constrainable; import org.hibernate.validator.internal.properties.ConstrainableType; import org.hibernate.validator.internal.properties.Property; -import org.hibernate.validator.properties.GetterPropertyMatcher; +import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredConstructors; +import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredFields; +import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredMethods; +import org.hibernate.validator.spi.properties.ConstrainableExecutable; +import org.hibernate.validator.spi.properties.GetterPropertyMatcher; /** + * {@link ConstrainableType} that describes a JavaBean. + * * @author Marko Bekhta */ public class JavaBean implements ConstrainableType { - private final GetterPropertyMatcher getterPropertyMatcher; - - private final Class clazz; + private final String name; + private final List beanFields; + private final List beanExecutables; public JavaBean(GetterPropertyMatcher getterPropertyMatcher, Class clazz) { - this.getterPropertyMatcher = getterPropertyMatcher; - this.clazz = clazz; + this.name = clazz.getName(); + this.beanFields = Arrays.stream( run( GetDeclaredFields.action( clazz ) ) ) + .filter( this::staticAndSyntheticFilter ) + .map( JavaBeanField::new ) + .collect( Collectors.collectingAndThen( Collectors.toList(), Collections::unmodifiableList ) ); + + this.beanExecutables = Stream.concat( + Arrays.stream( run( GetDeclaredMethods.action( clazz ) ) ) + .filter( this::staticAndSyntheticFilter ) + .map( JavaBeanConstrainableExecutable::new ) + .map( executable -> JavaBean.toJavaBeanExecutable( getterPropertyMatcher, executable ) ), + Arrays.stream( run( GetDeclaredConstructors.action( clazz ) ) ) + .filter( this::staticAndSyntheticFilter ) + .map( JavaBeanExecutable::new ) + ).collect( Collectors.collectingAndThen( Collectors.toList(), Collections::unmodifiableList ) ); + } + + private boolean staticAndSyntheticFilter(Member member) { + // HV-172; ignoring synthetic members (inserted by the compiler), as they can't have any constraints + // anyway and possibly hide the actual member with the same signature in the built meta model + return !( Modifier.isStatic( member.getModifiers() ) || member.isSynthetic() ); + } + + public static JavaBeanExecutable toJavaBeanExecutable(GetterPropertyMatcher getterPropertyMatcher, Executable executable) { + // if it's a constructor return fast: + if ( executable instanceof Constructor ) { + return new JavaBeanExecutable( executable ); + } + + return toJavaBeanExecutable( getterPropertyMatcher, new JavaBeanConstrainableExecutable( (Method) executable ) ); + } + + public Stream getAllConstrainables() { + return Stream.concat( beanFields.stream(), beanExecutables.stream() ); + } + + public Optional getCallableByNameAndParameters(String methodName, Class... parameterTypes) { + return beanExecutables.stream() + .filter( callable -> !callable.isConstructor() ) + .filter( callable -> callable.getName().equals( methodName ) ) + .filter( callable -> Arrays.equals( callable.getParameterTypes(), parameterTypes ) ) + .findAny().map( javaBeanExecutable -> javaBeanExecutable.as( Callable.class ) ); + } + + public Optional getConstructorByParameters(Class... parameterTypes) { + return beanExecutables.stream() + .filter( Callable::isConstructor ) + .filter( callable -> Arrays.equals( callable.getParameterTypes(), parameterTypes ) ) + .findAny().map( javaBeanExecutable -> javaBeanExecutable.as( Callable.class ) ); } - public Stream getFieldProperties() { - return Arrays.stream( clazz.getDeclaredFields() ) - .map( JavaBeanField::new ); + public Optional getCallablePropertyByName(String propertyName) { + return beanExecutables.stream() + .filter( executable -> executable instanceof Property ) + .map( executable -> executable.as( Property.class ) ) + .filter( property -> property.getPropertyName().equals( propertyName ) ) + .findAny(); + } + + public Optional getFieldPropertyByName(String propertyName) { + return beanFields.stream() + .filter( field -> field.getName().equals( propertyName ) ) + .findAny().map( executable -> executable.as( Property.class ) ); + } + + private static JavaBeanExecutable toJavaBeanExecutable(GetterPropertyMatcher getterPropertyMatcher, JavaBeanConstrainableExecutable executable) { + if ( getterPropertyMatcher.isProperty( executable ) ) { + return new JavaBeanGetter( executable.getMethod(), getterPropertyMatcher.getPropertyName( executable ) ); + } + else { + return new JavaBeanExecutable( executable.getMethod() ); + } + } + + @Override + public String getName() { + return name; + } + + 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 Type[] getParameterTypes() { + return method.getParameterTypes(); + } + + public Method getMethod() { + return method; + } } - public Stream getGetterProperties() { - return Arrays.stream( clazz.getDeclaredMethods() ) - .filter( getterPropertyMatcher::isProperty ) - .map( m -> new JavaBeanGetter( m, getterPropertyMatcher.getPropertyName( m ) ) ); + /** + * Runs the given privileged action, using a privileged block if required. + *

    + * NOTE: This must never be changed into a publicly available method to avoid execution of arbitrary + * privileged actions within HV's protection domain. + */ + private T run(PrivilegedAction action) { + return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanExecutable.java b/engine/src/main/java/org/hibernate/validator/internal/properties/javabean/JavaBeanExecutable.java index 960e9c7c1f..0c25e7d433 100644 --- 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 @@ -18,7 +18,6 @@ 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.properties.GetterPropertyMatcher; /** * @author Marko Bekhta @@ -41,16 +40,6 @@ public class JavaBeanExecutable implements Callable { this.hasReturnValue = hasReturnValue( executable ); } - public static JavaBeanExecutable of(GetterPropertyMatcher getterPropertyMatcher, Executable executable) { - if ( getterPropertyMatcher.isProperty( executable ) ) { - Method method = (Method) executable; - return new JavaBeanGetter( method, getterPropertyMatcher.getPropertyName( method ) ); - } - else { - return new JavaBeanExecutable( executable ); - } - } - @Override public boolean hasReturnValue() { return hasReturnValue; 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 index 70611dab1e..37e9055eaf 100644 --- 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 @@ -17,12 +17,11 @@ import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.ReflectionHelper; import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredMethod; -import org.hibernate.validator.properties.ExecutableProperty; /** * @author Marko Bekhta */ -public class JavaBeanGetter extends JavaBeanExecutable implements Property, ExecutableProperty { +public class JavaBeanGetter extends JavaBeanExecutable implements Property { private static final Class[] PARAMETER_TYPES = new Class[0]; 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 cdcfdcc510..ccab9c9316 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 @@ -25,10 +25,8 @@ import java.util.List; import java.util.Map; -import org.hibernate.validator.internal.properties.DefaultGetterPropertyMatcher; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; -import org.hibernate.validator.properties.GetterPropertyMatcher; /** * Some reflection utility methods. Where necessary calls will be performed as {@code PrivilegedAction} which is necessary @@ -126,29 +124,6 @@ public static String getPropertyName(Member member) { return name; } - private static final GetterPropertyMatcher PROPERTY_FILTER = new DefaultGetterPropertyMatcher(); - - /** - * 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. - */ - @Deprecated - public static boolean isGetterMethod(Executable executable) { - return PROPERTY_FILTER.isProperty( executable ); - } - /** * @param member The {@code Member} instance for which to retrieve the type. * 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 40b759d040..ca4aa9d560 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 @@ -13,10 +13,8 @@ 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; @@ -50,16 +48,19 @@ import org.hibernate.validator.internal.metadata.location.ConstraintLocation; import org.hibernate.validator.internal.properties.Callable; import org.hibernate.validator.internal.properties.Constrainable; +import org.hibernate.validator.internal.properties.ConstrainableType; 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; import org.hibernate.validator.internal.util.logging.formatter.ConstrainableFormatter; +import org.hibernate.validator.internal.util.logging.formatter.ConstrainableTypeObjectFormatter; import org.hibernate.validator.internal.util.logging.formatter.DurationFormatter; 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.mapping.ContainerElementTypePath; +import org.hibernate.validator.spi.properties.GetterPropertyMatcher; import org.hibernate.validator.spi.scripting.ScriptEvaluationException; import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; import org.hibernate.validator.spi.scripting.ScriptEvaluatorNotFoundException; @@ -385,13 +386,13 @@ RuntimeException getTryingToInstantiateAnnotationWithUnknownAttributesException( 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); + ValidationException getIsDefinedTwiceInMappingXmlForBeanException(String name, @FormatWith(ConstrainableTypeObjectFormatter.class) ConstrainableType constrainableType); @Message(id = 105, value = "%1$s does not contain the fieldType %2$s.") - ValidationException getBeanDoesNotContainTheFieldException(@FormatWith(ClassObjectFormatter.class) Class beanClass, String fieldName); + ValidationException getBeanDoesNotContainTheFieldException(@FormatWith(ConstrainableTypeObjectFormatter.class) ConstrainableType constrainableType, String fieldName); @Message(id = 106, value = "%1$s does not contain the property %2$s.") - ValidationException getBeanDoesNotContainThePropertyException(@FormatWith(ClassObjectFormatter.class) Class beanClass, String getterName); + ValidationException getBeanDoesNotContainThePropertyException(@FormatWith(ConstrainableTypeObjectFormatter.class) ConstrainableType constrainableType, String getterName); @Message(id = 107, value = "Annotation of type %1$s does not contain a parameter %2$s.") ValidationException getAnnotationDoesNotContainAParameterException(@FormatWith(ClassObjectFormatter.class) Class annotationClass, @@ -470,24 +471,24 @@ ConstraintDeclarationException getMultipleGroupConversionsForSameSourceException ConstraintDeclarationException getVoidMethodsMustNotBeConstrainedException(@FormatWith(ConstrainableFormatter.class) 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, + ValidationException getBeanDoesNotContainConstructorException(@FormatWith(ConstrainableTypeObjectFormatter.class) ConstrainableType constrainableType, @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); + ValidationException getInvalidParameterTypeException(String type, @FormatWith(ConstrainableTypeObjectFormatter.class) ConstrainableType constrainableType); @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, + ValidationException getBeanDoesNotContainMethodException(@FormatWith(ConstrainableTypeObjectFormatter.class) ConstrainableType constrainableType, String methodName, @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(Callable callable, @FormatWith(ConstrainableTypeObjectFormatter.class) ConstrainableType constrainableType); @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(Callable constructor, @FormatWith(ConstrainableTypeObjectFormatter.class) ConstrainableType constrainableType); @Message(id = 139, value = "The constraint '%1$s' defines multiple cross parameter validators. Only one is allowed.") @@ -857,4 +858,22 @@ ConstraintDefinitionException getConstraintValidatorDefinitionConstraintMismatch @FormatWith(ClassObjectFormatter.class) Class> constraintValidatorImplementationType, @FormatWith(ClassObjectFormatter.class) Class registeredConstraintAnnotationType, @FormatWith(TypeFormatter.class) Type declaredConstraintAnnotationType); + + @LogMessage(level = INFO) + @Message(id = 244, value = "Using %s as getter property matcher.") + void usingGetterPropertyMatcher(@FormatWith(ClassObjectFormatter.class) Class getterPropertyMatcherClass); + + @Message(id = 245, value = "Unable to instantiate getter property matcher class %s.") + ValidationException getUnableToInstantiateGetterPropertyMatcherClassException(String getterPropertyMatcherClassName, @Cause Exception e); + + @LogMessage(level = WARN) + @Message(id = 246, value = "Note that constraint mappings will be build with %s getter property matcher.") + void creatingConstraintMappingPriorToBuildingFactory(@FormatWith(ClassObjectFormatter.class) Class getterPropertyMatcherClass); + + @LogMessage(level = WARN) + @Message(id = 247, value = "As no property filter was defined for current configuration a default one will be used.") + void creatingConstraintMappingPriorToBuildingFactoryWithoutDefinedFilter(); + + @Message(id = 248, value = "Unable to access field %3$s of class %2$s using lookup %1$s.") + ValidationException getUnableToAccessFieldException(Lookup lookup, @FormatWith(ClassObjectFormatter.class) Class clazz, String property, @Cause Throwable e); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/logging/formatter/ConstrainableTypeObjectFormatter.java b/engine/src/main/java/org/hibernate/validator/internal/util/logging/formatter/ConstrainableTypeObjectFormatter.java new file mode 100644 index 0000000000..39c6d3be8e --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/util/logging/formatter/ConstrainableTypeObjectFormatter.java @@ -0,0 +1,29 @@ +/* + * 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 org.hibernate.validator.internal.properties.ConstrainableType; + +/** + * Used with JBoss Logging to display {@link ConstrainableType} names in log messages. + * + * @author Gunnar Morling + * @author Marko Bekhta + */ +public class ConstrainableTypeObjectFormatter { + + private final String stringRepresentation; + + public ConstrainableTypeObjectFormatter(ConstrainableType constrainableType) { + this.stringRepresentation = constrainableType.getName(); + } + + @Override + public String toString() { + return stringRepresentation; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetDeclaredFieldValueMethodHandle.java b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetDeclaredFieldValueMethodHandle.java new file mode 100644 index 0000000000..705710c824 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetDeclaredFieldValueMethodHandle.java @@ -0,0 +1,61 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.util.privilegedactions; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.reflect.Field; +import java.security.PrivilegedAction; + +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; + +/** + * Returns the {@link MethodHandle} getter for declared field value with the specified name, + * throws an exception if it does not exist or cannot be accessed. + * + * @author Marko Bekhta + */ +public final class GetDeclaredFieldValueMethodHandle implements PrivilegedAction { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private final Lookup lookup; + private final Class clazz; + private final String property; + private final boolean makeAccessible; + + /** + * Before using this method on arbitrary classes, you need to check the {@code HibernateValidatorPermission.ACCESS_PRIVATE_MEMBERS} + * permission against the security manager, if the calling class exposes the handle to clients. + */ + public static GetDeclaredFieldValueMethodHandle action(Lookup lookup, Class clazz, String property, boolean makeAccessible) { + return new GetDeclaredFieldValueMethodHandle( lookup, clazz, property, makeAccessible ); + } + + private GetDeclaredFieldValueMethodHandle(Lookup lookup, Class clazz, String property, boolean makeAccessible) { + this.lookup = lookup; + this.clazz = clazz; + this.property = property; + this.makeAccessible = makeAccessible; + } + + @Override + public MethodHandle run() { + try { + Field field = clazz.getDeclaredField( property ); + if ( makeAccessible ) { + field.setAccessible( true ); + } + return lookup.unreflectGetter( field ); + } + catch (NoSuchFieldException | IllegalAccessException e) { + throw LOG.getUnableToAccessFieldException( lookup, clazz, property, e ); + } + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetJavaBeanExecutableProperty.java b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetJavaBeanExecutableProperty.java new file mode 100644 index 0000000000..3c780f19e6 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetJavaBeanExecutableProperty.java @@ -0,0 +1,59 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.util.privilegedactions; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Executable; +import java.security.AccessController; +import java.security.PrivilegedAction; + +import org.hibernate.validator.internal.properties.javabean.JavaBeanExecutable; + +/** + * Returns the {@link Executable} property from the {@link JavaBeanExecutable} instance. + * + * @author Marko Bekhta + */ +public final class GetJavaBeanExecutableProperty implements PrivilegedAction { + + private static final MethodHandle fieldGetter; + + static { + fieldGetter = run( GetDeclaredFieldValueMethodHandle.action( MethodHandles.lookup(), JavaBeanExecutable.class, "executable", true ) ); + } + + private final JavaBeanExecutable executable; + + public GetJavaBeanExecutableProperty(JavaBeanExecutable executable) { + this.executable = executable; + } + + public static GetJavaBeanExecutableProperty action(JavaBeanExecutable field) { + return new GetJavaBeanExecutableProperty( field ); + } + + @Override + public Executable run() { + try { + return (Executable) fieldGetter.invoke( executable ); + } + catch (Throwable throwable) { + throw new IllegalStateException( "Unable to retrieve value from method handle", throwable ); + } + } + + /** + * 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/util/privilegedactions/GetJavaBeanFieldProperty.java b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetJavaBeanFieldProperty.java new file mode 100644 index 0000000000..7e04b770d4 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetJavaBeanFieldProperty.java @@ -0,0 +1,59 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.util.privilegedactions; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; +import java.security.AccessController; +import java.security.PrivilegedAction; + +import org.hibernate.validator.internal.properties.javabean.JavaBeanField; + +/** + * Returns the {@link Field} property from the {@link JavaBeanField} instance. + * + * @author Marko Bekhta + */ +public final class GetJavaBeanFieldProperty implements PrivilegedAction { + + private static final MethodHandle fieldGetter; + + static { + fieldGetter = run( GetDeclaredFieldValueMethodHandle.action( MethodHandles.lookup(), JavaBeanField.class, "field", true ) ); + } + + private final JavaBeanField field; + + public GetJavaBeanFieldProperty(JavaBeanField field) { + this.field = field; + } + + public static GetJavaBeanFieldProperty action(JavaBeanField field) { + return new GetJavaBeanFieldProperty( field ); + } + + @Override + public Field run() { + try { + return (Field) fieldGetter.invoke( field ); + } + catch (Throwable throwable) { + throw new IllegalStateException( "Unable to retrieve value from method handle", throwable ); + } + } + + /** + * 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/util/privilegedactions/GetMethodFromPropertyName.java b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetMethodFromPropertyName.java deleted file mode 100644 index 1013569663..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; - -import org.hibernate.validator.properties.GetterPropertyMatcher; - -/** - * 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 - * @author Marko Bekhta - */ -public final class GetMethodFromPropertyName implements PrivilegedAction { - private final Class clazz; - private final GetterPropertyMatcher getterPropertyMatcher; - private final String property; - - public static GetMethodFromPropertyName action(Class clazz, GetterPropertyMatcher getterPropertyMatcher, String property) { - return new GetMethodFromPropertyName( clazz, getterPropertyMatcher, property ); - } - - private GetMethodFromPropertyName(Class clazz, GetterPropertyMatcher getterPropertyMatcher, String property) { - this.clazz = clazz; - this.getterPropertyMatcher = getterPropertyMatcher; - this.property = property; - } - - @Override - public Method run() { - for ( String possibleName : getterPropertyMatcher.getPossibleMethodNamesForProperty( property ) ) { - try { - return clazz.getMethod( possibleName ); - } - catch (NoSuchMethodException e) { - // silently ignore the exception - } - } - return null; - } -} 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 index 892dcb89f8..95b3aa2af6 100644 --- 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 @@ -9,8 +9,6 @@ import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; import java.lang.invoke.MethodHandles; -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -29,11 +27,13 @@ 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.properties.Callable; +import org.hibernate.validator.internal.properties.javabean.JavaBean; 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.xml.AbstractStaxBuilder; -import org.hibernate.validator.properties.GetterPropertyMatcher; +import org.hibernate.validator.spi.properties.GetterPropertyMatcher; /** * Builder for definition of all bean constraints. @@ -156,6 +156,8 @@ void build(Set> processedClasses, Map, Set ignoreAnnotations.orElse( true ) ); + JavaBean javaBean = new JavaBean( getterPropertyMatcher, beanClass ); + if ( classConstraintTypeStaxBuilder != null ) { addConstrainedElements( constrainedElementsByType, @@ -168,7 +170,7 @@ void build(Set> processedClasses, Map, Set addConstrainedElements( constrainedElementsByType, beanClass, constrainedFieldStaxBuilders.stream() - .map( builder -> builder.build( beanClass, alreadyProcessedFieldNames ) ) + .map( builder -> builder.build( javaBean, alreadyProcessedFieldNames ) ) .collect( Collectors.toList() ) ); @@ -177,25 +179,25 @@ void build(Set> processedClasses, Map, Set constrainedElementsByType, beanClass, constrainedGetterStaxBuilders.stream() - .map( builder -> builder.build( beanClass, getterPropertyMatcher, alreadyProcessedGetterNames ) ) + .map( builder -> builder.build( javaBean, alreadyProcessedGetterNames ) ) .collect( Collectors.toList() ) ); - List alreadyProcessedMethods = new ArrayList<>( constrainedMethodStaxBuilders.size() ); + List alreadyProcessedMethods = new ArrayList<>( constrainedMethodStaxBuilders.size() ); addConstrainedElements( constrainedElementsByType, beanClass, constrainedMethodStaxBuilders.stream() - .map( builder -> builder.build( beanClass, getterPropertyMatcher, alreadyProcessedMethods ) ) + .map( builder -> builder.build( javaBean, alreadyProcessedMethods ) ) .collect( Collectors.toList() ) ); - List> alreadyProcessedConstructors = new ArrayList<>( constrainedConstructorStaxBuilders.size() ); + List alreadyProcessedConstructors = new ArrayList<>( constrainedConstructorStaxBuilders.size() ); addConstrainedElements( constrainedElementsByType, beanClass, constrainedConstructorStaxBuilders.stream() - .map( builder -> builder.build( beanClass, getterPropertyMatcher, alreadyProcessedConstructors ) ) + .map( builder -> builder.build( javaBean, alreadyProcessedConstructors ) ) .collect( Collectors.toList() ) ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedConstructorStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedConstructorStaxBuilder.java index 593b0ec9bb..e36b420a12 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedConstructorStaxBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedConstructorStaxBuilder.java @@ -7,9 +7,6 @@ package org.hibernate.validator.internal.xml.mapping; import java.lang.invoke.MethodHandles; -import java.lang.reflect.Constructor; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -26,13 +23,12 @@ 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.JavaBeanExecutable; +import org.hibernate.validator.internal.properties.Callable; +import org.hibernate.validator.internal.properties.javabean.JavaBean; import org.hibernate.validator.internal.util.CollectionHelper; 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.properties.GetterPropertyMatcher; /** * Builder for constrained constructors. @@ -68,28 +64,15 @@ public String getMethodName() { return mainAttributeValue; } - ConstrainedExecutable build(Class beanClass, GetterPropertyMatcher getterPropertyMatcher, List> alreadyProcessedConstructors) { + ConstrainedExecutable build(JavaBean javaBean, List alreadyProcessedConstructors) { Class[] parameterTypes = constrainedParameterStaxBuilders.stream() - .map( builder -> builder.getParameterType( beanClass ) ) + .map( builder -> builder.getParameterType( javaBean ) ) .toArray( Class[]::new ); - final Constructor constructor = run( - GetDeclaredConstructor.action( - beanClass, - parameterTypes - ) - ); - - if ( constructor == null ) { - throw LOG.getBeanDoesNotContainConstructorException( - beanClass, - parameterTypes - ); - } - JavaBeanExecutable executable = JavaBeanExecutable.of( getterPropertyMatcher, constructor ); + final Callable constructor = findConstructor( javaBean, parameterTypes ); if ( alreadyProcessedConstructors.contains( constructor ) ) { - throw LOG.getConstructorIsDefinedTwiceInMappingXmlForBeanException( constructor, beanClass ); + throw LOG.getConstructorIsDefinedTwiceInMappingXmlForBeanException( constructor, javaBean ); } else { alreadyProcessedConstructors.add( constructor ); @@ -98,7 +81,7 @@ ConstrainedExecutable build(Class beanClass, GetterPropertyMatcher getterProp // ignore annotations if ( ignoreAnnotations.isPresent() ) { annotationProcessingOptions.ignoreConstraintAnnotationsOnMember( - executable, + constructor, ignoreAnnotations.get() ); } @@ -106,21 +89,21 @@ ConstrainedExecutable build(Class beanClass, GetterPropertyMatcher getterProp List constrainedParameters = CollectionHelper.newArrayList( constrainedParameterStaxBuilders.size() ); for ( int index = 0; index < constrainedParameterStaxBuilders.size(); index++ ) { ConstrainedParameterStaxBuilder builder = constrainedParameterStaxBuilders.get( index ); - constrainedParameters.add( builder.build( executable, index ) ); + constrainedParameters.add( builder.build( constructor, index ) ); } Set> crossParameterConstraints = getCrossParameterStaxBuilder() - .map( builder -> builder.build( executable ) ).orElse( Collections.emptySet() ); + .map( builder -> builder.build( constructor ) ).orElse( Collections.emptySet() ); // parse the return value Set> returnValueConstraints = new HashSet<>(); Set> returnValueTypeArgumentConstraints = new HashSet<>(); - CascadingMetaDataBuilder cascadingMetaDataBuilder = getReturnValueStaxBuilder().map( builder -> builder.build( executable, returnValueConstraints, returnValueTypeArgumentConstraints ) ) + CascadingMetaDataBuilder cascadingMetaDataBuilder = getReturnValueStaxBuilder().map( builder -> builder.build( constructor, returnValueConstraints, returnValueTypeArgumentConstraints ) ) .orElse( CascadingMetaDataBuilder.nonCascading() ); return new ConstrainedExecutable( ConfigurationSource.XML, - executable, + constructor, constrainedParameters, crossParameterConstraints, returnValueConstraints, @@ -129,13 +112,8 @@ ConstrainedExecutable build(Class beanClass, GetterPropertyMatcher getterProp ); } - /** - * 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 Callable findConstructor(JavaBean javaBean, Class[] parameterTypes) { + return javaBean.getConstructorByParameters( parameterTypes ) + .orElseThrow( () -> LOG.getBeanDoesNotContainConstructorException( javaBean, 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 index 856f7b3476..10d74bd843 100644 --- 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 @@ -7,9 +7,6 @@ package org.hibernate.validator.internal.xml.mapping; import java.lang.invoke.MethodHandles; -import java.lang.reflect.Field; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.List; import java.util.Optional; import java.util.Set; @@ -24,12 +21,11 @@ import org.hibernate.validator.internal.metadata.location.ConstraintLocation; import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; import org.hibernate.validator.internal.metadata.raw.ConstrainedProperty; -import org.hibernate.validator.internal.properties.javabean.JavaBeanField; -import org.hibernate.validator.internal.util.ReflectionHelper; +import org.hibernate.validator.internal.properties.Property; +import org.hibernate.validator.internal.properties.javabean.JavaBean; 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.GetDeclaredField; import org.hibernate.validator.internal.xml.mapping.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration; /** @@ -62,15 +58,14 @@ protected String getAcceptableQName() { return FIELD_QNAME_LOCAL_PART; } - ConstrainedProperty build(Class beanClass, List alreadyProcessedFieldNames) { + ConstrainedProperty build(JavaBean javaBean, List alreadyProcessedFieldNames) { if ( alreadyProcessedFieldNames.contains( mainAttributeValue ) ) { - throw LOG.getIsDefinedTwiceInMappingXmlForBeanException( mainAttributeValue, beanClass ); + throw LOG.getIsDefinedTwiceInMappingXmlForBeanException( mainAttributeValue, javaBean ); } else { alreadyProcessedFieldNames.add( mainAttributeValue ); } - Field field = findField( beanClass, mainAttributeValue ); - JavaBeanField property = new JavaBeanField( field ); + Property property = findField( javaBean, mainAttributeValue ); ConstraintLocation constraintLocation = ConstraintLocation.forProperty( property ); Set> metaConstraints = constraintTypeStaxBuilders.stream() @@ -85,7 +80,7 @@ ConstrainedProperty build(Class beanClass, List alreadyProcessedField property, metaConstraints, containerElementTypeConfiguration.getMetaConstraints(), - getCascadingMetaData( containerElementTypeConfiguration.getTypeParametersCascadingMetaData(), ReflectionHelper.typeOf( field ) ) + getCascadingMetaData( containerElementTypeConfiguration.getTypeParametersCascadingMetaData(), property.getType() ) ); // ignore annotations @@ -99,21 +94,8 @@ ConstrainedProperty build(Class beanClass, List alreadyProcessedField return constrainedField; } - private static Field findField(Class beanClass, String 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(); + private static Property findField(JavaBean javaBean, String fieldName) { + return javaBean.getFieldPropertyByName( fieldName ) + .orElseThrow( () -> LOG.getBeanDoesNotContainTheFieldException( javaBean, 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 index 98692881a1..2ffde64184 100644 --- 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 @@ -7,9 +7,6 @@ package org.hibernate.validator.internal.xml.mapping; import java.lang.invoke.MethodHandles; -import java.lang.reflect.Method; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -28,14 +25,11 @@ import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter; import org.hibernate.validator.internal.properties.Callable; import org.hibernate.validator.internal.properties.Property; -import org.hibernate.validator.internal.properties.javabean.JavaBeanGetter; -import org.hibernate.validator.internal.util.ReflectionHelper; +import org.hibernate.validator.internal.properties.javabean.JavaBean; 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.GetMethodFromPropertyName; import org.hibernate.validator.internal.xml.mapping.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration; -import org.hibernate.validator.properties.GetterPropertyMatcher; /** * Builder for constrained getters. @@ -67,19 +61,18 @@ protected String getAcceptableQName() { return GETTER_QNAME_LOCAL_PART; } - private String getPropertyName(){ + private String getPropertyName() { return mainAttributeValue; } - ConstrainedExecutable build(Class beanClass, GetterPropertyMatcher getterPropertyMatcher, List alreadyProcessedGetterNames) { + ConstrainedExecutable build(JavaBean javaBean, List alreadyProcessedGetterNames) { if ( alreadyProcessedGetterNames.contains( getPropertyName() ) ) { - throw LOG.getIsDefinedTwiceInMappingXmlForBeanException( getPropertyName(), beanClass ); + throw LOG.getIsDefinedTwiceInMappingXmlForBeanException( getPropertyName(), javaBean ); } else { alreadyProcessedGetterNames.add( getPropertyName() ); } - Method getter = findGetter( beanClass, getterPropertyMatcher, getPropertyName() ); - Property property = new JavaBeanGetter( getter, getPropertyName() ); + Property property = findGetter( javaBean, getPropertyName() ); ConstraintLocation constraintLocation = ConstraintLocation.forProperty( property ); Set> metaConstraints = constraintTypeStaxBuilders.stream() @@ -87,7 +80,7 @@ ConstrainedExecutable build(Class beanClass, GetterPropertyMatcher getterProp .collect( Collectors.toSet() ); ContainerElementTypeConfiguration containerElementTypeConfiguration = getContainerElementTypeConfiguration( - ReflectionHelper.typeOf( getter ), constraintLocation ); + property.getType(), constraintLocation ); ConstrainedExecutable constrainedGetter = new ConstrainedExecutable( ConfigurationSource.XML, @@ -96,7 +89,7 @@ ConstrainedExecutable build(Class beanClass, GetterPropertyMatcher getterProp Collections.>emptySet(), metaConstraints, containerElementTypeConfiguration.getMetaConstraints(), - getCascadingMetaData( containerElementTypeConfiguration.getTypeParametersCascadingMetaData(), ReflectionHelper.typeOf( getter ) ) + getCascadingMetaData( containerElementTypeConfiguration.getTypeParametersCascadingMetaData(), property.getType() ) ); // ignore annotations @@ -110,23 +103,9 @@ ConstrainedExecutable build(Class beanClass, GetterPropertyMatcher getterProp return constrainedGetter; } - private static Method findGetter(Class beanClass, GetterPropertyMatcher getterPropertyMatcher, String getterName) { - final Method method = run( GetMethodFromPropertyName.action( beanClass, getterPropertyMatcher, getterName ) ); - if ( method == null ) { - throw LOG.getBeanDoesNotContainThePropertyException( beanClass, getterName ); - } - - return method; - } + private static Property findGetter(JavaBean javaBean, String getterName) { + Optional property = javaBean.getCallablePropertyByName( getterName ); - /** - * 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(); + return property.orElseThrow( () -> LOG.getBeanDoesNotContainThePropertyException( javaBean, 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 index 8931cecf6b..3092a80439 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedMethodStaxBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedMethodStaxBuilder.java @@ -7,9 +7,6 @@ package org.hibernate.validator.internal.xml.mapping; import java.lang.invoke.MethodHandles; -import java.lang.reflect.Method; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -26,13 +23,12 @@ 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.JavaBeanExecutable; +import org.hibernate.validator.internal.properties.Callable; +import org.hibernate.validator.internal.properties.javabean.JavaBean; import org.hibernate.validator.internal.util.CollectionHelper; 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.GetDeclaredMethod; -import org.hibernate.validator.properties.GetterPropertyMatcher; /** * Builder for constrained methods. @@ -69,41 +65,26 @@ public String getMethodName() { return mainAttributeValue; } - ConstrainedExecutable build(Class beanClass, GetterPropertyMatcher getterPropertyMatcher, List alreadyProcessedMethods) { + ConstrainedExecutable build(JavaBean javaBean, List alreadyProcessedMethods) { Class[] parameterTypes = constrainedParameterStaxBuilders.stream() - .map( builder -> builder.getParameterType( beanClass ) ) + .map( builder -> builder.getParameterType( javaBean ) ) .toArray( Class[]::new ); String methodName = getMethodName(); - final Method method = run( - GetDeclaredMethod.action( - beanClass, - methodName, - parameterTypes - ) - ); - - if ( method == null ) { - throw LOG.getBeanDoesNotContainMethodException( - beanClass, - methodName, - parameterTypes - ); - } + Callable callable = findCallable( javaBean, methodName, parameterTypes ); - if ( alreadyProcessedMethods.contains( method ) ) { - throw LOG.getMethodIsDefinedTwiceInMappingXmlForBeanException( method, beanClass ); + if ( alreadyProcessedMethods.contains( callable ) ) { + throw LOG.getMethodIsDefinedTwiceInMappingXmlForBeanException( callable, javaBean ); } else { - alreadyProcessedMethods.add( method ); + alreadyProcessedMethods.add( callable ); } - JavaBeanExecutable executable = JavaBeanExecutable.of( getterPropertyMatcher, method ); // ignore annotations if ( ignoreAnnotations.isPresent() ) { annotationProcessingOptions.ignoreConstraintAnnotationsOnMember( - executable, + callable, ignoreAnnotations.get() ); } @@ -111,21 +92,21 @@ ConstrainedExecutable build(Class beanClass, GetterPropertyMatcher getterProp List constrainedParameters = CollectionHelper.newArrayList( constrainedParameterStaxBuilders.size() ); for ( int index = 0; index < constrainedParameterStaxBuilders.size(); index++ ) { ConstrainedParameterStaxBuilder builder = constrainedParameterStaxBuilders.get( index ); - constrainedParameters.add( builder.build( executable, index ) ); + constrainedParameters.add( builder.build( callable, index ) ); } Set> crossParameterConstraints = getCrossParameterStaxBuilder() - .map( builder -> builder.build( executable ) ).orElse( Collections.emptySet() ); + .map( builder -> builder.build( callable ) ).orElse( Collections.emptySet() ); // parse the return value Set> returnValueConstraints = new HashSet<>(); Set> returnValueTypeArgumentConstraints = new HashSet<>(); - CascadingMetaDataBuilder cascadingMetaDataBuilder = getReturnValueStaxBuilder().map( builder -> builder.build( executable, returnValueConstraints, returnValueTypeArgumentConstraints ) ) + CascadingMetaDataBuilder cascadingMetaDataBuilder = getReturnValueStaxBuilder().map( builder -> builder.build( callable, returnValueConstraints, returnValueTypeArgumentConstraints ) ) .orElse( CascadingMetaDataBuilder.nonCascading() ); return new ConstrainedExecutable( ConfigurationSource.XML, - executable, + callable, constrainedParameters, crossParameterConstraints, returnValueConstraints, @@ -134,13 +115,8 @@ ConstrainedExecutable build(Class beanClass, GetterPropertyMatcher getterProp ); } - /** - * 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 Callable findCallable(JavaBean javaBean, String methodName, Class[] parameterTypes) { + return javaBean.getCallableByNameAndParameters( methodName, parameterTypes ) + .orElseThrow( () -> LOG.getBeanDoesNotContainMethodException( javaBean, 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 index 5344231f1c..352c0e8329 100644 --- 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 @@ -23,6 +23,7 @@ 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.properties.javabean.JavaBean; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; @@ -58,12 +59,12 @@ protected String getAcceptableQName() { return PARAMETER_QNAME_LOCAL_PART; } - public Class getParameterType(Class beanClass) { + public Class getParameterType(JavaBean javaBean) { try { return classLoadingHelper.loadClass( mainAttributeValue, defaultPackageStaxBuilder.build().orElse( "" ) ); } catch (ValidationException e) { - throw LOG.getInvalidParameterTypeException( mainAttributeValue, beanClass ); + throw LOG.getInvalidParameterTypeException( mainAttributeValue, javaBean ); } } 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 index 6c2a8f8ae5..e5bed33335 100644 --- 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 @@ -21,7 +21,7 @@ import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.xml.AbstractStaxBuilder; -import org.hibernate.validator.properties.GetterPropertyMatcher; +import org.hibernate.validator.spi.properties.GetterPropertyMatcher; /** * Top level builder for constraint mappings. Reads the whole mapping file and builds the constraint definitions defined 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 index b4af6a83c7..faf9f985ad 100644 --- 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 @@ -38,7 +38,7 @@ import org.hibernate.validator.internal.util.privilegedactions.SetContextClassLoader; import org.hibernate.validator.internal.xml.CloseIgnoringInputStream; import org.hibernate.validator.internal.xml.XmlParserHelper; -import org.hibernate.validator.properties.GetterPropertyMatcher; +import org.hibernate.validator.spi.properties.GetterPropertyMatcher; import org.xml.sax.SAXException; diff --git a/engine/src/main/java/org/hibernate/validator/properties/ExecutableProperty.java b/engine/src/main/java/org/hibernate/validator/properties/ExecutableProperty.java deleted file mode 100644 index b04e35d671..0000000000 --- a/engine/src/main/java/org/hibernate/validator/properties/ExecutableProperty.java +++ /dev/null @@ -1,29 +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.properties; - -import java.lang.reflect.Type; - -/** - * A class to be used for filtering properties. - * - * @author Marko Bekhta - */ -public interface ExecutableProperty { - - /** - * @return a return type of the method. - */ - Type getType(); - - /** - * @return full name of the method. - */ - String getName(); - - Type[] getParameterTypes(); -} diff --git a/engine/src/main/java/org/hibernate/validator/properties/GetterPropertyMatcher.java b/engine/src/main/java/org/hibernate/validator/properties/GetterPropertyMatcher.java deleted file mode 100644 index 9231c5c5d9..0000000000 --- a/engine/src/main/java/org/hibernate/validator/properties/GetterPropertyMatcher.java +++ /dev/null @@ -1,23 +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.properties; - -import java.lang.reflect.Executable; -import java.lang.reflect.Method; -import java.util.Set; - -/** - * @author Marko Bekhta - */ -public interface GetterPropertyMatcher { - - boolean isProperty(Executable executable); - - String getPropertyName(Method method); - - Set getPossibleMethodNamesForProperty(String propertyName); -} 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..888b1284f0 --- /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 java.lang.reflect.Type; + +/** + * A class that describes JavaBeans method. Is used for determining if a given + * JavaBeans method is a property or not. + * + * @author Marko Bekhta + * @since 6.1.0 + */ +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 + */ + Type[] getParameterTypes(); +} diff --git a/engine/src/main/java/org/hibernate/validator/spi/properties/GetterPropertyMatcher.java b/engine/src/main/java/org/hibernate/validator/spi/properties/GetterPropertyMatcher.java new file mode 100644 index 0000000000..a19ec7136e --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/spi/properties/GetterPropertyMatcher.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.spi.properties; + +import org.hibernate.validator.Incubating; +import org.hibernate.validator.internal.properties.DefaultGetterPropertyMatcher; + +/** + *

    + * Used to define JavaBeans property detection algorithm. The default + * implementation ({@link DefaultGetterPropertyMatcher}) uses next definition + * of JavaBeans property: + *

    + *

    + * A JavaBeans method is considered to be a valid getter method (property), if + * one of the next rules can be applied to it: + *

    + *
      + *
    • its name starts with "get" and it has a return type but no parameters
    • + *
    • its name starts with "is", it has no parameters and is returning {@code boolean}
    • + *
    • its name starts with "has", it has no parameters and is returning {@code boolean}.
    • + *
    + *

    + * The last rule is Hibernate Validator specific one and is not mandated by the JavaBeans spec. + *

    + * + * @author Marko Bekhta + * @since 6.1.0 + */ +@Incubating +public interface GetterPropertyMatcher { + + /** + * Determines if a given {@link ConstrainableExecutable} is a valid JavaBeans getter method (property). + * + * @param executable the {@link ConstrainableExecutable} under test + * + * @return {@code true} if given executable is a valid JavaBeans property, {@code false} otherwise + */ + boolean isProperty(ConstrainableExecutable executable); + + /** + * Performs a transformation of JavaBeans method name to its corresponding property name. + * For example by removing the prefixes like {@code get}/{@code is} etc. + * + * @param method the {@link ConstrainableExecutable} which methods name should be transformed + * + * @return a property name of a given executable + */ + String getPropertyName(ConstrainableExecutable method); + +} 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..4f1c293b6b --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/spi/properties/package-info.java @@ -0,0 +1,14 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ + +/** + *

    This package provides support for customization of the JavaBeans property detection logic.

    + *

    This package is part of the public Hibernate Validator SPI.

    + * + * @author Marko Bekhta + */ +package org.hibernate.validator.spi.properties; 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 088552b0b1..c0e2731213 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 @@ -21,7 +21,7 @@ import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; import org.hibernate.validator.internal.metadata.location.ConstraintLocation; import org.hibernate.validator.internal.properties.DefaultGetterPropertyMatcher; -import org.hibernate.validator.internal.properties.javabean.JavaBeanExecutable; +import org.hibernate.validator.internal.properties.javabean.JavaBean; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; import org.hibernate.validator.testutil.TestForIssue; @@ -52,13 +52,13 @@ public void setUp() throws Exception { public void two_meta_constraints_for_the_same_constraint_should_be_equal() throws Exception { DefaultGetterPropertyMatcher propertyFilter = new DefaultGetterPropertyMatcher(); ConstraintDescriptorImpl constraintDescriptor1 = new ConstraintDescriptorImpl<>( - constraintHelper, JavaBeanExecutable.of( propertyFilter, barMethod ), constraintAnnotationDescriptor, METHOD + constraintHelper, JavaBean.toJavaBeanExecutable( propertyFilter, barMethod ), constraintAnnotationDescriptor, METHOD ); ConstraintLocation location1 = ConstraintLocation.forClass( Foo.class ); MetaConstraint metaConstraint1 = MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintDescriptor1, location1 ); ConstraintDescriptorImpl constraintDescriptor2 = new ConstraintDescriptorImpl<>( - constraintHelper, JavaBeanExecutable.of( propertyFilter, barMethod ), constraintAnnotationDescriptor, METHOD + constraintHelper, JavaBean.toJavaBeanExecutable( propertyFilter, barMethod ), constraintAnnotationDescriptor, METHOD ); ConstraintLocation location2 = ConstraintLocation.forClass( Foo.class ); MetaConstraint metaConstraint2 = MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintDescriptor2, location2 ); 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 7904192084..01d50fed76 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 @@ -41,7 +41,7 @@ import org.hibernate.validator.internal.metadata.raw.ConstrainedProperty; import org.hibernate.validator.internal.metadata.raw.ConstrainedType; import org.hibernate.validator.internal.properties.DefaultGetterPropertyMatcher; -import org.hibernate.validator.internal.properties.javabean.JavaBeanExecutable; +import org.hibernate.validator.internal.properties.javabean.JavaBean; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.testutil.TestForIssue; import org.joda.time.DateMidnight; @@ -105,7 +105,7 @@ public void testGetCrossParameterMetaData() throws Exception { assertThat( createEvent.getCrossParameterConstraints() ).hasSize( 1 ); assertThat( createEvent.getCallable() ).isEqualTo( - JavaBeanExecutable.of( + JavaBean.toJavaBeanExecutable( new DefaultGetterPropertyMatcher(), Calendar.class.getMethod( "createEvent", 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 4e9f0eb8d6..c0e1edc4c4 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 @@ -18,7 +18,7 @@ import org.hibernate.validator.internal.metadata.raw.ConstrainedType; import org.hibernate.validator.internal.properties.Constrainable; import org.hibernate.validator.internal.properties.DefaultGetterPropertyMatcher; -import org.hibernate.validator.internal.properties.javabean.JavaBeanExecutable; +import org.hibernate.validator.internal.properties.javabean.JavaBean; import org.hibernate.validator.internal.properties.javabean.JavaBeanField; /** @@ -68,7 +68,7 @@ protected ConstrainedElement findConstrainedElement(BeanConfiguration beanCon constrainable = new JavaBeanField( (Field) member ); } else { - constrainable = JavaBeanExecutable.of( new DefaultGetterPropertyMatcher(), (Executable) member ); + constrainable = JavaBean.toJavaBeanExecutable( new DefaultGetterPropertyMatcher(), (Executable) member ); } for ( ConstrainedElement constrainedElement : beanConfiguration.getConstrainedElements() ) { From 4cb8d138bba48bbb13f1ea2fc8b70b29b4d21f21 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Sun, 18 Mar 2018 23:14:37 +0100 Subject: [PATCH 5/6] HV-1363 Added ability to configure property filter for ValidatorFactory --- .../HibernateValidatorConfiguration.java | 22 ++ .../validator/HibernateValidatorFactory.java | 11 + .../cfg/context/DefaultConstraintMapping.java | 8 +- .../internal/engine/ConfigurationImpl.java | 27 ++- .../internal/engine/ValidatorFactoryImpl.java | 52 ++++- .../validator/internal/util/logging/Log.java | 2 +- .../metadata/core/MetaConstraintTest.java | 6 +- .../properties/GetterPropertyMatcherTest.java | 220 ++++++++++++++++++ 8 files changed, 331 insertions(+), 17 deletions(-) create mode 100644 engine/src/test/java/org/hibernate/validator/test/properties/GetterPropertyMatcherTest.java diff --git a/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java b/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java index 45773d3949..69a241e64f 100644 --- a/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java +++ b/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java @@ -20,6 +20,7 @@ import org.hibernate.validator.cfg.ConstraintMapping; import org.hibernate.validator.constraints.ParameterScriptAssert; import org.hibernate.validator.constraints.ScriptAssert; +import org.hibernate.validator.spi.properties.GetterPropertyMatcher; import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; import org.hibernate.validator.spi.scripting.ScriptEvaluator; import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; @@ -107,6 +108,15 @@ public interface HibernateValidatorConfiguration extends Configuration * Returns the {@link ResourceBundleLocator} used by the @@ -311,4 +321,16 @@ public interface HibernateValidatorConfiguration extends Configuration> configuredTypes; private final Set> typeContexts; private final Set> definedConstraints; private final Set> constraintContexts; - public DefaultConstraintMapping() { + public DefaultConstraintMapping(GetterPropertyMatcher getterPropertyMatcher) { + this.getterPropertyMatcher = getterPropertyMatcher; this.annotationProcessingOptions = new AnnotationProcessingOptionsImpl(); this.configuredTypes = newHashSet(); this.typeContexts = newHashSet(); @@ -63,7 +65,7 @@ public final TypeConstraintMappingContext type(Class type) { throw LOG.getBeanClassHasAlreadyBeConfiguredViaProgrammaticApiException( type ); } - TypeConstraintMappingContextImpl typeContext = new TypeConstraintMappingContextImpl<>( this, type, new DefaultGetterPropertyMatcher() ); + TypeConstraintMappingContextImpl typeContext = new TypeConstraintMappingContextImpl<>( this, type, getterPropertyMatcher ); typeContexts.add( typeContext ); configuredTypes.add( type ); 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 863467d48a..11997bdc0b 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 @@ -42,6 +42,7 @@ 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.DefaultGetterPropertyMatcher; import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.Version; import org.hibernate.validator.internal.util.logging.Log; @@ -53,6 +54,7 @@ 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.properties.GetterPropertyMatcher; import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; @@ -103,6 +105,7 @@ public class ConfigurationImpl implements HibernateValidatorConfiguration, Confi private ScriptEvaluatorFactory scriptEvaluatorFactory; private Duration temporalValidationTolerance; private Object constraintValidatorPayload; + private GetterPropertyMatcher getterPropertyMatcher; public ConfigurationImpl(BootstrapState state) { this(); @@ -294,6 +297,14 @@ public HibernateValidatorConfiguration constraintValidatorPayload(Object constra return this; } + @Override + public HibernateValidatorConfiguration getterPropertyMatcher(GetterPropertyMatcher getterPropertyMatcher) { + Contracts.assertNotNull( getterPropertyMatcher, MESSAGES.parameterMustNotBeNull( "getterPropertyMatcher" ) ); + + this.getterPropertyMatcher = getterPropertyMatcher; + return this; + } + public boolean isAllowParallelMethodsDefineParameterConstraints() { return this.methodValidationConfigurationBuilder.isAllowParallelMethodsDefineParameterConstraints(); } @@ -304,7 +315,17 @@ public MethodValidationConfiguration getMethodValidationConfiguration() { @Override public final DefaultConstraintMapping createConstraintMapping() { - return new DefaultConstraintMapping(); + GetterPropertyMatcher getterPropertyMatcherToUse = null; + if ( getterPropertyMatcher == null ) { + getterPropertyMatcherToUse = new DefaultGetterPropertyMatcher(); + LOG.creatingConstraintMappingPriorToBuildingFactoryWithoutDefinedFilter(); + } + else { + getterPropertyMatcherToUse = getterPropertyMatcher; + } + LOG.creatingConstraintMappingPriorToBuildingFactory( getterPropertyMatcherToUse.getClass() ); + + return new DefaultConstraintMapping( getterPropertyMatcherToUse ); } @Override @@ -450,6 +471,10 @@ public Object getConstraintValidatorPayload() { return constraintValidatorPayload; } + public GetterPropertyMatcher getGetterPropertyMatcher() { + return getterPropertyMatcher; + } + @Override public Set> getValueExtractors() { return validationBootstrapParameters.getValueExtractorDescriptors() 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 a7750ebe44..39faab0f79 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 @@ -142,7 +142,6 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) { ClassLoader externalClassLoader = getExternalClassLoader( configurationState ); this.valueExtractorManager = new ValueExtractorManager( configurationState.getValueExtractors() ); - this.getterPropertyMatcher = new DefaultGetterPropertyMatcher(); this.beanMetaDataManagers = new ConcurrentHashMap<>(); this.constraintHelper = new ConstraintHelper(); this.typeResolutionHelper = new TypeResolutionHelper(); @@ -152,6 +151,9 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) { if ( configurationState instanceof ConfigurationImpl ) { hibernateSpecificConfig = (ConfigurationImpl) configurationState; } + Map properties = configurationState.getProperties(); + + this.getterPropertyMatcher = getGetterPropertyMatcher( hibernateSpecificConfig, properties, externalClassLoader ); // HV-302; don't load XmlMappingParser if not necessary if ( configurationState.getMappingStreams().isEmpty() ) { @@ -167,14 +169,13 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) { getConstraintMappings( typeResolutionHelper, configurationState, + getterPropertyMatcher, externalClassLoader ) ); registerCustomConstraintValidators( constraintMappings, constraintHelper ); - Map properties = configurationState.getProperties(); - this.methodValidationConfiguration = new MethodValidationConfiguration.Builder() .allowOverridingMethodAlterParameterConstraint( getAllowOverridingMethodAlterParameterConstraint( hibernateSpecificConfig, properties ) @@ -213,7 +214,7 @@ private static ClassLoader getExternalClassLoader(ConfigurationState configurati } private static Set getConstraintMappings(TypeResolutionHelper typeResolutionHelper, - ConfigurationState configurationState, ClassLoader externalClassLoader) { + ConfigurationState configurationState, GetterPropertyMatcher getterPropertyMatcher, ClassLoader externalClassLoader) { Set constraintMappings = newHashSet(); if ( configurationState instanceof ConfigurationImpl ) { @@ -229,7 +230,7 @@ private static Set getConstraintMappings(TypeResolutio ConstraintMappingContributor serviceLoaderBasedContributor = new ServiceLoaderBasedConstraintMappingContributor( typeResolutionHelper, externalClassLoader != null ? externalClassLoader : run( GetClassLoader.fromContext() ) ); - DefaultConstraintMappingBuilder builder = new DefaultConstraintMappingBuilder( constraintMappings ); + DefaultConstraintMappingBuilder builder = new DefaultConstraintMappingBuilder( getterPropertyMatcher, constraintMappings ); serviceLoaderBasedContributor.createConstraintMappings( builder ); } @@ -238,7 +239,7 @@ private static Set getConstraintMappings(TypeResolutio externalClassLoader ); for ( ConstraintMappingContributor contributor : contributors ) { - DefaultConstraintMappingBuilder builder = new DefaultConstraintMappingBuilder( constraintMappings ); + DefaultConstraintMappingBuilder builder = new DefaultConstraintMappingBuilder( getterPropertyMatcher, constraintMappings ); contributor.createConstraintMappings( builder ); } @@ -294,6 +295,11 @@ public Duration getTemporalValidationTolerance() { return validatorFactoryScopedContext.getTemporalValidationTolerance(); } + @Override + public GetterPropertyMatcher getGetterPropertyMatcher() { + return getterPropertyMatcher; + } + public boolean isFailFast() { return validatorFactoryScopedContext.isFailFast(); } @@ -555,6 +561,32 @@ private Object getConstraintValidatorPayload(ConfigurationState configurationSta return null; } + private static GetterPropertyMatcher getGetterPropertyMatcher(ConfigurationImpl hibernateSpecificConfig, Map properties, ClassLoader externalClassLoader) { + if ( hibernateSpecificConfig.getGetterPropertyMatcher() != null ) { + LOG.usingGetterPropertyMatcher( hibernateSpecificConfig.getGetterPropertyMatcher().getClass() ); + return hibernateSpecificConfig.getGetterPropertyMatcher(); + } + + String getterPropertyMatcherFqcn = properties.get( HibernateValidatorConfiguration.GETTER_PROPERTY_MATCHER_CLASSNAME ); + if ( getterPropertyMatcherFqcn != null ) { + try { + @SuppressWarnings("unchecked") + Class clazz = (Class) run( + LoadClass.action( getterPropertyMatcherFqcn, externalClassLoader ) + ); + GetterPropertyMatcher getterPropertyMatcher = run( NewInstance.action( clazz, "getter property matcher class" ) ); + LOG.usingGetterPropertyMatcher( clazz ); + + return getterPropertyMatcher; + } + catch (Exception e) { + throw LOG.getUnableToInstantiateGetterPropertyMatcherClassException( getterPropertyMatcherFqcn, e ); + } + } + + return new DefaultGetterPropertyMatcher(); + } + private static void registerCustomConstraintValidators(Set constraintMappings, ConstraintHelper constraintHelper) { Set> definedConstraints = newHashSet(); @@ -603,16 +635,18 @@ private static T run(PrivilegedAction action) { */ private static class DefaultConstraintMappingBuilder implements ConstraintMappingContributor.ConstraintMappingBuilder { + + private final GetterPropertyMatcher getterPropertyMatcher; private final Set mappings; - public DefaultConstraintMappingBuilder(Set mappings) { - super(); + public DefaultConstraintMappingBuilder(GetterPropertyMatcher getterPropertyMatcher, Set mappings) { + this.getterPropertyMatcher = getterPropertyMatcher; this.mappings = mappings; } @Override public ConstraintMapping addConstraintMapping() { - DefaultConstraintMapping mapping = new DefaultConstraintMapping(); + DefaultConstraintMapping mapping = new DefaultConstraintMapping( getterPropertyMatcher ); mappings.add( mapping ); return mapping; } 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 ca4aa9d560..13c94b683d 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 @@ -871,7 +871,7 @@ ConstraintDefinitionException getConstraintValidatorDefinitionConstraintMismatch void creatingConstraintMappingPriorToBuildingFactory(@FormatWith(ClassObjectFormatter.class) Class getterPropertyMatcherClass); @LogMessage(level = WARN) - @Message(id = 247, value = "As no property filter was defined for current configuration a default one will be used.") + @Message(id = 247, value = "As no getter property matcher was defined for current configuration a default one will be used.") void creatingConstraintMappingPriorToBuildingFactoryWithoutDefinedFilter(); @Message(id = 248, value = "Unable to access field %3$s of class %2$s using lookup %1$s.") 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 c0e2731213..d613dc790f 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 @@ -50,15 +50,15 @@ public void setUp() throws Exception { @Test @TestForIssue(jiraKey = "HV-930") public void two_meta_constraints_for_the_same_constraint_should_be_equal() throws Exception { - DefaultGetterPropertyMatcher propertyFilter = new DefaultGetterPropertyMatcher(); + DefaultGetterPropertyMatcher getterPropertyMatcher = new DefaultGetterPropertyMatcher(); ConstraintDescriptorImpl constraintDescriptor1 = new ConstraintDescriptorImpl<>( - constraintHelper, JavaBean.toJavaBeanExecutable( propertyFilter, barMethod ), constraintAnnotationDescriptor, METHOD + constraintHelper, JavaBean.toJavaBeanExecutable( getterPropertyMatcher, barMethod ), constraintAnnotationDescriptor, METHOD ); ConstraintLocation location1 = ConstraintLocation.forClass( Foo.class ); MetaConstraint metaConstraint1 = MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintDescriptor1, location1 ); ConstraintDescriptorImpl constraintDescriptor2 = new ConstraintDescriptorImpl<>( - constraintHelper, JavaBean.toJavaBeanExecutable( propertyFilter, barMethod ), constraintAnnotationDescriptor, METHOD + constraintHelper, JavaBean.toJavaBeanExecutable( getterPropertyMatcher, barMethod ), constraintAnnotationDescriptor, METHOD ); ConstraintLocation location2 = ConstraintLocation.forClass( Foo.class ); MetaConstraint metaConstraint2 = MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintDescriptor2, location2 ); diff --git a/engine/src/test/java/org/hibernate/validator/test/properties/GetterPropertyMatcherTest.java b/engine/src/test/java/org/hibernate/validator/test/properties/GetterPropertyMatcherTest.java new file mode 100644 index 0000000000..55b0d91bdb --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/properties/GetterPropertyMatcherTest.java @@ -0,0 +1,220 @@ +/* + * 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.lang.reflect.Type; +import java.util.Arrays; +import java.util.List; +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.Size; + +import org.hibernate.validator.HibernateValidator; +import org.hibernate.validator.HibernateValidatorConfiguration; +import org.hibernate.validator.spi.properties.ConstrainableExecutable; +import org.hibernate.validator.spi.properties.GetterPropertyMatcher; +import org.hibernate.validator.testutil.ConstraintViolationAssert; +import org.hibernate.validator.testutils.ValidatorUtil; + +import org.testng.annotations.Test; + +/** + * @author Marko Bekhta + */ +public class GetterPropertyMatcherTest { + + @Test + public void testGetterPropertyMatcher() throws Exception { + GetterPropertyMatcher filter = new FooGetterPropertyMatcher(); + + ConstrainableExecutableImpl fooMethod = new ConstrainableExecutableImpl( Foo.class.getDeclaredMethod( "fooMethod" ) ); + ConstrainableExecutableImpl getMethod = new ConstrainableExecutableImpl( Foo.class.getDeclaredMethod( "getMethod" ) ); + + assertThat( filter.isProperty( fooMethod ) ).isTrue(); + assertThat( filter.isProperty( getMethod ) ).isFalse(); + + assertThat( filter.getPropertyName( fooMethod ) ).isEqualTo( "method" ); + } + + @Test + public void testConfigureGetterPropertyMatcher() throws Exception { + Validator validator = Validation.byProvider( HibernateValidator.class ) + .configure() + .getterPropertyMatcher( new FooGetterPropertyMatcher() ) + .buildValidatorFactory() + .getValidator(); + Set> violations = validator.validate( new Foo() ); + + ConstraintViolationAssert.assertThat( violations ).containsOnlyViolations( + violationOf( Min.class ) + ); + } + + @Test + public void testConfigureGetterPropertyMatcherWithProperty() throws Exception { + Validator validator = Validation.byProvider( HibernateValidator.class ) + .configure() + .addProperty( HibernateValidatorConfiguration.GETTER_PROPERTY_MATCHER_CLASSNAME, FooGetterPropertyMatcher.class.getName() ) + .buildValidatorFactory() + .getValidator(); + Set> violations = validator.validate( new Foo() ); + + ConstraintViolationAssert.assertThat( violations ).containsOnlyViolations( + violationOf( Min.class ) + ); + } + + @Test + public void testNoPrefixGetterPropertyMatcher() { + Validator validator = Validation.byProvider( HibernateValidator.class ) + .configure() + .getterPropertyMatcher( new NoPrefixGetterPropertyMatcher() ) + .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" ) + ); + } + + 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 NoPrefixGetterPropertyMatcher implements GetterPropertyMatcher { + + @Override + public boolean isProperty(ConstrainableExecutable executable) { + String name = executable.getName(); + return executable.getParameterTypes().length == 0 + && !name.startsWith( "is" ) + && !name.startsWith( "get" ); + } + + @Override + public String getPropertyName(ConstrainableExecutable method) { + return method.getName(); + } + } + + public static class FooGetterPropertyMatcher implements GetterPropertyMatcher { + + @Override + public boolean isProperty(ConstrainableExecutable executable) { + return executable.getParameterTypes().length == 0 + && executable.getName().startsWith( "foo" ); + } + + @Override + public String getPropertyName(ConstrainableExecutable method) { + char[] chars = method.getName().substring( 3 ).toCharArray(); + chars[0] = Character.toLowerCase( chars[0] ); + return new String( chars ); + } + } + + 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 Type[] getParameterTypes() { + return method.getParameterTypes(); + } + } +} From 1b558860d4dfaa14568baf8099f5f85e4090914d Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Sun, 18 Mar 2018 23:32:58 +0100 Subject: [PATCH 6/6] HV-1363 Updated ValidationExtension to use new property filtering - introduced new helper in cdi module to deal with new PropertyFilter spi. --- .../validator/cdi/ValidationExtension.java | 9 ++- .../util/GetterPropertyMatcherHelper.java | 75 +++++++++++++++++++ .../DefaultGetterPropertyMatcher.java | 2 +- .../internal/util/ReflectionHelper.java | 42 ----------- 4 files changed, 81 insertions(+), 47 deletions(-) create mode 100644 cdi/src/main/java/org/hibernate/validator/cdi/internal/util/GetterPropertyMatcherHelper.java 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 ea0f4c52f6..173ee93568 100644 --- a/cdi/src/main/java/org/hibernate/validator/cdi/ValidationExtension.java +++ b/cdi/src/main/java/org/hibernate/validator/cdi/ValidationExtension.java @@ -51,9 +51,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.GetterPropertyMatcherHelper; 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 +99,7 @@ public class ValidationExtension implements Extension { */ private final Validator validator; private final ValidatorFactory validatorFactory; + private final GetterPropertyMatcherHelper getterPropertyMatcherHelper; private final Set globalExecutableTypes; private final boolean isExecutableValidationEnabled; @@ -119,6 +120,7 @@ public ValidationExtension() { isExecutableValidationEnabled = bootstrap.isExecutableValidationEnabled(); validatorFactory = config.buildValidatorFactory(); validator = validatorFactory.getValidator(); + getterPropertyMatcherHelper = GetterPropertyMatcherHelper.forValidationFactory( validatorFactory ); executableHelper = new ExecutableHelper( new TypeResolutionHelper() ); } @@ -263,8 +265,7 @@ private void determineConstrainedMethods(AnnotatedType type, BeanDescript for ( AnnotatedMethod annotatedMethod : type.getMethods() ) { Method method = annotatedMethod.getJavaMember(); - //TODO: need to update the getter logic here: - boolean isGetter = false/*ReflectionHelper.isGetterMethod( method )*/; + boolean isGetter = getterPropertyMatcherHelper.isProperty( method ); // obtain @ValidateOnExecution from the top-most method in the hierarchy Method methodForExecutableTypeRetrieval = replaceWithOverriddenOrInterfaceMethod( method, overriddenAndImplementedMethods ); @@ -317,7 +318,7 @@ private boolean isNonGetterConstrained(Method method, BeanDescriptor beanDescrip } private boolean isGetterConstrained(Method method, BeanDescriptor beanDescriptor) { - String propertyName = ReflectionHelper.getPropertyName( method ); + String propertyName = getterPropertyMatcherHelper.getPropertyName( method ); PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty( propertyName ); return propertyDescriptor != null && propertyDescriptor.findConstraints() .declaredOn( ElementType.METHOD ) diff --git a/cdi/src/main/java/org/hibernate/validator/cdi/internal/util/GetterPropertyMatcherHelper.java b/cdi/src/main/java/org/hibernate/validator/cdi/internal/util/GetterPropertyMatcherHelper.java new file mode 100644 index 0000000000..5b39de7d11 --- /dev/null +++ b/cdi/src/main/java/org/hibernate/validator/cdi/internal/util/GetterPropertyMatcherHelper.java @@ -0,0 +1,75 @@ +/* + * 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.lang.reflect.Type; + +import javax.validation.ValidatorFactory; + +import org.hibernate.validator.HibernateValidatorFactory; +import org.hibernate.validator.internal.properties.DefaultGetterPropertyMatcher; +import org.hibernate.validator.spi.properties.ConstrainableExecutable; +import org.hibernate.validator.spi.properties.GetterPropertyMatcher; + +/** + * A wrapper around {@link GetterPropertyMatcher}. + * + * @author Marko Bekhta + */ +public class GetterPropertyMatcherHelper { + + private final GetterPropertyMatcher getterPropertyMatcher; + + private GetterPropertyMatcherHelper(GetterPropertyMatcher getterPropertyMatcher) { + this.getterPropertyMatcher = getterPropertyMatcher; + } + + public boolean isProperty(Method method) { + return getterPropertyMatcher.isProperty( new ConstrainableMethod( method ) ); + } + + public String getPropertyName(Method method) { + return getterPropertyMatcher.getPropertyName( new ConstrainableMethod( method ) ); + } + + public static GetterPropertyMatcherHelper forValidationFactory(ValidatorFactory factory) { + GetterPropertyMatcher getterPropertyMatcher; + if ( factory instanceof HibernateValidatorFactory ) { + getterPropertyMatcher = factory.unwrap( HibernateValidatorFactory.class ).getGetterPropertyMatcher(); + } + else { + getterPropertyMatcher = new DefaultGetterPropertyMatcher(); + } + return new GetterPropertyMatcherHelper( getterPropertyMatcher ); + } + + 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 Type[] getParameterTypes() { + return method.getParameterTypes(); + } + + public Method getMethod() { + return method; + } + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/properties/DefaultGetterPropertyMatcher.java b/engine/src/main/java/org/hibernate/validator/internal/properties/DefaultGetterPropertyMatcher.java index cb437d5cd5..94123fe024 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/properties/DefaultGetterPropertyMatcher.java +++ b/engine/src/main/java/org/hibernate/validator/internal/properties/DefaultGetterPropertyMatcher.java @@ -18,7 +18,7 @@ public class DefaultGetterPropertyMatcher implements GetterPropertyMatcher { 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 = { + private static final String[] PROPERTY_ACCESSOR_PREFIXES = { PROPERTY_ACCESSOR_PREFIX_GET, PROPERTY_ACCESSOR_PREFIX_IS, PROPERTY_ACCESSOR_PREFIX_HAS 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 ccab9c9316..dca9279db1 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 @@ -6,7 +6,6 @@ */ package org.hibernate.validator.internal.util; -import static org.hibernate.validator.internal.properties.DefaultGetterPropertyMatcher.PROPERTY_ACCESSOR_PREFIXES; import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; import java.lang.invoke.MethodHandles; @@ -83,47 +82,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. - */ - @Deprecated - 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; - } - /** * @param member The {@code Member} instance for which to retrieve the type. *