buildConstraintDescriptor(Constrainable constrainable,
A annotation,
ConstraintLocationKind type) {
return new ConstraintDescriptorImpl<>(
constraintCreationContext.getConstraintHelper(),
constrainable,
new ConstraintAnnotationDescriptor<>( annotation ),
type
);
}
/**
* 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();
}
/**
* Finds type arguments constraints for fields.
*/
protected Set> findTypeAnnotationConstraints(JavaBeanField javaBeanField) {
return findTypeArgumentsConstraints(
javaBeanField,
new TypeArgumentFieldLocation( javaBeanField ),
javaBeanField.getAnnotatedType()
);
}
/**
* Finds type arguments constraints for method return values.
*/
protected Set> findTypeAnnotationConstraints(JavaBeanExecutable javaBeanExecutable) {
return findTypeArgumentsConstraints(
javaBeanExecutable,
new TypeArgumentReturnValueLocation( javaBeanExecutable ),
javaBeanExecutable.getAnnotatedType()
);
}
private CascadingMetaDataBuilder findCascadingMetaData(JavaBeanParameter javaBeanParameter) {
Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData = getTypeParametersCascadingMetadata( javaBeanParameter.getAnnotatedType(),
javaBeanParameter.getTypeParameters() );
try {
return getCascadingMetaData( javaBeanParameter, containerElementTypesCascadingMetaData );
}
catch (ArrayIndexOutOfBoundsException ex) {
LOG.warn( MESSAGES.constraintOnConstructorOfNonStaticInnerClass(), ex );
return CascadingMetaDataBuilder.nonCascading();
}
}
private CascadingMetaDataBuilder findCascadingMetaData(JavaBeanField javaBeanField) {
Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData = getTypeParametersCascadingMetadata(
javaBeanField.getAnnotatedType(),
javaBeanField.getTypeParameters() );
return getCascadingMetaData( javaBeanField, containerElementTypesCascadingMetaData );
}
private CascadingMetaDataBuilder findCascadingMetaData(JavaBeanExecutable javaBeanExecutable) {
Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData = getTypeParametersCascadingMetadata( javaBeanExecutable.getAnnotatedType(),
javaBeanExecutable.getTypeParameters() );
return getCascadingMetaData( javaBeanExecutable, containerElementTypesCascadingMetaData );
}
private Map, CascadingMetaDataBuilder> getTypeParametersCascadingMetadata(AnnotatedType annotatedType,
TypeVariable[] typeParameters) {
if ( annotatedType instanceof AnnotatedArrayType ) {
return getTypeParametersCascadingMetaDataForArrayType( (AnnotatedArrayType) annotatedType );
}
else if ( annotatedType instanceof AnnotatedParameterizedType ) {
return getTypeParametersCascadingMetaDataForParameterizedType( (AnnotatedParameterizedType) annotatedType, typeParameters );
}
else {
return Collections.emptyMap();
}
}
private Map, CascadingMetaDataBuilder> getTypeParametersCascadingMetaDataForParameterizedType(
AnnotatedParameterizedType annotatedParameterizedType, TypeVariable[] typeParameters) {
Map, CascadingMetaDataBuilder> typeParametersCascadingMetadata = CollectionHelper.newHashMap( typeParameters.length );
AnnotatedType[] annotatedTypeArguments = annotatedParameterizedType.getAnnotatedActualTypeArguments();
int i = 0;
for ( AnnotatedType annotatedTypeArgument : annotatedTypeArguments ) {
Map, CascadingMetaDataBuilder> nestedTypeParametersCascadingMetadata = getTypeParametersCascadingMetaDataForAnnotatedType(
annotatedTypeArgument );
typeParametersCascadingMetadata.put( typeParameters[i], new CascadingMetaDataBuilder( annotatedParameterizedType.getType(), typeParameters[i],
annotatedTypeArgument.isAnnotationPresent( Valid.class ), nestedTypeParametersCascadingMetadata,
getGroupConversions( annotatedTypeArgument ) ) );
i++;
}
return typeParametersCascadingMetadata;
}
private Map, CascadingMetaDataBuilder> getTypeParametersCascadingMetaDataForArrayType(AnnotatedArrayType annotatedArrayType) {
// HV-1428 Container element support is disabled for arrays
return Collections.emptyMap();
// Map, CascadingTypeParameter> typeParametersCascadingMetadata = CollectionHelper.newHashMap( 1 );
// AnnotatedType containerElementAnnotatedType = annotatedArrayType.getAnnotatedGenericComponentType();
//
// Map, CascadingTypeParameter> nestedTypeParametersCascadingMetadata = getTypeParametersCascadingMetaDataForAnnotatedType(
// containerElementAnnotatedType );
//
// TypeVariable arrayElement = new ArrayElement( annotatedArrayType );
// typeParametersCascadingMetadata.put( arrayElement, new CascadingTypeParameter( annotatedArrayType.getType(),
// arrayElement,
// annotatedArrayType.isAnnotationPresent( Valid.class ),
// nestedTypeParametersCascadingMetadata,
// getGroupConversions( annotatedArrayType ) ) );
//
// return typeParametersCascadingMetadata;
}
private Map, CascadingMetaDataBuilder> getTypeParametersCascadingMetaDataForAnnotatedType(AnnotatedType annotatedType) {
if ( annotatedType instanceof AnnotatedArrayType ) {
return getTypeParametersCascadingMetaDataForArrayType( (AnnotatedArrayType) annotatedType );
}
else if ( annotatedType instanceof AnnotatedParameterizedType ) {
return getTypeParametersCascadingMetaDataForParameterizedType( (AnnotatedParameterizedType) annotatedType,
ReflectionHelper.getClassFromType( annotatedType.getType() ).getTypeParameters() );
}
else {
return Collections.emptyMap();
}
}
/**
* Finds type arguments constraints for parameters.
*
* @param javaBeanParameter the parameter
*
* @return a set of type arguments constraints, or an empty set if no constrained type arguments are found
*/
protected Set> findTypeAnnotationConstraintsForExecutableParameter(JavaBeanExecutable javaBeanExecutable,
JavaBeanParameter javaBeanParameter) {
try {
return findTypeArgumentsConstraints(
javaBeanExecutable,
new TypeArgumentExecutableParameterLocation( javaBeanExecutable, javaBeanParameter.getIndex() ),
javaBeanParameter.getAnnotatedType()
);
}
catch (ArrayIndexOutOfBoundsException ex) {
LOG.warn( MESSAGES.constraintOnConstructorOfNonStaticInnerClass(), ex );
return Collections.emptySet();
}
}
private Set> findTypeArgumentsConstraints(Constrainable constrainable, TypeArgumentLocation location, AnnotatedType annotatedType) {
// HV-1428 Container element support is disabled for arrays
if ( !(annotatedType instanceof AnnotatedParameterizedType) ) {
return Collections.emptySet();
}
Set> typeArgumentConstraints = new HashSet<>();
// if we have an array, we need to unwrap the array first
if ( annotatedType instanceof AnnotatedArrayType ) {
AnnotatedArrayType annotatedArrayType = (AnnotatedArrayType) annotatedType;
Type validatedType = annotatedArrayType.getAnnotatedGenericComponentType().getType();
TypeVariable arrayElementTypeArgument = new ArrayElement( annotatedArrayType );
typeArgumentConstraints.addAll( findTypeUseConstraints( constrainable, annotatedArrayType, arrayElementTypeArgument, location, validatedType ) );
typeArgumentConstraints.addAll( findTypeArgumentsConstraints( constrainable,
new NestedTypeArgumentLocation( location, arrayElementTypeArgument, validatedType ),
annotatedArrayType.getAnnotatedGenericComponentType() ) );
}
else if ( annotatedType instanceof AnnotatedParameterizedType ) {
AnnotatedParameterizedType annotatedParameterizedType = (AnnotatedParameterizedType) annotatedType;
int i = 0;
for ( TypeVariable typeVariable : ReflectionHelper.getClassFromType( annotatedType.getType() ).getTypeParameters() ) {
AnnotatedType annotatedTypeParameter = annotatedParameterizedType.getAnnotatedActualTypeArguments()[i];
// HV-925
// We need to determine the validated type used for constraint validator resolution.
// Iterables and maps need special treatment at this point, since the validated type is the type of the
// specified type parameter. In the other cases the validated type is the parameterized type, eg Optional.
// In the latter case a value unwrapping has to occur
Type validatedType = annotatedTypeParameter.getType();
typeArgumentConstraints.addAll( findTypeUseConstraints( constrainable, annotatedTypeParameter, typeVariable, location, validatedType ) );
if ( validatedType instanceof ParameterizedType ) {
typeArgumentConstraints.addAll( findTypeArgumentsConstraints( constrainable,
new NestedTypeArgumentLocation( location, typeVariable, validatedType ),
annotatedTypeParameter ) );
}
i++;
}
}
return typeArgumentConstraints.isEmpty() ? Collections.emptySet() : typeArgumentConstraints;
}
/**
* Finds type use annotation constraints defined on the type argument.
*/
private Set> findTypeUseConstraints(Constrainable constrainable, AnnotatedType typeArgument, TypeVariable typeVariable,
TypeArgumentLocation location, Type type) {
List> constraintDescriptors = findConstraints( constrainable, typeArgument.getAnnotations(), ConstraintLocationKind.TYPE_USE );
if ( constraintDescriptors.isEmpty() ) {
return Collections.emptySet();
}
Set> constraints = newHashSet( constraintDescriptors.size() );
ConstraintLocation constraintLocation = ConstraintLocation.forTypeArgument( location.toConstraintLocation(), typeVariable, type );
for ( ConstraintDescriptorImpl constraintDescriptor : constraintDescriptors ) {
constraints.add( MetaConstraints.create( constraintCreationContext.getTypeResolutionHelper(),
constraintCreationContext.getValueExtractorManager(),
constraintCreationContext.getConstraintValidatorManager(), constraintDescriptor,
constraintLocation ) );
}
return constraints;
}
private CascadingMetaDataBuilder getCascadingMetaData(JavaBeanAnnotatedElement annotatedElement,
Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData) {
return CascadingMetaDataBuilder.annotatedObject( annotatedElement.getType(), annotatedElement.isAnnotationPresent( Valid.class ),
containerElementTypesCascadingMetaData, getGroupConversions( annotatedElement.getAnnotatedType() ) );
}
/**
* The location of a type argument before it is really considered a constraint location.
*
* It avoids initializing a constraint location if we did not find any constraints. This is especially useful in
* a Java 9 environment as {@link ConstraintLocation#forField(org.hibernate.validator.internal.properties.Field)}
* or {@link ConstraintLocation#forGetter(Getter)} tries to make the {@code Member} accessible
* which might not be possible (for instance for {@code java.util} classes).
*/
private interface TypeArgumentLocation {
ConstraintLocation toConstraintLocation();
}
private static class TypeArgumentExecutableParameterLocation implements TypeArgumentLocation {
private final JavaBeanExecutable javaBeanExecutable;
private final int index;
private TypeArgumentExecutableParameterLocation(JavaBeanExecutable javaBeanExecutable, int index) {
this.javaBeanExecutable = javaBeanExecutable;
this.index = index;
}
@Override
public ConstraintLocation toConstraintLocation() {
return ConstraintLocation.forParameter( javaBeanExecutable, index );
}
}
private static class TypeArgumentFieldLocation implements TypeArgumentLocation {
private final JavaBeanField javaBeanField;
private TypeArgumentFieldLocation(JavaBeanField javaBeanField) {
this.javaBeanField = javaBeanField;
}
@Override
public ConstraintLocation toConstraintLocation() {
return ConstraintLocation.forField( javaBeanField );
}
}
private static class TypeArgumentReturnValueLocation implements TypeArgumentLocation {
private final JavaBeanExecutable javaBeanExecutable;
private TypeArgumentReturnValueLocation(JavaBeanExecutable javaBeanExecutable) {
this.javaBeanExecutable = javaBeanExecutable;
}
@Override
public ConstraintLocation toConstraintLocation() {
return ConstraintLocation.forReturnValue( javaBeanExecutable );
}
}
private static class NestedTypeArgumentLocation implements TypeArgumentLocation {
private final TypeArgumentLocation parentLocation;
private final TypeVariable typeParameter;
private final Type typeOfAnnotatedElement;
private NestedTypeArgumentLocation(TypeArgumentLocation parentLocation, TypeVariable typeParameter, Type typeOfAnnotatedElement) {
this.parentLocation = parentLocation;
this.typeParameter = typeParameter;
this.typeOfAnnotatedElement = typeOfAnnotatedElement;
}
@Override
public ConstraintLocation toConstraintLocation() {
return ConstraintLocation.forTypeArgument( parentLocation.toConstraintLocation(), typeParameter, typeOfAnnotatedElement );
}
}
}