001/** 002 * Copyright 2005-2015 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; 017 018import java.util.Date; 019import java.util.Iterator; 020import java.util.List; 021import java.util.Map; 022 023import javax.xml.parsers.DocumentBuilder; 024import javax.xml.parsers.DocumentBuilderFactory; 025import javax.xml.parsers.ParserConfigurationException; 026 027import org.apache.logging.log4j.Logger; 028import org.apache.logging.log4j.LogManager; 029import org.kuali.rice.edl.impl.components.MatchingParam; 030import org.kuali.rice.kew.api.WorkflowRuntimeException; 031import org.w3c.dom.Document; 032import org.w3c.dom.Element; 033import org.w3c.dom.Node; 034import org.w3c.dom.NodeList; 035 036 037/** 038 * Contains a bunch of dom utility methods. 039 * 040 * @author Kuali Rice Team (rice.collab@kuali.org) 041 * 042 */ 043public final class EDLXmlUtils { 044 045 private static final Logger LOG = LogManager.getLogger(EDLXmlUtils.class); 046 047 public static final String EDL_E = "edl"; 048 public static final String EDLCONTENT_E = "edlContent"; 049 public static final String DATA_E = "data"; 050 public static final String TYPE_E = "type"; 051 public static final String VALIDATION_E = "validation"; 052 public static final String VERSION_E = "version"; 053 public static final String DOCID_E = "docId"; 054 055 private EDLXmlUtils() { 056 throw new UnsupportedOperationException("do not call"); 057 } 058 059 /** 060 * Returns a valid DocumentBuilder 061 * @return a valid DocumentBuilder 062 */ 063 public static DocumentBuilder getDocumentBuilder() { 064 try { 065 return DocumentBuilderFactory.newInstance().newDocumentBuilder(); 066 } catch (ParserConfigurationException pce) { 067 // well folks, there is not much we can do if we get a ParserConfigurationException 068 // so might as well isolate the evilness here, and just balk if this occurs 069 String message = "Error obtaining document builder"; 070 LOG.error(message, pce); 071 throw new RuntimeException(message, pce); 072 } 073 } 074 075 076 public static Element createFieldDataElement(Element parentVersionElement, MatchingParam matchingParam) { 077 Element fieldData = createChildElement(parentVersionElement, "field"); 078 fieldData.setAttribute("name", matchingParam.getParamName()); 079 if (matchingParam.getError().booleanValue()) { 080 fieldData.setAttribute("invalid", "true"); 081 Element errorMessage = getOrCreateChildElement(fieldData, "errorMessage", true); 082 placeTextInElement(errorMessage, matchingParam.getErrorMessage()); 083 } 084 Element fieldValue = getOrCreateChildElement(fieldData, "value", true); 085 placeTextInElement(fieldValue, matchingParam.getParamValue()); 086 return fieldData; 087 } 088 089 public static Element createChildElement(Element parentElement, String elementName) { 090 Element child = parentElement.getOwnerDocument().createElement(elementName); 091 parentElement.appendChild(child); 092 return child; 093 } 094 095 public static Element getDocumentStateElement(Document dom) { 096 return EDLXmlUtils.getOrCreateChildElement(dom.getDocumentElement(), "documentState", true); 097 } 098 099 100 public static void addGlobalErrorMessage(Document dom, String errorMessage) { 101 Element documentState = getDocumentStateElement(dom); 102 createTextElementOnParent(documentState, "error", errorMessage); 103 } 104 105 private static void placeTextInElement(Element element, String text) { 106 if (element.getOwnerDocument() == null) { 107 throw new WorkflowRuntimeException("The element must have an owner document in order to add text"); 108 } 109 element.appendChild(element.getOwnerDocument().createTextNode(text)); 110 } 111 112 public static Element createTextElementOnParent(Element parent, String childElementName, String text) { 113 if (text == null) { 114 throw new WorkflowRuntimeException("The text placed in an Element cannot be null"); 115 } 116 Element childElement = parent.getOwnerDocument().createElement(childElementName); 117 parent.appendChild(childElement); 118 childElement.appendChild(parent.getOwnerDocument().createTextNode(text)); 119 return childElement; 120 } 121 122 public static Element getVersionFromData(Element dataElement, Integer versionCount) { 123 if (dataElement == null) { 124 throw new WorkflowRuntimeException("Attempting to put version element inside null data Element"); 125 } 126 if (!dataElement.getTagName().equals(DATA_E)) { 127 throw new WorkflowRuntimeException("Attempting to put version element inside a parent that is not a data element " + dataElement.getTagName()); 128 } 129 Element version = createChildElement(dataElement, VERSION_E); 130 version.setAttribute("current", "true"); 131 version.setAttribute("date", new Date().toString()); 132 version.setAttribute("version", versionCount.toString()); 133 return version; 134 } 135 136 public static Element getDataFromEDLDocument(Element edlContent, boolean create) { 137 return getOrCreateChildElement(edlContent, DATA_E, create); 138 } 139 140 public static Element getEDLContent(Document displayDoc, boolean create) { 141 return getOrCreateChildElement(displayDoc.getDocumentElement(), EDLCONTENT_E, create); 142 } 143 144 /** 145 * Returns, and creates if absent, a child element 146 * @param parent the parent element 147 * @param name the name of the child element to create and/or return 148 * @return reference to the child element 149 */ 150 public static Element getOrCreateChildElement(Element parent, String name, boolean create) { 151 if (parent == null) { 152 throw new WorkflowRuntimeException("Passed in null parent element attempting to create child element '" + name + "'"); 153 } 154 Element child = getChildElement(parent, name); 155 if (child == null && create) { 156 LOG.debug("Creating child element '" + name + "' of parent: " + parent); 157 child = parent.getOwnerDocument().createElement(name); 158 parent.appendChild(child); 159 } 160 return child; 161 } 162 163 /** 164 * Returns a node child with the specified tag name of the specified parent node, 165 * or null if no such child node is found. 166 * @param parent the parent node 167 * @param name the name of the child node 168 * @return child node if found, null otherwise 169 */ 170 public static Element getChildElement(Node parent, String name) { 171 NodeList childList = parent.getChildNodes(); 172 for (int i = 0; i < childList.getLength(); i++) { 173 Node node = childList.item(i); 174 // we must test against NodeName, not just LocalName 175 // LocalName seems to be null - I am guessing this is because 176 // the DocumentBuilderFactory is not "namespace aware" 177 // although I would have expected LocalName to default to 178 // NodeName 179 if (node.getNodeType() == Node.ELEMENT_NODE 180 && (name.equals(node.getLocalName()) 181 || name.equals(node.getNodeName()))) { 182 return (Element) node; 183 } 184 } 185 return null; 186 } 187 188 /** 189 * Returns the text value of a child element with the given name, of the given parent element, 190 * or null if the child does not exist or does not have a child text node 191 * @param parent parent element 192 * @param name name of child element 193 * @return the text value of a child element with the given name, of the given parent element, 194 * or null if the child does not exist or does not have a child text node 195 */ 196 public static String getChildElementTextValue(Node parent, String name) { 197 Element child = getChildElement(parent, name); 198 if (child == null) { 199 return null; 200 } 201 Node textNode = child.getFirstChild(); 202 if (textNode == null) { 203 return null; 204 } 205 return textNode.getNodeValue(); 206 } 207 208 209 210 /** 211 * Adds the specified errors and messages to the <documentState> element of the 212 * given EDL doc 213 * @param doc the EDL doc 214 * @param errors the list of error Strings 215 * @param messages the list of message Strings 216 */ 217 public static void addErrorsAndMessagesToDocument(Document doc, List errors, List messages, Map<String, String> fieldErrors) { 218 Node documentState = EDLXmlUtils.getDocumentStateElement(doc); 219 Iterator it = errors.iterator(); 220 while (it.hasNext()) { 221 Element error = doc.createElement("error"); 222 error.appendChild(doc.createTextNode(it.next().toString())); 223 documentState.appendChild(error); 224 } 225 it = messages.iterator(); 226 while (it.hasNext()) { 227 Element error = doc.createElement("message"); 228 error.appendChild(doc.createTextNode(it.next().toString())); 229 documentState.appendChild(error); 230 } 231 for (String errorKey : fieldErrors.keySet()) { 232 Element error = doc.createElement("fieldError"); 233 error.setAttribute("key", errorKey); 234 error.appendChild(doc.createTextNode(fieldErrors.get(errorKey))); 235 documentState.appendChild(error); 236 } 237 } 238 239} 240 241