001/** 002 * Copyright 2005-2018 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.validator; 017 018import org.apache.commons.logging.Log; 019import org.apache.commons.logging.LogFactory; 020import org.kuali.rice.krad.datadictionary.uif.UifDictionaryBean; 021import org.kuali.rice.krad.uif.component.Component; 022import org.kuali.rice.krad.uif.component.DataBinding; 023import org.springframework.core.io.ResourceLoader; 024import org.w3c.dom.Document; 025import org.w3c.dom.NodeList; 026 027import javax.xml.parsers.DocumentBuilder; 028import javax.xml.parsers.DocumentBuilderFactory; 029import java.util.ArrayList; 030import java.util.HashMap; 031import java.util.Iterator; 032import java.util.Map; 033 034/** 035 * Linear collection of identifiers for individual Spring Beans starting with the base bean and ending with the most 036 * recent. Has the ability to located xml files related to the trace. 037 * 038 * @author Kuali Rice Team (rice.collab@kuali.org) 039 */ 040public class ValidationTrace { 041 private static final Log LOG = LogFactory.getLog(ValidationTrace.class); 042 043 // Constant identifer for a trace entry where the bean has no identifier itself 044 public static final String NO_BEAN_ID = "NOBEANID"; 045 046 // Constant identifier for a trace during startup 047 public static final int START_UP = 0; 048 049 // Constant identifier for a trace during render 050 public static final int BUILD = 1; 051 052 private ArrayList<String> beanIds; 053 private ArrayList<String> beanTypes; 054 private Map<String, Document> beanMap; 055 private int validationStage; 056 057 /** 058 * Constructor for an empty token to start a trace 059 */ 060 public ValidationTrace() { 061 beanIds = new ArrayList<String>(); 062 beanTypes = new ArrayList<String>(); 063 beanMap = new HashMap<String, Document>(); 064 } 065 066 /** 067 * Constructor for an empty token to start a trace 068 * @param files files to load 069 * @param loader resource loader 070 */ 071 public ValidationTrace(String[] files, ResourceLoader loader) { 072 beanIds = new ArrayList<String>(); 073 beanTypes = new ArrayList<String>(); 074 beanMap = new HashMap<String, Document>(); 075 loadFiles(files, loader); 076 } 077 078 /** 079 * Adds a single entry into the trace 080 * 081 * @param beanId - An identifier for the bean 082 * @param beanType - The type of bean 083 */ 084 public void addBean(String beanType, String beanId) { 085 beanIds.add(beanId); 086 beanTypes.add(beanType); 087 } 088 089 /** 090 * Adds a UIF Component to the trace 091 * 092 * @param component - The object to be added 093 */ 094 public void addBean(Component component) { 095 String beanId = NO_BEAN_ID; 096 String beanType = component.getClass().getSimpleName(); 097 if (component.getId() != null) { 098 if (component.getId().compareTo("null") != 0) { 099 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}