Package org.hibernate.validator.internal.metadata.aggregated

Source Code of org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData$Builder

/*
* 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.
* <p>
* Instances are retrieved by creating a {@link Builder} and adding all required
* {@link ConstrainedExecutable} objects to it. Instances are read-only after
* creation.
* </p>
* <p>
* 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.
* </p>
*
* @author Gunnar Morling
*/
public class ExecutableMetaData extends AbstractConstraintMetaData {

  private static final Log log = LoggerFactory.make();

  private final Class<?>[] parameterTypes;
  private final List<ParameterMetaData> parameterMetaDataList;
  private final Set<MetaConstraint<?>> 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<MetaConstraint<?>> returnValueConstraints,
      List<ParameterMetaData> parameterMetaData,
      Set<MetaConstraint<?>> crossParameterConstraints,
      Map<Class<?>, 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;
  }

  public boolean isGetter() {
    return isGetter;
  }

  /**
   * 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<MetaConstraint<?>> getCrossParameterConstraints() {
    return crossParameterConstraints;
  }

  public ValidatableParametersMetaData getValidatableParametersMetaData() {
    Set<ParameterMetaData> 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<Class<?>> defaultGroupSequence) {
    return new ExecutableDescriptorImpl(
        getType(),
        getName(),
        asDescriptors( getCrossParameterConstraints() ),
        returnValueMetaData.asDescriptor(
            defaultGroupSequenceRedefined,
            defaultGroupSequence
        ),
        parametersAsDescriptors( defaultGroupSequenceRedefined, defaultGroupSequence ),
        defaultGroupSequenceRedefined,
        isGetter,
        defaultGroupSequence
    );
  }

  private List<ParameterDescriptor> parametersAsDescriptors(boolean defaultGroupSequenceRedefined, List<Class<?>> defaultGroupSequence) {
    List<ParameterDescriptor> 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 <kevin.pollet@serli.com> (C) 2011 SERLI
   */
  public static class Builder extends MetaDataBuilder {

    /**
     * The rules applying for the definition of executable constraints.
     */
    private static final Set<MethodConfigurationRule> rules = Collections.unmodifiableSet(
        CollectionHelper.<MethodConfigurationRule>asSet(
            new OverridingMethodMustNotAlterParameterConstraints(),
            new ParallelMethodsMustNotDefineParameterConstraints(),
            new VoidMethodsMustNotBeReturnValueConstrained(),
            new ReturnValueMayOnlyBeMarkedOnceAsCascadedPerHierarchyLine(),
            new ParallelMethodsMustNotDefineGroupConversionForCascadedReturnValue()
        )
    );

    /**
     * Either CONSTRUCTOR or METHOD.
     */
    private final ConstrainedElement.ConstrainedElementKind kind;
    private final Set<ConstrainedExecutable> constrainedExecutables = newHashSet();
    private final ExecutableConstraintLocation location;
    private final Set<MetaConstraint<?>> 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<Class<?>, 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<ParameterMetaData> findParameterMetaData() {
      List<ParameterMetaData.Builder> 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<ParameterMetaData> parameterMetaDatas = newArrayList();

      for ( ParameterMetaData.Builder oneBuilder : parameterBuilders ) {
        parameterMetaDatas.add( oneBuilder.build() );
      }

      return parameterMetaDatas;
    }

    /**
     * <p>
     * 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").
     * </p>
     * <p>
     * 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.
     * </p>
     *
     * @throws ConstraintDeclarationException In case any of the rules mandated by the specification is
     * violated.
     */
    private ConstraintDeclarationException assertCorrectnessOfConfiguration() {
      for ( Entry<Class<?>, ConstrainedExecutable> entry : executablesByDeclaringType.entrySet() ) {
        for ( Entry<Class<?>, ConstrainedExecutable> otherEntry : executablesByDeclaringType.entrySet() ) {
          for ( MethodConfigurationRule rule : rules ) {
            rule.apply( entry.getValue(), otherEntry.getValue() );
          }
        }
      }

      return null;
    }
  }
}
TOP

Related Classes of org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData$Builder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.