private void initClassConstraints(
      Class<?> clazz, AnnotationIgnores annotationIgnores, BeanMetaDataCache beanMetaDataCache) {
    if (annotationIgnores.isIgnoreAnnotations(clazz)) {
      return;
    }

    // HV-262
    BeanMetaDataImpl<?> cachedMetaData = beanMetaDataCache.getBeanMetaData(clazz);
    List<ConstraintDescriptorImpl<?>> classMetaData;
    if (cachedMetaData != null && cachedMetaData.getMetaConstraintsAsMap().get(clazz) != null) {
      classMetaData = new ArrayList<ConstraintDescriptorImpl<?>>();
      for (MetaConstraint<?> metaConstraint : cachedMetaData.getMetaConstraintsAsMap().get(clazz)) {
        ConstraintDescriptorImpl<?> descriptor = metaConstraint.getDescriptor();
        if (descriptor.getElementType() == ElementType.TYPE) {
          classMetaData.add(descriptor);
        }
      }
    } else {
      classMetaData = findClassLevelConstraints(clazz);
    }

    for (ConstraintDescriptorImpl<?> constraintDescription : classMetaData) {
      BeanMetaConstraint<?> metaConstraint =
          createBeanMetaConstraint(clazz, null, constraintDescription);
      addMetaConstraint(clazz, metaConstraint);
    }
  }
  private void initFieldConstraints(
      Class<?> clazz, AnnotationIgnores annotationIgnores, BeanMetaDataCache beanMetaDataCache) {
    final Field[] fields = ReflectionHelper.getDeclaredFields(clazz);
    for (Field field : fields) {
      addToPropertyNameList(field);

      // HV-172
      if (Modifier.isStatic(field.getModifiers())) {
        continue;
      }

      if (annotationIgnores.isIgnoreAnnotations(field)) {
        continue;
      }

      // HV-262
      BeanMetaDataImpl<?> cachedMetaData = beanMetaDataCache.getBeanMetaData(clazz);
      boolean metaDataCached = cachedMetaData != null;
      List<ConstraintDescriptorImpl<?>> fieldMetaData;
      if (metaDataCached && cachedMetaData.getMetaConstraintsAsMap().get(clazz) != null) {
        fieldMetaData = new ArrayList<ConstraintDescriptorImpl<?>>();
        for (BeanMetaConstraint<?> metaConstraint :
            cachedMetaData.getMetaConstraintsAsMap().get(clazz)) {
          ConstraintDescriptorImpl<?> descriptor = metaConstraint.getDescriptor();
          if (descriptor.getElementType() == ElementType.FIELD
              && metaConstraint
                  .getLocation()
                  .getPropertyName()
                  .equals(ReflectionHelper.getPropertyName(field))) {
            fieldMetaData.add(descriptor);
          }
        }
      } else {
        fieldMetaData = findConstraints(field, ElementType.FIELD);
      }

      for (ConstraintDescriptorImpl<?> constraintDescription : fieldMetaData) {
        ReflectionHelper.setAccessibility(field);
        BeanMetaConstraint<?> metaConstraint =
            createBeanMetaConstraint(clazz, field, constraintDescription);
        addMetaConstraint(clazz, metaConstraint);
      }

      // HV-433 Make sure the field is marked as cascaded in case it was configured via
      // xml/programmatic API or
      // it hosts the @Valid annotation
      boolean isCascadedField =
          metaDataCached && cachedMetaData.getCascadedMembers().contains(field);
      if (isCascadedField || field.isAnnotationPresent(Valid.class)) {
        addCascadedMember(field);
      }
    }
  }
  private void addMethodMetaConstraint(Class<?> clazz, MethodMetaData methodMetaData) {

    addToBuilder(methodMetaData);

    if (ReflectionHelper.isGetterMethod(methodMetaData.getMethod())) {

      addToPropertyNameList(methodMetaData.getMethod());
      ReflectionHelper.setAccessibility(methodMetaData.getMethod());

      for (MethodMetaConstraint<?> metaConstraint : methodMetaData) {

        addMetaConstraint(
            clazz, getAsBeanMetaConstraint(metaConstraint, methodMetaData.getMethod()));
      }

      if (methodMetaData.isCascading()) {
        addCascadedMember(methodMetaData.getMethod());
      }
    }
  }
  /**
   * Constructor used when creating a bean meta data instance via the xml or programmatic API. In
   * this case additional metadata (the already configured constraints, cascaded members, etc) are
   * passed as well.
   *
   * @param beanClass The bean type for which to create the meta data
   * @param constraintHelper constraint helper
   * @param defaultGroupSequence programmatic/xml configured default group sequence (overrides
   *     annotations)
   * @param defaultGroupSequenceProvider programmatic configured default group sequence provider
   *     class (overrides annotations)
   * @param constraints programmatic/xml configured constraints
   * @param methodMetaDatas programmatic configured method constraints
   * @param cascadedMembers programmatic/xml configured cascaded members
   * @param annotationIgnores in xml configured ignores for annotations
   * @param beanMetaDataCache the cache of already configured meta data instances
   */
  public BeanMetaDataImpl(
      Class<T> beanClass,
      ConstraintHelper constraintHelper,
      List<Class<?>> defaultGroupSequence,
      Class<? extends DefaultGroupSequenceProvider<?>> defaultGroupSequenceProvider,
      Map<Class<?>, List<BeanMetaConstraint<?>>> constraints,
      Set<AggregatedMethodMetaData> methodMetaDatas,
      Set<Member> cascadedMembers,
      AnnotationIgnores annotationIgnores,
      BeanMetaDataCache beanMetaDataCache) {
    this.beanClass = beanClass;
    this.constraintHelper = constraintHelper;
    this.defaultGroupSequenceProvider = null;
    for (Member member : cascadedMembers) {
      addCascadedMember(member);
    }

    classHierarchyWithoutInterfaces = ReflectionHelper.computeClassHierarchy(beanClass, false);

    // start the annotation discovery phase (look for annotations in the whole class hierarchy)
    createMetaData(annotationIgnores, beanMetaDataCache);

    // set the default explicitly specified default group sequence after the discovery process is
    // complete
    if (!defaultGroupSequence.isEmpty()) {
      setDefaultGroupSequence(defaultGroupSequence);
    }

    // set the default explicitly specified default group sequence provider after the discovery
    // process is complete
    if (defaultGroupSequenceProvider != null) {
      this.defaultGroupSequenceProvider =
          newGroupSequenceProviderInstance(defaultGroupSequenceProvider);
    }

    // validates that programmatic/xml definition of default group sequence or default group
    // sequence provider
    // doesn't introduce illegal default group sequence definition.
    if (hasDefaultGroupSequenceProvider() && this.defaultGroupSequence.size() > 1) {
      throw new GroupDefinitionException(
          "Default group sequence and default group sequence provider cannot be defined at the same time");
    }

    // add the explicitly configured constraints
    for (Map.Entry<Class<?>, List<BeanMetaConstraint<?>>> entry : constraints.entrySet()) {
      Class<?> clazz = entry.getKey();

      // will hold the method constraints (getter and non-getter) of the given class keyed by method
      Map<Method, List<MethodMetaConstraint<?>>> constraintsByMethod = newHashMap();

      for (BeanMetaConstraint<?> constraint : entry.getValue()) {

        if (constraint.getDescriptor().getElementType() == ElementType.METHOD) {

          List<MethodMetaConstraint<?>> constraintsForMethod =
              constraintsByMethod.get(constraint.getLocation().getMember());
          if (constraintsForMethod == null) {
            constraintsForMethod = newArrayList();
            constraintsByMethod.put(
                (Method) constraint.getLocation().getMember(), constraintsForMethod);
          }

          constraintsForMethod.add(getAsMethodMetaConstraint(constraint));
        }
        // register non-method constraints
        else {
          addMetaConstraint(clazz, constraint);
        }
      }

      // register the constraints for each method in methodMetaConstraints. Constraints at getters
      // will also registered in metaConstraints
      for (Entry<Method, List<MethodMetaConstraint<?>>> methodAndConstraints :
          constraintsByMethod.entrySet()) {

        MethodMetaData methodMetaData =
            new MethodMetaData(
                methodAndConstraints.getKey(),
                methodAndConstraints.getValue(),
                cascadedMembers.contains(methodAndConstraints.getKey()));
        addMethodMetaConstraint(clazz, methodMetaData);
      }
    }

    allMetaConstraints = buildAllConstraintSets();
    directMetaConstraints = buildDirectConstraintSets();

    // add the explicitly configured method constraints, here we need to merge the programmatic and
    // discovered
    // metadata built with the "automatic" discovering.
    if (!methodMetaDatas.isEmpty()) {
      for (AggregatedMethodMetaData aggregatedMethodMetaData : methodMetaDatas) {
        for (MethodMetaData methodMetaData : aggregatedMethodMetaData.getAllMethodMetaData()) {
          Method method = methodMetaData.getMethod();
          addMethodMetaConstraint(method.getDeclaringClass(), methodMetaData);
        }
      }
    }

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

    // reset class members we don't need any longer
    this.methodMetaDataBuilders = null;
    this.constraintHelper = null;
  }