private <T> void constraintChoices(
      PropertyDefinition<T> definition, PropertyData<T> propertyData, String objectId) {
    // Check OpenChoice
    boolean openChoice = (definition.isOpenChoice() == null) ? true : false;
    if (openChoice) return;

    List<T> data = propertyData.getValues();
    // null or blank String value should be permitted within any choice list
    if (CollectionUtils.isEmpty(data)) return;

    List<Choice<T>> choices = definition.getChoices();
    if (CollectionUtils.isEmpty(choices) || CollectionUtils.isEmpty(data)) return;

    boolean included = false;
    if (definition.getCardinality() == Cardinality.SINGLE) {
      T d = data.get(0);

      if (d instanceof String && StringUtils.isBlank((String) d) || d == null) {
        return;
      } else {
        for (Choice<T> choice : choices) {
          List<T> value = choice.getValue();
          T v = value.get(0);
          if (v.equals(d)) {
            included = true;
            break;
          }
        }
      }

    } else if (definition.getCardinality() == Cardinality.MULTI) {
      List<T> values = new ArrayList<T>();
      for (Choice<T> choice : choices) {
        values.addAll(choice.getValue());
      }

      for (T d : data) {
        if (values.contains(d)) {
          included = true;
        } else {
          if (d instanceof String && StringUtils.isBlank((String) d) || d == null) {
            included = true;
          } else {
            included = false;
            break;
          }
        }
      }
    }

    if (!included) {
      constraint(objectId, propertyData.getId() + " property value must be one of choices");
    }
  }
  private <T> List<T> flattenChoiceValues(List<Choice<T>> choices) {
    if (CollectionUtils.isEmpty(choices)) return null;

    List<T> result = new ArrayList<T>();
    for (Choice<T> choice : choices) {
      List<T> value = choice.getValue();
      if (CollectionUtils.isEmpty(value)) continue;
      for (T v : value) {
        result.add(v);
      }

      result.addAll(flattenChoiceValues(choice.getChoice()));
    }
    return result;
  }