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.service.impl; import static org.opentestsystem.authoring.testauth.config.TestAuthUtil.safeParseInt; import static org.opentestsystem.authoring.testauth.service.impl.BlueprintHelper.BLUEPRINT_ELEMENT_PARENT_KEY_FUNCTION; import static org.opentestsystem.authoring.testauth.service.impl.BlueprintHelper.BLUEPRINT_ELEMENT_UNIQUE_KEY_FUNCTION; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.lang.StringUtils; import org.opentestsystem.authoring.testauth.domain.BlueprintElement; import org.opentestsystem.authoring.testauth.domain.BlueprintElementValue; import org.opentestsystem.authoring.testauth.domain.Segment; import org.opentestsystem.authoring.testauth.domain.ValidationResult; import org.opentestsystem.authoring.testauth.service.impl.BlueprintHelper.BPE_BPEV_TRANSFORMER; import org.springframework.util.CollectionUtils; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimaps; public final class BlueprintValidationHelper { private static final String MASTER_KEY = "MASTER"; private static final String TOP_BLUEPRINT_ELEMENT_LEVEL = "1"; private static final String OP_MIN_FIELD = "operationalItemMinValue"; private static final String OP_MAX_FIELD = "operationalItemMaxValue"; private static final String FT_MIN_FIELD = "fieldTestItemMinValue"; private static final String FT_MAX_FIELD = "fieldTestItemMaxValue"; private static final String ACTIVE_FIELD = "active"; private static final String PARENT_MESSAGE = "The %s value of the content standard (%s) is %s than the sum of its children's %s value (%s)"; private static final String PARENT_SEGMENT_MESSAGE = "The %s value of the segment (%s) is %s than the sum of its blueprint's top-level standard's %s value (%s)"; private static final String INACTIVE_MESSAGE = "Child element may not be Active if parent element is Inactive."; private BlueprintValidationHelper() { // do not instantiate } /** * Handles hierarchical validation for each individual <code>BlueprintElement</code>. This includes: * <ul> * <li>The sum of the child min values must be less than or equal to the parent max</li> * <li>The sum of the child max values must be greater than or equal to the parent min</li> * </ul> */ protected static List<ValidationResult<BlueprintElement>> validateBlueprintHierarchicalRelationships( final List<BlueprintElement> blueprintElementList) { final List<ValidationResult<BlueprintElement>> errors = Lists.newArrayList(); if (!CollectionUtils.isEmpty(blueprintElementList)) { final List<String> valueMapKeys = Lists .newArrayList(blueprintElementList.get(0).getBlueprintElementValueMap().keySet()); final Map<String, BlueprintElement> blueprintElementMap = Maps.uniqueIndex(blueprintElementList, BLUEPRINT_ELEMENT_UNIQUE_KEY_FUNCTION); final List<BlueprintElement> filteredChildrenBlueprintElementList = Lists .newArrayList(Iterables.filter(blueprintElementList, BLUEPRINT_ELEMENT_CHILD_FILTER)); final Map<String, Collection<BlueprintElement>> blueprintElementGroupedMap = Multimaps .index(filteredChildrenBlueprintElementList, BLUEPRINT_ELEMENT_PARENT_KEY_FUNCTION).asMap(); for (final Entry<String, Collection<BlueprintElement>> blueprintElementEntry : blueprintElementGroupedMap .entrySet()) { if (StringUtils.isNotBlank(blueprintElementEntry.getKey())) { final BlueprintElement parentBlueprintElement = blueprintElementMap .get(blueprintElementEntry.getKey()); if (parentBlueprintElement != null) { for (final String valueMapKey : valueMapKeys) { final List<BlueprintElementValue> bpValueList = Lists.transform( Lists.newArrayList(blueprintElementEntry.getValue()), BPE_BPEV_TRANSFORMER.getInstance(valueMapKey)); final BlueprintElementValue parentValue = parentBlueprintElement .getBlueprintElementValueMap().get(valueMapKey); final BlueprintElementValue childSummedValue = getActualListSum(bpValueList); if (childSummedValue.getOperationalItemMinValue() > parentValue .getOperationalItemMaxValue()) { errors.add(buildValidationResult(parentBlueprintElement, valueMapKey, OP_MAX_FIELD, String.format(PARENT_MESSAGE, "OP Max", parentValue.getOperationalItemMaxValue(), "less", "OP Min", childSummedValue.getOperationalItemMinValue()))); } if (childSummedValue.getOperationalItemMaxValue() < parentValue .getOperationalItemMinValue()) { errors.add(buildValidationResult(parentBlueprintElement, valueMapKey, OP_MIN_FIELD, String.format(PARENT_MESSAGE, "OP Min", parentValue.getOperationalItemMinValue(), "greater", "OP Max", childSummedValue.getOperationalItemMaxValue()))); } if (childSummedValue.getFieldTestItemMinValue() > parentValue .getFieldTestItemMaxValue()) { errors.add(buildValidationResult(parentBlueprintElement, valueMapKey, FT_MAX_FIELD, String.format(PARENT_MESSAGE, "FT Max", parentValue.getFieldTestItemMaxValue(), "less", "FT Min", childSummedValue.getFieldTestItemMinValue()))); } if (childSummedValue.getFieldTestItemMaxValue() < parentValue .getFieldTestItemMinValue()) { errors.add(buildValidationResult(parentBlueprintElement, valueMapKey, FT_MIN_FIELD, String.format(PARENT_MESSAGE, "FT Min", parentValue.getFieldTestItemMinValue(), "greater", "FT Max", childSummedValue.getFieldTestItemMaxValue()))); } } } } } } return errors; } /** * Handles active/inactive hierarchical validation for each individual <code>BlueprintElement</code>. This includes: * <ul> * <li>A child standard may not be ACTIVE if it's parent is INACTIVE</li> * </ul> */ protected static List<ValidationResult<BlueprintElement>> validateInactiveBlueprintHierarchicalRelationships( final List<BlueprintElement> blueprintElementList) { final List<ValidationResult<BlueprintElement>> errors = Lists.newArrayList(); if (!CollectionUtils.isEmpty(blueprintElementList)) { final Map<String, BlueprintElement> blueprintElementMap = Maps.uniqueIndex(blueprintElementList, BLUEPRINT_ELEMENT_UNIQUE_KEY_FUNCTION); final List<BlueprintElement> filteredChildrenBlueprintElementList = Lists .newArrayList(Iterables.filter(blueprintElementList, BLUEPRINT_ELEMENT_CHILD_FILTER)); final Map<String, Collection<BlueprintElement>> blueprintElementGroupedMap = Multimaps .index(filteredChildrenBlueprintElementList, BLUEPRINT_ELEMENT_PARENT_KEY_FUNCTION).asMap(); for (final Entry<String, Collection<BlueprintElement>> blueprintElementEntry : blueprintElementGroupedMap .entrySet()) { if (StringUtils.isNotBlank(blueprintElementEntry.getKey())) { final BlueprintElement parentBlueprintElement = blueprintElementMap .get(blueprintElementEntry.getKey()); if (parentBlueprintElement != null) { // validate parent/child inactive rules if (!parentBlueprintElement.isActive()) { for (final BlueprintElement childElement : blueprintElementGroupedMap .get(blueprintElementEntry.getKey())) { if (childElement.isActive()) { errors.add(buildValidationResult(childElement, MASTER_KEY, ACTIVE_FIELD, INACTIVE_MESSAGE)); } } } } } } } return errors; } /** * Handles top level blueprint vs. segment validation for each individual <code>BlueprintElement</code>. This includes: * <ul> * <li>The sum of the blueprint top-level min values must be less than or equal to the segment max</li> * <li>The sum of the blueprint top-level max values must be greater than or equal to the segment min</li> * </ul> */ protected static List<ValidationResult<BlueprintElement>> validateTopLevelBlueprintVsSegmentRelationships( final List<Segment> segments, final List<BlueprintElement> blueprintElementList) { final List<ValidationResult<BlueprintElement>> errors = Lists.newArrayList(); final List<BlueprintElement> topLevelBlueprintElements = Lists .newArrayList(Iterables.filter(blueprintElementList, BLUEPRINT_ELEMENT_TOP_LEVEL_FILTER)); for (final Segment segment : segments) { final List<BlueprintElementValue> topLevelBpValuesBySegment = Lists.newArrayList(Iterables.transform( Iterables.filter(topLevelBlueprintElements, BLUEPRINT_ELEMENT_SEGMENT_VALUE_FILTER.getInstance(segment.getId())), BLUEPRINT_ELEMENT_SEGMENT_VALUE_TRANSFORMER.getInstance(segment.getId()))); if (!topLevelBpValuesBySegment.isEmpty()) { final BlueprintElementValue segmentSumBpValue = BlueprintHelper .getListSum(topLevelBpValuesBySegment); if (segmentSumBpValue.getOperationalItemMinValue() > segment.getMaxOpItems()) { errors.add(buildValidationResult(null, segment.getId(), OP_MAX_FIELD, String.format(PARENT_SEGMENT_MESSAGE, "OP Max", segment.getMaxOpItems(), "less", "OP Min", segmentSumBpValue.getOperationalItemMinValue()))); } if (segmentSumBpValue.getOperationalItemMaxValue() < segment.getMinOpItems()) { errors.add(buildValidationResult(null, segment.getId(), OP_MIN_FIELD, String.format(PARENT_SEGMENT_MESSAGE, "OP Min", segment.getMinOpItems(), "greater", "OP Max", segmentSumBpValue.getOperationalItemMaxValue()))); } if (segmentSumBpValue.getFieldTestItemMinValue() > segment.getMaxFtItems()) { errors.add(buildValidationResult(null, segment.getId(), FT_MAX_FIELD, String.format(PARENT_SEGMENT_MESSAGE, "FT Max", segment.getMaxFtItems(), "less", "FT Min", segmentSumBpValue.getFieldTestItemMinValue()))); } if (segmentSumBpValue.getFieldTestItemMaxValue() < segment.getMinFtItems()) { errors.add(buildValidationResult(null, segment.getId(), FT_MIN_FIELD, String.format(PARENT_SEGMENT_MESSAGE, "FT Min", segment.getMinFtItems(), "greater", "FT Max", segmentSumBpValue.getFieldTestItemMaxValue()))); } } } return errors; } /** * Handles basic validation for each individual <code>BlueprintElementValue</code> in the blueprint list. This includes: * <ul> * <li>No min item value may be larger than the corresponding max item value</li> * </ul> */ protected static List<ValidationResult<BlueprintElement>> validateBlueprintElementValueItemRanges( final List<BlueprintElement> blueprintElementList) { return Lists.newArrayList(Iterables.concat( Iterables.transform(blueprintElementList, BLUEPRINT_ELEMENT_ITEM_RANGE_VALIDATION_TRANSFORMER))); } private static final Function<BlueprintElement, List<ValidationResult<BlueprintElement>>> BLUEPRINT_ELEMENT_ITEM_RANGE_VALIDATION_TRANSFORMER = new Function<BlueprintElement, List<ValidationResult<BlueprintElement>>>() { @Override public List<ValidationResult<BlueprintElement>> apply(final BlueprintElement blueprintElement) { final List<ValidationResult<BlueprintElement>> errors = Lists.newArrayList(); // validate individual blueprint element values for (final Entry<String, BlueprintElementValue> entry : blueprintElement.getBlueprintElementValueMap() .entrySet()) { if (entry.getValue().getOperationalItemMinValue() > entry.getValue().getOperationalItemMaxValue()) { errors.add(buildValidationResult(blueprintElement, entry.getKey(), OP_MIN_FIELD, "OP Min Value must be less than or equal to OP Max Value.")); } if (entry.getValue().getFieldTestItemMinValue() > entry.getValue().getFieldTestItemMaxValue()) { errors.add(buildValidationResult(blueprintElement, entry.getKey(), FT_MIN_FIELD, "FT Min Value must be less than or equal to FT Max Value.")); } } return errors; } }; private static final class BLUEPRINT_ELEMENT_SEGMENT_VALUE_FILTER implements Predicate<BlueprintElement> { private final String segmentId; public static BLUEPRINT_ELEMENT_SEGMENT_VALUE_FILTER getInstance(final String segmentId) { return new BLUEPRINT_ELEMENT_SEGMENT_VALUE_FILTER(segmentId); } private BLUEPRINT_ELEMENT_SEGMENT_VALUE_FILTER(final String segmentId) { this.segmentId = segmentId; } @Override public boolean apply(final BlueprintElement blueprintElement) { return blueprintElement != null && blueprintElement.getBlueprintElementValueMap() != null && blueprintElement.getBlueprintElementValueMap().get(this.segmentId) != null; } }; private static final class BLUEPRINT_ELEMENT_SEGMENT_VALUE_TRANSFORMER implements Function<BlueprintElement, BlueprintElementValue> { private final String segmentId; public static BLUEPRINT_ELEMENT_SEGMENT_VALUE_TRANSFORMER getInstance(final String segmentId) { return new BLUEPRINT_ELEMENT_SEGMENT_VALUE_TRANSFORMER(segmentId); } private BLUEPRINT_ELEMENT_SEGMENT_VALUE_TRANSFORMER(final String segmentId) { this.segmentId = segmentId; } @Override public BlueprintElementValue apply(final BlueprintElement blueprintElement) { return blueprintElement.getBlueprintElementValueMap().get(this.segmentId); } }; private static final Predicate<BlueprintElement> BLUEPRINT_ELEMENT_CHILD_FILTER = new Predicate<BlueprintElement>() { @Override public boolean apply(final BlueprintElement blueprintElement) { return blueprintElement != null && StringUtils.isNotBlank(blueprintElement.getParentKey()); } }; private static final Predicate<BlueprintElement> BLUEPRINT_ELEMENT_TOP_LEVEL_FILTER = new Predicate<BlueprintElement>() { @Override public boolean apply(final BlueprintElement blueprintElement) { return blueprintElement != null && StringUtils.isNotBlank(blueprintElement.getLevel()) && blueprintElement.getLevel().trim().equalsIgnoreCase(TOP_BLUEPRINT_ELEMENT_LEVEL); } }; private static final ValidationResult<BlueprintElement> buildValidationResult( final BlueprintElement blueprintElement, final String segmentId, final String fieldName, final String message) { final ValidationResult<BlueprintElement> result = new ValidationResult<BlueprintElement>(); result.setValidatedObject(blueprintElement); result.setSegmentId(segmentId); result.setFieldName(fieldName); result.setMessage(message); return result; } private static final BlueprintElementValue getActualListSum( final Collection<BlueprintElementValue> bpValueList) { final BlueprintElementValue sumValue = new BlueprintElementValue(0, 0, 0, 0); for (final BlueprintElementValue nextValue : bpValueList) { sumValue.setOperationalItemMinValue( sumValue.getOperationalItemMinValue() + safeParseInt(nextValue.getOperationalItemMinValue())); sumValue.setOperationalItemMaxValue( sumValue.getOperationalItemMaxValue() + safeParseInt(nextValue.getOperationalItemMaxValue())); sumValue.setFieldTestItemMinValue( sumValue.getFieldTestItemMinValue() + safeParseInt(nextValue.getFieldTestItemMinValue())); sumValue.setFieldTestItemMaxValue( sumValue.getFieldTestItemMaxValue() + safeParseInt(nextValue.getFieldTestItemMaxValue())); } return sumValue; } }