001 /**
002 * Copyright 2005-2014 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 */
016 package org.kuali.rice.krad.datadictionary.validation.result;
017
018 import org.kuali.rice.krad.datadictionary.validation.AttributeValueReader;
019 import org.kuali.rice.krad.datadictionary.validation.ErrorLevel;
020 import org.kuali.rice.krad.datadictionary.validation.ValidationUtils;
021
022 import java.util.Iterator;
023 import java.util.LinkedHashMap;
024 import java.util.Map;
025
026 /**
027 * DictionaryValidationResult holds dictionary validation results
028 *
029 * @author Kuali Rice Team (rice.collab@kuali.org)
030 */
031 public class DictionaryValidationResult implements Iterable<ConstraintValidationResult> {
032
033 private Map<String, EntryValidationResult> entryValidationResultMap;
034 private ErrorLevel errorLevel;
035
036 private int numberOfErrors;
037 private int numberOfWarnings;
038
039 private Iterator<ConstraintValidationResult> iterator;
040
041 /**
042 * default constructor
043 */
044 public DictionaryValidationResult() {
045 this.entryValidationResultMap = new LinkedHashMap<String, EntryValidationResult>();
046 this.errorLevel = ErrorLevel.ERROR;
047 this.numberOfErrors = 0;
048 this.numberOfWarnings = 0;
049 }
050
051 /**
052 * adds the result of a constraint validation performed on an attribute
053 *
054 * @param attributeValueReader - provides access to the attribute being validated
055 * @param constraintValidationResult - the result of processing a constraint
056 */
057 public void addConstraintValidationResult(AttributeValueReader attributeValueReader,
058 ConstraintValidationResult constraintValidationResult) {
059
060 // Don't bother to store this if the error level of the constraint validation result is lower than the level this dictionary validation result is tracking
061 if (constraintValidationResult.getStatus().getLevel() < errorLevel.getLevel()) {
062 return;
063 }
064
065 switch (constraintValidationResult.getStatus()) {
066 case ERROR:
067 numberOfErrors++;
068 break;
069 case WARN:
070 numberOfWarnings++;
071 break;
072 default:
073 // Do nothing
074 }
075
076 // Give the constraint a chance to override the entry and attribute name - important if the attribute name is not the same as the one in the attribute value reader!
077 String entryName = constraintValidationResult.getEntryName();
078 String attributeName = constraintValidationResult.getAttributeName();
079 String attributePath = constraintValidationResult.getAttributePath();
080
081 if (entryName == null) {
082 entryName = attributeValueReader.getEntryName();
083 }
084
085 if (attributeName == null) {
086 attributeName = attributeValueReader.getAttributeName();
087 }
088
089 if (attributePath == null) {
090 attributePath = attributeValueReader.getPath();
091 }
092
093 constraintValidationResult.setEntryName(entryName);
094 constraintValidationResult.setAttributeName(attributeName);
095 constraintValidationResult.setAttributePath(attributePath);
096
097 String entryKey = getEntryValidationResultKey(entryName, attributePath);
098 getEntryValidationResult(entryKey).getAttributeValidationResult(attributeName).addConstraintValidationResult(
099 constraintValidationResult);
100 }
101
102 /**
103 * provides information used to display error messages to the user concerning a constraint validation
104 *
105 * @param attributeValueReader - provides access to the attribute being validated
106 * @param constraintName - a descriptive name of the current constraint processor
107 * @param errorKey - a key used to fetch an informational message to show the user
108 * @param errorParameters - parameters to substitute into the informational message
109 * @return a constraint validation result encompassing the information provided
110 */
111 public ConstraintValidationResult addError(AttributeValueReader attributeValueReader, String constraintName,
112 String errorKey, String... errorParameters) {
113 ConstraintValidationResult constraintValidationResult = getConstraintValidationResult(
114 attributeValueReader.getEntryName(), attributeValueReader.getAttributeName(),
115 attributeValueReader.getPath(), constraintName);
116 constraintValidationResult.setError(errorKey, errorParameters);
117 numberOfErrors++;
118 return constraintValidationResult;
119 }
120
121 /**
122 * provides information used to display error messages to the user concerning a constraint validation
123 *
124 * @param constraintLabelKey - a key used to fetch an information message to show the user
125 * @param attributeValueReader - provides access to the attribute being validated
126 * @param constraintName - a descriptive name of the current constraint processor
127 * @param errorKey - a key used to fetch an error message to show the user
128 * @param errorParameters - parameters to substitute into the error message
129 * @return a constraint validation result encompassing the information provided
130 */
131 public ConstraintValidationResult addError(String constraintLabelKey, AttributeValueReader attributeValueReader,
132 String constraintName, String errorKey, String... errorParameters) {
133 ConstraintValidationResult constraintValidationResult = getConstraintValidationResult(
134 attributeValueReader.getEntryName(), attributeValueReader.getAttributeName(),
135 attributeValueReader.getPath(), constraintName);
136 constraintValidationResult.setError(errorKey, errorParameters);
137 constraintValidationResult.setConstraintLabelKey(constraintLabelKey);
138 numberOfErrors++;
139 return constraintValidationResult;
140 }
141
142 /**
143 * provides information used to display warning messages to the user concerning a constraint validation
144 *
145 * @param attributeValueReader - provides access to the attribute being validated
146 * @param constraintName - a descriptive name of the current constraint processor
147 * @param errorKey - a key used to fetch a warning message to show the user
148 * @param errorParameters - parameters to substitute into the warning message
149 * @return a constraint validation result encompassing the information provided
150 */
151 public ConstraintValidationResult addWarning(AttributeValueReader attributeValueReader, String constraintName,
152 String errorKey, String... errorParameters) {
153 if (errorLevel.getLevel() > ErrorLevel.WARN.getLevel()) {
154 return new ConstraintValidationResult(constraintName, ErrorLevel.WARN);
155 }
156
157 ConstraintValidationResult constraintValidationResult = getConstraintValidationResult(
158 attributeValueReader.getEntryName(), attributeValueReader.getAttributeName(),
159 attributeValueReader.getPath(), constraintName);
160 constraintValidationResult.setWarning(errorKey, errorParameters);
161 numberOfWarnings++;
162 return constraintValidationResult;
163 }
164
165 /**
166 * indicates that a constraint validation has succeeded
167 *
168 * @param attributeValueReader - provides access to the attribute being validated
169 * @param constraintName - a descriptive name of the current constraint processor
170 * @return a constraint validation result encompassing the information provided
171 */
172 public ConstraintValidationResult addSuccess(AttributeValueReader attributeValueReader, String constraintName) {
173 if (errorLevel.getLevel() > ErrorLevel.OK.getLevel()) {
174 return new ConstraintValidationResult(constraintName, ErrorLevel.OK);
175 }
176
177 return getConstraintValidationResult(attributeValueReader.getEntryName(),
178 attributeValueReader.getAttributeName(), attributeValueReader.getPath(), constraintName);
179 }
180
181 /**
182 * indicates that a constraint validation has been skipped
183 *
184 * @param attributeValueReader - provides access to the attribute being validated
185 * @param constraintName - a descriptive name of the current constraint processor
186 * @return a constraint validation result encompassing the information provided
187 */
188 public ConstraintValidationResult addSkipped(AttributeValueReader attributeValueReader, String constraintName) {
189 if (errorLevel.getLevel() > ErrorLevel.OK.getLevel()) {
190 return new ConstraintValidationResult(constraintName, ErrorLevel.INAPPLICABLE);
191 }
192
193 ConstraintValidationResult constraintValidationResult = getConstraintValidationResult(
194 attributeValueReader.getEntryName(), attributeValueReader.getAttributeName(),
195 attributeValueReader.getPath(), constraintName);
196 constraintValidationResult.setStatus(ErrorLevel.INAPPLICABLE);
197 return constraintValidationResult;
198 }
199
200 /**
201 * indicates that a constraint validation processing has been skipped
202 *
203 * @param attributeValueReader - provides access to the attribute being validated
204 * @param constraintName - a descriptive name of the current constraint processor
205 * @return a constraint validation result encompassing the information provided
206 */
207 public ConstraintValidationResult addNoConstraint(AttributeValueReader attributeValueReader,
208 String constraintName) {
209 if (errorLevel.getLevel() > ErrorLevel.OK.getLevel()) {
210 return new ConstraintValidationResult(constraintName, ErrorLevel.NOCONSTRAINT);
211 }
212
213 ConstraintValidationResult constraintValidationResult = getConstraintValidationResult(
214 attributeValueReader.getEntryName(), attributeValueReader.getAttributeName(),
215 attributeValueReader.getPath(), constraintName);
216 constraintValidationResult.setStatus(ErrorLevel.NOCONSTRAINT);
217 return constraintValidationResult;
218 }
219
220 /**
221 * gets an iterator over the various {@code ConstraintValidationResult}'s contained in this class
222 *
223 * @return an iterator
224 */
225 public Iterator<ConstraintValidationResult> iterator() {
226
227 iterator = new Iterator<ConstraintValidationResult>() {
228
229 private Iterator<EntryValidationResult> entryIterator;
230 private Iterator<AttributeValidationResult> attributeIterator;
231 private Iterator<ConstraintValidationResult> constraintIterator;
232
233 @Override
234 public boolean hasNext() {
235 Iterator<ConstraintValidationResult> currentConstraintIterator = getCurrentConstraintIterator();
236 return currentConstraintIterator != null && currentConstraintIterator.hasNext();
237 }
238
239 @Override
240 public ConstraintValidationResult next() {
241 Iterator<ConstraintValidationResult> currentConstraintIterator = getCurrentConstraintIterator();
242 return currentConstraintIterator != null ? currentConstraintIterator.next() : null;
243 }
244
245 @Override
246 public void remove() {
247 throw new RuntimeException("Can't remove from this iterator!");
248 }
249
250 private Iterator<ConstraintValidationResult> getCurrentConstraintIterator() {
251 if (constraintIterator == null || constraintIterator.hasNext() == false) {
252 Iterator<AttributeValidationResult> currentAttributeIterator = getCurrentAttributeIterator();
253 if (currentAttributeIterator != null && currentAttributeIterator.hasNext()) {
254 AttributeValidationResult currentAttributeValidationResult = currentAttributeIterator.next();
255 constraintIterator = currentAttributeValidationResult.iterator();
256 }
257 }
258 return constraintIterator;
259 }
260
261 private Iterator<AttributeValidationResult> getCurrentAttributeIterator() {
262 if (attributeIterator == null || attributeIterator.hasNext() == false) {
263 Iterator<EntryValidationResult> currentEntryIterator = getCurrentEntryIterator();
264 if (currentEntryIterator != null && currentEntryIterator.hasNext()) {
265 EntryValidationResult currentEntryValidationResult = currentEntryIterator.next();
266 attributeIterator = currentEntryValidationResult.iterator();
267 }
268 }
269 return attributeIterator;
270 }
271
272 private Iterator<EntryValidationResult> getCurrentEntryIterator() {
273 if (entryIterator == null) // || entryIterator.hasNext() == false)
274 {
275 entryIterator = entryValidationResultMap.values().iterator();
276 }
277 return entryIterator;
278 }
279
280 };
281
282 return iterator;
283 }
284
285 /**
286 * gets an entry validation result for the given {@code entryName}
287 *
288 * @param entryName - the name that the data dictionary uses to store metadata about the attribute
289 * @return the existing {@code EntryValidationResult} for the given {@code entryName} or creates a new one if
290 * absent
291 */
292 protected EntryValidationResult getEntryValidationResult(String entryName) {
293 EntryValidationResult entryValidationResult = entryValidationResultMap.get(entryName);
294 if (entryValidationResult == null) {
295 entryValidationResult = new EntryValidationResult(entryName);
296 entryValidationResultMap.put(entryName, entryValidationResult);
297 }
298 return entryValidationResult;
299 }
300
301 /**
302 * composes a {@code ConstraintValidationResult} from the parameters given
303 *
304 * @param entryName - the name that the data dictionary uses to store metadata about the attribute
305 * @param attributeName - the attribute name
306 * @param attributePath - a string representation of specifically which attribute (at some depth) is being accessed
307 * @param constraintName - a descriptive name of the current constraint processor
308 * @return validation result
309 */
310 private ConstraintValidationResult getConstraintValidationResult(String entryName, String attributeName,
311 String attributePath, String constraintName) {
312 String entryKey = getEntryValidationResultKey(entryName, attributePath);
313 ConstraintValidationResult constraintValidationResult = getEntryValidationResult(entryKey)
314 .getAttributeValidationResult(attributeName).getConstraintValidationResult(constraintName);
315 constraintValidationResult.setEntryName(entryName);
316 constraintValidationResult.setAttributeName(attributeName);
317 constraintValidationResult.setAttributePath(attributePath);
318 return constraintValidationResult;
319 }
320
321 /**
322 * gets the key to the {@link EntryValidationResult} entry in the EntryValidationResultMap
323 *
324 * <p>Most cases entry key will be the entryName, unless the attribute is part of a collection,
325 * in which case entry key will be suffixed with index of attribute's parent item.</p>
326 *
327 * @param entryName - the name that the data dictionary uses to store metadata about the attribute
328 * @param attributePath - a string representation of specifically which attribute (at some depth) is being accessed
329 * @return a key used to fetch an associated {@code EntryValidationResult}
330 */
331 private String getEntryValidationResultKey(String entryName, String attributePath) {
332 if (attributePath.contains("[")) {
333 return entryName + "[" + ValidationUtils.getLastPathIndex(attributePath) + "]";
334 }
335 return entryName;
336 }
337
338 /**
339 * @return the errorLevel
340 */
341 public ErrorLevel getErrorLevel() {
342 return this.errorLevel;
343 }
344
345 /**
346 * @param errorLevel the errorLevel to set
347 */
348 public void setErrorLevel(ErrorLevel errorLevel) {
349 this.errorLevel = errorLevel;
350 }
351
352 /**
353 * @return the numberOfErrors
354 */
355 public int getNumberOfErrors() {
356 return this.numberOfErrors;
357 }
358
359 /**
360 * @return the numberOfWarnings
361 */
362 public int getNumberOfWarnings() {
363 return this.numberOfWarnings;
364 }
365
366 }