/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.bpm.console.server.util; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.w3c.dom.*; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import javax.xml.namespace.QName; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.*; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import java.io.*; import java.util.*; /** * DOM2 utilites * * @author Thomas.Diesler@jboss.org * @author alessio.soldano@jboss.com */ public final class DOMUtils { private static Log log = LogFactory.getLog(DOMUtils.class); // All elements created by the same thread are created by the same builder and belong to the same doc private static ThreadLocal<Document> documentThreadLocal = new ThreadLocal<Document>(); private static ThreadLocal<DocumentBuilder> builderThreadLocal = new ThreadLocal<DocumentBuilder>() { protected DocumentBuilder initialValue() { try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(false); factory.setNamespaceAware(true); DocumentBuilder builder = factory.newDocumentBuilder(); setEntityResolver(builder); return builder; } catch (ParserConfigurationException e) { throw new RuntimeException("Failed to create DocumentBuilder", e); } } private void setEntityResolver(DocumentBuilder builder) { String[] resolvers = new String[] { "org.jboss.ws.core.utils.JBossWSEntityResolver", "org.jboss.util.xml.JBossEntityResolver" }; EntityResolver entityResolver = null; ClassLoader loader = Thread.currentThread().getContextClassLoader(); for (String resolver : resolvers) { try { Class<?> resolverClass = loader.loadClass(resolver); entityResolver = (EntityResolver)resolverClass.newInstance(); } catch (Exception ex) { log.debug("Cannot load: " + resolver); } } if (entityResolver != null) builder.setEntityResolver(entityResolver); } }; public static void clearThreadLocals() { documentThreadLocal.remove(); builderThreadLocal.remove(); } // Hide the constructor private DOMUtils() { } /** Initialize the DocumentBuilder */ public static DocumentBuilder getDocumentBuilder() { return builderThreadLocal.get(); } /** Parse the given XML string and return the root Element */ public static Element parse(String xmlString) throws IOException { try { return parse(new ByteArrayInputStream(xmlString.getBytes("UTF-8"))); } catch (IOException e) { log.error("Cannot parse: " + xmlString); throw e; } } /** Parse the given XML stream and return the root Element */ public static Element parse(InputStream xmlStream) throws IOException { try { return getDocumentBuilder().parse(xmlStream).getDocumentElement(); } catch (SAXException se) { throw new IOException(se.toString()); } finally { xmlStream.close(); } } /** Parse the given input source and return the root Element */ public static Element parse(InputSource source) throws IOException { try { return getDocumentBuilder().parse(source).getDocumentElement(); } catch (SAXException se) { throw new IOException(se.toString()); } finally { InputStream is = source.getByteStream(); if (is != null) { is.close(); } Reader r = source.getCharacterStream(); if (r != null) { r.close(); } } } /** Create an Element for a given name */ public static Element createElement(String localPart) { Document doc = getOwnerDocument(); log.trace("createElement {}" + localPart); return doc.createElement(localPart); } /** Create an Element for a given name and prefix */ public static Element createElement(String localPart, String prefix) { Document doc = getOwnerDocument(); log.trace("createElement {}" + prefix + ":" + localPart); return doc.createElement(prefix + ":" + localPart); } /** Create an Element for a given name, prefix and uri */ public static Element createElement(String localPart, String prefix, String uri) { Document doc = getOwnerDocument(); if (prefix == null || prefix.length() == 0) { log.trace("createElement {" + uri + "}" + localPart); return doc.createElementNS(uri, localPart); } else { log.trace("createElement {" + uri + "}" + prefix + ":" + localPart); return doc.createElementNS(uri, prefix + ":" + localPart); } } /** Create an Element for a given QName */ public static Element createElement(QName qname) { return createElement(qname.getLocalPart(), qname.getPrefix(), qname.getNamespaceURI()); } /** Create a org.w3c.dom.Text node */ public static Text createTextNode(String value) { Document doc = getOwnerDocument(); return doc.createTextNode(value); } /** Get the qname of the given node. */ public static QName getElementQName(Element el) { String qualifiedName = el.getNodeName(); return resolveQName(el, qualifiedName); } /** Transform the given qualified name into a QName */ public static QName resolveQName(Element el, String qualifiedName) { QName qname; String prefix = ""; String namespaceURI = ""; String localPart = qualifiedName; int colIndex = qualifiedName.indexOf(":"); if (colIndex > 0) { prefix = qualifiedName.substring(0, colIndex); localPart = qualifiedName.substring(colIndex + 1); if ("xmlns".equals(prefix)) { namespaceURI = "URI:XML_PREDEFINED_NAMESPACE"; } else { Element nsElement = el; while (namespaceURI.equals("") && nsElement != null) { namespaceURI = nsElement.getAttribute("xmlns:" + prefix); if (namespaceURI.equals("")) nsElement = getParentElement(nsElement); } } if (namespaceURI.equals("")) throw new IllegalArgumentException("Cannot find namespace uri for: " + qualifiedName); } else { Element nsElement = el; while (namespaceURI.equals("") && nsElement != null) { namespaceURI = nsElement.getAttribute("xmlns"); if (namespaceURI.equals("")) nsElement = getParentElement(nsElement); } } qname = new QName(namespaceURI, localPart, prefix); return qname; } /** Get the value from the given attribute * * @return null if the attribute value is empty or the attribute is not present */ public static String getAttributeValue(Element el, String attrName) { return getAttributeValue(el, new QName(attrName)); } /** Get the value from the given attribute * * @return null if the attribute value is empty or the attribute is not present */ public static String getAttributeValue(Element el, QName attrName) { String attr = null; if ("".equals(attrName.getNamespaceURI())) attr = el.getAttribute(attrName.getLocalPart()); else attr = el.getAttributeNS(attrName.getNamespaceURI(), attrName.getLocalPart()); if ("".equals(attr)) attr = null; return attr; } /** Get the qname value from the given attribute */ public static QName getAttributeValueAsQName(Element el, String attrName) { return getAttributeValueAsQName(el, new QName(attrName)); } /** Get the qname value from the given attribute */ public static QName getAttributeValueAsQName(Element el, QName attrName) { QName qname = null; String qualifiedName = getAttributeValue(el, attrName); if (qualifiedName != null) { qname = resolveQName(el, qualifiedName); } return qname; } /** Get the boolean value from the given attribute */ public static boolean getAttributeValueAsBoolean(Element el, String attrName) { return getAttributeValueAsBoolean(el, new QName(attrName)); } /** Get the boolean value from the given attribute */ public static boolean getAttributeValueAsBoolean(Element el, QName attrName) { String attrVal = getAttributeValue(el, attrName); boolean ret = "true".equalsIgnoreCase(attrVal) || "1".equalsIgnoreCase(attrVal); return ret; } /** Get the integer value from the given attribute */ public static Integer getAttributeValueAsInteger(Element el, String attrName) { return getAttributeValueAsInteger(el, new QName(attrName)); } /** Get the integer value from the given attribute */ public static Integer getAttributeValueAsInteger(Element el, QName attrName) { String attrVal = getAttributeValue(el, attrName); return (attrVal != null ? new Integer(attrVal) : null); } /** Get the attributes as Map<QName, String> */ public static Map getAttributes(Element el) { Map attmap = new HashMap(); NamedNodeMap attribs = el.getAttributes(); for (int i = 0; i < attribs.getLength(); i++) { Attr attr = (Attr)attribs.item(i); String name = attr.getName(); QName qname = resolveQName(el, name); String value = attr.getNodeValue(); attmap.put(qname, value); } return attmap; } /** Copy attributes between elements */ public static void copyAttributes(Element destElement, Element srcElement) { NamedNodeMap attribs = srcElement.getAttributes(); for (int i = 0; i < attribs.getLength(); i++) { Attr attr = (Attr)attribs.item(i); String uri = attr.getNamespaceURI(); String qname = attr.getName(); String value = attr.getNodeValue(); // Prevent DOMException: NAMESPACE_ERR: An attempt is made to create or // change an object in a way which is incorrect with regard to namespaces. if (uri == null && qname.startsWith("xmlns")) { log.trace("Ignore attribute: [uri=" + uri + ",qname=" + qname + ",value=" + value + "]"); } else { destElement.setAttributeNS(uri, qname, value); } } } /** True if the node has text child elements only */ public static boolean hasTextChildNodesOnly(Node node) { NodeList nodeList = node.getChildNodes(); if (nodeList.getLength() == 0) return false; for (int i = 0; i < nodeList.getLength(); i++) { Node acksToChildNode = nodeList.item(i); if (acksToChildNode.getNodeType() != Node.TEXT_NODE) return false; } return true; } /** True if the node has child elements */ public static boolean hasChildElements(Node node) { NodeList nlist = node.getChildNodes(); for (int i = 0; i < nlist.getLength(); i++) { Node child = nlist.item(i); if (child.getNodeType() == Node.ELEMENT_NODE) return true; } return false; } /** Gets child elements */ public static Iterator<Element> getChildElements(Node node) { List<Element> list = new LinkedList<Element>(); NodeList nlist = node.getChildNodes(); for (int i = 0; i < nlist.getLength(); i++) { Node child = nlist.item(i); if (child.getNodeType() == Node.ELEMENT_NODE) list.add((Element)child); } return list.iterator(); } /** Get the concatenated text content, or null. */ public static String getTextContent(Node node) { boolean hasTextContent = false; StringBuffer buffer = new StringBuffer(); NodeList nlist = node.getChildNodes(); for (int i = 0; i < nlist.getLength(); i++) { Node child = nlist.item(i); if (child.getNodeType() == Node.TEXT_NODE) { buffer.append(child.getNodeValue()); hasTextContent = true; } } return (hasTextContent ? buffer.toString() : null); } /** Gets the first child element */ public static Element getFirstChildElement(Node node) { return getFirstChildElement(node, false); } /** Gets the first child element */ public static Element getFirstChildElement(Node node, boolean recursive) { return getFirstChildElementIntern(node, null, recursive); } /** Gets the first child element for a given local name without namespace */ public static Element getFirstChildElement(Node node, String nodeName) { return getFirstChildElement(node, nodeName, false); } /** Gets the first child element for a given local name without namespace */ public static Element getFirstChildElement(Node node, String nodeName, boolean recursive) { return getFirstChildElementIntern(node, new QName(nodeName), recursive); } /** Gets the first child element for a given qname */ public static Element getFirstChildElement(Node node, QName nodeName) { return getFirstChildElement(node, nodeName, false); } /** Gets the first child element for a given qname */ public static Element getFirstChildElement(Node node, QName nodeName, boolean recursive) { return getFirstChildElementIntern(node, nodeName, recursive); } private static Element getFirstChildElementIntern(Node node, QName nodeName, boolean recursive) { Element childElement = null; Iterator it = getChildElementsIntern(node, nodeName, recursive); if (it.hasNext()) { childElement = (Element)it.next(); } return childElement; } /** Gets the child elements for a given local name without namespace */ public static Iterator getChildElements(Node node, String nodeName) { return getChildElements(node, nodeName, false); } /** Gets the child elements for a given local name without namespace */ public static Iterator getChildElements(Node node, String nodeName, boolean recursive) { return getChildElementsIntern(node, new QName(nodeName), recursive); } /** Gets the child element for a given qname */ public static Iterator getChildElements(Node node, QName nodeName) { return getChildElements(node, nodeName, false); } /** Gets the child element for a given qname */ public static Iterator getChildElements(Node node, QName nodeName, boolean recursive) { return getChildElementsIntern(node, nodeName, recursive); } public static List<Element> getChildElementsAsList(Node node, String nodeName) { return getChildElementsAsList(node, nodeName, false); } public static List<Element> getChildElementsAsList(Node node, String nodeName, boolean recursive) { return getChildElementsAsListIntern(node, new QName(nodeName), recursive); } public static List<Element> getChildElementsAsList(Node node, QName nodeName) { return getChildElementsAsList(node, nodeName, false); } public static List<Element> getChildElementsAsList(Node node, QName nodeName, boolean recursive) { return getChildElementsAsListIntern(node, nodeName, recursive); } private static List<Element> getChildElementsAsListIntern(Node node, QName nodeName, boolean recursive) { List<Element> list = new LinkedList<Element>(); NodeList nlist = node.getChildNodes(); for (int i = 0; i < nlist.getLength(); i++) { Node child = nlist.item(i); if (child.getNodeType() == Node.ELEMENT_NODE) { search(list, (Element)child, nodeName, recursive); } } return list; } private static void search(List<Element> list, Element baseElement, QName nodeName, boolean recursive) { if (nodeName == null) { list.add(baseElement); } else { QName qname; if (nodeName.getNamespaceURI().length() > 0) { qname = new QName(baseElement.getNamespaceURI(), baseElement.getLocalName()); } else { qname = new QName(baseElement.getLocalName()); } if (qname.equals(nodeName)) { list.add(baseElement); } } if (recursive) { NodeList nlist = baseElement.getChildNodes(); for (int i = 0; i < nlist.getLength(); i++) { Node child = nlist.item(i); if (child.getNodeType() == Node.ELEMENT_NODE) { search(list, (Element)child, nodeName, recursive); } } } } private static Iterator getChildElementsIntern(Node node, QName nodeName, boolean recursive) { return getChildElementsAsListIntern(node, nodeName, recursive).iterator(); } /** Gets parent element or null if there is none */ public static Element getParentElement(Node node) { Node parent = node.getParentNode(); return (parent instanceof Element ? (Element)parent : null); } /** Get the owner document that is associated with the current thread */ public static Document getOwnerDocument() { Document doc = documentThreadLocal.get(); if (doc == null) { doc = getDocumentBuilder().newDocument(); documentThreadLocal.set(doc); } return doc; } public static Element sourceToElement(Source source) throws IOException { Element retElement = null; try { if (source instanceof StreamSource) { StreamSource streamSource = (StreamSource)source; InputStream ins = streamSource.getInputStream(); if (ins != null) { retElement = DOMUtils.parse(ins); } else { Reader reader = streamSource.getReader(); retElement = DOMUtils.parse(new InputSource(reader)); } } else if (source instanceof DOMSource) { DOMSource domSource = (DOMSource)source; Node node = domSource.getNode(); if (node instanceof Element) { retElement = (Element)node; } else if (node instanceof Document) { retElement = ((Document)node).getDocumentElement(); } else { throw new RuntimeException("Unsupported Node type: " + node.getClass().getName()); } } else if (source instanceof SAXSource) { // The fact that JAXBSource derives from SAXSource is an implementation detail. // Thus in general applications are strongly discouraged from accessing methods defined on SAXSource. // The XMLReader object obtained by the getXMLReader method shall be used only for parsing the InputSource object returned by the getInputSource method. TransformerFactory tf = TransformerFactory.newInstance(); ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); Transformer transformer = tf.newTransformer(); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); transformer.setOutputProperty(OutputKeys.METHOD, "xml"); transformer.transform(source, new StreamResult(baos)); retElement = DOMUtils.parse(new ByteArrayInputStream(baos.toByteArray())); } else { throw new RuntimeException("Source type not implemented: " + source.getClass().getName()); } } catch (TransformerException ex) { IOException ioex = new IOException(); ioex.initCause(ex); throw ioex; } return retElement; } }