001 /** 002 * Copyright 2005-2012 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.edl.impl.components; 017 018 import java.util.ArrayList; 019 import java.util.List; 020 import java.util.Map; 021 022 import javax.xml.xpath.XPath; 023 import javax.xml.xpath.XPathConstants; 024 025 import org.apache.commons.lang.StringUtils; 026 import org.kuali.rice.edl.impl.EDLContext; 027 import org.kuali.rice.edl.impl.EDLModelComponent; 028 import org.kuali.rice.edl.impl.EDLXmlUtils; 029 import org.kuali.rice.edl.impl.RequestParser; 030 import org.kuali.rice.edl.impl.service.EdlServiceLocator; 031 import org.kuali.rice.kew.api.WorkflowRuntimeException; 032 import org.kuali.rice.kew.rule.xmlrouting.XPathHelper; 033 import org.w3c.dom.Document; 034 import org.w3c.dom.Element; 035 import org.w3c.dom.NodeList; 036 037 038 /** 039 * Executes validations that are defined on the EDL Definitions. These validation exist in a form 040 * similiar to the following: 041 * 042 * <validations> 043 * <validation type="xpath"> 044 * <expression>wf:field('grade') = 'other' and not(wf:empty(wf:field('otherGrade'))</expression> 045 * <message>Other Grade is required when grade is marked as 'other'</message> 046 * </validation> 047 * </validations> 048 * 049 * @author Kuali Rice Team (rice.collab@kuali.org) 050 */ 051 public class ValidationComponent extends SimpleWorkflowEDLConfigComponent implements EDLModelComponent { 052 053 private static final String XPATH_TYPE = "xpath"; 054 private EDLContext edlContext; 055 056 public void updateDOM(Document dom, Element configElement, EDLContext edlContext) { 057 if (edlContext.getUserAction().isValidatableAction()) { 058 try { 059 Document edlDef = EdlServiceLocator.getEDocLiteService().getDefinitionXml(edlContext.getEdocLiteAssociation()); 060 List<EDLValidation> validations = parseValidations(edlDef); 061 if (!validations.isEmpty()) { 062 XPath xpath = XPathHelper.newXPath(dom); 063 for (EDLValidation validation : validations) { 064 executeValidation(xpath, dom, validation, edlContext); 065 } 066 } 067 } catch (Exception e) { 068 if (e instanceof RuntimeException) { 069 throw (RuntimeException)e; 070 } 071 throw new WorkflowRuntimeException("Failed to execute EDL validations.", e); 072 } 073 } 074 } 075 076 protected List<EDLValidation> parseValidations(Document document) throws Exception { 077 List<EDLValidation> validations = new ArrayList<EDLValidation>(); 078 XPath xpath = XPathHelper.newXPath(document); 079 NodeList validationNodes = (NodeList)xpath.evaluate("/edl/validations/validation", document, XPathConstants.NODESET); 080 for (int index = 0; index < validationNodes.getLength(); index++) { 081 Element validationElem = (Element)validationNodes.item(index); 082 EDLValidation validation = new EDLValidation(); 083 String type = validationElem.getAttribute("type"); 084 String key = validationElem.getAttribute("key"); 085 String expression = EDLXmlUtils.getChildElementTextValue(validationElem, "expression"); 086 String message = EDLXmlUtils.getChildElementTextValue(validationElem, "message"); 087 if (StringUtils.isBlank(type)) { 088 throw new WorkflowRuntimeException("An improperly configured validation was found with an empty type."); 089 } 090 if (StringUtils.isBlank(expression)) { 091 throw new WorkflowRuntimeException("An improperly configured validation was found with an empty expression."); 092 } 093 if (StringUtils.isBlank(message)) { 094 throw new WorkflowRuntimeException("An improperly configured validation was found with an empty message."); 095 } 096 validation.setType(type); 097 validation.setKey(key); 098 validation.setExpression(expression); 099 validation.setMessage(message); 100 validations.add(validation); 101 } 102 return validations; 103 } 104 105 protected void executeValidation(XPath xpath, Document dom, EDLValidation validation, EDLContext edlContext) throws Exception { 106 // TODO: in the future, allow this to be pluggable, hardcode for now 107 if (XPATH_TYPE.equals(validation.getType())) { 108 Boolean result = (Boolean)xpath.evaluate(validation.getExpression(), dom, XPathConstants.BOOLEAN); 109 // if validation returns false, we'll flag the error 110 if (!result) { 111 String key = validation.getKey(); 112 if (!StringUtils.isEmpty(key)) { 113 Map<String, String> fieldErrors = (Map<String, String>)edlContext.getRequestParser().getAttribute(RequestParser.GLOBAL_FIELD_ERRORS_KEY); 114 fieldErrors.put(key, validation.getMessage()); 115 116 // set invalid attribute to true on corresponding field 117 //TODO remove - handled this in the widgets 118 // Element edlElement = EDLXmlUtils.getEDLContent(dom, false); 119 // Element edlSubElement = EDLXmlUtils.getOrCreateChildElement(edlElement, "data", true); 120 // NodeList versionNodes = edlSubElement.getChildNodes(); 121 // for (int i = 0; i < versionNodes.getLength(); i++) { 122 // Element version = (Element) versionNodes.item(i); 123 // String current = version.getAttribute("current"); 124 // if (current == "true") { 125 // NodeList fieldNodes = version.getChildNodes(); 126 // for (int j = 0; j < fieldNodes.getLength(); j++) { 127 // Element field = (Element) fieldNodes.item(j); 128 // String fieldName = field.getAttribute("name"); 129 // if(fieldName.equals(key)) { 130 // field.setAttribute("invalid", "true"); 131 // break; 132 // } 133 // } 134 // } 135 // } 136 137 } else { 138 List globalErrors = (List)edlContext.getRequestParser().getAttribute(RequestParser.GLOBAL_ERRORS_KEY); 139 globalErrors.add(validation.getMessage()); 140 } 141 edlContext.setInError(true); 142 } 143 } else { 144 throw new WorkflowRuntimeException("Illegal validation type specified. Only 'xpath' is currently supported."); 145 } 146 } 147 148 149 }