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.validator;
17  
18  import org.apache.commons.logging.Log;
19  import org.apache.commons.logging.LogFactory;
20  import org.kuali.rice.krad.datadictionary.uif.UifDictionaryBean;
21  import org.kuali.rice.krad.uif.component.Component;
22  import org.kuali.rice.krad.uif.component.DataBinding;
23  import org.springframework.core.io.ResourceLoader;
24  import org.w3c.dom.Document;
25  import org.w3c.dom.NodeList;
26  
27  import javax.xml.parsers.DocumentBuilder;
28  import javax.xml.parsers.DocumentBuilderFactory;
29  import java.util.ArrayList;
30  import java.util.HashMap;
31  import java.util.Iterator;
32  import java.util.Map;
33  
34  /**
35   * Linear collection of identifiers for individual Spring Beans starting with the base bean and ending with the most
36   * recent.  Has the ability to located xml files related to the trace.
37   *
38   * @author Kuali Rice Team (rice.collab@kuali.org)
39   */
40  public class ValidationTrace {
41      private static final Log LOG = LogFactory.getLog(ValidationTrace.class);
42  
43      // Constant identifer for a trace entry where the bean has no identifier itself
44      public static final String NO_BEAN_ID = "NOBEANID";
45  
46      // Constant identifier for a trace during startup
47      public static final int START_UP = 0;
48  
49      // Constant identifier for a trace during render
50      public static final int BUILD = 1;
51  
52      private ArrayList<String> beanIds;
53      private ArrayList<String> beanTypes;
54      private Map<String, Document> beanMap;
55      private int validationStage;
56  
57      /**
58       * Constructor for an empty token to start a trace
59       */
60      public ValidationTrace() {
61          beanIds = new ArrayList<String>();
62          beanTypes = new ArrayList<String>();
63          beanMap = new HashMap<String, Document>();
64      }
65  
66      /**
67       * Constructor for an empty token to start a trace
68       * @param files files to load
69       * @param loader resource loader
70       */
71      public ValidationTrace(String[] files, ResourceLoader loader) {
72          beanIds = new ArrayList<String>();
73          beanTypes = new ArrayList<String>();
74          beanMap = new HashMap<String, Document>();
75          loadFiles(files, loader);
76      }
77  
78      /**
79       * Adds a single entry into the trace
80       *
81       * @param beanId - An identifier for the bean
82       * @param beanType - The type of bean
83       */
84      public void addBean(String beanType, String beanId) {
85          beanIds.add(beanId);
86          beanTypes.add(beanType);
87      }
88  
89      /**
90       * Adds a UIF Component to the trace
91       *
92       * @param component - The object to be added
93       */
94      public void addBean(Component component) {
95          String beanId = NO_BEAN_ID;
96          String beanType = component.getClass().getSimpleName();
97          if (component.getId() != null) {
98              if (component.getId().compareTo("null") != 0) {
99                  beanId = component.getId();
100             } else {
101                 try {
102                     beanId = ((DataBinding) component).getPropertyName();
103 
104                 } catch (Exception e) {
105                     beanId = NO_BEAN_ID;
106                 }
107             }
108         } else {
109             try {
110                 beanId = ((DataBinding) component).getPropertyName();
111             } catch (Exception e) {
112                 beanId = NO_BEAN_ID;
113             }
114         }
115         addBean(beanType, beanId);
116     }
117 
118     /**
119      * Adds a UIF Configurable to the trace
120      *
121      * @param configurable - The object to be added
122      */
123     public void addBean(UifDictionaryBean configurable) {
124         String beanId = "configurable";
125         String beanType = configurable.getClass().getSimpleName();
126         addBean(beanType, beanId);
127     }
128 
129     /**
130      * Removes an entry from the trace
131      *
132      * @param index
133      */
134     public void removeBean(int index) {
135         beanIds.remove(index);
136         beanTypes.remove(index);
137     }
138 
139     /**
140      * Replaces a trace entry's information
141      *
142      * @param index - The location of the bean
143      * @param beanId - An identifier for the bean
144      * @param beanType - The type of bean
145      */
146     public void modifyBean(int index, String beanId, String beanType) {
147         beanIds.set(index, beanId);
148         beanTypes.set(index, beanType);
149     }
150 
151     /**
152      * Creates a copy of the ValidationTrace
153      *
154      * @return A complete copy of the current token
155      */
156     public ValidationTrace getCopy() {
157         ValidationTrace copy = new ValidationTrace();
158 
159         for (int i = 0; i < getTraceSize(); i++) {
160             copy.addBean(getBeanType(i), getBeanId(i));
161         }
162         copy.setValidationStage(getValidationStage());
163         copy.setBeanMap(beanMap);
164         return copy;
165     }
166 
167     /**
168      * Loads the xmlFiles of the data objects being validated into a list of Documents that can be parsed to find the
169      * xmls related to the error.
170      *
171      * @param beanFiles - The list of file paths used in the creation of the beans
172      * @param loader - The source that was used to load the beans
173      */
174     private void loadFiles(String[] beanFiles, ResourceLoader loader) {
175         LOG.debug("Started Loading Parser Files");
176 
177         for (int i = 0; i < beanFiles.length; i++) {
178             try {
179                 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
180                 DocumentBuilder builder = factory.newDocumentBuilder();
181                 Document document;
182                 String file = beanFiles[i];//.substring(0,10)+"/"+beanFiles[i].substring(10);
183                 LOG.debug("Loading file: " + file);
184                 document = builder.parse(loader.getResource(file).getInputStream());
185                 beanMap.put(file, document);
186             } catch (Exception e) {
187                 LOG.error("Not Found: " + beanFiles[i]);
188             }
189         }
190         LOG.debug("Finished Loading Parser Files");
191     }
192 
193     /**
194      * Parse the the Documents contained in the Map an finding the map entries whos documents contain the passed in Id
195      * in a bean element's attributes. All attributes are checked because the id used in the trace can be from
196      * different
197      * properties on different beans and not just the id attribute.
198      *
199      * @param id - The attribute value to be found
200      * @param beans - A Map containing the Documents to be looked through
201      * @return - A sub set of maps from the past in list that contains the value being looked for
202      */
203     private Map<String, Document> findBeanById(String id, Map<String, Document> beans) {
204         Map<String, Document> result = new HashMap<String, Document>();
205         LOG.debug("Searching for bean of Id: " + id);
206 
207         Iterator iter = beans.entrySet().iterator();
208 
209         while (iter.hasNext()) {
210             Map.Entry entry = (Map.Entry) iter.next();
211             Document document = (Document) entry.getValue();
212             NodeList nodes = document.getElementsByTagName("bean");
213 
214             for (int i = 0; i < nodes.getLength(); i++) {
215                 if (nodes.item(i).hasAttributes()) {
216                     for (int j = 0; j < nodes.item(i).getAttributes().getLength(); j++) {
217                         if (nodes.item(i).getAttributes().item(j).getNodeValue().toLowerCase().compareTo(
218                                 id.toLowerCase()) == 0) {
219                             LOG.debug("Found bean of Id = " + id);
220 
221                             result.put((String) entry.getKey(), (Document) entry.getValue());
222 
223                             break;
224                         }
225                     }
226                 }
227             }
228         }
229 
230         return result;
231     }
232 
233     /**
234      * Finds related xml files to an error by searching for files that contain beans that have been encountered in the
235      * validation.  The file path and Document version of the xmls are paired and stored in a Map.  This allows for
236      * returning the file paths easy when searching through the Documents.
237      *
238      * @return A list of file paths to the xmls in which the beans were found
239      */
240     public ArrayList<String> findXmlFiles() {
241         Map<String, Document> result = new HashMap<String, Document>();
242         LOG.debug("Looking for Xml files");
243 
244         for (int i = 0; i < getTraceSize(); i++) {
245             if (getBeanId(i) != null) {
246                 if (getBeanId(i).compareTo(NO_BEAN_ID) != 0) {
247                     result.putAll(findBeanById(getBeanId(i), beanMap));
248                 }
249             }
250         }
251 
252         ArrayList<String> files = new ArrayList<String>();
253         Iterator iter = result.entrySet().iterator();
254         while (iter.hasNext()) {
255             Map.Entry entry = (Map.Entry) iter.next();
256             files.add((String) entry.getKey());
257         }
258 
259         return files;
260     }
261 
262     /**
263      * Sets the stage of the validation where the trace is taking place
264      *
265      * @param stage - The stage of the validation
266      */
267     public void setValidationStage(int stage) {
268         validationStage = stage;
269     }
270 
271     /**
272      * Sets the beanMap for when copying the tracer
273      *
274      * @param newMap - The map to be stored
275      */
276     private void setBeanMap(Map<String, Document> newMap) {
277         beanMap = newMap;
278     }
279 
280     /**
281      * Creates a new error report as an Error and adds it to the global list.
282      *
283      * @param validation - The validation that fails.
284      * @param values - The values involved.
285      */
286     public void createError(String validation, String values[]) {
287         ErrorReport report = new ErrorReport(ErrorReport.ERROR, validation, this, values);
288         Validator.addErrorReport(report);
289 
290     }
291 
292     /**
293      * Creates a new error report as a Warning and adds it to the global list.
294      *
295      * @param validation - The validation that fails.
296      * @param values - The values involved.
297      */
298     public void createWarning(String validation, String values[]) {
299         ErrorReport report = new ErrorReport(ErrorReport.WARNING, validation, this, values);
300         Validator.addErrorReport(report);
301 
302     }
303 
304     /**
305      * Retrieves a single entry in the BeanId trace list, a collection identifiers for the traced beans
306      *
307      * @param index - The location of the bean
308      * @return String Identifier for the bean at the provided index of the trace
309      */
310     public String getBeanId(int index) {
311         return beanIds.get(index);
312     }
313 
314     /**
315      * Retrieves a single entry in the BeanType trace list, a collection of types for the traced beansa collection
316      * identifiers for the traced beans
317      *
318      * @param index - The location of the bean type
319      * @return String Type for the bean at the provided index of the trace
320      */
321     public String getBeanType(int index) {
322         return beanTypes.get(index);
323     }
324 
325     /**
326      * Retrieves the stage when the trace is taking place
327      * The stage is the time frame when the validation is taking place in the application
328      *
329      * @return Returns the stage of the validation.
330      */
331     public int getValidationStage() {
332         return validationStage;
333     }
334 
335     /**
336      * Retrieves the number of beans in the trace list
337      *
338      * @return Number of beans stored in the trace
339      */
340     public int getTraceSize() {
341         return beanIds.size();
342     }
343 
344     /**
345      * Retrieves the complete trace path with each bean shown in the form beanId(BeanType)
346      *
347      * @return The String path of the trace
348      */
349     public String getBeanLocation() {
350         String path = "";
351 
352         for (int i = 0; i < beanTypes.size() - 1; i++) {
353             path = path + beanTypes.get(i) + "(" + beanIds.get(i) + ")" + ".";
354         }
355 
356         if (getTraceSize() > 0) {
357             path = path + beanTypes.get(beanTypes.size() - 1) + "(" + beanIds.get(beanTypes.size() - 1) + ")";
358         }
359 
360         return path;
361     }
362 
363     /**
364      * Retrieves the list of xmls file paths found to be related to error
365      *
366      * @return A list of file paths to the related xmls
367      */
368     public ArrayList<String> getRelatedXmls() {
369         return findXmlFiles();
370     }
371 }