001/** 002 * Copyright 2005-2018 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.krad.datadictionary.validation.processor; 017 018import org.kuali.rice.core.api.util.RiceKeyConstants; 019import org.kuali.rice.krad.datadictionary.exception.AttributeValidationException; 020import org.kuali.rice.krad.datadictionary.validation.AttributeValueReader; 021import org.kuali.rice.krad.datadictionary.validation.ValidationUtils; 022import org.kuali.rice.krad.datadictionary.validation.ValidationUtils.Result; 023import org.kuali.rice.krad.datadictionary.validation.constraint.CollectionSizeConstraint; 024import org.kuali.rice.krad.datadictionary.validation.constraint.Constraint; 025import org.kuali.rice.krad.datadictionary.validation.result.ConstraintValidationResult; 026import org.kuali.rice.krad.datadictionary.validation.result.DictionaryValidationResult; 027import org.kuali.rice.krad.datadictionary.validation.result.ProcessorResult; 028 029import java.util.Collection; 030 031/** 032 * This class validates attributes that are collection size constrained - ones that can only have between x and y 033 * number 034 * 035 * @author Kuali Rice Team (rice.collab@kuali.org) 036 */ 037public class CollectionSizeConstraintProcessor implements CollectionConstraintProcessor<Collection<?>, CollectionSizeConstraint> { 038 039 private static final String CONSTRAINT_NAME = "collection size constraint"; 040 041 /** 042 * @see org.kuali.rice.krad.datadictionary.validation.processor.ConstraintProcessor#process(org.kuali.rice.krad.datadictionary.validation.result.DictionaryValidationResult, 043 * Object, org.kuali.rice.krad.datadictionary.validation.constraint.Constraint, 044 * org.kuali.rice.krad.datadictionary.validation.AttributeValueReader) 045 */ 046 @Override 047 public ProcessorResult process(DictionaryValidationResult result, Collection<?> collection, 048 CollectionSizeConstraint constraint, 049 AttributeValueReader attributeValueReader) throws AttributeValidationException { 050 051 // To accommodate the needs of other processors, the ConstraintProcessor.process() method returns a list of ConstraintValidationResult objects 052 // but since a definition that is collection size constrained only provides a single max and minimum, there is effectively a single constraint 053 // being imposed. 054 return new ProcessorResult(processSingleCollectionSizeConstraint(result, collection, constraint, 055 attributeValueReader)); 056 } 057 058 @Override 059 public String getName() { 060 return CONSTRAINT_NAME; 061 } 062 063 /** 064 * @see org.kuali.rice.krad.datadictionary.validation.processor.ConstraintProcessor#getConstraintType() 065 */ 066 @Override 067 public Class<? extends Constraint> getConstraintType() { 068 return CollectionSizeConstraint.class; 069 } 070 071 /** 072 * @see org.kuali.rice.krad.datadictionary.validation.processor.ConstraintProcessor#isOptional() 073 */ 074 @Override 075 public boolean isOptional() { 076 return false; 077 } 078 079 protected ConstraintValidationResult processSingleCollectionSizeConstraint(DictionaryValidationResult result, 080 Collection<?> collection, CollectionSizeConstraint constraint, 081 AttributeValueReader attributeValueReader) throws AttributeValidationException { 082 Integer sizeOfCollection = new Integer(0); 083 if (collection != null) { 084 sizeOfCollection = Integer.valueOf(collection.size()); 085 } 086 087 Integer maxOccurances = constraint.getMaximumNumberOfElements(); 088 Integer minOccurances = constraint.getMinimumNumberOfElements(); 089 090 Result lessThanMax = ValidationUtils.isLessThanOrEqual(sizeOfCollection, maxOccurances); 091 Result greaterThanMin = ValidationUtils.isGreaterThanOrEqual(sizeOfCollection, minOccurances); 092 093 // It's okay for one end of the range to be undefined - that's not an error. It's only an error if one of them is invalid 094 if (lessThanMax != Result.INVALID && greaterThanMin != Result.INVALID) { 095 // Of course, if they're both undefined then we didn't actually have a real constraint 096 if (lessThanMax == Result.UNDEFINED && greaterThanMin == Result.UNDEFINED) { 097 return result.addNoConstraint(attributeValueReader, CONSTRAINT_NAME); 098 } 099 100 // In this case, we've succeeded 101 return result.addSuccess(attributeValueReader, CONSTRAINT_NAME); 102 } 103 104 String maxErrorParameter = maxOccurances != null ? maxOccurances.toString() : null; 105 String minErrorParameter = minOccurances != null ? minOccurances.toString() : null; 106 107 // If both comparisons happened then if either comparison failed we can show the end user the expected range on both sides. 108 if (lessThanMax != Result.UNDEFINED && greaterThanMin != Result.UNDEFINED) { 109 return result.addError(attributeValueReader, CONSTRAINT_NAME, RiceKeyConstants.ERROR_QUANTITY_RANGE, 110 minErrorParameter, maxErrorParameter); 111 } 112 // If it's the max comparison that fails, then just tell the end user what the max can be 113 else if (lessThanMax == Result.INVALID) { 114 return result.addError(attributeValueReader, CONSTRAINT_NAME, RiceKeyConstants.ERROR_MAX_OCCURS, 115 maxErrorParameter); 116 } 117 // Otherwise, just tell them what the min can be 118 else { 119 return result.addError(attributeValueReader, CONSTRAINT_NAME, RiceKeyConstants.ERROR_MIN_OCCURS, 120 minErrorParameter); 121 } 122 123 // Obviously the last else above is unnecessary, since anything after it is dead code, but keeping it seems clearer than dropping it 124 } 125 126}