protected ConstraintValidationResult processSingleLengthConstraint(
      DictionaryValidationResult result,
      Object value,
      LengthConstraint constraint,
      AttributeValueReader attributeValueReader)
      throws AttributeValidationException {
    // Can't process any range constraints on null values
    if (ValidationUtils.isNullOrEmpty(value)) {
      return result.addSkipped(attributeValueReader, CONSTRAINT_NAME);
    }

    DataType dataType = constraint.getDataType();
    Object typedValue = value;

    if (dataType != null) {
      typedValue = ValidationUtils.convertToDataType(value, dataType, dateTimeService);
    }

    // The only thing that can have a length constraint currently is a string.
    if (typedValue instanceof String) {
      return validateLength(result, (String) typedValue, constraint, attributeValueReader);
    }

    return result.addSkipped(attributeValueReader, CONSTRAINT_NAME);
  }
  /**
   * validates the value provided using {@code RangeConstraint}
   *
   * @param result - a holder for any already run validation results
   * @param value - the value to validate
   * @param constraint - the range constraint to use
   * @param attributeValueReader - provides access to the attribute being validated
   * @return the passed in result, updated with the results of the processing
   * @throws AttributeValidationException if validation fails
   */
  protected ConstraintValidationResult processSingleRangeConstraint(
      DictionaryValidationResult result,
      Object value,
      RangeConstraint constraint,
      AttributeValueReader attributeValueReader)
      throws AttributeValidationException {
    // Can't process any range constraints on null values
    if (ValidationUtils.isNullOrEmpty(value)
        || (constraint.getExclusiveMin() == null && constraint.getInclusiveMax() == null)) {
      return result.addSkipped(attributeValueReader, CONSTRAINT_NAME);
    }

    // This is necessary because sometimes we'll be getting a string, for example, that represents a
    // date.
    DataType dataType = constraint.getDataType();
    Object typedValue = value;

    if (dataType != null) {
      typedValue = ValidationUtils.convertToDataType(value, dataType, dateTimeService);
    } else if (value instanceof String) {
      // assume string is a number of type double
      try {
        Double d = Double.parseDouble((String) value);
        typedValue = d;
      } catch (NumberFormatException n) {
        // do nothing, typedValue is never reset
      }
    }

    // TODO: decide if there is any reason why the following would be insufficient - i.e. if
    // something numeric could still be cast to String at this point
    if (typedValue instanceof Date) {
      return validateRange(result, (Date) typedValue, constraint, attributeValueReader);
    } else if (typedValue instanceof Number) {
      return validateRange(result, (Number) typedValue, constraint, attributeValueReader);
    }

    return result.addSkipped(attributeValueReader, CONSTRAINT_NAME);
  }