View Javadoc

1   /**
2    * Copyright 2005-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.datadictionary.validation.processor;
17  
18  import org.kuali.rice.core.api.uif.DataType;
19  import org.kuali.rice.krad.datadictionary.DataDictionaryEntry;
20  import org.kuali.rice.krad.datadictionary.exception.AttributeValidationException;
21  import org.kuali.rice.krad.datadictionary.validation.AttributeValueReader;
22  import org.kuali.rice.krad.datadictionary.validation.DictionaryObjectAttributeValueReader;
23  import org.kuali.rice.krad.datadictionary.validation.ValidationUtils;
24  import org.kuali.rice.krad.datadictionary.validation.capability.Constrainable;
25  import org.kuali.rice.krad.datadictionary.validation.capability.HierarchicallyConstrainable;
26  import org.kuali.rice.krad.datadictionary.validation.constraint.CaseConstraint;
27  import org.kuali.rice.krad.datadictionary.validation.constraint.Constraint;
28  import org.kuali.rice.krad.datadictionary.validation.constraint.DataTypeConstraint;
29  import org.kuali.rice.krad.datadictionary.validation.constraint.WhenConstraint;
30  import org.kuali.rice.krad.datadictionary.validation.result.DictionaryValidationResult;
31  import org.kuali.rice.krad.datadictionary.validation.result.ProcessorResult;
32  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
33  
34  import java.util.ArrayList;
35  import java.util.List;
36  
37  /**
38   * CaseConstraintProcessor processes 'case constraints', which are constraints that are imposed only in specific cases
39   *
40   * <p>For example, when a value is equal to some constant, or greater than some limit.</p>
41   *
42   * @author Kuali Rice Team (rice.collab@kuali.org)
43   */
44  public class CaseConstraintProcessor extends MandatoryElementConstraintProcessor<CaseConstraint> {
45  
46      private static final String CONSTRAINT_NAME = "case constraint";
47  
48      /**
49       * @see org.kuali.rice.krad.datadictionary.validation.processor.ConstraintProcessor#process(org.kuali.rice.krad.datadictionary.validation.result.DictionaryValidationResult,
50       *      Object, org.kuali.rice.krad.datadictionary.validation.constraint.Constraint,
51       *      org.kuali.rice.krad.datadictionary.validation.AttributeValueReader)
52       */
53      @Override
54      public ProcessorResult process(DictionaryValidationResult result, Object value, CaseConstraint caseConstraint,
55              AttributeValueReader attributeValueReader) throws AttributeValidationException {
56  
57          // Don't process this constraint if it's null
58          if (null == caseConstraint) {
59              return new ProcessorResult(result.addNoConstraint(attributeValueReader, CONSTRAINT_NAME));
60          }
61          AttributeValueReader constraintAttributeReader = attributeValueReader.clone();
62  
63          String operator = (ValidationUtils.hasText(caseConstraint.getOperator())) ? caseConstraint.getOperator() :
64                  "EQUALS";
65          AttributeValueReader fieldPathReader = (ValidationUtils.hasText(caseConstraint.getPropertyName())) ?
66                  getChildAttributeValueReader(caseConstraint.getPropertyName(), attributeValueReader) :
67                  attributeValueReader;
68  
69          Constrainable caseField = (null != fieldPathReader) ? fieldPathReader.getDefinition(
70                  fieldPathReader.getAttributeName()) : null;
71          Object fieldValue = (null != fieldPathReader) ? fieldPathReader.getValue(fieldPathReader.getAttributeName()) :
72                  value;
73          DataType fieldDataType = (null != caseField && caseField instanceof DataTypeConstraint) ?
74                  ((DataTypeConstraint) caseField).getDataType() : null;
75  
76          // Default to a string comparison
77          if (fieldDataType == null) {
78              fieldDataType = DataType.STRING;
79          }
80  
81          // If fieldValue is null then skip Case check
82          if (null == fieldValue) {
83              // FIXME: not sure if the definition and attribute value reader should change under this case
84              return new ProcessorResult(result.addSkipped(attributeValueReader, CONSTRAINT_NAME), caseField,
85                      fieldPathReader);
86          }
87  
88          List<Constraint> constraints = new ArrayList<Constraint>();
89          // Extract value for field Key
90          for (WhenConstraint wc : caseConstraint.getWhenConstraint()) {
91              evaluateWhenConstraint(fieldValue, fieldDataType, operator, caseConstraint, wc, attributeValueReader,
92                      constraints);
93          }
94          if (!constraints.isEmpty()) {
95              return new ProcessorResult(result.addSuccess(attributeValueReader, CONSTRAINT_NAME), null,
96                      constraintAttributeReader, constraints);
97          }
98  
99          // 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 }