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 java.util.ArrayList; 019import java.util.List; 020 021import org.kuali.rice.core.api.data.DataType; 022import org.kuali.rice.krad.datadictionary.DataDictionaryEntry; 023import org.kuali.rice.krad.datadictionary.exception.AttributeValidationException; 024import org.kuali.rice.krad.datadictionary.validation.AttributeValueReader; 025import org.kuali.rice.krad.datadictionary.validation.DictionaryObjectAttributeValueReader; 026import org.kuali.rice.krad.datadictionary.validation.ValidationUtils; 027import org.kuali.rice.krad.datadictionary.validation.capability.Constrainable; 028import org.kuali.rice.krad.datadictionary.validation.capability.HierarchicallyConstrainable; 029import org.kuali.rice.krad.datadictionary.validation.constraint.CaseConstraint; 030import org.kuali.rice.krad.datadictionary.validation.constraint.Constraint; 031import org.kuali.rice.krad.datadictionary.validation.constraint.DataTypeConstraint; 032import org.kuali.rice.krad.datadictionary.validation.constraint.WhenConstraint; 033import org.kuali.rice.krad.datadictionary.validation.result.DictionaryValidationResult; 034import org.kuali.rice.krad.datadictionary.validation.result.ProcessorResult; 035import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 036 037/** 038 * CaseConstraintProcessor processes 'case constraints', which are constraints that are imposed only in specific cases 039 * 040 * <p>For example, when a value is equal to some constant, or greater than some limit.</p> 041 * 042 * @author Kuali Rice Team (rice.collab@kuali.org) 043 */ 044public class CaseConstraintProcessor extends MandatoryElementConstraintProcessor<CaseConstraint> { 045 046 private static final String CONSTRAINT_NAME = "case constraint"; 047 048 /** 049 * @see org.kuali.rice.krad.datadictionary.validation.processor.ConstraintProcessor#process(org.kuali.rice.krad.datadictionary.validation.result.DictionaryValidationResult, 050 * Object, org.kuali.rice.krad.datadictionary.validation.constraint.Constraint, 051 * org.kuali.rice.krad.datadictionary.validation.AttributeValueReader) 052 */ 053 @Override 054 public ProcessorResult process(DictionaryValidationResult result, Object value, CaseConstraint caseConstraint, 055 AttributeValueReader attributeValueReader) throws AttributeValidationException { 056 057 // Don't process this constraint if it's null 058 if (null == caseConstraint) { 059 return new ProcessorResult(result.addNoConstraint(attributeValueReader, CONSTRAINT_NAME)); 060 } 061 AttributeValueReader constraintAttributeReader = attributeValueReader.clone(); 062 063 String operator = (ValidationUtils.hasText(caseConstraint.getOperator())) ? caseConstraint.getOperator() : 064 "EQUALS"; 065 AttributeValueReader fieldPathReader = (ValidationUtils.hasText(caseConstraint.getPropertyName())) ? 066 getChildAttributeValueReader(caseConstraint.getPropertyName(), attributeValueReader) : 067 attributeValueReader; 068 069 Constrainable caseField = (null != fieldPathReader) ? fieldPathReader.getDefinition( 070 fieldPathReader.getAttributeName()) : null; 071 Object fieldValue = (null != fieldPathReader) ? fieldPathReader.getValue(fieldPathReader.getAttributeName()) : 072 value; 073 DataType fieldDataType = (null != caseField && caseField instanceof DataTypeConstraint) ? 074 ((DataTypeConstraint) caseField).getDataType() : null; 075 076 // Default to a string comparison 077 if (fieldDataType == null) { 078 fieldDataType = DataType.STRING; 079 } 080 081 // If fieldValue is null then skip Case check 082 if (null == fieldValue) { 083 // FIXME: not sure if the definition and attribute value reader should change under this case 084 return new ProcessorResult(result.addSkipped(attributeValueReader, CONSTRAINT_NAME), caseField, 085 fieldPathReader); 086 } 087 088 List<Constraint> constraints = new ArrayList<Constraint>(); 089 // Extract value for field Key 090 for (WhenConstraint wc : caseConstraint.getWhenConstraint()) { 091 evaluateWhenConstraint(fieldValue, fieldDataType, operator, caseConstraint, wc, attributeValueReader, 092 constraints); 093 } 094 if (!constraints.isEmpty()) { 095 return new ProcessorResult(result.addSuccess(attributeValueReader, CONSTRAINT_NAME), null, 096 constraintAttributeReader, constraints); 097 } 098 099 // Assuming that not finding any case constraints is equivalent to 'skipping' the constraint 100 return new ProcessorResult(result.addSkipped(attributeValueReader, CONSTRAINT_NAME)); 101 } 102 103 /** 104 * evaluates the provided {@link WhenConstraint} 105 * 106 * @param fieldValue - the value of the field 107 * @param fieldDataType - the data type of the field which caseConstraint's propertyName refers to 108 * @param operator - the relationship to check between the fieldValue and the value provided in the whenConstraint 109 * @param caseConstraint - the case constraint containing the provided whenConstraint 110 * @param wc - the whenConstraint to evaluate 111 * @param attributeValueReader - provides access to the attribute being validated 112 * @param constraints - the constraints to populate as discovered in the provided whenConstraint 113 */ 114 private void evaluateWhenConstraint(Object fieldValue, DataType fieldDataType, String operator, 115 CaseConstraint caseConstraint, WhenConstraint wc, AttributeValueReader attributeValueReader, 116 List<Constraint> constraints) { 117 if (ValidationUtils.hasText(wc.getValuePath())) { 118 Object whenValue = null; 119 120 //String originalName = attributeValueReader.getAttributeName(); 121 AttributeValueReader whenValueReader = getChildAttributeValueReader(wc.getValuePath(), 122 attributeValueReader); 123 whenValue = whenValueReader.getValue(whenValueReader.getAttributeName()); 124 125 if (ValidationUtils.compareValues(fieldValue, whenValue, fieldDataType, operator, 126 caseConstraint.isCaseSensitive(), dateTimeService) && null != wc.getConstraint()) { 127 constraints.add(wc.getConstraint()); 128 } 129 //whenValueReader.setAttributeName(originalName); 130 } else { 131 List<Object> whenValueList = wc.getValues(); 132 133 for (Object whenValue : whenValueList) { 134 if (ValidationUtils.compareValues(fieldValue, whenValue, fieldDataType, operator, 135 caseConstraint.isCaseSensitive(), dateTimeService) && null != wc.getConstraint()) { 136 constraints.add(wc.getConstraint()); 137 break; 138 } 139 } 140 } 141 } 142 143 @Override 144 public String getName() { 145 return CONSTRAINT_NAME; 146 } 147 148 /** 149 * @see org.kuali.rice.krad.datadictionary.validation.processor.ConstraintProcessor#getConstraintType() 150 */ 151 @Override 152 public Class<? extends Constraint> getConstraintType() { 153 return CaseConstraint.class; 154 } 155 156 /** 157 * provides access to the attribute specified in a whenConstraint 158 * 159 * @param key - a string representation of specifically which attribute (at some depth) is being accessed 160 * @param attributeValueReader - provides access to the attribute being validated 161 * @return an attribute value reader for the path represented by the key param 162 * @throws AttributeValidationException 163 */ 164 private AttributeValueReader getChildAttributeValueReader(String key, 165 AttributeValueReader attributeValueReader) throws AttributeValidationException { 166 String[] lookupPathTokens = ValidationUtils.getPathTokens(key); 167 168 AttributeValueReader localAttributeValueReader = attributeValueReader.clone(); 169 for (int i = 0; i < lookupPathTokens.length; i++) { 170 for (Constrainable definition : localAttributeValueReader.getDefinitions()) { 171 String attributeName = definition.getName(); 172 if (attributeName.equals(lookupPathTokens[i])) { 173 if (i == lookupPathTokens.length - 1) { 174 localAttributeValueReader.setAttributeName(attributeName); 175 return localAttributeValueReader; 176 } 177 if (definition instanceof HierarchicallyConstrainable) { 178 String childEntryName = ((HierarchicallyConstrainable) definition).getChildEntryName(); 179 DataDictionaryEntry entry = KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary() 180 .getDictionaryObjectEntry(childEntryName); 181 Object value = attributeValueReader.getValue(attributeName); 182 attributeValueReader.setAttributeName(attributeName); 183 String attributePath = attributeValueReader.getPath(); 184 localAttributeValueReader = new DictionaryObjectAttributeValueReader(value, childEntryName, 185 entry, attributePath); 186 } 187 break; 188 } 189 } 190 } 191 return null; 192 } 193 194}