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.result;
017
018import org.kuali.rice.krad.datadictionary.validation.AttributeValueReader;
019import org.kuali.rice.krad.datadictionary.validation.ErrorLevel;
020import org.kuali.rice.krad.datadictionary.validation.ValidationUtils;
021
022import java.util.Iterator;
023import java.util.LinkedHashMap;
024import java.util.Map;
025
026/**
027 * 
028 * @author Kuali Rice Team (rice.collab@kuali.org) 
029 */
030public class DictionaryValidationResult implements Iterable<ConstraintValidationResult> {
031        
032        private Map<String, EntryValidationResult> entryValidationResultMap;
033        private ErrorLevel errorLevel;
034        
035        private int numberOfErrors;
036        private int numberOfWarnings;
037        
038        private Iterator<ConstraintValidationResult> iterator;
039        
040        public DictionaryValidationResult() {
041                this.entryValidationResultMap = new LinkedHashMap<String, EntryValidationResult>();
042                this.errorLevel = ErrorLevel.ERROR;
043                this.numberOfErrors = 0;
044                this.numberOfWarnings = 0;
045        }
046        
047        public void addConstraintValidationResult(AttributeValueReader attributeValueReader, ConstraintValidationResult constraintValidationResult) {
048                
049                // 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
050                if (constraintValidationResult.getStatus().getLevel() < errorLevel.getLevel())
051                        return;
052                
053                switch (constraintValidationResult.getStatus()) {
054                case ERROR:
055                        numberOfErrors++;
056                        break;
057                case WARN:
058                        numberOfWarnings++;
059                        break;
060                default:
061                        // Do nothing
062                }
063                
064                // 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!
065                String entryName = constraintValidationResult.getEntryName();
066                String attributeName = constraintValidationResult.getAttributeName();
067                String attributePath = constraintValidationResult.getAttributePath();
068                
069                if (entryName == null){
070                        entryName = attributeValueReader.getEntryName();
071                }
072                
073                if (attributeName == null){
074                        attributeName = attributeValueReader.getAttributeName();
075                }
076                
077                if (attributePath == null){
078                    attributePath = attributeValueReader.getPath();
079                }
080                
081                constraintValidationResult.setEntryName(entryName);
082                constraintValidationResult.setAttributeName(attributeName);
083                constraintValidationResult.setAttributePath(attributePath);
084                
085                String entryKey = getEntryValdidationResultKey(entryName, attributePath);
086                getEntryValidationResult(entryKey).getAttributeValidationResult(attributeName).addConstraintValidationResult(constraintValidationResult);
087        }
088        
089        public ConstraintValidationResult addError(AttributeValueReader attributeValueReader, String constraintName, String errorKey, String... errorParameters) {
090                ConstraintValidationResult constraintValidationResult = getConstraintValidationResult(attributeValueReader.getEntryName(), attributeValueReader.getAttributeName(), attributeValueReader.getPath(), constraintName);
091                constraintValidationResult.setError(errorKey, errorParameters);
092                numberOfErrors++;
093                return constraintValidationResult;
094        }
095
096    public ConstraintValidationResult addError(String constraintLabelKey, AttributeValueReader attributeValueReader, String constraintName,  String errorKey, String... errorParameters) {
097        ConstraintValidationResult constraintValidationResult = getConstraintValidationResult(attributeValueReader.getEntryName(), attributeValueReader.getAttributeName(), attributeValueReader.getPath(), constraintName);
098        constraintValidationResult.setError(errorKey, errorParameters);
099        constraintValidationResult.setConstraintLabelKey(constraintLabelKey);
100        numberOfErrors++;
101        return constraintValidationResult;
102    }
103        
104        public ConstraintValidationResult addWarning(AttributeValueReader attributeValueReader, String constraintName, String errorKey, String... errorParameters) {
105                if (errorLevel.getLevel() > ErrorLevel.WARN.getLevel())
106                        return new ConstraintValidationResult(constraintName, ErrorLevel.WARN);
107                
108                ConstraintValidationResult constraintValidationResult = getConstraintValidationResult(attributeValueReader.getEntryName(), attributeValueReader.getAttributeName(), attributeValueReader.getPath(), constraintName);
109                constraintValidationResult.setWarning(errorKey, errorParameters);
110                numberOfWarnings++;
111                return constraintValidationResult;
112        }
113
114        public ConstraintValidationResult addSuccess(AttributeValueReader attributeValueReader, String constraintName) {
115                if (errorLevel.getLevel() > ErrorLevel.OK.getLevel())
116                        return new ConstraintValidationResult(constraintName, ErrorLevel.OK);
117                
118                return getConstraintValidationResult(attributeValueReader.getEntryName(), attributeValueReader.getAttributeName(), attributeValueReader.getPath(), constraintName);
119        }
120        
121        public ConstraintValidationResult addSkipped(AttributeValueReader attributeValueReader, String constraintName) {
122                if (errorLevel.getLevel() > ErrorLevel.OK.getLevel())
123                        return new ConstraintValidationResult(constraintName, ErrorLevel.INAPPLICABLE);
124                
125                ConstraintValidationResult constraintValidationResult = getConstraintValidationResult(attributeValueReader.getEntryName(), attributeValueReader.getAttributeName(), attributeValueReader.getPath(), constraintName);
126                constraintValidationResult.setStatus(ErrorLevel.INAPPLICABLE);
127                return constraintValidationResult;
128        }
129        
130        public ConstraintValidationResult addNoConstraint(AttributeValueReader attributeValueReader, String constraintName) {
131                if (errorLevel.getLevel() > ErrorLevel.OK.getLevel())
132                        return new ConstraintValidationResult(constraintName, ErrorLevel.NOCONSTRAINT);
133                
134                ConstraintValidationResult constraintValidationResult = getConstraintValidationResult(attributeValueReader.getEntryName(), attributeValueReader.getAttributeName(), attributeValueReader.getPath(), constraintName);
135                constraintValidationResult.setStatus(ErrorLevel.NOCONSTRAINT);
136                return constraintValidationResult;
137        }
138        
139        public Iterator<ConstraintValidationResult> iterator() {
140
141        iterator = new Iterator<ConstraintValidationResult>() {
142
143            private Iterator<EntryValidationResult> entryIterator;
144            private Iterator<AttributeValidationResult> attributeIterator;
145            private Iterator<ConstraintValidationResult> constraintIterator;
146
147            @Override
148            public boolean hasNext() {
149                Iterator<ConstraintValidationResult> currentConstraintIterator = getCurrentConstraintIterator();
150                return currentConstraintIterator != null && currentConstraintIterator.hasNext();
151            }
152
153            @Override
154            public ConstraintValidationResult next() {
155                Iterator<ConstraintValidationResult> currentConstraintIterator = getCurrentConstraintIterator();
156                return currentConstraintIterator != null ? currentConstraintIterator.next() : null;
157            }
158
159            @Override
160            public void remove() {
161                throw new RuntimeException("Can't remove from this iterator!");
162            }
163
164            private Iterator<ConstraintValidationResult> getCurrentConstraintIterator() {
165                if (constraintIterator == null || constraintIterator.hasNext() == false) {
166                    Iterator<AttributeValidationResult> currentAttributeIterator = getCurrentAttributeIterator();
167                    if (currentAttributeIterator != null && currentAttributeIterator.hasNext()) {
168                        AttributeValidationResult currentAttributeValidationResult = currentAttributeIterator.next();
169                        constraintIterator = currentAttributeValidationResult.iterator();
170                    }
171                }
172                return constraintIterator;
173            }
174
175            private Iterator<AttributeValidationResult> getCurrentAttributeIterator() {
176                if (attributeIterator == null || attributeIterator.hasNext() == false) {
177                    Iterator<EntryValidationResult> currentEntryIterator = getCurrentEntryIterator();
178                    if (currentEntryIterator != null && currentEntryIterator.hasNext()) {
179                        EntryValidationResult currentEntryValidationResult = currentEntryIterator.next();
180                        attributeIterator = currentEntryValidationResult.iterator();
181                    }
182                }
183                return attributeIterator;
184            }
185
186            private Iterator<EntryValidationResult> getCurrentEntryIterator() {
187                if (entryIterator == null) // || entryIterator.hasNext() == false)
188                    entryIterator = entryValidationResultMap.values().iterator();
189                return entryIterator;
190            }
191
192        };
193
194                return iterator;
195        }
196        
197        protected EntryValidationResult getEntryValidationResult(String entryName) {
198                EntryValidationResult entryValidationResult = entryValidationResultMap.get(entryName);
199                if (entryValidationResult == null) {
200                        entryValidationResult = new EntryValidationResult(entryName);
201                        entryValidationResultMap.put(entryName, entryValidationResult);
202                }
203                return entryValidationResult;
204        }
205        
206        private ConstraintValidationResult getConstraintValidationResult(String entryName, String attributeName, String attributePath, String constraintName) {
207            String entryKey = getEntryValdidationResultKey(entryName, attributePath);
208            ConstraintValidationResult constraintValidationResult = getEntryValidationResult(entryKey).getAttributeValidationResult(attributeName).getConstraintValidationResult(constraintName);
209                constraintValidationResult.setEntryName(entryName);
210                constraintValidationResult.setAttributeName(attributeName);
211                constraintValidationResult.setAttributePath(attributePath);
212                return constraintValidationResult;
213        }
214                
215        /**
216     * Returns the key to the EntryValidationResult entry in the EntryValidationResultMap.
217     * Most cases entry key will be the entryName, unless the attribute is part of a collection,
218     * in which case entry key will be suffixed with index of attribute's parent item.
219     * 
220     * @param entryName
221     * @param attributePath
222     * @return
223     */
224    private String getEntryValdidationResultKey(String entryName, String attributePath) {
225        if (attributePath.contains("[")){
226            return entryName + "[" + ValidationUtils.getLastPathIndex(attributePath) + "]";
227        } 
228        return entryName;
229    }
230
231    /**
232         * @return the errorLevel
233         */
234        public ErrorLevel getErrorLevel() {
235                return this.errorLevel;
236        }
237
238        /**
239         * @param errorLevel the errorLevel to set
240         */
241        public void setErrorLevel(ErrorLevel errorLevel) {
242                this.errorLevel = errorLevel;
243        }
244
245        /**
246         * @return the numberOfErrors
247         */
248        public int getNumberOfErrors() {
249                return this.numberOfErrors;
250        }
251
252        /**
253         * @return the numberOfWarnings
254         */
255        public int getNumberOfWarnings() {
256                return this.numberOfWarnings;
257        }
258        
259}