buildConstraintDescriptor(Member member,
A annotation,
ElementType type) {
return new ConstraintDescriptorImpl<>(
constraintHelper,
member,
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(Field field) {
return findTypeArgumentsConstraints(
field,
new TypeArgumentFieldLocation( field ),
field.getAnnotatedType()
);
}
/**
* Finds type arguments constraints for method return values.
*/
protected Set> findTypeAnnotationConstraints(Executable executable, AnnotatedType annotatedReturnType) {
return findTypeArgumentsConstraints(
executable,
new TypeArgumentReturnValueLocation( executable ),
annotatedReturnType
);
}
private CascadingMetaDataBuilder findCascadingMetaData(Executable executable, Parameter[] parameters, int i, AnnotatedType parameterAnnotatedType) {
Parameter parameter = parameters[i];
TypeVariable[] typeParameters = parameter.getType().getTypeParameters();
Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData = getTypeParametersCascadingMetadata( parameterAnnotatedType,
typeParameters );
try {
return getCascadingMetaData( ReflectionHelper.typeOf( parameter.getDeclaringExecutable(), i ),
parameter, containerElementTypesCascadingMetaData );
}
catch (ArrayIndexOutOfBoundsException ex) {
LOG.warn( MESSAGES.constraintOnConstructorOfNonStaticInnerClass(), ex );
return CascadingMetaDataBuilder.nonCascading();
}
}
private CascadingMetaDataBuilder findCascadingMetaData(Field field) {
TypeVariable[] typeParameters = field.getType().getTypeParameters();
AnnotatedType annotatedType = field.getAnnotatedType();
Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData = getTypeParametersCascadingMetadata( annotatedType, typeParameters );
return getCascadingMetaData( ReflectionHelper.typeOf( field ), field, containerElementTypesCascadingMetaData );
}
private CascadingMetaDataBuilder findCascadingMetaData(Executable executable, AnnotatedType annotatedReturnType) {
TypeVariable[] typeParameters;
if ( executable instanceof Method ) {
typeParameters = ( (Method) executable ).getReturnType().getTypeParameters();
}
else {
typeParameters = ( (Constructor) executable ).getDeclaringClass().getTypeParameters();
}
Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData = getTypeParametersCascadingMetadata( annotatedReturnType,
typeParameters );
return getCascadingMetaData( ReflectionHelper.typeOf( executable ), executable, 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 executable the executable
* @param i the parameter index
*
* @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) {
try {
return findTypeArgumentsConstraints(
executable,
new TypeArgumentExecutableParameterLocation( executable, i ),
parameterAnnotatedType
);
}
catch (ArrayIndexOutOfBoundsException ex) {
LOG.warn( MESSAGES.constraintOnConstructorOfNonStaticInnerClass(), ex );
return Collections.emptySet();
}
}
private Set> findTypeArgumentsConstraints(Member member, 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( member, annotatedArrayType, arrayElementTypeArgument, location, validatedType ) );
typeArgumentConstraints.addAll( findTypeArgumentsConstraints( member,
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( member, annotatedTypeParameter, typeVariable, location, validatedType ) );
if ( validatedType instanceof ParameterizedType ) {
typeArgumentConstraints.addAll( findTypeArgumentsConstraints( member,
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(Member member, AnnotatedType typeArgument, TypeVariable typeVariable, TypeArgumentLocation location, Type type) {
Set> constraints = Arrays.stream( typeArgument.getAnnotations() )
.flatMap( a -> findConstraintAnnotations( member, a, ElementType.TYPE_USE ).stream() )
.map( d -> createTypeArgumentMetaConstraint( d, location, typeVariable, type ) )
.collect( Collectors.toSet() );
return constraints;
}
/**
* Creates a {@code MetaConstraint} for a type argument constraint.
*/
private MetaConstraint createTypeArgumentMetaConstraint(ConstraintDescriptorImpl descriptor, TypeArgumentLocation location,
TypeVariable typeVariable, Type type) {
ConstraintLocation constraintLocation = ConstraintLocation.forTypeArgument( location.toConstraintLocation(), typeVariable, type );
return MetaConstraints.create( typeResolutionHelper, valueExtractorManager, descriptor, constraintLocation );
}
private CascadingMetaDataBuilder getCascadingMetaData(Type type, AnnotatedElement annotatedElement,
Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData) {
return CascadingMetaDataBuilder.annotatedObject( type, annotatedElement.isAnnotationPresent( Valid.class ), containerElementTypesCascadingMetaData,
getGroupConversions( annotatedElement ) );
}
/**
* 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
* 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 Executable executable;
private final int index;
private TypeArgumentExecutableParameterLocation(Executable executable, int index) {
this.executable = executable;
this.index = index;
}
@Override
public ConstraintLocation toConstraintLocation() {
return ConstraintLocation.forParameter( executable, index );
}
}
private static class TypeArgumentFieldLocation implements TypeArgumentLocation {
private final Field field;
private TypeArgumentFieldLocation(Field field) {
this.field = field;
}
@Override
public ConstraintLocation toConstraintLocation() {
return ConstraintLocation.forField( field );
}
}
private static class TypeArgumentReturnValueLocation implements TypeArgumentLocation {
private final Executable executable;
private TypeArgumentReturnValueLocation(Executable executable) {
this.executable = executable;
}
@Override
public ConstraintLocation toConstraintLocation() {
return ConstraintLocation.forReturnValue( executable );
}
}
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 );
}
}
}