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 }