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.edl.impl.components; 017 018import java.util.ArrayList; 019import java.util.List; 020import java.util.Map; 021 022import javax.xml.xpath.XPath; 023import javax.xml.xpath.XPathConstants; 024 025import org.apache.commons.lang.StringUtils; 026import org.kuali.rice.edl.impl.EDLContext; 027import org.kuali.rice.edl.impl.EDLModelComponent; 028import org.kuali.rice.edl.impl.EDLXmlUtils; 029import org.kuali.rice.edl.impl.RequestParser; 030import org.kuali.rice.edl.impl.service.EdlServiceLocator; 031import org.kuali.rice.kew.api.WorkflowRuntimeException; 032import org.kuali.rice.kew.rule.xmlrouting.XPathHelper; 033import org.w3c.dom.Document; 034import org.w3c.dom.Element; 035import 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 */ 051public 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}