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

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

/*
* JBoss, Home of Professional Open Source
* Copyright 2011, Red Hat, Inc. and/or its affiliates, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.hibernate.validator.internal.metadata.aggregated;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.validation.ConstraintDeclarationException;
import javax.validation.ElementKind;
import javax.validation.metadata.ParameterDescriptor;

import org.hibernate.validator.internal.metadata.aggregated.rule.MethodConfigurationRule;
import org.hibernate.validator.internal.metadata.aggregated.rule.OverridingMethodMustNotAlterParameterConstraints;
import org.hibernate.validator.internal.metadata.aggregated.rule.ParallelMethodsMustNotDefineGroupConversionForCascadedReturnValue;
import org.hibernate.validator.internal.metadata.aggregated.rule.ParallelMethodsMustNotDefineParameterConstraints;
import org.hibernate.validator.internal.metadata.aggregated.rule.ReturnValueMayOnlyBeMarkedOnceAsCascadedPerHierarchyLine;
import org.hibernate.validator.internal.metadata.aggregated.rule.VoidMethodsMustNotBeReturnValueConstrained;
import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
import org.hibernate.validator.internal.metadata.core.MetaConstraint;
import org.hibernate.validator.internal.metadata.descriptor.ExecutableDescriptorImpl;
import org.hibernate.validator.internal.metadata.location.ExecutableConstraintLocation;
import org.hibernate.validator.internal.metadata.raw.ConstrainedElement;
import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable;
import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter;
import org.hibernate.validator.internal.metadata.raw.ExecutableElement;
import org.hibernate.validator.internal.util.CollectionHelper;
import org.hibernate.validator.internal.util.logging.Log;
import org.hibernate.validator.internal.util.logging.LoggerFactory;

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

/**
 * An aggregated view of the constraint related meta data for a given method or
 * constructors and in (case of methods) all the methods in the inheritance
 * hierarchy which it overrides or implements.
 * 

* Instances are retrieved by creating a {@link Builder} and adding all required * {@link ConstrainedExecutable} objects to it. Instances are read-only after * creation. *

*

* Identity is solely based on the method's name and parameter types, hence sets * and similar collections of this type may only be created in the scope of one * Java type. *

* * @author Gunnar Morling */ public class ExecutableMetaData extends AbstractConstraintMetaData { private static final Log log = LoggerFactory.make(); private final Class[] parameterTypes; private final List parameterMetaDataList; private final Set> crossParameterConstraints; private final boolean isGetter; /** * An identifier for storing this object in maps etc. */ private final String identifier; private final ReturnValueMetaData returnValueMetaData; private ExecutableMetaData( String name, Class returnType, Class[] parameterTypes, ElementKind kind, Set> returnValueConstraints, List parameterMetaData, Set> crossParameterConstraints, Map, Class> returnValueGroupConversions, boolean isCascading, boolean isConstrained, boolean isGetter) { super( name, returnType, returnValueConstraints, kind, isCascading, isConstrained ); this.parameterTypes = parameterTypes; this.parameterMetaDataList = Collections.unmodifiableList( parameterMetaData ); this.crossParameterConstraints = Collections.unmodifiableSet( crossParameterConstraints ); this.identifier = name + Arrays.toString( parameterTypes ); this.returnValueMetaData = new ReturnValueMetaData( returnType, returnValueConstraints, isCascading, returnValueGroupConversions ); this.isGetter = isGetter; } /** * Returns meta data for the specified parameter of the represented executable. * * @param parameterIndex the index of the parameter * * @return Meta data for the specified parameter. Will never be {@code null}. */ public ParameterMetaData getParameterMetaData(int parameterIndex) { if ( parameterIndex < 0 || parameterIndex > parameterMetaDataList.size() - 1 ) { throw log.getInvalidMethodParameterIndexException( getName(), parameterIndex ); } return parameterMetaDataList.get( parameterIndex ); } public Class[] getParameterTypes() { return parameterTypes; } /** * Returns an identifier for this meta data object, based on the represented * executable's name and its parameter types. * * @return An identifier for this meta data object. */ public String getIdentifier() { return identifier; } /** * Returns the cross-parameter constraints declared for the represented * method or constructor. * * @return the cross-parameter constraints declared for the represented * method or constructor. May be empty but will never be * {@code null}. */ public Set> getCrossParameterConstraints() { return crossParameterConstraints; } public ValidatableParametersMetaData getValidatableParametersMetaData() { Set cascadedParameters = newHashSet(); for ( ParameterMetaData parameterMetaData : parameterMetaDataList ) { if ( parameterMetaData.isCascading() ) { cascadedParameters.add( parameterMetaData ); } } return new ValidatableParametersMetaData( cascadedParameters ); } public ReturnValueMetaData getReturnValueMetaData() { return returnValueMetaData; } @Override public ExecutableDescriptorImpl asDescriptor(boolean defaultGroupSequenceRedefined, List> defaultGroupSequence) { return new ExecutableDescriptorImpl( getType(), getName(), asDescriptors( getCrossParameterConstraints() ), returnValueMetaData.asDescriptor( defaultGroupSequenceRedefined, defaultGroupSequence ), parametersAsDescriptors( defaultGroupSequenceRedefined, defaultGroupSequence ), defaultGroupSequenceRedefined, isGetter, defaultGroupSequence ); } private List parametersAsDescriptors(boolean defaultGroupSequenceRedefined, List> defaultGroupSequence) { List parameterDescriptorList = newArrayList(); for ( ParameterMetaData parameterMetaData : parameterMetaDataList ) { parameterDescriptorList.add( parameterMetaData.asDescriptor( defaultGroupSequenceRedefined, defaultGroupSequence ) ); } return parameterDescriptorList; } @Override public String toString() { StringBuilder parameterBuilder = new StringBuilder(); for ( Class oneParameterType : getParameterTypes() ) { parameterBuilder.append( oneParameterType.getSimpleName() ); parameterBuilder.append( ", " ); } String parameters = parameterBuilder.length() > 0 ? parameterBuilder.substring( 0, parameterBuilder.length() - 2 ) : parameterBuilder.toString(); return "ExecutableMetaData [executable=" + getType() + " " + getName() + "(" + parameters + "), isCascading=" + isCascading() + ", isConstrained=" + isConstrained() + "]"; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + Arrays.hashCode( parameterTypes ); return result; } @Override public boolean equals(Object obj) { if ( this == obj ) { return true; } if ( !super.equals( obj ) ) { return false; } if ( getClass() != obj.getClass() ) { return false; } ExecutableMetaData other = (ExecutableMetaData) obj; if ( !Arrays.equals( parameterTypes, other.parameterTypes ) ) { return false; } return true; } /** * Creates new {@link ExecutableMetaData} instances. * * @author Gunnar Morling * @author Kevin Pollet (C) 2011 SERLI */ public static class Builder extends MetaDataBuilder { /** * The rules applying for the definition of executable constraints. */ private static final Set rules = Collections.unmodifiableSet( CollectionHelper.asSet( new OverridingMethodMustNotAlterParameterConstraints(), new ParallelMethodsMustNotDefineParameterConstraints(), new VoidMethodsMustNotBeReturnValueConstrained(), new ReturnValueMayOnlyBeMarkedOnceAsCascadedPerHierarchyLine(), new ParallelMethodsMustNotDefineGroupConversionForCascadedReturnValue() ) ); /** * Either CONSTRUCTOR or METHOD. */ private final ConstrainedElement.ConstrainedElementKind kind; private final Set constrainedExecutables = newHashSet(); private final ExecutableConstraintLocation location; private final Set> crossParameterConstraints = newHashSet(); private boolean isConstrained = false; /** * Holds a merged representation of the configurations for one method * from the hierarchy contributed by the different meta data providers. * Used to check for violations of the Liskov substitution principle by * e.g. adding parameter constraints in sub type methods. */ private final Map, ConstrainedExecutable> executablesByDeclaringType = newHashMap(); /** * Creates a new builder based on the given executable meta data. * * @param constrainedExecutable The base executable for this builder. This is the lowest * executable with a given signature within a type hierarchy. * @param constraintHelper the constraint helper */ public Builder(Class beanClass, ConstrainedExecutable constrainedExecutable, ConstraintHelper constraintHelper) { super( beanClass, constraintHelper ); kind = constrainedExecutable.getKind(); location = constrainedExecutable.getLocation(); add( constrainedExecutable ); } @Override public boolean accepts(ConstrainedElement constrainedElement) { if ( kind != constrainedElement.getKind() ) { return false; } ExecutableElement executableElement = ( (ConstrainedExecutable) constrainedElement ).getLocation() .getExecutableElement(); //are the locations equal (created by different builders) or //does one of the executables override the other one? return location.getExecutableElement().equals( executableElement ) || location.getExecutableElement().overrides( executableElement ) || executableElement.overrides( location.getExecutableElement() ); } @Override public void add(ConstrainedElement constrainedElement) { super.add( constrainedElement ); ConstrainedExecutable constrainedExecutable = (ConstrainedExecutable) constrainedElement; constrainedExecutables.add( constrainedExecutable ); isConstrained = isConstrained || constrainedExecutable.isConstrained(); crossParameterConstraints.addAll( constrainedExecutable.getCrossParameterConstraints() ); addToExecutablesByDeclaringType( constrainedExecutable ); } /** * Merges the given executable with the metadata contributed by other * providers for the same executable in the hierarchy. * * @param executable The executable to merge. */ private void addToExecutablesByDeclaringType(ConstrainedExecutable executable) { Class beanClass = executable.getLocation().getBeanClass(); ConstrainedExecutable mergedExecutable = executablesByDeclaringType.get( beanClass ); if ( mergedExecutable != null ) { mergedExecutable = mergedExecutable.merge( executable ); } else { mergedExecutable = executable; } executablesByDeclaringType.put( beanClass, mergedExecutable.merge( executable ) ); } @Override public ExecutableMetaData build() { assertCorrectnessOfConfiguration(); ExecutableElement executableElement = location.getExecutableElement(); return new ExecutableMetaData( executableElement.getSimpleName(), executableElement.getReturnType(), executableElement.getParameterTypes(), kind == ConstrainedElement.ConstrainedElementKind.CONSTRUCTOR ? ElementKind.CONSTRUCTOR : ElementKind.METHOD, adaptOriginsAndImplicitGroups( getConstraints() ), findParameterMetaData(), adaptOriginsAndImplicitGroups( crossParameterConstraints ), getGroupConversions(), isCascading(), isConstrained, executableElement.isGetterMethod() ); } /** * Finds the one executable from the underlying hierarchy with parameter * constraints. If no executable in the hierarchy is parameter constrained, * the parameter meta data from this builder's base executable is returned. * * @return The parameter meta data for this builder's executable. */ private List findParameterMetaData() { List parameterBuilders = null; for ( ConstrainedExecutable oneExecutable : constrainedExecutables ) { if ( parameterBuilders == null ) { parameterBuilders = newArrayList(); for ( ConstrainedParameter oneParameter : oneExecutable.getAllParameterMetaData() ) { parameterBuilders.add( new ParameterMetaData.Builder( location.getBeanClass(), oneParameter, constraintHelper ) ); } } else { int i = 0; for ( ConstrainedParameter oneParameter : oneExecutable.getAllParameterMetaData() ) { parameterBuilders.get( i ).add( oneParameter ); i++; } } } List parameterMetaDatas = newArrayList(); for ( ParameterMetaData.Builder oneBuilder : parameterBuilders ) { parameterMetaDatas.add( oneBuilder.build() ); } return parameterMetaDatas; } /** *

* Checks the configuration of this method for correctness as per the * rules outlined in the Bean Validation specification, section 4.5.5 * ("Method constraints in inheritance hierarchies"). *

*

* In particular, overriding methods in sub-types may not add parameter * constraints and the return value of an overriding method may not be * marked as cascaded if the return value is marked as cascaded already * on the overridden method. *

* * @throws ConstraintDeclarationException In case any of the rules mandated by the specification is * violated. */ private void assertCorrectnessOfConfiguration() { for ( Entry, ConstrainedExecutable> entry : executablesByDeclaringType.entrySet() ) { for ( Entry, ConstrainedExecutable> otherEntry : executablesByDeclaringType.entrySet() ) { for ( MethodConfigurationRule rule : rules ) { rule.apply( entry.getValue(), otherEntry.getValue() ); } } } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy