org.hibernate.validator.internal.cfg.context.CascadableConstraintMappingContextImplBase Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bean-validator Show documentation
Show all versions of bean-validator Show documentation
JSR 380's RI, Hibernate Validator version ${hibernate-validator.version} and its dependencies repackaged as OSGi bundle
/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or .
*/
package org.hibernate.validator.internal.cfg.context;
import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.hibernate.validator.cfg.context.Cascadable;
import org.hibernate.validator.cfg.context.ContainerElementConstraintMappingContext;
import org.hibernate.validator.cfg.context.ContainerElementTarget;
import org.hibernate.validator.cfg.context.GroupConversionTargetContext;
import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager;
import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder;
import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
import org.hibernate.validator.internal.metadata.core.MetaConstraint;
import org.hibernate.validator.internal.metadata.location.ConstraintLocation;
import org.hibernate.validator.internal.util.Contracts;
import org.hibernate.validator.internal.util.TypeHelper;
import org.hibernate.validator.internal.util.TypeResolutionHelper;
import org.hibernate.validator.internal.util.logging.Log;
import org.hibernate.validator.internal.util.logging.LoggerFactory;
/**
* Base class for all implementations of cascadable context types.
*
* @author Gunnar Morling
*/
abstract class CascadableConstraintMappingContextImplBase>
extends ConstraintMappingContextImplBase implements Cascadable {
private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
private final Type configuredType;
protected boolean isCascading;
protected final Map, Class> groupConversions = newHashMap();
/**
* Contexts for configuring nested container elements, if any. Indexed by type parameter.
*/
private final Map containerElementContexts = new HashMap<>();
private final Set configuredPaths = new HashSet<>();
CascadableConstraintMappingContextImplBase(DefaultConstraintMapping mapping, Type configuredType) {
super( mapping );
this.configuredType = configuredType;
}
/**
* Returns this object, narrowed down to the specific sub-type.
*
* @return this object, narrowed down to the specific sub-type
*
* @see "Get this" trick
*/
protected abstract C getThis();
/**
* Adds a group conversion for this element.
*
* @param from the source group of the conversion
* @param to the target group of the conversion
*/
public void addGroupConversion(Class from, Class to) {
groupConversions.put( from, to );
}
@Override
public C valid() {
isCascading = true;
return getThis();
}
@Override
public GroupConversionTargetContext convertGroup(Class from) {
return new GroupConversionTargetContextImpl<>( from, getThis(), this );
}
public ContainerElementConstraintMappingContext containerElement(ContainerElementTarget parent, TypeConstraintMappingContextImpl typeContext,
ConstraintLocation location) {
// HV-1428 Container element support is disabled for arrays
if ( TypeHelper.isArray( configuredType ) ) {
throw LOG.getContainerElementConstraintsAndCascadedValidationNotSupportedOnArraysException( configuredType );
}
if ( configuredType instanceof ParameterizedType ) {
if ( ( (ParameterizedType) configuredType ).getActualTypeArguments().length > 1 ) {
throw LOG.getNoTypeArgumentIndexIsGivenForTypeWithMultipleTypeArgumentsException( configuredType );
}
}
else if ( !TypeHelper.isArray( configuredType ) ) {
throw LOG.getTypeIsNotAParameterizedNorArrayTypeException( configuredType );
}
return containerElement( parent, typeContext, location, 0 );
}
public ContainerElementConstraintMappingContext containerElement(ContainerElementTarget parent, TypeConstraintMappingContextImpl typeContext,
ConstraintLocation location, int index, int... nestedIndexes) {
Contracts.assertTrue( index >= 0, "Type argument index must not be negative" );
// HV-1428 Container element support is disabled for arrays
if ( TypeHelper.isArray( configuredType ) ) {
throw LOG.getContainerElementConstraintsAndCascadedValidationNotSupportedOnArraysException( configuredType );
}
if ( !( configuredType instanceof ParameterizedType ) && !( TypeHelper.isArray( configuredType ) ) ) {
throw LOG.getTypeIsNotAParameterizedNorArrayTypeException( configuredType );
}
ContainerElementPathKey key = new ContainerElementPathKey( index, nestedIndexes );
boolean configuredBefore = !configuredPaths.add( key );
if ( configuredBefore ) {
throw LOG.getContainerElementTypeHasAlreadyBeenConfiguredViaProgrammaticApiException(
location.getTypeForValidatorResolution()
);
}
// As we already checked that the specific path was not yet configured we should not worry about returning the same context here,
// as it means that there are some nested indexes which make a difference, And at the end a new context will be returned by call
// to containerElementContext#nestedContainerElement().
ContainerElementConstraintMappingContextImpl containerElementContext = containerElementContexts.get( index );
if ( containerElementContext == null ) {
containerElementContext = new ContainerElementConstraintMappingContextImpl( typeContext, parent, location, index );
containerElementContexts.put( index, containerElementContext );
}
if ( nestedIndexes.length > 0 ) {
return containerElementContext.nestedContainerElement( nestedIndexes );
}
else {
return containerElementContext;
}
}
public boolean isCascading() {
return isCascading;
}
protected Set> getTypeArgumentConstraints(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager) {
return containerElementContexts.values()
.stream()
.map( t -> t.build( constraintHelper, typeResolutionHelper, valueExtractorManager ) )
.flatMap( Set::stream )
.collect( Collectors.toSet() );
}
protected CascadingMetaDataBuilder getCascadingMetaDataBuilder() {
Map, CascadingMetaDataBuilder> typeParametersCascadingMetaData = containerElementContexts.values().stream()
.filter( c -> c.getContainerElementCascadingMetaDataBuilder() != null )
.collect( Collectors.toMap( c -> c.getContainerElementCascadingMetaDataBuilder().getTypeParameter(),
c -> c.getContainerElementCascadingMetaDataBuilder() ) );
for ( ContainerElementConstraintMappingContextImpl typeArgumentContext : containerElementContexts.values() ) {
CascadingMetaDataBuilder cascadingMetaDataBuilder = typeArgumentContext.getContainerElementCascadingMetaDataBuilder();
if ( cascadingMetaDataBuilder != null ) {
typeParametersCascadingMetaData.put( cascadingMetaDataBuilder.getTypeParameter(), cascadingMetaDataBuilder );
}
}
return CascadingMetaDataBuilder.annotatedObject( configuredType, isCascading, typeParametersCascadingMetaData, groupConversions );
}
private static class ContainerElementPathKey {
private final int index;
private final int[] nestedIndexes;
public ContainerElementPathKey(int index, int[] nestedIndexes) {
this.index = index;
this.nestedIndexes = nestedIndexes;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + index;
result = prime * result + Arrays.hashCode( nestedIndexes );
return result;
}
@Override
public boolean equals(Object obj) {
if ( this == obj ) {
return true;
}
if ( obj == null ) {
return false;
}
if ( getClass() != obj.getClass() ) {
return false;
}
ContainerElementPathKey other = (ContainerElementPathKey) obj;
if ( index != other.index ) {
return false;
}
if ( !Arrays.equals( nestedIndexes, other.nestedIndexes ) ) {
return false;
}
return true;
}
}
}