/** Checks whether there is a default group sequence defined for this class. See HV-113. */
  private void initDefaultGroupSequence() {
    List<Class<?>> groupSequence = new ArrayList<Class<?>>();
    GroupSequenceProvider groupSequenceProviderAnnotation =
        beanClass.getAnnotation(GroupSequenceProvider.class);
    GroupSequence groupSequenceAnnotation = beanClass.getAnnotation(GroupSequence.class);

    if (groupSequenceAnnotation != null && groupSequenceProviderAnnotation != null) {
      throw new GroupDefinitionException(
          "GroupSequence and GroupSequenceProvider annotations cannot be used at the same time");
    }

    if (groupSequenceProviderAnnotation != null) {
      defaultGroupSequenceProvider =
          newGroupSequenceProviderInstance(groupSequenceProviderAnnotation.value());
    } else if (groupSequenceAnnotation != null) {
      groupSequence.addAll(Arrays.asList(groupSequenceAnnotation.value()));
      setDefaultGroupSequence(groupSequence);
    } else {
      groupSequence.add(beanClass);
      setDefaultGroupSequence(groupSequence);
    }
  }
  /**
   * 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;
  }