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