| /* |
| * 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.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| 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 Logger __log = LoggerFactory.getLogger(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.error(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; |
| } |
| } |