001/** 002 * Copyright 2005-2016 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.uif.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 * This object processes 'case constraints', which are constraints that are imposed only in specific cases, for 039 * example, 040 * when a value is 041 * equal to some constant, or greater than some limit. 042 * 043 * @author Kuali Rice Team (rice.collab@kuali.org) 044 */ 045public class CaseConstraintProcessor extends MandatoryElementConstraintProcessor<CaseConstraint> { 046 047 private static final String CONSTRAINT_NAME = "case constraint"; 048 049 /** 050 * @see org.kuali.rice.krad.datadictionary.validation.processor.ConstraintProcessor#process(DictionaryValidationResult, 051 * Object, org.kuali.rice.krad.datadictionary.validation.capability.Constrainable, 052 * org.kuali.rice.krad.datadictionary.validation.AttributeValueReader) 053 */ 054 @Override 055 public ProcessorResult process(DictionaryValidationResult result, Object value, CaseConstraint caseConstraint, 056 AttributeValueReader attributeValueReader) throws AttributeValidationException { 057 058 // Don't process this constraint if it's null 059 if (null == caseConstraint) { 060 return new ProcessorResult(result.addNoConstraint(attributeValueReader, CONSTRAINT_NAME)); 061 } 062 AttributeValueReader constraintAttributeReader = attributeValueReader.clone(); 063 064 String operator = (ValidationUtils.hasText(caseConstraint.getOperator())) ? caseConstraint.getOperator() : 065 "EQUALS"; 066 AttributeValueReader fieldPathReader = (ValidationUtils.hasText(caseConstraint.getPropertyName())) ? 067 getChildAttributeValueReader(caseConstraint.getPropertyName(), attributeValueReader) : 068 attributeValueReader; 069 070 Constrainable caseField = (null != fieldPathReader) ? fieldPathReader.getDefinition( 071 fieldPathReader.getAttributeName()) : null; 072 Object fieldValue = (null != fieldPathReader) ? fieldPathReader.getValue(fieldPathReader.getAttributeName()) : 073 value; 074 DataType fieldDataType = (null != caseField && caseField instanceof DataTypeConstraint) ? 075 ((DataTypeConstraint) caseField).getDataType() : null; 076 077 // Default to a string comparison 078 if (fieldDataType == null) { 079 fieldDataType = DataType.STRING; 080 } 081 082 // If fieldValue is null then skip Case check 083 if (null == fieldValue) { 084 // FIXME: not sure if the definition and attribute value reader should change under this case 085 return new ProcessorResult(result.addSkipped(attributeValueReader, CONSTRAINT_NAME), caseField, 086 fieldPathReader); 087 } 088 089 List<Constraint> constraints = new ArrayList<Constraint>(); 090 // Extract value for field Key 091 for (WhenConstraint wc : caseConstraint.getWhenConstraint()) { 092 evaluateWhenConstraint(fieldValue, fieldDataType, operator, caseConstraint, wc, attributeValueReader, 093 constraints); 094 } 095 if (!constraints.isEmpty()) { 096 return new ProcessorResult(result.addSuccess(attributeValueReader, CONSTRAINT_NAME), null, 097 constraintAttributeReader, constraints); 098 } 099 100 // Assuming that not finding any case constraints is equivalent to 'skipping' the constraint 101 return new ProcessorResult(result.addSkipped(attributeValueReader, CONSTRAINT_NAME)); 102 } 103 104 private void evaluateWhenConstraint(Object fieldValue, DataType fieldDataType, String operator, 105 CaseConstraint caseConstraint, WhenConstraint wc, AttributeValueReader attributeValueReader, 106 List<Constraint> constraints) { 107 if (ValidationUtils.hasText(wc.getValuePath())) { 108 Object whenValue = null; 109 110 AttributeValueReader whenValueReader = getChildAttributeValueReader(wc.getValuePath(), 111 attributeValueReader); 112 whenValue = whenValueReader.getValue(whenValueReader.getAttributeName()); 113 114 if (ValidationUtils.compareValues(fieldValue, whenValue, fieldDataType, operator, 115 caseConstraint.isCaseSensitive(), dateTimeService) && null != wc.getConstraint()) { 116 constraints.add(wc.getConstraint()); 117 } 118 } else { 119 List<Object> whenValueList = wc.getValues(); 120 121 for (Object whenValue : whenValueList) { 122 if (ValidationUtils.compareValues(fieldValue, whenValue, fieldDataType, operator, 123 caseConstraint.isCaseSensitive(), dateTimeService) && null != wc.getConstraint()) { 124 constraints.add(wc.getConstraint()); 125 break; 126 } 127 } 128 } 129 } 130 131 @Override 132 public String getName() { 133 return CONSTRAINT_NAME; 134 } 135 136 /** 137 * @see org.kuali.rice.krad.datadictionary.validation.processor.ConstraintProcessor#getConstraintType() 138 */ 139 @Override 140 public Class<? extends Constraint> getConstraintType() { 141 return CaseConstraint.class; 142 } 143 144 private AttributeValueReader getChildAttributeValueReader(String key, 145 AttributeValueReader attributeValueReader) throws AttributeValidationException { 146 String[] lookupPathTokens = ValidationUtils.getPathTokens(key); 147 148 AttributeValueReader localAttributeValueReader = attributeValueReader; 149 for (int i = 0; i < lookupPathTokens.length; i++) { 150 for (Constrainable definition : localAttributeValueReader.getDefinitions()) { 151 String attributeName = definition.getName(); 152 if (attributeName.equals(lookupPathTokens[i])) { 153 if (i == lookupPathTokens.length - 1) { 154 localAttributeValueReader.setAttributeName(attributeName); 155 return localAttributeValueReader; 156 } 157 if (definition instanceof HierarchicallyConstrainable) { 158 String childEntryName = ((HierarchicallyConstrainable) definition).getChildEntryName(); 159 DataDictionaryEntry entry = KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary() 160 .getDictionaryObjectEntry(childEntryName); 161 Object value = attributeValueReader.getValue(attributeName); 162 attributeValueReader.setAttributeName(attributeName); 163 String attributePath = attributeValueReader.getPath(); 164 localAttributeValueReader = new DictionaryObjectAttributeValueReader(value, childEntryName, 165 entry, attributePath); 166 } 167 break; 168 } 169 } 170 } 171 return null; 172 } 173 174}