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

org.hibernate.validator.internal.xml.ConstrainedExecutableBuilder Maven / Gradle / Ivy

Go to download

JSR 380's RI, Hibernate Validator version ${hibernate-validator.version} and its dependencies repackaged as OSGi bundle

There is a newer version: 5.1.0
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.xml;

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

import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.validation.ValidationException;

import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder;
import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl;
import org.hibernate.validator.internal.metadata.core.MetaConstraint;
import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl;
import org.hibernate.validator.internal.metadata.location.ConstraintLocation;
import org.hibernate.validator.internal.metadata.raw.ConfigurationSource;
import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable;
import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter;
import org.hibernate.validator.internal.util.ExecutableHelper;
import org.hibernate.validator.internal.util.ReflectionHelper;
import org.hibernate.validator.internal.util.logging.Log;
import org.hibernate.validator.internal.util.logging.LoggerFactory;
import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredConstructor;
import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredMethod;
import org.hibernate.validator.internal.xml.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration;
import org.hibernate.validator.internal.xml.binding.ConstraintType;
import org.hibernate.validator.internal.xml.binding.ConstructorType;
import org.hibernate.validator.internal.xml.binding.CrossParameterType;
import org.hibernate.validator.internal.xml.binding.MethodType;
import org.hibernate.validator.internal.xml.binding.ParameterType;
import org.hibernate.validator.internal.xml.binding.ReturnValueType;

/**
 * Builder for constrained methods and constructors.
 *
 * @author Hardy Ferentschik
 * @author Guillaume Smet
 */
class ConstrainedExecutableBuilder {

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

	private final ClassLoadingHelper classLoadingHelper;
	private final MetaConstraintBuilder metaConstraintBuilder;
	private final GroupConversionBuilder groupConversionBuilder;
	private final ConstrainedParameterBuilder constrainedParameterBuilder;
	private final AnnotationProcessingOptionsImpl annotationProcessingOptions;

	ConstrainedExecutableBuilder(ClassLoadingHelper classLoadingHelper, MetaConstraintBuilder metaConstraintBuilder,
			GroupConversionBuilder groupConversionBuilder, AnnotationProcessingOptionsImpl annotationProcessingOptions) {
		this.classLoadingHelper = classLoadingHelper;
		this.metaConstraintBuilder = metaConstraintBuilder;
		this.groupConversionBuilder = groupConversionBuilder;
		this.constrainedParameterBuilder = new ConstrainedParameterBuilder(
				metaConstraintBuilder,
				groupConversionBuilder,
				annotationProcessingOptions
		);
		this.annotationProcessingOptions = annotationProcessingOptions;
	}

	Set buildMethodConstrainedExecutable(List methods,
																			  Class beanClass,
																			  String defaultPackage) {
		Set constrainedExecutables = newHashSet();
		List alreadyProcessedMethods = newArrayList();
		for ( MethodType methodType : methods ) {
			// parse the parameters
			List> parameterTypes = createParameterTypes(
					methodType.getParameter(),
					beanClass,
					defaultPackage
			);

			String methodName = methodType.getName();

			final Method method = run(
					GetDeclaredMethod.action(
							beanClass,
							methodName,
							parameterTypes.toArray( new Class[parameterTypes.size()] )
					)
			);

			if ( method == null ) {
				throw LOG.getBeanDoesNotContainMethodException(
						beanClass,
						methodName,
						parameterTypes
				);
			}

			if ( alreadyProcessedMethods.contains( method ) ) {
				throw LOG.getMethodIsDefinedTwiceInMappingXmlForBeanException( method, beanClass );
			}
			else {
				alreadyProcessedMethods.add( method );
			}

			// ignore annotations
			if ( methodType.getIgnoreAnnotations() != null ) {
				annotationProcessingOptions.ignoreConstraintAnnotationsOnMember(
						method,
						methodType.getIgnoreAnnotations()
				);
			}

			ConstrainedExecutable constrainedExecutable = parseExecutableType(
					defaultPackage,
					methodType.getParameter(),
					methodType.getCrossParameter(),
					methodType.getReturnValue(),
					method
			);

			constrainedExecutables.add( constrainedExecutable );
		}
		return constrainedExecutables;
	}

	Set buildConstructorConstrainedExecutable(List constructors,
																				   Class beanClass,
																				   String defaultPackage) {
		Set constrainedExecutables = newHashSet();
		List> alreadyProcessedConstructors = newArrayList();
		for ( ConstructorType constructorType : constructors ) {
			// parse the parameters
			List> constructorParameterTypes = createParameterTypes(
					constructorType.getParameter(),
					beanClass,
					defaultPackage
			);

			final Constructor constructor = run(
					GetDeclaredConstructor.action(
							beanClass,
							constructorParameterTypes.toArray( new Class[constructorParameterTypes.size()] )
					)
			);

			if ( constructor == null ) {
				throw LOG.getBeanDoesNotContainConstructorException(
						beanClass,
						constructorParameterTypes
				);
			}
			if ( alreadyProcessedConstructors.contains( constructor ) ) {
				throw LOG.getConstructorIsDefinedTwiceInMappingXmlForBeanException(
						constructor,
						beanClass
				);
			}
			else {
				alreadyProcessedConstructors.add( constructor );
			}

			// ignore annotations
			if ( constructorType.getIgnoreAnnotations() != null ) {
				annotationProcessingOptions.ignoreConstraintAnnotationsOnMember(
						constructor,
						constructorType.getIgnoreAnnotations()
				);
			}

			ConstrainedExecutable constrainedExecutable = parseExecutableType(
					defaultPackage,
					constructorType.getParameter(),
					constructorType.getCrossParameter(),
					constructorType.getReturnValue(),
					constructor
			);
			constrainedExecutables.add( constrainedExecutable );
		}
		return constrainedExecutables;
	}

	private ConstrainedExecutable parseExecutableType(String defaultPackage,
															 List parameterTypeList,
															 CrossParameterType crossParameterType,
															 ReturnValueType returnValueType,
															 Executable executable) {
		List parameterMetaData = constrainedParameterBuilder.buildConstrainedParameters(
				parameterTypeList,
				executable,
				defaultPackage
		);

		Set> crossParameterConstraints = parseCrossParameterConstraints(
				defaultPackage,
				crossParameterType,
				executable
		);

		// parse the return value
		Set> returnValueConstraints = new HashSet<>();
		Set> returnValueTypeArgumentConstraints = new HashSet<>();
		CascadingMetaDataBuilder cascadingMetaDataBuilder = parseReturnValueType(
				returnValueType,
				executable,
				returnValueConstraints,
				returnValueTypeArgumentConstraints,
				defaultPackage
		);

		return new ConstrainedExecutable(
				ConfigurationSource.XML,
				executable,
				parameterMetaData,
				crossParameterConstraints,
				returnValueConstraints,
				returnValueTypeArgumentConstraints,
				cascadingMetaDataBuilder
		);
	}

	private Set> parseCrossParameterConstraints(String defaultPackage,
																		 CrossParameterType crossParameterType,
																		 Executable executable) {

		Set> crossParameterConstraints = newHashSet();
		if ( crossParameterType == null ) {
			return crossParameterConstraints;
		}

		ConstraintLocation constraintLocation = ConstraintLocation.forCrossParameter( executable );

		for ( ConstraintType constraintType : crossParameterType.getConstraint() ) {
			MetaConstraint metaConstraint = metaConstraintBuilder.buildMetaConstraint(
					constraintLocation,
					constraintType,
					ExecutableHelper.getElementType( executable ),
					defaultPackage,
					ConstraintDescriptorImpl.ConstraintType.CROSS_PARAMETER
			);
			crossParameterConstraints.add( metaConstraint );
		}

		// ignore annotations
		if ( crossParameterType.getIgnoreAnnotations() != null ) {
			annotationProcessingOptions.ignoreConstraintAnnotationsForCrossParameterConstraint(
					executable,
					crossParameterType.getIgnoreAnnotations()
			);
		}

		return crossParameterConstraints;
	}

	private CascadingMetaDataBuilder parseReturnValueType(ReturnValueType returnValueType,
												Executable executable,
												Set> returnValueConstraints,
												Set> returnValueTypeArgumentConstraints,
												String defaultPackage) {
		if ( returnValueType == null ) {
			return CascadingMetaDataBuilder.nonCascading();
		}

		ConstraintLocation constraintLocation = ConstraintLocation.forReturnValue( executable );
		for ( ConstraintType constraint : returnValueType.getConstraint() ) {
			MetaConstraint metaConstraint = metaConstraintBuilder.buildMetaConstraint(
					constraintLocation,
					constraint,
					ExecutableHelper.getElementType( executable ),
					defaultPackage,
					ConstraintDescriptorImpl.ConstraintType.GENERIC
			);
			returnValueConstraints.add( metaConstraint );
		}

		ContainerElementTypeConfigurationBuilder containerElementTypeConfigurationBuilder = new ContainerElementTypeConfigurationBuilder(
				metaConstraintBuilder, groupConversionBuilder, constraintLocation, defaultPackage );
		ContainerElementTypeConfiguration containerElementTypeConfiguration = containerElementTypeConfigurationBuilder
				.build( returnValueType.getContainerElementType(), ReflectionHelper.typeOf( executable ) );

		returnValueTypeArgumentConstraints.addAll( containerElementTypeConfiguration.getMetaConstraints() );

		// ignore annotations
		if ( returnValueType.getIgnoreAnnotations() != null ) {
			annotationProcessingOptions.ignoreConstraintAnnotationsForReturnValue(
					executable,
					returnValueType.getIgnoreAnnotations()
			);
		}

		return getCascadingMetaDataForReturnValue( containerElementTypeConfiguration.getTypeParametersCascadingMetaData(), executable, returnValueType,
				defaultPackage );
	}

	private List> createParameterTypes(List parameterList,
													   Class beanClass,
													   String defaultPackage) {
		List> parameterTypes = newArrayList();
		for ( ParameterType parameterType : parameterList ) {
			String type = null;
			try {
				type = parameterType.getType();
				Class parameterClass = classLoadingHelper.loadClass( type, defaultPackage );
				parameterTypes.add( parameterClass );
			}
			catch (ValidationException e) {
				throw LOG.getInvalidParameterTypeException( type, beanClass );
			}
		}

		return parameterTypes;
	}

	private CascadingMetaDataBuilder getCascadingMetaDataForReturnValue(Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, Executable executable,
			ReturnValueType returnValueType, String defaultPackage) {
		Type type = ReflectionHelper.typeOf( executable );
		boolean isCascaded = returnValueType.getValid() != null;
		Map, Class> groupConversions = groupConversionBuilder.buildGroupConversionMap(
				returnValueType.getConvertGroup(),
				defaultPackage
		);

		return CascadingMetaDataBuilder.annotatedObject( type, isCascaded, containerElementTypesCascadingMetaData, groupConversions );
	}

	/**
	 * 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 static T run(PrivilegedAction action) { return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy