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

Source Code of org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl$BuilderDelegate

/*
* JBoss, Home of Professional Open Source
* Copyright 2009, 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.lang.annotation.ElementType;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.validation.groups.Default;
import javax.validation.metadata.BeanDescriptor;
import javax.validation.metadata.PropertyDescriptor;

import org.hibernate.validator.group.DefaultGroupSequenceProvider;
import org.hibernate.validator.internal.metadata.aggregated.ConstraintMetaData.ConstraintMetaDataKind;
import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
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.raw.BeanConfiguration;
import org.hibernate.validator.internal.metadata.raw.ConfigurationSource;
import org.hibernate.validator.internal.metadata.raw.ConstrainedElement;
import org.hibernate.validator.internal.metadata.raw.ConstrainedElement.ConstrainedElementKind;
import org.hibernate.validator.internal.metadata.raw.ConstrainedField;
import org.hibernate.validator.internal.metadata.raw.ConstrainedMethod;
import org.hibernate.validator.internal.metadata.raw.ConstrainedType;
import org.hibernate.validator.internal.util.CollectionHelper.Partitioner;
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.method.metadata.MethodDescriptor;
import org.hibernate.validator.method.metadata.TypeDescriptor;

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;
import static org.hibernate.validator.internal.util.CollectionHelper.partition;
import static org.hibernate.validator.internal.util.ReflectionHelper.computeAllImplementedInterfaces;
import static org.hibernate.validator.internal.util.ReflectionHelper.getMethods;
import static org.hibernate.validator.internal.util.ReflectionHelper.newInstance;

/**
* 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 <kevin.pollet@serli.com> (C) 2011 SERLI
*/
public final class BeanMetaDataImpl<T> implements BeanMetaData<T> {

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

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

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

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

  /**
   * Contains constrained related meta data for all methods of the type
   * represented by this bean meta data. Keyed by method, values are an
   * aggregated view on each method together with all the methods from the
   * inheritance hierarchy with the same signature.
   */
  private final Map<String, MethodMetaData> methodMetaData;

  private final Map<String, PropertyMetaData> propertyMetaData;

  /**
   * List of cascaded members.
   */
  private final Set<Member> cascadedMembers;

  /**
   * The default groups sequence for this bean class.
   */
  private List<Class<?>> defaultGroupSequence = newArrayList();

  /**
   * The default group sequence provider.
   *
   * @see org.hibernate.validator.group.GroupSequenceProvider
   * @see DefaultGroupSequenceProvider
   */
  private DefaultGroupSequenceProvider<T> defaultGroupSequenceProvider;

  /**
   * The class hierarchy for this class starting with the class itself going up the inheritance chain. Interfaces
   * are not included.
   */
  private List<Class<?>> classHierarchyWithoutInterfaces;

  /**
   * 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 constraintMetaData All constraint meta data relating to the represented type.
   */
  public BeanMetaDataImpl(Class<T> beanClass,
              List<Class<?>> defaultGroupSequence,
              Class<? extends DefaultGroupSequenceProvider<?>> defaultGroupSequenceProvider,
              Set<ConstraintMetaData> constraintMetaData) {

    this.beanClass = beanClass;
    this.propertyMetaData = newHashMap();

    Set<PropertyMetaData> propertyMetaDataSet = newHashSet();
    Set<MethodMetaData> methodMetaDataSet = newHashSet();

    for ( ConstraintMetaData oneElement : constraintMetaData ) {
      if ( oneElement.getKind() == ConstraintMetaDataKind.PROPERTY ) {
        propertyMetaDataSet.add( (PropertyMetaData) oneElement );
      }
      else {
        methodMetaDataSet.add( (MethodMetaData) oneElement );
      }
    }

    Set<Member> cascadedMembers = newHashSet();
    Set<MetaConstraint<?>> allMetaConstraints = newHashSet();

    for ( PropertyMetaData oneProperty : propertyMetaDataSet ) {

      propertyMetaData.put( oneProperty.getName(), oneProperty );

      if ( oneProperty.isCascading() ) {
        cascadedMembers.addAll( oneProperty.getCascadingMembers() );
      }

      allMetaConstraints.addAll( oneProperty.getConstraints() );
    }

    this.cascadedMembers = Collections.unmodifiableSet( cascadedMembers );
    this.allMetaConstraints = Collections.unmodifiableSet( allMetaConstraints );

    classHierarchyWithoutInterfaces = ReflectionHelper.computeClassHierarchy( beanClass, false );

    setDefaultGroupSequenceOrProvider( defaultGroupSequence, defaultGroupSequenceProvider );

    directMetaConstraints = buildDirectConstraintSets();

    this.methodMetaData = Collections.unmodifiableMap( buildMethodMetaData( methodMetaDataSet ) );
  }

  public Class<T> getBeanClass() {
    return beanClass;
  }

  public BeanDescriptor getBeanDescriptor() {
    return getBeanDescriptorInternal();
  }

  public TypeDescriptor getTypeDescriptor() {
    return getBeanDescriptorInternal();
  }

  public Set<Member> getCascadedMembers() {
    return cascadedMembers;
  }

  public Set<MetaConstraint<?>> getMetaConstraints() {
    return allMetaConstraints;
  }

  public Set<MetaConstraint<?>> getDirectMetaConstraints() {
    return directMetaConstraints;
  }

  public MethodMetaData getMetaDataFor(Method method) {
    return methodMetaData.get( method.getName() + Arrays.toString( method.getParameterTypes() ) );
  }

  public Set<MethodMetaData> getAllMethodMetaData() {
    return new HashSet<MethodMetaData>( methodMetaData.values() );
  }

  public PropertyMetaData getMetaDataFor(String propertyName) {
    return propertyMetaData.get( propertyName );
  }

  public boolean isPropertyPresent(String name) {
    return propertyMetaData.containsKey( name );
  }

  public List<Class<?>> getDefaultGroupSequence(T beanState) {
    if ( hasDefaultGroupSequenceProvider() ) {
      List<Class<?>> providerDefaultGroupSequence = defaultGroupSequenceProvider.getValidationGroups( beanState );
      return getValidDefaultGroupSequence( providerDefaultGroupSequence );
    }

    return Collections.unmodifiableList( defaultGroupSequence );
  }

  public boolean defaultGroupSequenceIsRedefined() {
    return defaultGroupSequence.size() > 1 || hasDefaultGroupSequenceProvider();
  }

  public Set<PropertyMetaData> getAllPropertyMetaData() {
    return Collections.unmodifiableSet( new HashSet<PropertyMetaData>( propertyMetaData.values() ) );
  }

  public List<Class<?>> getClassHierarchy() {
    return classHierarchyWithoutInterfaces;
  }

  /**
   * Returns a bean descriptor representing this meta data object. A new
   * descriptor instance is created with each invocation. The descriptor might
   * be cached internally in the future should that need arise.
   *
   * @return A bean descriptor for this meta data object.
   */
  private BeanDescriptorImpl<T> getBeanDescriptorInternal() {

    return new BeanDescriptorImpl<T>(
        beanClass,
        getClassLevelConstraintsAsDescriptors(),
        getConstrainedPropertiesAsDescriptors(),
        getMethodsAsDescriptors(),
        defaultGroupSequenceIsRedefined(),
        getDefaultGroupSequence( null )
    );
  }

  private Set<ConstraintDescriptorImpl<?>> getClassLevelConstraintsAsDescriptors() {

    Set<MetaConstraint<?>> classLevelConstraints = getClassLevelConstraints( allMetaConstraints );

    Set<ConstraintDescriptorImpl<?>> theValue = newHashSet();

    for ( MetaConstraint<?> oneConstraint : classLevelConstraints ) {
      theValue.add( oneConstraint.getDescriptor() );
    }

    return theValue;
  }

  private Map<String, PropertyDescriptor> getConstrainedPropertiesAsDescriptors() {
    Map<String, PropertyDescriptor> theValue = newHashMap();

    for ( Entry<String, PropertyMetaData> oneProperty : propertyMetaData.entrySet() ) {
      if ( oneProperty.getValue().isConstrained() ) {
        theValue.put(
            oneProperty.getKey(),
            oneProperty.getValue()
                .asDescriptor( defaultGroupSequenceIsRedefined(), getDefaultGroupSequence( null ) )
        );
      }
    }

    return theValue;
  }

  private Map<String, MethodDescriptor> getMethodsAsDescriptors() {
    Map<String, MethodDescriptor> theValue = newHashMap();

    for ( Entry<String, MethodMetaData> oneMethod : methodMetaData.entrySet() ) {
      theValue.put(
          oneMethod.getKey(), oneMethod.getValue().asDescriptor(
          defaultGroupSequenceIsRedefined(),
          getDefaultGroupSequence( null )
      )
      );
    }

    return theValue;
  }

  private void setDefaultGroupSequenceOrProvider(List<Class<?>> defaultGroupSequence, Class<? extends DefaultGroupSequenceProvider<?>> defaultGroupSequenceProvider) {

    if ( defaultGroupSequence != null && defaultGroupSequenceProvider != null ) {
      throw log.getInvalidDefaultGroupSequenceDefinitionException();
    }

    if ( defaultGroupSequenceProvider != null ) {
      this.defaultGroupSequenceProvider = newGroupSequenceProviderInstance( defaultGroupSequenceProvider );
    }
    else if ( defaultGroupSequence != null && !defaultGroupSequence.isEmpty() ) {
      setDefaultGroupSequence( defaultGroupSequence );
    }
    else {
      setDefaultGroupSequence( Arrays.<Class<?>>asList( beanClass ) );
    }
  }

  private Set<MetaConstraint<?>> getClassLevelConstraints(Set<MetaConstraint<?>> constraints) {

    Set<MetaConstraint<?>> classLevelConstraints = partition(
        constraints,
        byElementType()
    ).get( ElementType.TYPE );

    return classLevelConstraints != null ? classLevelConstraints : Collections.<MetaConstraint<?>>emptySet();
  }

  private Set<MetaConstraint<?>> buildDirectConstraintSets() {

    Set<MetaConstraint<?>> constraints = newHashSet();

    Set<Class<?>> classAndInterfaces = computeAllImplementedInterfaces( beanClass );
    classAndInterfaces.add( beanClass );

    for ( Class<?> clazz : classAndInterfaces ) {
      for ( MetaConstraint<?> oneConstraint : allMetaConstraints ) {
        if ( oneConstraint.getLocation().getBeanClass().equals( clazz ) ) {
          constraints.add( oneConstraint );
        }
      }
    }

    return Collections.unmodifiableSet( constraints );
  }

  /**
   * Builds up the method meta data for this type
   */
  private Map<String, MethodMetaData> buildMethodMetaData(Set<MethodMetaData> allMethodMetaData) {

    Map<String, MethodMetaData> theValue = newHashMap();

    for ( MethodMetaData oneAggregatedMethodMetaData : allMethodMetaData ) {
      theValue.put(
          oneAggregatedMethodMetaData.getName() + Arrays.toString( oneAggregatedMethodMetaData.getParameterTypes() ),
          oneAggregatedMethodMetaData
      );
    }

    return theValue;
  }

  private void setDefaultGroupSequence(List<Class<?>> groupSequence) {
    defaultGroupSequence = getValidDefaultGroupSequence( groupSequence );
  }

  private List<Class<?>> getValidDefaultGroupSequence(List<Class<?>> groupSequence) {
    List<Class<?>> validDefaultGroupSequence = new ArrayList<Class<?>>();

    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.getName() );
    }
    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;
  }

  @SuppressWarnings("unchecked")
  private <U extends DefaultGroupSequenceProvider<?>> DefaultGroupSequenceProvider<T> newGroupSequenceProviderInstance(Class<U> providerClass) {
    Method[] providerMethods = getMethods( providerClass );
    for ( Method method : providerMethods ) {
      Class<?>[] paramTypes = method.getParameterTypes();
      if ( "getValidationGroups".equals( method.getName() ) && !method.isBridge()
          && paramTypes.length == 1 && paramTypes[0].isAssignableFrom( beanClass ) ) {

        return (DefaultGroupSequenceProvider<T>) newInstance(
            providerClass, "the default group sequence provider"
        );
      }
    }

    throw log.getWrongDefaultGroupSequenceProviderTypeException( beanClass.getName() );
  }

  private Partitioner<ElementType, MetaConstraint<?>> byElementType() {
    return new Partitioner<ElementType, MetaConstraint<?>>() {
      public ElementType getPartition(MetaConstraint<?> constraint) {
        return constraint.getElementType();
      }
    };
  }

  @Override
  public String toString() {
    final StringBuilder sb = new StringBuilder();
    sb.append( "BeanMetaDataImpl" );
    sb.append( "{beanClass=" ).append( beanClass.getSimpleName() );
    sb.append( ", constraintCount=" ).append( getMetaConstraints().size() );
    sb.append( ", cascadedMemberCount=" ).append( cascadedMembers.size() );
    sb.append( ", defaultGroupSequence=" ).append( getDefaultGroupSequence( null ) );
    sb.append( '}' );
    return sb.toString();
  }

  public static class BeanMetaDataBuilder<T> {

    private final ConstraintHelper constraintHelper;

    private final Class<T> beanClass;

    private final Set<BuilderDelegate> builders = newHashSet();

    private ConfigurationSource sequenceSource;

    private ConfigurationSource providerSource;

    private List<Class<?>> defaultGroupSequence;

    private Class<? extends DefaultGroupSequenceProvider<?>> defaultGroupSequenceProvider;


    public BeanMetaDataBuilder(ConstraintHelper constraintHelper, Class<T> beanClass) {
      this.beanClass = beanClass;
      this.constraintHelper = constraintHelper;
    }

    public static <T> BeanMetaDataBuilder<T> getInstance(ConstraintHelper constraintHelper, Class<T> beanClass) {
      return new BeanMetaDataBuilder<T>( constraintHelper, beanClass );
    }

    public void add(BeanConfiguration<?> configuration) {

      if ( configuration.getBeanClass().equals( beanClass ) ) {

        if ( configuration.getDefaultGroupSequence() != null &&
            ( sequenceSource == null || configuration.getSource()
                .getPriority() >= sequenceSource.getPriority() ) ) {

          sequenceSource = configuration.getSource();
          defaultGroupSequence = configuration.getDefaultGroupSequence();
        }

        if ( configuration.getDefaultGroupSequenceProvider() != null &&
            ( providerSource == null || configuration.getSource()
                .getPriority() >= providerSource.getPriority() ) ) {

          providerSource = configuration.getSource();
          defaultGroupSequenceProvider = configuration.getDefaultGroupSequenceProvider();
        }
      }

      for ( ConstrainedElement oneConstrainedElement : configuration.getConstrainedElements() ) {
        addMetaDataToBuilder( oneConstrainedElement, builders );
      }
    }

    private void addMetaDataToBuilder(ConstrainedElement constrainableElement, Set<BuilderDelegate> builders) {

      for ( BuilderDelegate oneBuilder : builders ) {
        boolean foundBuilder = oneBuilder.add( constrainableElement );

        if ( foundBuilder ) {
          return;
        }
      }

      builders.add(
          new BuilderDelegate(
              constrainableElement,
              constraintHelper
          )
      );
    }

    public BeanMetaDataImpl<T> build() {

      Set<ConstraintMetaData> aggregatedElements = newHashSet();

      for ( BuilderDelegate oneBuilder : builders ) {
        aggregatedElements.addAll(
            oneBuilder.build(
                ( defaultGroupSequence != null && defaultGroupSequence.size() > 1 ) || defaultGroupSequenceProvider != null,
                defaultGroupSequence
            )
        );
      }

      return new BeanMetaDataImpl<T>(
          beanClass,
          defaultGroupSequence,
          defaultGroupSequenceProvider,
          aggregatedElements
      );
    }
  }

  private static class BuilderDelegate {

    private final ConstraintHelper constraintHelper;

    private MetaDataBuilder propertyBuilder;

    private MethodMetaData.Builder methodBuilder;

    public BuilderDelegate(ConstrainedElement constrainedElement, ConstraintHelper constraintHelper) {

      this.constraintHelper = constraintHelper;

      switch ( constrainedElement.getKind() ) {

        case FIELD:

          ConstrainedField constrainedField = (ConstrainedField) constrainedElement;
          propertyBuilder = new PropertyMetaData.Builder(
              constrainedField,
              constraintHelper
          );
          break;

        case METHOD:

          ConstrainedMethod constrainedMethod = (ConstrainedMethod) constrainedElement;
          methodBuilder = new MethodMetaData.Builder(
              constrainedMethod,
              constraintHelper
          );

          if ( constrainedMethod.isGetterMethod() ) {
            propertyBuilder = new PropertyMetaData.Builder(
                constrainedMethod,
                constraintHelper
            );
          }
          break;

        case TYPE:

          ConstrainedType constrainedType = (ConstrainedType) constrainedElement;
          propertyBuilder = new PropertyMetaData.Builder(
              constrainedType,
              constraintHelper
          );
          break;
      }
    }

    public boolean add(ConstrainedElement constrainedElement) {

      boolean added = false;

      if ( methodBuilder != null && methodBuilder.accepts( constrainedElement ) ) {
        methodBuilder.add( constrainedElement );
        added = true;
      }

      if ( propertyBuilder != null && propertyBuilder.accepts( constrainedElement ) ) {
        propertyBuilder.add( constrainedElement );

        if ( added == false && constrainedElement.getKind() == ConstrainedElementKind.METHOD && methodBuilder == null ) {
          ConstrainedMethod constrainedMethod = (ConstrainedMethod) constrainedElement;
          methodBuilder = new MethodMetaData.Builder(
              constrainedMethod,
              constraintHelper
          );
        }

        added = true;
      }

      return added;
    }

    public Set<ConstraintMetaData> build(boolean defaultGroupSequenceRedefined, List<Class<?>> defaultGroupSequence) {

      Set<ConstraintMetaData> theValue = newHashSet();

      if ( propertyBuilder != null ) {
        theValue.add( propertyBuilder.build() );
      }

      if ( methodBuilder != null ) {
        theValue.add( methodBuilder.build() );
      }

      return theValue;
    }

  }
}
TOP

Related Classes of org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl$BuilderDelegate

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.