All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl Maven / Gradle / Ivy

There is a newer version: 8.0.1.Final
Show newest version
/*
 * 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 static org.hibernate.validator.internal.util.CollectionHelper.newHashMap;
import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet;

import java.lang.invoke.MethodHandles;
import java.lang.reflect.Executable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import javax.validation.ElementKind;
import javax.validation.groups.Default;
import javax.validation.metadata.BeanDescriptor;
import javax.validation.metadata.ConstructorDescriptor;
import javax.validation.metadata.PropertyDescriptor;

import org.hibernate.validator.internal.engine.groups.Sequence;
import org.hibernate.validator.internal.engine.groups.ValidationOrder;
import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator;
import org.hibernate.validator.internal.metadata.core.MetaConstraint;
import org.hibernate.validator.internal.metadata.descriptor.BeanDescriptorImpl;
import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl;
import org.hibernate.validator.internal.metadata.descriptor.ExecutableDescriptorImpl;
import org.hibernate.validator.internal.metadata.facets.Cascadable;
import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind;
import org.hibernate.validator.internal.util.CollectionHelper;
import org.hibernate.validator.internal.util.ExecutableHelper;
import org.hibernate.validator.internal.util.classhierarchy.ClassHierarchyHelper;
import org.hibernate.validator.internal.util.classhierarchy.Filters;
import org.hibernate.validator.internal.util.logging.Log;
import org.hibernate.validator.internal.util.logging.LoggerFactory;
import org.hibernate.validator.internal.util.stereotypes.Immutable;
import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider;

/**
 * This class encapsulates all meta data needed for validation. Implementations of {@code Validator} interface can
 * instantiate an instance of this class and delegate the metadata extraction to it.
 *
 * @author Hardy Ferentschik
 * @author Gunnar Morling
 * @author Kevin Pollet <[email protected]> (C) 2011 SERLI
 * @author Chris Beckey <[email protected]>
 * @author Guillaume Smet
 * @author Marko Bekhta
 */
public final class BeanMetaDataImpl implements BeanMetaData {

	private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );

	/**
	 * Represents the "sequence" of just Default.class.
	 */
	private static final List> DEFAULT_GROUP_SEQUENCE = Collections.>singletonList( Default.class );

	/**
	 * Whether there are any constraints or cascades at all.
	 */
	private final boolean hasConstraints;

	private final ValidationOrderGenerator validationOrderGenerator;

	/**
	 * The root bean class for this meta data.
	 */
	private final Class beanClass;

	/**
	 * Set of all constraints for this bean type (defined on any implemented interfaces or super types)
	 */
	@Immutable
	private final Set> allMetaConstraints;

	/**
	 * Set of all constraints which are directly defined on the bean or any of the directly implemented interfaces
	 */
	@Immutable
	private final Set> directMetaConstraints;

	/**
	 * Contains constrained related meta data for all the constrained methods and constructors of the type represented
	 * by this bean meta data. Keyed by executable, values are an aggregated view on each executable together with all
	 * the executables from the inheritance hierarchy with the same signature.
	 * 

* An entry will be stored once under the signature of the represented method and all the methods it overrides * (there will only be more than one entry in case of generics in the parameters, e.g. in case of a super-type * method {@code foo(T)} and an overriding sub-type method {@code foo(String)} two entries for the same executable * meta-data will be stored). */ @Immutable private final Map executableMetaDataMap; /** * The set of unconstrained executables of the bean. It contains all the relevant signatures, following the same * rules as {@code executableMetaDataMap}. */ @Immutable private final Set unconstrainedExecutables; /** * Property meta data keyed against the property name */ @Immutable private final Map propertyMetaDataMap; /** * The cascaded properties of this bean. */ @Immutable private final Set cascadedProperties; /** * The default groups sequence for this bean class. */ @Immutable private final List> defaultGroupSequence; /** * The default group sequence provider. * * @see org.hibernate.validator.group.GroupSequenceProvider * @see DefaultGroupSequenceProvider */ private final DefaultGroupSequenceProvider defaultGroupSequenceProvider; private final ValidationOrder validationOrder; /** * The class hierarchy for this class starting with the class itself going up the inheritance chain. Interfaces * are not included. */ @Immutable private final List> classHierarchyWithoutInterfaces; /** * {code true} if the default group sequence is redefined, either via a group sequence redefinition or a group * sequence provider. */ private final boolean defaultGroupSequenceRedefined; /** * The resolved default group sequence. */ private final List> resolvedDefaultGroupSequence; /** * The bean descriptor for this bean. Lazily created. */ private volatile BeanDescriptor beanDescriptor; /** * Creates a new {@link BeanMetaDataImpl} * * @param beanClass The Java type represented by this meta data object. * @param defaultGroupSequence The default group sequence. * @param defaultGroupSequenceProvider The default group sequence provider if set. * @param constraintMetaDataSet All constraint meta data relating to the represented type. */ public BeanMetaDataImpl(Class beanClass, List> defaultGroupSequence, DefaultGroupSequenceProvider defaultGroupSequenceProvider, Set constraintMetaDataSet, ValidationOrderGenerator validationOrderGenerator) { this.validationOrderGenerator = validationOrderGenerator; this.beanClass = beanClass; this.propertyMetaDataMap = newHashMap(); Set propertyMetaDataSet = newHashSet(); Set executableMetaDataSet = newHashSet(); Set tmpUnconstrainedExecutables = newHashSet(); boolean hasConstraints = false; Set> allMetaConstraints = newHashSet(); for ( ConstraintMetaData constraintMetaData : constraintMetaDataSet ) { boolean elementHasConstraints = constraintMetaData.isCascading() || constraintMetaData.isConstrained(); hasConstraints |= elementHasConstraints; if ( constraintMetaData.getKind() == ElementKind.PROPERTY ) { propertyMetaDataSet.add( (PropertyMetaData) constraintMetaData ); } else if ( constraintMetaData.getKind() == ElementKind.BEAN ) { allMetaConstraints.addAll( ( (ClassMetaData) constraintMetaData ).getAllConstraints() ); } else { ExecutableMetaData executableMetaData = (ExecutableMetaData) constraintMetaData; if ( elementHasConstraints ) { executableMetaDataSet.add( executableMetaData ); } else { tmpUnconstrainedExecutables.addAll( executableMetaData.getSignatures() ); } } } Set cascadedProperties = newHashSet(); for ( PropertyMetaData propertyMetaData : propertyMetaDataSet ) { propertyMetaDataMap.put( propertyMetaData.getName(), propertyMetaData ); cascadedProperties.addAll( propertyMetaData.getCascadables() ); allMetaConstraints.addAll( propertyMetaData.getAllConstraints() ); } this.hasConstraints = hasConstraints; this.cascadedProperties = CollectionHelper.toImmutableSet( cascadedProperties ); this.allMetaConstraints = CollectionHelper.toImmutableSet( allMetaConstraints ); this.classHierarchyWithoutInterfaces = CollectionHelper.toImmutableList( ClassHierarchyHelper.getHierarchy( beanClass, Filters.excludeInterfaces() ) ); DefaultGroupSequenceContext defaultGroupContext = getDefaultGroupSequenceData( beanClass, defaultGroupSequence, defaultGroupSequenceProvider, validationOrderGenerator ); this.defaultGroupSequenceProvider = defaultGroupContext.defaultGroupSequenceProvider; this.defaultGroupSequence = CollectionHelper.toImmutableList( defaultGroupContext.defaultGroupSequence ); this.validationOrder = defaultGroupContext.validationOrder; this.directMetaConstraints = getDirectConstraints(); this.executableMetaDataMap = CollectionHelper.toImmutableMap( bySignature( executableMetaDataSet ) ); this.unconstrainedExecutables = CollectionHelper.toImmutableSet( tmpUnconstrainedExecutables ); // We initialize those elements eagerly so that any eventual error is thrown when bootstrapping the bean metadata this.defaultGroupSequenceRedefined = this.defaultGroupSequence.size() > 1 || hasDefaultGroupSequenceProvider(); this.resolvedDefaultGroupSequence = getDefaultGroupSequence( null ); } @Override public Class getBeanClass() { return beanClass; } @Override public boolean hasConstraints() { return hasConstraints; } @Override public BeanDescriptor getBeanDescriptor() { BeanDescriptor beanDescriptor = this.beanDescriptor; if ( beanDescriptor == null ) { synchronized (this) { beanDescriptor = this.beanDescriptor; if ( beanDescriptor == null ) { beanDescriptor = createBeanDescriptor( beanClass, allMetaConstraints, propertyMetaDataMap, executableMetaDataMap, defaultGroupSequenceRedefined, resolvedDefaultGroupSequence ); this.beanDescriptor = beanDescriptor; } } } return beanDescriptor; } @Override public Set getCascadables() { return cascadedProperties; } @Override public boolean hasCascadables() { return !cascadedProperties.isEmpty(); } @Override public PropertyMetaData getMetaDataFor(String propertyName) { PropertyMetaData propertyMetaData = propertyMetaDataMap.get( propertyName ); if ( propertyMetaData == null ) { throw LOG.getPropertyNotDefinedByValidatedTypeException( beanClass, propertyName ); } return propertyMetaData; } @Override public Set> getMetaConstraints() { return allMetaConstraints; } @Override public Set> getDirectMetaConstraints() { return directMetaConstraints; } @Override public Optional getMetaDataFor(Executable executable) { String signature = ExecutableHelper.getSignature( executable ); if ( unconstrainedExecutables.contains( signature ) ) { return Optional.empty(); } ExecutableMetaData executableMetaData = executableMetaDataMap.get( ExecutableHelper.getSignature( executable ) ); if ( executableMetaData == null ) { // there is no executable metadata - specified object and method do not match throw LOG.getMethodOrConstructorNotDefinedByValidatedTypeException( beanClass, executable ); } return Optional.of( executableMetaData ); } @Override public List> getDefaultGroupSequence(T beanState) { if ( hasDefaultGroupSequenceProvider() ) { List> providerDefaultGroupSequence = defaultGroupSequenceProvider.getValidationGroups( beanState ); return getValidDefaultGroupSequence( beanClass, providerDefaultGroupSequence ); } return defaultGroupSequence; } @Override public Iterator getDefaultValidationSequence(T beanState) { if ( hasDefaultGroupSequenceProvider() ) { List> providerDefaultGroupSequence = defaultGroupSequenceProvider.getValidationGroups( beanState ); return validationOrderGenerator.getDefaultValidationOrder( beanClass, getValidDefaultGroupSequence( beanClass, providerDefaultGroupSequence ) ) .getSequenceIterator(); } else { return validationOrder.getSequenceIterator(); } } @Override public boolean isDefaultGroupSequenceRedefined() { return defaultGroupSequenceRedefined; } @Override public List> getClassHierarchy() { return classHierarchyWithoutInterfaces; } private static BeanDescriptor createBeanDescriptor(Class beanClass, Set> allMetaConstraints, Map propertyMetaDataMap, Map executableMetaDataMap, boolean defaultGroupSequenceRedefined, List> resolvedDefaultGroupSequence) { Map propertyDescriptors = getConstrainedPropertiesAsDescriptors( propertyMetaDataMap, defaultGroupSequenceRedefined, resolvedDefaultGroupSequence ); Map methodsDescriptors = getConstrainedMethodsAsDescriptors( executableMetaDataMap, defaultGroupSequenceRedefined, resolvedDefaultGroupSequence ); Map constructorsDescriptors = getConstrainedConstructorsAsDescriptors( executableMetaDataMap, defaultGroupSequenceRedefined, resolvedDefaultGroupSequence ); return new BeanDescriptorImpl( beanClass, getClassLevelConstraintsAsDescriptors( allMetaConstraints ), propertyDescriptors, methodsDescriptors, constructorsDescriptors, defaultGroupSequenceRedefined, resolvedDefaultGroupSequence ); } private static Set> getClassLevelConstraintsAsDescriptors(Set> constraints) { return constraints.stream() .filter( c -> c.getConstraintLocationKind() == ConstraintLocationKind.TYPE ) .map( MetaConstraint::getDescriptor ) .collect( Collectors.toSet() ); } private static Map getConstrainedPropertiesAsDescriptors(Map propertyMetaDataMap, boolean defaultGroupSequenceIsRedefined, List> resolvedDefaultGroupSequence) { Map theValue = newHashMap(); for ( Entry entry : propertyMetaDataMap.entrySet() ) { if ( entry.getValue().isConstrained() && entry.getValue().getName() != null ) { theValue.put( entry.getKey(), entry.getValue().asDescriptor( defaultGroupSequenceIsRedefined, resolvedDefaultGroupSequence ) ); } } return theValue; } private static Map getConstrainedMethodsAsDescriptors(Map executableMetaDataMap, boolean defaultGroupSequenceIsRedefined, List> resolvedDefaultGroupSequence) { Map constrainedMethodDescriptors = newHashMap(); for ( ExecutableMetaData executableMetaData : executableMetaDataMap.values() ) { if ( executableMetaData.getKind() == ElementKind.METHOD && executableMetaData.isConstrained() ) { ExecutableDescriptorImpl descriptor = executableMetaData.asDescriptor( defaultGroupSequenceIsRedefined, resolvedDefaultGroupSequence ); for ( String signature : executableMetaData.getSignatures() ) { constrainedMethodDescriptors.put( signature, descriptor ); } } } return constrainedMethodDescriptors; } private static Map getConstrainedConstructorsAsDescriptors(Map executableMetaDataMap, boolean defaultGroupSequenceIsRedefined, List> resolvedDefaultGroupSequence) { Map constrainedMethodDescriptors = newHashMap(); for ( ExecutableMetaData executableMetaData : executableMetaDataMap.values() ) { if ( executableMetaData.getKind() == ElementKind.CONSTRUCTOR && executableMetaData.isConstrained() ) { constrainedMethodDescriptors.put( // constructors never override, so there will be exactly one identifier executableMetaData.getSignatures().iterator().next(), executableMetaData.asDescriptor( defaultGroupSequenceIsRedefined, resolvedDefaultGroupSequence ) ); } } return constrainedMethodDescriptors; } private static DefaultGroupSequenceContext getDefaultGroupSequenceData(Class beanClass, List> defaultGroupSequence, DefaultGroupSequenceProvider defaultGroupSequenceProvider, ValidationOrderGenerator validationOrderGenerator) { if ( defaultGroupSequence != null && defaultGroupSequenceProvider != null ) { throw LOG.getInvalidDefaultGroupSequenceDefinitionException(); } DefaultGroupSequenceContext context = new DefaultGroupSequenceContext<>(); if ( defaultGroupSequenceProvider != null ) { context.defaultGroupSequenceProvider = defaultGroupSequenceProvider; context.defaultGroupSequence = Collections.emptyList(); context.validationOrder = null; } else if ( defaultGroupSequence != null && !defaultGroupSequence.isEmpty() ) { context.defaultGroupSequence = getValidDefaultGroupSequence( beanClass, defaultGroupSequence ); context.validationOrder = validationOrderGenerator.getDefaultValidationOrder( beanClass, context.defaultGroupSequence ); } else { context.defaultGroupSequence = DEFAULT_GROUP_SEQUENCE; context.validationOrder = ValidationOrder.DEFAULT_SEQUENCE; } return context; } private Set> getDirectConstraints() { Set> constraints = newHashSet(); Set> classAndInterfaces = newHashSet(); classAndInterfaces.add( beanClass ); classAndInterfaces.addAll( ClassHierarchyHelper.getDirectlyImplementedInterfaces( beanClass ) ); for ( Class clazz : classAndInterfaces ) { for ( MetaConstraint metaConstraint : allMetaConstraints ) { if ( metaConstraint.getLocation().getDeclaringClass().equals( clazz ) ) { constraints.add( metaConstraint ); } } } return CollectionHelper.toImmutableSet( constraints ); } /** * Builds up the method meta data for this type; each meta-data entry will be stored under the signature of the * represented method and all the methods it overrides. */ private Map bySignature(Set executables) { Map theValue = newHashMap(); for ( ExecutableMetaData executableMetaData : executables ) { for ( String signature : executableMetaData.getSignatures() ) { theValue.put( signature, executableMetaData ); } } return theValue; } private static List> getValidDefaultGroupSequence(Class beanClass, List> groupSequence) { List> validDefaultGroupSequence = new ArrayList<>(); boolean groupSequenceContainsDefault = false; if ( groupSequence != null ) { for ( Class group : groupSequence ) { if ( group.getName().equals( beanClass.getName() ) ) { validDefaultGroupSequence.add( Default.class ); groupSequenceContainsDefault = true; } else if ( group.getName().equals( Default.class.getName() ) ) { throw LOG.getNoDefaultGroupInGroupSequenceException(); } else { validDefaultGroupSequence.add( group ); } } } if ( !groupSequenceContainsDefault ) { throw LOG.getBeanClassMustBePartOfRedefinedDefaultGroupSequenceException( beanClass ); } if ( LOG.isTraceEnabled() ) { LOG.tracef( "Members of the default group sequence for bean %s are: %s.", beanClass.getName(), validDefaultGroupSequence ); } return validDefaultGroupSequence; } private boolean hasDefaultGroupSequenceProvider() { return defaultGroupSequenceProvider != null; } @Override public String toString() { return "BeanMetaDataImpl" + "{beanClass=" + beanClass.getSimpleName() + ", constraintCount=" + getMetaConstraints().size() + ", cascadedPropertiesCount=" + cascadedProperties.size() + ", defaultGroupSequence=" + getDefaultGroupSequence( null ) + '}'; } /** * Tuple for returning default group sequence, provider and validation order at once. */ private static class DefaultGroupSequenceContext { List> defaultGroupSequence; DefaultGroupSequenceProvider defaultGroupSequenceProvider; ValidationOrder validationOrder; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy