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

org.hibernate.validator.internal.metadata.PredefinedScopeBeanMetaDataManager 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;

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

import java.lang.annotation.ElementType;
import java.lang.reflect.Executable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.validation.metadata.BeanDescriptor;
import javax.validation.metadata.ConstraintDescriptor;
import javax.validation.metadata.ConstructorDescriptor;
import javax.validation.metadata.ElementDescriptor.ConstraintFinder;
import javax.validation.metadata.MethodDescriptor;
import javax.validation.metadata.MethodType;
import javax.validation.metadata.PropertyDescriptor;
import javax.validation.metadata.Scope;

import org.hibernate.validator.internal.engine.ConstraintCreationContext;
import org.hibernate.validator.internal.engine.MethodValidationConfiguration;
import org.hibernate.validator.internal.engine.groups.Sequence;
import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator;
import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData;
import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataBuilder;
import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl;
import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData;
import org.hibernate.validator.internal.metadata.aggregated.PropertyMetaData;
import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions;
import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl;
import org.hibernate.validator.internal.metadata.core.MetaConstraint;
import org.hibernate.validator.internal.metadata.facets.Cascadable;
import org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider;
import org.hibernate.validator.internal.metadata.provider.MetaDataProvider;
import org.hibernate.validator.internal.metadata.raw.BeanConfiguration;
import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper;
import org.hibernate.validator.internal.util.ExecutableHelper;
import org.hibernate.validator.internal.util.ExecutableParameterNameProvider;
import org.hibernate.validator.internal.util.classhierarchy.ClassHierarchyHelper;
import org.hibernate.validator.internal.util.classhierarchy.Filters;
import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer;

public class PredefinedScopeBeanMetaDataManager implements BeanMetaDataManager {

	private final BeanMetaDataClassNormalizer beanMetaDataClassNormalizer;

	/**
	 * Used to cache the constraint meta data for validated entities.
	 */
	private final ConcurrentMap, BeanMetaData> beanMetaDataMap = new ConcurrentHashMap<>();

	public PredefinedScopeBeanMetaDataManager(ConstraintCreationContext constraintCreationContext,
			ExecutableHelper executableHelper,
			ExecutableParameterNameProvider parameterNameProvider,
			JavaBeanHelper javaBeanHelper,
			ValidationOrderGenerator validationOrderGenerator,
			List optionalMetaDataProviders,
			MethodValidationConfiguration methodValidationConfiguration,
			BeanMetaDataClassNormalizer beanMetaDataClassNormalizer,
			Set> beanClassesToInitialize) {
		AnnotationProcessingOptions annotationProcessingOptions = getAnnotationProcessingOptionsFromNonDefaultProviders( optionalMetaDataProviders );
		AnnotationMetaDataProvider defaultProvider = new AnnotationMetaDataProvider(
				constraintCreationContext,
				javaBeanHelper,
				annotationProcessingOptions
		);

		List metaDataProviders = new ArrayList<>( optionalMetaDataProviders.size() + 1 );
		// We add the annotation based metadata provider at the first position so that the entire metadata model is assembled
		// first.
		// The other optional metadata providers will then contribute their additional metadata to the preexisting model.
		// This helps to mitigate issues like HV-1450.
		metaDataProviders.add( defaultProvider );
		metaDataProviders.addAll( optionalMetaDataProviders );

		for ( Class validatedClass : beanClassesToInitialize ) {
			Class normalizedValidatedClass = beanMetaDataClassNormalizer.normalize( validatedClass );

			@SuppressWarnings("unchecked")
			List> classHierarchy = (List>) (Object) ClassHierarchyHelper.getHierarchy( normalizedValidatedClass, Filters.excludeInterfaces() );

			// note that the hierarchy also contains the initial class
			for ( Class hierarchyElement : classHierarchy ) {
				if ( this.beanMetaDataMap.containsKey( hierarchyElement ) ) {
					continue;
				}

				this.beanMetaDataMap.put( hierarchyElement,
						createBeanMetaData( constraintCreationContext, executableHelper, parameterNameProvider,
								javaBeanHelper, validationOrderGenerator, optionalMetaDataProviders, methodValidationConfiguration,
								metaDataProviders, hierarchyElement ) );
			}
		}

		this.beanMetaDataClassNormalizer = beanMetaDataClassNormalizer;
	}

	@SuppressWarnings("unchecked")
	@Override
	public  BeanMetaData getBeanMetaData(Class beanClass) {
		Class normalizedBeanClass = beanMetaDataClassNormalizer.normalize( beanClass );
		BeanMetaData beanMetaData = (BeanMetaData) beanMetaDataMap.get( normalizedBeanClass );
		if ( beanMetaData == null ) {
			// note that if at least one element of the hierarchy is constrained, the child classes should really be initialized
			// otherwise they will be considered unconstrained.
			beanMetaData = (BeanMetaData) beanMetaDataMap.computeIfAbsent( normalizedBeanClass, UninitializedBeanMetaData::new );
		}
		return beanMetaData;
	}

	@Override
	public void clear() {
		beanMetaDataMap.clear();
	}

	/**
	 * Creates a {@link org.hibernate.validator.internal.metadata.aggregated.BeanMetaData} containing the meta data from all meta
	 * data providers for the given type and its hierarchy.
	 *
	 * @param  The type of interest.
	 * @param clazz The type's class.
	 *
	 * @return A bean meta data object for the given type.
	 */
	private static  BeanMetaDataImpl createBeanMetaData(ConstraintCreationContext constraintCreationContext,
			ExecutableHelper executableHelper,
			ExecutableParameterNameProvider parameterNameProvider,
			JavaBeanHelper javaBeanHelper,
			ValidationOrderGenerator validationOrderGenerator,
			List optionalMetaDataProviders,
			MethodValidationConfiguration methodValidationConfiguration,
			List metaDataProviders,
			Class clazz) {
		BeanMetaDataBuilder builder = BeanMetaDataBuilder.getInstance(
				constraintCreationContext, executableHelper, parameterNameProvider,
				validationOrderGenerator, clazz, methodValidationConfiguration );

		for ( MetaDataProvider provider : metaDataProviders ) {
			for ( BeanConfiguration beanConfiguration : getBeanConfigurationForHierarchy( provider, clazz ) ) {
				builder.add( beanConfiguration );
			}
		}

		return builder.build();
	}

	/**
	 * @return returns the annotation ignores from the non annotation based meta data providers
	 */
	private static AnnotationProcessingOptions getAnnotationProcessingOptionsFromNonDefaultProviders(List optionalMetaDataProviders) {
		AnnotationProcessingOptions options = new AnnotationProcessingOptionsImpl();
		for ( MetaDataProvider metaDataProvider : optionalMetaDataProviders ) {
			options.merge( metaDataProvider.getAnnotationProcessingOptions() );
		}

		return options;
	}

	/**
	 * Returns a list with the configurations for all types contained in the given type's hierarchy (including
	 * implemented interfaces) starting at the specified type.
	 *
	 * @param beanClass The type of interest.
	 * @param  The type of the class to get the configurations for.
	 * @return A set with the configurations for the complete hierarchy of the given type. May be empty, but never
	 * {@code null}.
	 */
	private static  List> getBeanConfigurationForHierarchy(MetaDataProvider provider, Class beanClass) {
		List> configurations = newArrayList();

		for ( Class clazz : ClassHierarchyHelper.getHierarchy( beanClass ) ) {
			BeanConfiguration configuration = provider.getBeanConfiguration( clazz );
			if ( configuration != null ) {
				configurations.add( configuration );
			}
		}

		return configurations;
	}

	private static class UninitializedBeanMetaData implements BeanMetaData {

		private final Class beanClass;

		private final BeanDescriptor beanDescriptor;

		private final List> classHierarchy;

		@SuppressWarnings("unchecked")
		private UninitializedBeanMetaData(Class beanClass) {
			this.beanClass = beanClass;
			this.classHierarchy = (List>) (Object) ClassHierarchyHelper.getHierarchy( beanClass, Filters.excludeInterfaces() );
			this.beanDescriptor = new UninitializedBeanDescriptor( beanClass );
		}

		@Override
		public Iterable getCascadables() {
			return Collections.emptyList();
		}

		@Override
		public boolean hasCascadables() {
			return false;
		}

		@Override
		public Class getBeanClass() {
			return beanClass;
		}

		@Override
		public boolean hasConstraints() {
			return false;
		}

		@Override
		public BeanDescriptor getBeanDescriptor() {
			return beanDescriptor;
		}

		@Override
		public PropertyMetaData getMetaDataFor(String propertyName) {
			throw new IllegalStateException( "Metadata has not been initialized for bean of type " + beanClass.getName() );
		}

		@Override
		public List> getDefaultGroupSequence(T beanState) {
			throw new IllegalStateException( "Metadata has not been initialized for bean of type " + beanClass.getName() );
		}

		@Override
		public Iterator getDefaultValidationSequence(T beanState) {
			throw new IllegalStateException( "Metadata has not been initialized for bean of type " + beanClass.getName() );
		}

		@Override
		public boolean isDefaultGroupSequenceRedefined() {
			return false;
		}

		@Override
		public Set> getMetaConstraints() {
			return Collections.emptySet();
		}

		@Override
		public Set> getDirectMetaConstraints() {
			return Collections.emptySet();
		}

		@Override
		public Optional getMetaDataFor(Executable executable) throws IllegalArgumentException {
			return Optional.empty();
		}

		@Override
		public List> getClassHierarchy() {
			return classHierarchy;
		}
	}

	private static class UninitializedBeanDescriptor implements BeanDescriptor {

		private final Class elementClass;

		private UninitializedBeanDescriptor(Class elementClass) {
			this.elementClass = elementClass;
		}

		@Override
		public boolean hasConstraints() {
			return false;
		}

		@Override
		public Class getElementClass() {
			return elementClass;
		}

		@Override
		public Set> getConstraintDescriptors() {
			return Collections.emptySet();
		}

		@Override
		public ConstraintFinder findConstraints() {
			return UninitializedConstaintFinder.INSTANCE;
		}

		@Override
		public boolean isBeanConstrained() {
			return false;
		}

		@Override
		public PropertyDescriptor getConstraintsForProperty(String propertyName) {
			return null;
		}

		@Override
		public Set getConstrainedProperties() {
			return Collections.emptySet();
		}

		@Override
		public MethodDescriptor getConstraintsForMethod(String methodName, Class... parameterTypes) {
			return null;
		}

		@Override
		public Set getConstrainedMethods(MethodType methodType, MethodType... methodTypes) {
			return Collections.emptySet();
		}

		@Override
		public ConstructorDescriptor getConstraintsForConstructor(Class... parameterTypes) {
			return null;
		}

		@Override
		public Set getConstrainedConstructors() {
			return Collections.emptySet();
		}
	}

	private static class UninitializedConstaintFinder implements ConstraintFinder {

		private static final UninitializedConstaintFinder INSTANCE = new UninitializedConstaintFinder();

		@Override
		public ConstraintFinder unorderedAndMatchingGroups(Class... groups) {
			return this;
		}

		@Override
		public ConstraintFinder lookingAt(Scope scope) {
			return this;
		}

		@Override
		public ConstraintFinder declaredOn(ElementType... types) {
			return this;
		}

		@Override
		public Set> getConstraintDescriptors() {
			return Collections.emptySet();
		}

		@Override
		public boolean hasConstraints() {
			return false;
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy