Java tutorial
/******************************************************************************* * Educational Online Test Delivery System * Copyright (c) 2013 American Institutes for Research * * Distributed under the AIR Open Source License, Version 1.0 * See accompanying file AIR-License-1_0.txt or at * http://www.smarterapp.org/documents/American_Institutes_for_Research_Open_Source_Software_License.pdf ******************************************************************************/ package org.opentestsystem.authoring.testauth.validation; import java.util.Collection; import org.apache.commons.lang.StringUtils; import org.opentestsystem.authoring.testauth.domain.ItemSelectionAlgorithmParameter; import org.opentestsystem.authoring.testauth.domain.ItemSelectionParameterElement; import org.opentestsystem.authoring.testauth.domain.ItemSelectionType; import org.springframework.validation.Errors; import org.springframework.validation.ValidationUtils; import org.springframework.validation.Validator; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Range; public abstract class AbstractDomainValidator implements Validator { public static final int MAX_STRING_LENGTH = 60; public static final int MAX_PARSEABLE_LENGTH = 18; protected static final String MSG_REQUIRED = ".required"; protected static final String MSG_REQUIRED_EMPTY = ".required.empty"; protected static final String MSG_REQUIRED_CONTAINS = ".required.contains"; protected static final String MSG_PARSEABLE_NUMBER = ".parseable.number"; protected static final String MSG_PARSEABLE_BOOLEAN = ".parseable.boolean"; protected static final String MSG_LENGTH_MAX = ".length.max"; protected static final String MSG_LENGTH_MIN = "length.min"; protected static final String MSG_COUNT = ".count"; protected static final String MSG_DUPLICATES = ".duplicates"; protected static final String MSG_INVALID = ".invalid"; protected static final String MSG_MIN = ".min"; protected static final String MSG_LESS_THAN_MAX = ".lessthan.max"; protected static final String MSG_WITHIN_MIN_MAX = ".within.minmax"; protected static final String MSG_TYPE_NOT_RECOGNIZED = ".type.not.recognized"; protected static final String MSG_INVALID_SELECTION = ".invalid.selection"; protected static final String MSG_OUT_OF_RANGE = ".out.of.range"; private static final class ITEM_SELECTION_VALUE_FILTER implements Predicate<ItemSelectionParameterElement> { private final String value; public static ITEM_SELECTION_VALUE_FILTER getInstance(final String value) { return new ITEM_SELECTION_VALUE_FILTER(value); } private ITEM_SELECTION_VALUE_FILTER(final String value) { this.value = value; } @Override public boolean apply(final ItemSelectionParameterElement element) { return element.getName() != null && element.getName().equals(this.value); } }; protected abstract String getErrorMessageRoot(); protected void rejectIfTooLong(final Errors errors, final String fieldName, final int maxLength, final Object... errorArgs) { final Object value = errors.getFieldValue(fieldName); if (!org.springframework.util.StringUtils.isEmpty(value)) { final int size = value.toString().length(); if (size > maxLength) { rejectValue(errors, fieldName, getErrorMessageRoot() + fieldName + MSG_LENGTH_MAX, flattenArgs(errorArgs, maxLength)); } } } protected void rejectIfTooShort(final Errors errors, final String fieldName, final int minLength, final Object... errorArgs) { final Object value = errors.getFieldValue(fieldName); if (!org.springframework.util.StringUtils.isEmpty(value)) { final int size = value.toString().length(); if (size < minLength) { rejectValue(errors, fieldName, getErrorMessageRoot() + fieldName + MSG_LENGTH_MIN, flattenArgs(errorArgs, minLength)); } } } protected void rejectIfNotBooleanParseable(final Errors errors, final String fieldName, final Object... errorArgs) { final Object value = errors.getFieldValue(fieldName); if (!org.springframework.util.StringUtils.isEmpty(value)) { if (!Boolean.FALSE.toString().equals(value.toString()) && !Boolean.TRUE.toString().equals(value.toString())) { rejectValue(errors, fieldName, getErrorMessageRoot() + fieldName + MSG_PARSEABLE_BOOLEAN, errorArgs); } } } protected void rejectIfNotFloatOrIntegerParseable(final Errors errors, final String fieldName, final boolean parseFloat, final Object... errorArgs) { final Object value = errors.getFieldValue(fieldName); if (!org.springframework.util.StringUtils.isEmpty(value) && value.toString().length() <= MAX_PARSEABLE_LENGTH) { if (!isFloatOrIntegerParseable(value.toString(), parseFloat)) { rejectValue(errors, fieldName, getErrorMessageRoot() + fieldName + MSG_PARSEABLE_NUMBER, errorArgs); } } } protected boolean isFloatOrIntegerParseable(final String fieldValue, final boolean isFloat) { if (StringUtils.isNotEmpty(fieldValue) && fieldValue.toString().length() <= MAX_PARSEABLE_LENGTH) { try { if (isFloat) { Float.parseFloat(fieldValue); } else { Long.parseLong(fieldValue); } return true; } catch (final NumberFormatException e) { return false; } } return false; } protected void rejectIfEmpty(final Errors e, final String fieldName, final Object... errorArgs) { final String errorCode = getErrorMessageRoot() + fieldName + MSG_REQUIRED; ValidationUtils.rejectIfEmpty(e, fieldName, errorCode, errorArgs, errorCode); } protected void rejectIfNotEmpty(final Errors e, final String fieldName, final Object... errorArgs) { final Object value = e.getFieldValue(fieldName); if (value != null && StringUtils.isNotEmpty(value.toString())) { rejectValue(e, fieldName, getErrorMessageRoot() + fieldName + MSG_REQUIRED_EMPTY, errorArgs); } } protected void rejectIfCollectionEmpty(final Errors e, final String fieldName, final Object... errorArgs) { final Collection<?> value = (Collection<?>) e.getFieldValue(fieldName); if (value == null || value.isEmpty()) { rejectValue(e, fieldName, getErrorMessageRoot() + fieldName + MSG_REQUIRED, errorArgs); } } protected void rejectIfCollectionNotEmpty(final Errors e, final String fieldName, final Object... errorArgs) { final Collection<?> value = (Collection<?>) e.getFieldValue(fieldName); if (value != null && !value.isEmpty()) { rejectValue(e, fieldName, getErrorMessageRoot() + fieldName + MSG_REQUIRED_EMPTY, errorArgs); } } protected void rejectIfNotInCollection(final Errors e, final String fieldName, final Collection<String> collection, final Object... errorArgs) { final Object value = e.getFieldValue(fieldName); if (!org.springframework.util.StringUtils.isEmpty(value) && collection != null && !collection.isEmpty()) { if (!collection.contains(value.toString())) { rejectValue(e, fieldName, getErrorMessageRoot() + fieldName + MSG_REQUIRED_CONTAINS, flattenArgs(errorArgs, collection.toString())); } } } protected void rejectValue(final Errors e, final String fieldName, final String errorCode, final Object... errorArgs) { e.rejectValue(fieldName, errorCode, errorArgs, errorCode); } protected void validateParameterValue(final String parameterValue, final String parametersFieldName, final ItemSelectionAlgorithmParameter algorithmParameter, final Errors errors) { final Object[] errorArgs = new Object[] { algorithmParameter.getItemSelectionType().name(), parameterValue, algorithmParameter.getMinimumValue(), algorithmParameter.getMaximumValue() }; if (StringUtils.isEmpty(parameterValue)) { rejectValue(errors, "", getErrorMessageRoot() + parametersFieldName + MSG_REQUIRED); } else { switch (algorithmParameter.getItemSelectionType()) { case FLOAT: case INTEGER: if (parameterValue.length() > MAX_PARSEABLE_LENGTH) { rejectValue(errors, "", getErrorMessageRoot() + parametersFieldName + MSG_LENGTH_MAX, MAX_PARSEABLE_LENGTH); } else { rejectIfNotWithinNumericConstraints(parameterValue, parametersFieldName, errors, algorithmParameter, errorArgs); } break; case BOOLEAN: if (!Boolean.TRUE.toString().equals(parameterValue) && !Boolean.FALSE.toString().equals(parameterValue)) { rejectValue(errors, "", getErrorMessageRoot() + parametersFieldName + MSG_INVALID, errorArgs); } break; case LIST: if (!Iterables.tryFind(algorithmParameter.getItemSelectionList(), ITEM_SELECTION_VALUE_FILTER.getInstance(parameterValue)).isPresent()) { rejectValue(errors, "", getErrorMessageRoot() + parametersFieldName + MSG_INVALID_SELECTION); } break; case STRING: if (parameterValue.length() > MAX_STRING_LENGTH) { rejectValue(errors, "", getErrorMessageRoot() + parametersFieldName + MSG_LENGTH_MAX, MAX_STRING_LENGTH); } break; default: rejectValue(errors, "", getErrorMessageRoot() + parametersFieldName + MSG_TYPE_NOT_RECOGNIZED); break; } } } /** * when an ItemSelectionAlgorithmParameter is defined as a numeric type (FLOAT or INTEGER), every instance * must be of that numeric type and fall within the range boundary of the ItemSelectionAlgorithmParameter MIN & MAX values */ private void rejectIfNotWithinNumericConstraints(final String fieldValue, final String parametersFieldName, final Errors errors, final ItemSelectionAlgorithmParameter parameter, final Object... errorArgs) { try { if (parameter.getItemSelectionType() == ItemSelectionType.FLOAT) { final Float floatValue = Float.parseFloat(fieldValue); final Range<Float> floatRange = buildFloatRange(parameter.getMinimumValue(), parameter.getMaximumValue()); if (floatRange != null && !floatRange.contains(floatValue)) { rejectValue(errors, "", getErrorMessageRoot() + parametersFieldName + MSG_OUT_OF_RANGE, flattenArgs(errorArgs, buildRangeConstraintString(parameter.getMinimumValue(), parameter.getMaximumValue()))); } } else { final Long longValue = Long.parseLong(fieldValue); final Range<Long> longRange = buildLongRange(parameter.getMinimumValue(), parameter.getMaximumValue()); if (longRange != null && !longRange.contains(longValue)) { rejectValue(errors, "", getErrorMessageRoot() + parametersFieldName + MSG_OUT_OF_RANGE, flattenArgs(errorArgs, buildRangeConstraintString(parameter.getMinimumValue(), parameter.getMaximumValue()))); } } } catch (final NumberFormatException e) { rejectValue(errors, "", getErrorMessageRoot() + parametersFieldName + MSG_PARSEABLE_NUMBER, errorArgs); } } protected Range<Float> buildFloatRange(final String min, final String max) { Range<Float> floatRange = null; if (StringUtils.isNotEmpty(min) && StringUtils.isNotEmpty(max)) { // has both ends of a range floatRange = Range.closed(Float.valueOf(min), Float.valueOf(max)); } else if (StringUtils.isNotEmpty(min) && StringUtils.isEmpty(max)) { // only has a minimum floatRange = Range.atLeast(Float.valueOf(min)); } else if (StringUtils.isEmpty(min) && StringUtils.isNotEmpty(max)) { // only has a maximum floatRange = Range.atMost(Float.valueOf(max)); } return floatRange; } protected Range<Long> buildLongRange(final String min, final String max) { Range<Long> longRange = null; if (StringUtils.isNotEmpty(min) && StringUtils.isNotEmpty(max)) { // has both ends of a range longRange = Range.closed(Long.valueOf(min), Long.valueOf(max)); } else if (StringUtils.isNotEmpty(min) && StringUtils.isEmpty(max)) { // only has a minimum longRange = Range.atLeast(Long.valueOf(min)); } else if (StringUtils.isEmpty(min) && StringUtils.isNotEmpty(max)) { // only has a maximum longRange = Range.atMost(Long.valueOf(max)); } return longRange; } protected String buildRangeConstraintString(final String min, final String max) { if (StringUtils.isNotEmpty(min) && StringUtils.isNotEmpty(max)) { // has both ends of a range return "(" + min + " - " + max + ")"; } else if (StringUtils.isNotEmpty(min) && StringUtils.isEmpty(max)) { // only has a minimum return "(Minimum: " + min + ")"; } else if (StringUtils.isEmpty(min) && StringUtils.isNotEmpty(max)) { // only has a maximum return "(Maximum: " + max + ")"; } return ""; } protected Object[] flattenArgs(final Object[] firstArray, final Object... additionalArgs) { return Iterables.toArray( Iterables.concat(Lists.newArrayList(firstArray), Lists.newArrayList(additionalArgs)), Object.class); } }