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 java.util.Map; import java.util.Map.Entry; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; import org.opentestsystem.authoring.testauth.domain.ComputationRule; import org.opentestsystem.authoring.testauth.domain.ComputationRuleParameter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import org.springframework.validation.Errors; import org.springframework.validation.ValidationUtils; import org.springframework.validation.Validator; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimaps; import com.google.common.collect.Ordering; @Component public class ComputationRuleValidator extends AbstractDomainValidator { private static final String ERROR_MSG_ROOT = "computationRule."; private static final String PARAMETERS = "parameters"; private static final String PARAMETERS_NAME = PARAMETERS + ".parameterName"; private static final String PARAMETERS_POSITION = PARAMETERS + ".position"; private static final String EMPTY_PARAMETER_NAME = ""; private static final Function<ComputationRuleParameter, String> PARAMETER_TO_NAME_TRANSFORMER = new Function<ComputationRuleParameter, String>() { @Override public String apply(final ComputationRuleParameter computationRuleParameter) { return StringUtils.isEmpty(computationRuleParameter.getParameterName()) ? EMPTY_PARAMETER_NAME : computationRuleParameter.getParameterName(); } }; private static final Function<ComputationRuleParameter, String> PARAMETER_TO_POSITION_TRANSFORMER = new Function<ComputationRuleParameter, String>() { @Override public String apply(final ComputationRuleParameter computationRuleParameter) { return computationRuleParameter != null ? String.valueOf(computationRuleParameter.getPosition()) : "-1"; } }; private static final Predicate<Entry<String, Collection<ComputationRuleParameter>>> PARAMETER_DUPLICATE_FILTER = new Predicate<Entry<String, Collection<ComputationRuleParameter>>>() { @Override public boolean apply(final Entry<String, Collection<ComputationRuleParameter>> entry) { return !EMPTY_PARAMETER_NAME.equals(entry.getKey()) && entry.getValue().size() > 1; } }; @Autowired private ComputationRuleParameterValidator computationRuleParameterValidator; @Autowired @Qualifier("jsr303Validator") private Validator jsrValidator; @Override public boolean supports(final Class<?> clazz) { return ComputationRule.class.equals(clazz); } @Override public void validate(final Object obj, final Errors errors) { // execute JSR-303 validations (annotations) this.jsrValidator.validate(obj, errors); final ComputationRule computationRule = (ComputationRule) obj; if (!CollectionUtils.isEmpty(computationRule.getParameters())) { // custom validation for each computationRuleParameter for (int i = 0; i < computationRule.getParameters().size(); i++) { final ComputationRuleParameter nextParameter = computationRule.getParameters().get(i); try { errors.pushNestedPath(PARAMETERS + "[" + i + "]"); ValidationUtils.invokeValidator(this.computationRuleParameterValidator, nextParameter, errors); if (nextParameter.getPosition() > computationRule.getParameters().size()) { rejectValue(errors, "position", getErrorMessageRoot() + PARAMETERS_POSITION + MSG_INVALID, nextParameter.getPosition()); } } finally { errors.popNestedPath(); } } // check for duplicate parameter names final Map<String, Collection<ComputationRuleParameter>> duplicates = Maps.filterEntries( Multimaps.index(computationRule.getParameters(), PARAMETER_TO_NAME_TRANSFORMER).asMap(), PARAMETER_DUPLICATE_FILTER); if (!duplicates.isEmpty()) { rejectValue(errors, PARAMETERS, getErrorMessageRoot() + PARAMETERS_NAME + MSG_DUPLICATES, duplicates.keySet().toString()); } // check for duplicate parameter position final Map<String, Collection<ComputationRuleParameter>> duplicatePositions = Maps.filterEntries( Multimaps.index(computationRule.getParameters(), PARAMETER_TO_POSITION_TRANSFORMER).asMap(), PARAMETER_DUPLICATE_FILTER); if (!duplicatePositions.isEmpty()) { rejectValue(errors, PARAMETERS, getErrorMessageRoot() + PARAMETERS_POSITION + MSG_DUPLICATES, duplicatePositions.keySet().toString()); } else if (!errors.hasErrors()) { computationRule.setParameters( Lists.newArrayList(Ordering.natural().onResultOf(PARAMETER_TO_POSITION_TRANSFORMER) .sortedCopy(computationRule.getParameters()))); } } if (StringUtils.trimToNull(computationRule.getVersion()) != null) { final String[] parts = computationRule.getVersion().split("\\."); if (parts.length != 2) { // not the right number version parts // should be 2 parts like "1.0" rejectValue(errors, "version", "version.format", new Object() { }); return; } if (NumberUtils.toInt(parts[0], -1) < 1 || parts[1].length() > 1 || (NumberUtils.toInt(parts[1], -1) < 0 || NumberUtils.toInt(parts[1], -1) > 9)) { //major is a negative number or some other character rejectValue(errors, "version", "version.majorminor.format", new Object() { }); } } } @Override protected String getErrorMessageRoot() { return ERROR_MSG_ROOT; } }