/*
 * Hibernate Validator, declare and validate application constraints
 *
 * License: Apache License, Version 2.0
 * See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
 */
package org.hibernate.validator.internal.metadata.aggregated;

import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet;

import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.validation.metadata.ContainerElementTypeDescriptor;
import javax.validation.metadata.GroupConversionDescriptor;

import org.hibernate.validator.internal.metadata.core.MetaConstraint;
import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl;
import org.hibernate.validator.internal.metadata.descriptor.ContainerElementTypeDescriptorImpl;
import org.hibernate.validator.internal.metadata.location.ConstraintLocation;
import org.hibernate.validator.internal.metadata.location.TypeArgumentConstraintLocation;
import org.hibernate.validator.internal.util.CollectionHelper;
import org.hibernate.validator.internal.util.TypeVariables;
import org.hibernate.validator.internal.util.stereotypes.Immutable;

Base implementation for ConstraintMetaData with attributes common to all type of meta data.
Author:Gunnar Morling, Hardy Ferentschik
/** * Base implementation for {@link ConstraintMetaData} with attributes common * to all type of meta data. * * @author Gunnar Morling * @author Hardy Ferentschik */
public abstract class AbstractConstraintMetaData implements ConstraintMetaData { private final String name; private final Type type; @Immutable private final Set<MetaConstraint<?>> directConstraints; @Immutable private final Set<MetaConstraint<?>> containerElementsConstraints; @Immutable private final Set<MetaConstraint<?>> allConstraints; private final boolean isCascading; private final boolean isConstrained; public AbstractConstraintMetaData(String name, Type type, Set<MetaConstraint<?>> directConstraints, Set<MetaConstraint<?>> containerElementsConstraints, boolean isCascading, boolean isConstrained) { this.name = name; this.type = type; this.directConstraints = CollectionHelper.toImmutableSet( directConstraints ); this.containerElementsConstraints = CollectionHelper.toImmutableSet( containerElementsConstraints ); this.allConstraints = Stream.concat( directConstraints.stream(), containerElementsConstraints.stream() ) .collect( Collectors.collectingAndThen( Collectors.toSet(), CollectionHelper::toImmutableSet ) ); this.isCascading = isCascading; this.isConstrained = isConstrained; } @Override public String getName() { return name; } @Override public Type getType() { return type; } @Override public Iterator<MetaConstraint<?>> iterator() { return allConstraints.iterator(); } public Set<MetaConstraint<?>> getAllConstraints() { return allConstraints; } public Set<MetaConstraint<?>> getDirectConstraints() { return directConstraints; } public Set<MetaConstraint<?>> getContainerElementsConstraints() { return containerElementsConstraints; } @Override public final boolean isCascading() { return isCascading; } @Override public boolean isConstrained() { return isConstrained; } @Override public String toString() { return "AbstractConstraintMetaData [name=" + name + ", type=" + type + ", directConstraints=" + directConstraints + ", containerElementsConstraints=" + containerElementsConstraints + ", isCascading=" + isCascading + ", isConstrained=" + isConstrained + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ( ( name == null ) ? 0 : name.hashCode() ); return result; } @Override public boolean equals(Object obj) { if ( this == obj ) { return true; } if ( obj == null ) { return false; } if ( getClass() != obj.getClass() ) { return false; } AbstractConstraintMetaData other = (AbstractConstraintMetaData) obj; if ( name == null ) { if ( other.name != null ) { return false; } } else if ( !name.equals( other.name ) ) { return false; } return true; } protected Set<ConstraintDescriptorImpl<?>> asDescriptors(Set<MetaConstraint<?>> constraints) { Set<ConstraintDescriptorImpl<?>> theValue = newHashSet(); for ( MetaConstraint<?> oneConstraint : constraints ) { theValue.add( oneConstraint.getDescriptor() ); } return theValue; } protected Set<ContainerElementTypeDescriptor> asContainerElementTypeDescriptors( Set<MetaConstraint<?>> containerElementsConstraints, CascadingMetaData cascadingMetaData, boolean defaultGroupSequenceRedefined, List<Class<?>> defaultGroupSequence) { return asContainerElementTypeDescriptors( type, ContainerElementMetaDataTree.of( cascadingMetaData, containerElementsConstraints ), defaultGroupSequenceRedefined, defaultGroupSequence ); } private Set<ContainerElementTypeDescriptor> asContainerElementTypeDescriptors(Type type, ContainerElementMetaDataTree containerElementMetaDataTree, boolean defaultGroupSequenceRedefined, List<Class<?>> defaultGroupSequence) { Set<ContainerElementTypeDescriptor> containerElementTypeDescriptors = new HashSet<>(); for ( Entry<TypeVariable<?>, ContainerElementMetaDataTree> entry : containerElementMetaDataTree.nodes.entrySet() ) { TypeVariable<?> childTypeParameter = entry.getKey(); ContainerElementMetaDataTree childContainerElementMetaDataTree = entry.getValue(); Set<ContainerElementTypeDescriptor> childrenDescriptors = asContainerElementTypeDescriptors( childContainerElementMetaDataTree.elementType, childContainerElementMetaDataTree, defaultGroupSequenceRedefined, defaultGroupSequence ); containerElementTypeDescriptors.add( new ContainerElementTypeDescriptorImpl( childContainerElementMetaDataTree.elementType, childContainerElementMetaDataTree.containerClass, TypeVariables.getTypeParameterIndex( childTypeParameter ), asDescriptors( childContainerElementMetaDataTree.constraints ), childrenDescriptors, childContainerElementMetaDataTree.cascading, defaultGroupSequenceRedefined, defaultGroupSequence, childContainerElementMetaDataTree.groupConversionDescriptors ) ); } return containerElementTypeDescriptors; }
This data structure is used to join the cascading metadata information with the constraint violations. It is a temporary data structure and it should be kept that way.

We might consider in the future having a common tree structure for the cascading metadata and the constraint violations that would be built earlier and shared. This class shouldn't be taken as a model as this data structure should be made immutable.

/** * This data structure is used to join the cascading metadata information with the constraint violations. It is a * temporary data structure and it should be kept that way. * <p> * We might consider in the future having a common tree structure for the cascading metadata and the constraint * violations that would be built earlier and shared. This class shouldn't be taken as a model as this data * structure should be made immutable. */
private static class ContainerElementMetaDataTree { private final Map<TypeVariable<?>, ContainerElementMetaDataTree> nodes = new HashMap<>(); private Type elementType = null; private Class<?> containerClass; private final Set<MetaConstraint<?>> constraints = new HashSet<>(); private boolean cascading = false; private Set<GroupConversionDescriptor> groupConversionDescriptors = new HashSet<>(); private static ContainerElementMetaDataTree of(CascadingMetaData cascadingMetaData, Set<MetaConstraint<?>> containerElementsConstraints) { ContainerElementMetaDataTree containerElementMetaConstraintTree = new ContainerElementMetaDataTree(); for ( MetaConstraint<?> constraint : containerElementsConstraints ) { ConstraintLocation currentLocation = constraint.getLocation(); List<TypeVariable<?>> constraintPath = new ArrayList<>(); while ( currentLocation instanceof TypeArgumentConstraintLocation ) { TypeArgumentConstraintLocation typeArgumentConstraintLocation = ( (TypeArgumentConstraintLocation) currentLocation ); constraintPath.add( typeArgumentConstraintLocation.getTypeParameter() ); currentLocation = typeArgumentConstraintLocation.getDelegate(); } Collections.reverse( constraintPath ); containerElementMetaConstraintTree.addConstraint( constraintPath, constraint ); } if ( cascadingMetaData != null && cascadingMetaData.isContainer() && cascadingMetaData.isMarkedForCascadingOnAnnotatedObjectOrContainerElements() ) { containerElementMetaConstraintTree.addCascadingMetaData( new ArrayList<>(), cascadingMetaData.as( ContainerCascadingMetaData.class ) ); } return containerElementMetaConstraintTree; } private void addConstraint(List<TypeVariable<?>> path, MetaConstraint<?> constraint) { ContainerElementMetaDataTree tree = this; for ( TypeVariable<?> typeArgument : path ) { tree = tree.nodes.computeIfAbsent( typeArgument, ta -> new ContainerElementMetaDataTree() ); } TypeArgumentConstraintLocation constraintLocation = (TypeArgumentConstraintLocation) constraint.getLocation(); tree.elementType = constraintLocation.getTypeForValidatorResolution(); tree.containerClass = ( (TypeArgumentConstraintLocation) constraint.getLocation() ).getContainerClass(); tree.constraints.add( constraint ); } private void addCascadingMetaData(List<TypeVariable<?>> path, ContainerCascadingMetaData cascadingMetaData) { for ( ContainerCascadingMetaData nestedCascadingMetaData : cascadingMetaData.getContainerElementTypesCascadingMetaData() ) { List<TypeVariable<?>> nestedPath = new ArrayList<>( path ); nestedPath.add( nestedCascadingMetaData.getTypeParameter() ); ContainerElementMetaDataTree tree = this; for ( TypeVariable<?> typeArgument : nestedPath ) { tree = tree.nodes.computeIfAbsent( typeArgument, ta -> new ContainerElementMetaDataTree() ); } tree.elementType = TypeVariables.getContainerElementType( nestedCascadingMetaData.getEnclosingType(), nestedCascadingMetaData.getTypeParameter() ); tree.containerClass = nestedCascadingMetaData.getDeclaredContainerClass(); tree.cascading = nestedCascadingMetaData.isCascading(); tree.groupConversionDescriptors = nestedCascadingMetaData.getGroupConversionDescriptors(); if ( nestedCascadingMetaData.isMarkedForCascadingOnAnnotatedObjectOrContainerElements() ) { addCascadingMetaData( nestedPath, nestedCascadingMetaData ); } } } } }