Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.ode.utils; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.xml.namespace.QName; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamSource; import net.sf.saxon.om.Name11Checker; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ode.utils.sax.LoggingErrorHandler; import org.apache.xerces.dom.DOMOutputImpl; import org.apache.xerces.impl.Constants; import org.apache.xml.serialize.DOMSerializerImpl; import org.apache.xml.serialize.OutputFormat; import org.apache.xml.serialize.XMLSerializer; import org.w3c.dom.Attr; import org.w3c.dom.CDATASection; import org.w3c.dom.CharacterData; import org.w3c.dom.Comment; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.ProcessingInstruction; import org.w3c.dom.Text; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * Utility class for dealing with the Document Object Model (DOM). */ public class DOMUtils { private static Log __log = LogFactory.getLog(DOMUtils.class); /** The namespaceURI represented by the prefix <code>xmlns</code>. */ public static final String NS_URI_XMLNS = "http://www.w3.org/2000/xmlns/"; private static ThreadLocal<Transformer> __txers = new ThreadLocal(); private static ThreadLocal<DocumentBuilder> __builders = new ThreadLocal(); private static TransformerFactory _transformerFactory = TransformerFactory.newInstance(); private static DocumentBuilderFactory __documentBuilderFactory; static { initDocumentBuilderFactory(); } /** * Initialize the document-builder factory. */ private static void initDocumentBuilderFactory() { DocumentBuilderFactory f = XMLParserUtils.getDocumentBuilderFactory(); f.setNamespaceAware(true); __documentBuilderFactory = f; } /** * Returns the value of an attribute of an element. Returns null if the * attribute is not found (whereas Element.getAttribute returns "" if an * attrib is not found). * * @param el Element whose attrib is looked for * @param attrName name of attribute to look for * * @return the attribute value */ static public String getAttribute(Element el, String attrName) { String sRet = null; Attr attr = el.getAttributeNode(attrName); if (attr != null) { sRet = attr.getValue(); } return sRet; } /** * @deprecated relies on XMLSerializer which is a deprecated Xerces class, use domToString instead */ static public String prettyPrint(Element e) throws IOException { OutputFormat format = new OutputFormat(e.getOwnerDocument()); format.setLineWidth(65); format.setIndenting(true); format.setIndent(2); StringWriter out = new StringWriter(); XMLSerializer serializer = new XMLSerializer(out, format); serializer.serialize(e); return out.toString(); } public static Element getFirstChildElement(Node node) { NodeList l = node.getChildNodes(); for (int i = 0; i < l.getLength(); i++) { if (l.item(i) instanceof Element) return (Element) l.item(i); } return null; } /** * Returns the value of an attribute of an element. Returns null if the * attribute is not found (whereas Element.getAttributeNS returns "" if an * attrib is not found). * * @param el Element whose attrib is looked for * @param namespaceURI namespace URI of attribute to look for * @param localPart local part of attribute to look for * * @return the attribute value */ static public String getAttributeNS(Element el, String namespaceURI, String localPart) { String sRet = null; Attr attr = el.getAttributeNodeNS(namespaceURI, localPart); if (attr != null) { sRet = attr.getValue(); } return sRet; } /** * Concat all the text and cdata node children of this elem and return the * resulting text. * * @param parentEl the element whose cdata/text node values are to be * combined. * * @return the concatanated string. */ static public String getChildCharacterData(Element parentEl) { if (parentEl == null) { return null; } Node tempNode = parentEl.getFirstChild(); StringBuffer strBuf = new StringBuffer(); CharacterData charData; while (tempNode != null) { switch (tempNode.getNodeType()) { case Node.TEXT_NODE: case Node.CDATA_SECTION_NODE: charData = (CharacterData) tempNode; strBuf.append(charData.getData()); break; } tempNode = tempNode.getNextSibling(); } return strBuf.toString(); } /** * DOCUMENTME * * @param el DOCUMENTME * @param id DOCUMENTME * * @return DOCUMENTME */ public static Element getElementByID(Element el, String id) { if (el == null) { return null; } String thisId = el.getAttribute("id"); if (id.equals(thisId)) { return el; } NodeList list = el.getChildNodes(); for (int i = 0; i < list.getLength(); i++) { Node node = list.item(i); if (node instanceof Element) { Element ret = getElementByID((Element) node, id); if (ret != null) { return ret; } } } return null; } /** * Return the first child element of the given element. Null if no children * are found. * * @param elem Element whose child is to be returned * * @return the first child element. */ public static Element getFirstChildElement(Element elem) { return (Element) findChildByType(elem, Node.ELEMENT_NODE); } /** * Given a prefix and a node, return the namespace URI that the prefix has * been associated with. This method is useful in resolving the namespace * URI of attribute values which are being interpreted as QNames. If prefix * is null, this method will return the default namespace. * * @param context the starting node (looks up recursively from here) * @param prefix the prefix to find an xmlns:prefix=uri for * * @return the namespace URI or null if not found */ public static String getNamespaceURIFromPrefix(Node context, String prefix) { short nodeType = context.getNodeType(); Node tempNode = null; switch (nodeType) { case Node.ATTRIBUTE_NODE: { tempNode = ((Attr) context).getOwnerElement(); break; } case Node.ELEMENT_NODE: { tempNode = context; break; } default: { tempNode = context.getParentNode(); break; } } while ((tempNode != null) && (tempNode.getNodeType() == Node.ELEMENT_NODE)) { Element tempEl = (Element) tempNode; String namespaceURI = (prefix == null) ? getAttribute(tempEl, "xmlns") : getAttributeNS(tempEl, NS_URI_XMLNS, prefix); if (namespaceURI != null) { return namespaceURI; } tempNode = tempEl.getParentNode(); } return null; } /** * Return the next sibling element of the given element. Null if no more * sibling elements are found. * * @param elem Element whose sibling element is to be returned * * @return the next sibling element. */ public static Element getNextSiblingElement(Element elem) { for (Node n = elem.getNextSibling(); n != null; n = n.getNextSibling()) { if (n.getNodeType() == Node.ELEMENT_NODE) { return (Element) n; } } return null; } /** * DOCUMENTME * * @param el DOCUMENTME * @param attrName DOCUMENTME * * @return DOCUMENTME * * @throws IllegalArgumentException DOCUMENTME */ public static QName getQualifiedAttributeValue(Element el, String attrName) throws IllegalArgumentException { String attrValue = DOMUtils.getAttribute(el, attrName); if (attrValue != null) { int index = attrValue.indexOf(':'); String attrValuePrefix = (index != -1) ? attrValue.substring(0, index) : null; String attrValueLocalPart = attrValue.substring(index + 1); String attrValueNamespaceURI = DOMUtils.getNamespaceURIFromPrefix(el, attrValuePrefix); if (attrValueNamespaceURI != null) { return new QName(attrValueNamespaceURI, attrValueLocalPart); } throw new IllegalArgumentException("Unable to determine " + "namespace of '" + ((attrValuePrefix != null) ? (attrValuePrefix + ":") : "") + attrValueLocalPart + "'."); } return null; } /** * Count number of children of a certain type of the given element. * * @param elem the element whose kids are to be counted * @param nodeType DOCUMENTME * * @return the number of matching kids. */ public static int countKids(Element elem, short nodeType) { int nkids = 0; for (Node n = elem.getFirstChild(); n != null; n = n.getNextSibling()) { if (n.getNodeType() == nodeType) { nkids++; } } return nkids; } /** * This method traverses the DOM and grabs namespace declarations * on parent elements with the intent of preserving them for children. <em>Note * that the DOM level 3 document method {@link Element#getAttribute(java.lang.String)} * is not desirable in this case, as it does not respect namespace prefix * bindings that may affect attribute values. (Namespaces in DOM are * uncategorically a mess, especially in the context of XML Schema.)</em> * @param el the starting element * @return a {@link Map} containing prefix bindings. */ public static Map<String, String> getParentNamespaces(Element el) { HashMap<String, String> pref = new HashMap<String, String>(); Map<String, String> mine = getMyNamespaces(el); Node n = el.getParentNode(); while (n != null && n.getNodeType() != Node.DOCUMENT_NODE) { if (n instanceof Element) { Element l = (Element) n; NamedNodeMap nnm = l.getAttributes(); int len = nnm.getLength(); for (int i = 0; i < len; ++i) { Attr a = (Attr) nnm.item(i); if (isNSAttribute(a)) { String key = getNSPrefixFromNSAttr(a); String uri = a.getValue(); // prefer prefix bindings that are lower down in the tree. if (pref.containsKey(key) || mine.containsKey(key)) continue; pref.put(key, uri); } } } n = n.getParentNode(); } return pref; } /** * Construct a {@link NSContext} instance for the supplied element. * @param el the <code>Element</code> to gather the namespace context for * @return the <code>NSContext</code> */ public static NSContext getMyNSContext(Element el) { NSContext ns = new NSContext(); ns.register(getParentNamespaces(el)); ns.register(getMyNamespaces(el)); return ns; } public static Map<String, String> getMyNamespaces(Element el) { HashMap<String, String> mine = new HashMap<String, String>(); NamedNodeMap nnm = el.getAttributes(); int len = nnm.getLength(); for (int i = 0; i < len; ++i) { Attr a = (Attr) nnm.item(i); if (isNSAttribute(a)) { mine.put(getNSPrefixFromNSAttr(a), a.getValue()); } } return mine; } /** * Test whether an attribute contains a namespace declaration. * @param a an {@link Attr} to test. * @return <code>true</code> if the {@link Attr} is a namespace declaration */ public static boolean isNSAttribute(Attr a) { assert a != null; String s = a.getNamespaceURI(); return (s != null && s.equals(NS_URI_XMLNS)); } /** * Fetch the non-null namespace prefix from a {@link Attr} that declares * a namespace. (The DOM APIs will return <code>null</code> for a non-prefixed * declaration. * @param a the {@link Attr} with the declaration (must be non-<code>null</code). * @return the namespace prefix or <code>""</code> if none was * declared, e.g., <code>xmlns="foo"</code>. */ public static String getNSPrefixFromNSAttr(Attr a) { assert a != null; assert isNSAttribute(a); if (a.getPrefix() == null) { return ""; } return a.getName().substring(a.getPrefix().length() + 1); } /** * Convert a DOM node to a stringified XML representation. */ static public String domToString(Node node) { if (node == null) { throw new IllegalArgumentException("Cannot stringify null Node!"); } String value = null; short nodeType = node.getNodeType(); if (nodeType == Node.ELEMENT_NODE || nodeType == Node.DOCUMENT_NODE || nodeType == Node.DOCUMENT_FRAGMENT_NODE) { // serializer doesn't handle Node type well, only Element DOMSerializerImpl ser = new DOMSerializerImpl(); ser.setParameter(Constants.DOM_NAMESPACES, Boolean.TRUE); ser.setParameter(Constants.DOM_WELLFORMED, Boolean.FALSE); ser.setParameter(Constants.DOM_VALIDATE, Boolean.FALSE); // create a proper XML encoding header based on the input document; // default to UTF-8 if the parent document's encoding is not accessible String usedEncoding = "UTF-8"; Document parent = node.getOwnerDocument(); if (parent != null) { String parentEncoding = parent.getXmlEncoding(); if (parentEncoding != null) { usedEncoding = parentEncoding; } } // the receiver of the DOM DOMOutputImpl out = new DOMOutputImpl(); out.setEncoding(usedEncoding); // we write into a String StringWriter writer = new StringWriter(4096); out.setCharacterStream(writer); // out, ye characters! ser.write(node, out); writer.flush(); // finally get the String value = writer.toString(); } else { value = node.getNodeValue(); } return value; } public static void serialize(Element elmt, OutputStream ostr) { String usedEncoding = "UTF-8"; Document parent = elmt.getOwnerDocument(); if (parent != null) { String parentEncoding = parent.getXmlEncoding(); if (parentEncoding != null) { usedEncoding = parentEncoding; } } DOMOutputImpl out = new DOMOutputImpl(); out.setEncoding(usedEncoding); DOMSerializerImpl ser = new DOMSerializerImpl(); out.setByteStream(ostr); ser.write(elmt, out); } /** * Convert a DOM node to a stringified XML representation. */ static public String domToStringLevel2(Node node) { if (node == null) { throw new IllegalArgumentException("Cannot stringify null Node!"); } String value = null; short nodeType = node.getNodeType(); if (nodeType == Node.ELEMENT_NODE || nodeType == Node.DOCUMENT_NODE) { // serializer doesn't handle Node type well, only Element DOMSerializerImpl ser = new DOMSerializerImpl(); ser.setParameter(Constants.DOM_NAMESPACES, Boolean.TRUE); ser.setParameter(Constants.DOM_WELLFORMED, Boolean.FALSE); ser.setParameter(Constants.DOM_VALIDATE, Boolean.FALSE); // the receiver of the DOM DOMOutputImpl out = new DOMOutputImpl(); out.setEncoding("UTF-8"); // we write into a String StringWriter writer = new StringWriter(4096); out.setCharacterStream(writer); // out, ye characters! ser.write(node, out); writer.flush(); // finally get the String value = writer.toString(); } else { value = node.getNodeValue(); } return value; } /** * Return the first child element of the given element which has the given * attribute with the given value. * * @param elem the element whose children are to be searched * @param attrName the attrib that must be present * @param attrValue the desired value of the attribute * * @return the first matching child element. */ public static Element findChildElementWithAttribute(Element elem, String attrName, String attrValue) { for (Node n = elem.getFirstChild(); n != null; n = n.getNextSibling()) { if (n.getNodeType() == Node.ELEMENT_NODE) { if (attrValue.equals(DOMUtils.getAttribute((Element) n, attrName))) { return (Element) n; } } } return null; } /** * Parse a String into a DOM. * * @param s DOCUMENTME * * @return DOCUMENTME * * @throws SAXException DOCUMENTME * @throws IOException DOCUMENTME */ static public Element stringToDOM(String s) throws SAXException, IOException { return parse(new InputSource(new StringReader(s))).getDocumentElement(); } /** * Perform a naive check to see if a document is a WSDL document * based on the root element name and namespace URI. * @param d the {@link Document} to check * @return <code>true</code> if the root element appears correct */ public static boolean isWsdlDocument(Document d) { Element e = d.getDocumentElement(); String uri = e.getNamespaceURI(); String localName = e.getLocalName(); if (uri == null || localName == null) { return false; } return uri.equals(WSDL_NS) && localName.equals(WSDL_ROOT_ELEMENT); } /** * Perform a naive check to see if a document is an XML schema document * based on the root element name and namespace URI. * @param d the {@link Document} to check * @return <code>true</code> if the root element appears correct */ public static boolean isXmlSchemaDocument(Document d) { Element e = d.getDocumentElement(); String uri = e.getNamespaceURI(); String localName = e.getLocalName(); if (uri == null || localName == null) { return false; } return uri.equals(XSD_NS) && localName.equals(XSD_ROOT_ELEMENT); } public static final String WSDL_NS = "http://schemas.xmlsoap.org/wsdl/"; public static final String WSDL_ROOT_ELEMENT = "definitions"; public static final String XSD_NS = "http://www.w3.org/2001/XMLSchema"; public static final String XSD_ROOT_ELEMENT = "schema"; /** * @param el */ public static void pancakeNamespaces(Element el) { Map ns = getParentNamespaces(el); Document d = el.getOwnerDocument(); assert d != null; Iterator it = ns.keySet().iterator(); while (it.hasNext()) { String key = (String) it.next(); String uri = (String) ns.get(key); Attr a = d.createAttributeNS(NS_URI_XMLNS, (key.length() != 0) ? ("xmlns:" + key) : ("xmlns")); a.setValue(uri); el.setAttributeNodeNS(a); } } public static Document newDocument() { DocumentBuilder db = getBuilder(); return db.newDocument(); } /** * Parse an XML stream using the pooled document builder. * @param inputStream input stream * @return parsed XML document */ public static Document parse(InputStream inputStream) throws SAXException, IOException { return parse(new InputSource(inputStream)); } /** * Parse an XML document located using an {@link InputSource} using the * pooled document builder. */ public static Document parse(InputSource inputSource) throws SAXException, IOException { DocumentBuilder db = getBuilder(); return db.parse(inputSource); } /** * Parse an XML document located using an {@link InputSource} using the * pooled document builder. */ public static Document sourceToDOM(Source inputSource) throws IOException { try { /* // Requires JDK 1.6+ if (inputSource instanceof StAXSource) { StAXSource stax = (StAXSource) inputSource; //if (stax.getXMLEventReader() != null || sax.getXMLStreamReader() != null) { if (sax.getXMLStreamReader() != null) { return parse(stax.getXMLStreamReader()); } } */ if (inputSource instanceof SAXSource) { InputSource sax = ((SAXSource) inputSource).getInputSource(); if (sax.getCharacterStream() != null || sax.getByteStream() != null) { return parse(((SAXSource) inputSource).getInputSource()); } } if (inputSource instanceof DOMSource) { Node node = ((DOMSource) inputSource).getNode(); if (node != null) { return toDOMDocument(node); } } if (inputSource instanceof StreamSource) { StreamSource stream = (StreamSource) inputSource; if (stream.getReader() != null || stream.getInputStream() != null) { return toDocumentFromStream((StreamSource) inputSource); } } DOMResult domresult = new DOMResult(newDocument()); Transformer txer = getTransformer(); txer.transform(inputSource, domresult); return (Document) domresult.getNode(); } catch (SAXException e) { throwIOException(e); } catch (TransformerException e) { throwIOException(e); } throw new IllegalArgumentException("Cannot parse XML source: " + inputSource.getClass()); } /** * Check that an element is empty, i.e., it contains no non-whitespace text or * elements as children. * @param el the element * @return <code>true</code> if the element is empty, <code>false</code> if not. */ public static boolean isEmptyElement(Element el) { NodeList nl = el.getChildNodes(); int len = nl.getLength(); for (int i = 0; i < len; ++i) { switch (nl.item(i).getNodeType()) { case Node.CDATA_SECTION_NODE: case Node.TEXT_NODE: String s = nl.item(i).getNodeValue(); if (s != null && s.trim().length() > 0) { return false; } break; case Node.ELEMENT_NODE: return false; } } return true; } public static QName getNodeQName(Node el) { String localName = el.getLocalName(); String namespaceUri = el.getNamespaceURI(); if (localName == null) { String nodeName = el.getNodeName(); int colonIndex = nodeName.indexOf(":"); if (colonIndex > 0) { localName = nodeName.substring(0, colonIndex); namespaceUri = nodeName.substring(colonIndex + 1); } else { localName = nodeName; namespaceUri = null; } } return new QName(namespaceUri, localName); } public static QName getNodeQName(String qualifiedName) { int index = qualifiedName.indexOf(":"); if (index >= 0) { return new QName(qualifiedName.substring(0, index), qualifiedName.substring(index + 1)); } else { return new QName(qualifiedName); } } /** * Remove the child nodes under another node. * @param target the <code>Node</code> to remove the children from. */ public static void removeChildren(Node target) { while (target.hasChildNodes()) { target.removeChild(target.getFirstChild()); } } /** * Drop the attributes from an element, except possibly an <code>xmlns</code> * attribute that declares its namespace. * @param target the element whose attributes will be removed. * @param flag preserve namespace declaration */ public static void removeAttributes(Element target, boolean flag) { if (!target.hasAttributes()) { return; } String prefix = target.getPrefix(); NamedNodeMap nnm = target.getAttributes(); Attr toPutBack = null; if (flag) { if (prefix == null) { toPutBack = target.getAttributeNodeNS(NS_URI_XMLNS, "xmlns"); } else { toPutBack = target.getAttributeNodeNS(NS_URI_XMLNS, "xmlns:" + prefix); } } while (nnm.getLength() != 0) { target.removeAttributeNode((Attr) nnm.item(0)); } if (toPutBack != null) { target.setAttributeNodeNS(toPutBack); } } public static Element findChildByName(Element parent, QName name) { return findChildByName(parent, name, false); } public static Element findChildByName(Element parent, QName name, boolean recurse) { if (parent == null) throw new IllegalArgumentException("null parent"); if (name == null) throw new IllegalArgumentException("null name"); NodeList nl = parent.getChildNodes(); for (int i = 0; i < nl.getLength(); ++i) { Node c = nl.item(i); if (c.getNodeType() != Node.ELEMENT_NODE) continue; // For a reason that I can't fathom, when using in-mem DAO we actually get elements with // no localname. String nodeName = c.getLocalName() != null ? c.getLocalName() : c.getNodeName(); if (new QName(c.getNamespaceURI(), nodeName).equals(name)) return (Element) c; } if (recurse) { NodeList cnl = parent.getChildNodes(); for (int i = 0; i < cnl.getLength(); ++i) { Node c = cnl.item(i); if (c.getNodeType() != Node.ELEMENT_NODE) continue; Element result = findChildByName((Element) c, name, recurse); if (result != null) return result; } } return null; } public static Node findChildByType(Element elem, int type) { if (elem == null) throw new NullPointerException("elem parameter must not be null!"); for (Node n = elem.getFirstChild(); n != null; n = n.getNextSibling()) { if (n.getNodeType() == type) { return n; } } return null; } public static String getTextContent(Node node) { for (int m = 0; m < node.getChildNodes().getLength(); m++) { Node child = node.getChildNodes().item(m); if (child.getNodeType() == Node.TEXT_NODE) { String childText = child.getNodeValue().trim(); if (childText.length() > 0) return childText; } } return null; } public static Element getElementContent(Node node) { for (int m = 0; m < node.getChildNodes().getLength(); m++) { Node child = node.getChildNodes().item(m); if (child.getNodeType() == Node.ELEMENT_NODE) return (Element) child; } return null; } public static void injectNamespaces(Element domElement, NSContext nscontext) { for (String uri : nscontext.getUriSet()) { String prefix = nscontext.getPrefix(uri); if (prefix == null || "".equals(prefix)) domElement.setAttributeNS(DOMUtils.NS_URI_XMLNS, "xmlns", uri); else domElement.setAttributeNS(DOMUtils.NS_URI_XMLNS, "xmlns:" + prefix, uri); } } /** * Adds namespaces including all prefixes. * This is needed for correct handling of xsi:type attributes. * @param domElement An element wi which the namespace attributes should be added. * @param nscontext A namespace context. * @author k.petrauskas */ public static void injectNamespacesWithAllPrefixes(Element domElement, NSContext nscontext) { if (__log.isDebugEnabled()) __log.debug("injectNamespacesWithAllPrefixes: element=" + domToString(domElement) + " nscontext=" + nscontext); for (Map.Entry<String, String> entry : nscontext.toMap().entrySet()) { String prefix = entry.getKey(); String uri = entry.getValue(); if (prefix == null || "".equals(prefix)) domElement.setAttributeNS(DOMUtils.NS_URI_XMLNS, "xmlns", uri); else domElement.setAttributeNS(DOMUtils.NS_URI_XMLNS, "xmlns:" + prefix, uri); if (__log.isDebugEnabled()) __log.debug("injectNamespacesWithAllPrefixes: added namespace: prefix=\"" + prefix + "\" uri=\"" + uri + "\""); } if (__log.isDebugEnabled()) __log.debug("injectNamespacesWithAllPrefixes: result: element=" + domToString(domElement)); } public static void copyNSContext(Element source, Element dest) { Map<String, String> sourceNS = getParentNamespaces(source); sourceNS.putAll(getMyNamespaces(source)); Map<String, String> destNS = getParentNamespaces(dest); destNS.putAll(getMyNamespaces(dest)); // (source - dest) to avoid adding twice the same ns on dest for (String pr : destNS.keySet()) sourceNS.remove(pr); for (Map.Entry<String, String> entry : sourceNS.entrySet()) { String prefix = entry.getKey(); String uri = entry.getValue(); if (prefix == null || "".equals(prefix)) dest.setAttributeNS(DOMUtils.NS_URI_XMLNS, "xmlns", uri); else dest.setAttributeNS(DOMUtils.NS_URI_XMLNS, "xmlns:" + prefix, uri); } } public static Document toDOMDocument(Node node) throws TransformerException { // If the node is the document, just cast it if (node instanceof Document) { return (Document) node; // If the node is an element } else if (node instanceof Element) { Element elem = (Element) node; // If this is the root element, return its owner document if (elem.getOwnerDocument().getDocumentElement() == elem) { return elem.getOwnerDocument(); // else, create a new doc and copy the element inside it } else { Document doc = newDocument(); doc.appendChild(doc.importNode(node, true)); return doc; } // other element types are not handled } else { throw new TransformerException("Unable to convert DOM node to a Document"); } } public static Document toDocumentFromStream(StreamSource source) throws IOException, SAXException { DocumentBuilder builder = getBuilder(); Document document = null; Reader reader = source.getReader(); if (reader != null) { document = builder.parse(new InputSource(reader)); } else { InputStream inputStream = source.getInputStream(); if (inputStream != null) { InputSource inputsource = new InputSource(inputStream); inputsource.setSystemId(source.getSystemId()); document = builder.parse(inputsource); } else { throw new IOException("No input stream or reader available"); } } return document; } // sadly, as of JDK 5.0 IOException still doesn't support new IOException(Throwable) private static void throwIOException(Throwable t) throws IOException { IOException e = new IOException(t.getMessage()); e.setStackTrace(t.getStackTrace()); throw e; } public static Document parse(XMLStreamReader reader) throws XMLStreamException { Document doc = newDocument(); parse(reader, doc, doc); return doc; } private static void parse(XMLStreamReader reader, Document doc, Node parent) throws XMLStreamException { int event = reader.getEventType(); while (reader.hasNext()) { switch (event) { case XMLStreamConstants.START_ELEMENT: // create element Element e = doc.createElementNS(reader.getNamespaceURI(), reader.getLocalName()); if (reader.getPrefix() != null && reader.getPrefix() != "") { e.setPrefix(reader.getPrefix()); } parent.appendChild(e); // copy namespaces for (int ns = 0; ns < reader.getNamespaceCount(); ns++) { String uri = reader.getNamespaceURI(ns); String prefix = reader.getNamespacePrefix(ns); declare(e, uri, prefix); } // copy attributes for (int att = 0; att < reader.getAttributeCount(); att++) { String name = reader.getAttributeLocalName(att); String prefix = reader.getAttributePrefix(att); if (prefix != null && prefix.length() > 0) { name = prefix + ":" + name; } Attr attr = doc.createAttributeNS(reader.getAttributeNamespace(att), name); attr.setValue(reader.getAttributeValue(att)); e.setAttributeNode(attr); } // sub-nodes if (reader.hasNext()) { reader.next(); parse(reader, doc, e); } if (parent instanceof Document) { while (reader.hasNext()) reader.next(); return; } break; case XMLStreamConstants.END_ELEMENT: return; case XMLStreamConstants.CHARACTERS: if (parent != null) { parent.appendChild(doc.createTextNode(reader.getText())); } break; case XMLStreamConstants.COMMENT: if (parent != null) { parent.appendChild(doc.createComment(reader.getText())); } break; case XMLStreamConstants.CDATA: parent.appendChild(doc.createCDATASection(reader.getText())); break; case XMLStreamConstants.PROCESSING_INSTRUCTION: parent.appendChild(doc.createProcessingInstruction(reader.getPITarget(), reader.getPIData())); break; case XMLStreamConstants.ENTITY_REFERENCE: parent.appendChild(doc.createProcessingInstruction(reader.getPITarget(), reader.getPIData())); break; case XMLStreamConstants.NAMESPACE: case XMLStreamConstants.ATTRIBUTE: break; default: break; } if (reader.hasNext()) { event = reader.next(); } } } private static void declare(Element node, String uri, String prefix) { if (prefix != null && prefix.length() > 0) { node.setAttributeNS(NS_URI_XMLNS, "xmlns:" + prefix, uri); } else { if (uri != null) { node.setAttributeNS(NS_URI_XMLNS, "xmlns", uri); } } } private static Transformer getTransformer() { Transformer txer = __txers.get(); if (txer == null) { synchronized (_transformerFactory) { try { txer = _transformerFactory.newTransformer(); } catch (TransformerConfigurationException e) { String errmsg = "Transformer configuration error!"; __log.fatal(errmsg, e); throw new Error(errmsg, e); } } __txers.set(txer); } return txer; } private static DocumentBuilder getBuilder() { DocumentBuilder builder = __builders.get(); if (builder == null) { synchronized (__documentBuilderFactory) { try { builder = __documentBuilderFactory.newDocumentBuilder(); builder.setErrorHandler(new LoggingErrorHandler()); } catch (ParserConfigurationException e) { __log.error(e); throw new RuntimeException(e); } } __builders.set(builder); } return builder; } public static List<Element> findChildrenByName(Element parent, QName name) { if (parent == null) throw new IllegalArgumentException("null parent"); if (name == null) throw new IllegalArgumentException("null name"); LinkedList<Element> ret = new LinkedList<Element>(); NodeList nl = parent.getChildNodes(); for (int i = 0; i < nl.getLength(); ++i) { Node c = nl.item(i); if (c.getNodeType() != Node.ELEMENT_NODE) continue; // For a reason that I can't fathom, when using in-mem DAO we actually get elements with // no localname. String nodeName = c.getLocalName() != null ? c.getLocalName() : c.getNodeName(); if (new QName(c.getNamespaceURI(), nodeName).equals(name)) ret.add((Element) c); } return ret; } /** * Somewhat eases the pain of dealing with both Lists and Nodelists by converting either * passed as parameter to a List. * @param nl a NodeList or a List * @return a List */ public static List<Node> toList(Object nl) { if (nl == null) return null; if (nl instanceof List) return (List<Node>) nl; NodeList cnl = (NodeList) nl; List<Node> ll = new ArrayList<Node>(); for (int m = 0; m < cnl.getLength(); m++) ll.add(cnl.item(m)); return ll; } public static Document getDocument(Node contextNode) { return (contextNode == null) ? DOMUtils.newDocument() : contextNode.getOwnerDocument(); } public static String getQualifiedName(QName qName) { String prefix = qName.getPrefix(), localPart = qName.getLocalPart(); return (prefix == null || "".equals(prefix)) ? localPart : (prefix + ":" + localPart); } /** * Deep clone, but don't fry, the given node in the context of the given document. * For all intents and purposes, the clone is the exact same copy of the node, * except that it might have a different owner document. * * This method is fool-proof, unlike the <code>adoptNode</code> or <code>adoptNode</code> methods, * in that it doesn't assume that the given node has a parent or a owner document. * * @param document * @param sourceNode * @return a clone of node */ public static Node cloneNode(Document document, Node sourceNode) { Node clonedNode = null; // what is my name? QName sourceQName = getNodeQName(sourceNode); String nodeName = sourceQName.getLocalPart(); String namespaceURI = sourceQName.getNamespaceURI(); // if the node is unqualified, don't assume that it inherits the WS-BPEL target namespace if (Namespaces.WSBPEL2_0_FINAL_EXEC.equals(namespaceURI)) { namespaceURI = null; } switch (sourceNode.getNodeType()) { case Node.ATTRIBUTE_NODE: if (namespaceURI == null) { clonedNode = document.createAttribute(nodeName); } else { String prefix = ((Attr) sourceNode).lookupPrefix(namespaceURI); // the prefix for the XML namespace can't be looked up, hence this... if (prefix == null && namespaceURI.equals(NS_URI_XMLNS)) { prefix = "xmlns"; } // if a prefix exists, qualify the name with it if (prefix != null && !"".equals(prefix)) { nodeName = prefix + ":" + nodeName; } // create the appropriate type of attribute if (prefix != null) { clonedNode = document.createAttributeNS(namespaceURI, nodeName); } else { clonedNode = document.createAttribute(nodeName); } } break; case Node.CDATA_SECTION_NODE: clonedNode = document.createCDATASection(((CDATASection) sourceNode).getData()); break; case Node.COMMENT_NODE: clonedNode = document.createComment(((Comment) sourceNode).getData()); break; case Node.DOCUMENT_FRAGMENT_NODE: clonedNode = document.createDocumentFragment(); break; case Node.DOCUMENT_NODE: clonedNode = document; break; case Node.ELEMENT_NODE: // create the appropriate type of element if (namespaceURI == null) { clonedNode = document.createElement(nodeName); } else { String prefix = namespaceURI.equals(Namespaces.XMLNS_URI) ? "xmlns" : ((Element) sourceNode).lookupPrefix(namespaceURI); if (prefix != null && !"".equals(prefix)) { nodeName = prefix + ":" + nodeName; clonedNode = document.createElementNS(namespaceURI, nodeName); } else { clonedNode = document.createElement(nodeName); } } // attributes are not treated as child nodes, so copy them explicitly NamedNodeMap attributes = ((Element) sourceNode).getAttributes(); for (int i = 0; i < attributes.getLength(); i++) { Attr attributeClone = (Attr) cloneNode(document, attributes.item(i)); if (attributeClone.getNamespaceURI() == null) { ((Element) clonedNode).setAttributeNode(attributeClone); } else { ((Element) clonedNode).setAttributeNodeNS(attributeClone); } } break; case Node.ENTITY_NODE: // TODO break; case Node.ENTITY_REFERENCE_NODE: clonedNode = document.createEntityReference(nodeName); // TODO break; case Node.NOTATION_NODE: // TODO break; case Node.PROCESSING_INSTRUCTION_NODE: clonedNode = document.createProcessingInstruction(((ProcessingInstruction) sourceNode).getData(), nodeName); break; case Node.TEXT_NODE: clonedNode = document.createTextNode(((Text) sourceNode).getData()); break; default: break; } // clone children of element and attribute nodes NodeList sourceChildren = sourceNode.getChildNodes(); if (sourceChildren != null) { for (int i = 0; i < sourceChildren.getLength(); i++) { Node sourceChild = sourceChildren.item(i); Node clonedChild = cloneNode(document, sourceChild); clonedNode.appendChild(clonedChild); // if the child has a textual value, parse it for any embedded prefixes if (clonedChild.getNodeType() == Node.TEXT_NODE || clonedChild.getNodeType() == Node.CDATA_SECTION_NODE) { parseEmbeddedPrefixes(sourceNode, clonedNode, clonedChild); } } } return clonedNode; } /** * Parse the text in the cloneChild for any embedded prefixes, and define it in it's parent element * * @param sourceNode * @param clonedNode * @param clonedChild */ private static void parseEmbeddedPrefixes(Node sourceNode, Node clonedNode, Node clonedChild) { Element clonedElement = null; if (clonedNode instanceof Attr) { clonedElement = ((Attr) clonedNode).getOwnerElement(); } else if (clonedNode instanceof Element) { clonedElement = (Element) clonedNode; } if (clonedElement == null) { // couldn't find an element to set prefixes on, so bail out return; } String text = ((Text) clonedChild).getNodeValue(); if (text != null && text.indexOf(":") > 0) { Name11Checker nameChecker = Name11Checker.getInstance(); for (int colonIndex = text.indexOf(":"); colonIndex != -1 && colonIndex < text.length(); colonIndex = text.indexOf(":", colonIndex + 1)) { StringBuffer prefixString = new StringBuffer(); for (int prefixIndex = colonIndex - 1; prefixIndex >= 0 && nameChecker.isNCNameChar(text.charAt(prefixIndex)); prefixIndex--) { prefixString.append(text.charAt(prefixIndex)); } prefixString.reverse(); if (prefixString.length() > 0) { String uri = sourceNode.lookupNamespaceURI(prefixString.toString()); if (uri != null) { clonedElement.setAttributeNS(NS_URI_XMLNS, "xmlns:" + prefixString, uri); } } } } } public static Element stringToDOM(byte[] bytes) throws SAXException, IOException { return stringToDOM(new String(bytes)); } public static byte[] domToBytes(Element element) { String stringifiedElement = domToString(element); return (stringifiedElement != null) ? stringifiedElement.getBytes() : null; } }