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