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 &lt;documentState&gt; 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