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