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