blob: 6d66e8eee1e4932b790ccf87a6a8585c9a4e2cb9 [file] [log] [blame]
/**
* 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.cxf.fediz.core.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.Text;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* Few simple utils to read DOM. This is originally from the Jakarta Commons Modeler.
*
* @author Costin Manolache
*/
public final class DOMUtils {
private static final String XMLNAMESPACE = "xmlns";
private static final Map<ClassLoader, DocumentBuilder> DOCUMENT_BUILDERS = Collections
.synchronizedMap(new WeakHashMap<ClassLoader, DocumentBuilder>());
private DOMUtils() {
}
private static DocumentBuilder getBuilder() throws ParserConfigurationException {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (loader == null) {
loader = DOMUtils.class.getClassLoader();
}
if (loader == null) {
DocumentBuilderFactory dbf = createDocumentBuilderFactory();
return dbf.newDocumentBuilder();
}
DocumentBuilder builder = DOCUMENT_BUILDERS.get(loader);
if (builder == null) {
DocumentBuilderFactory dbf = createDocumentBuilderFactory();
builder = dbf.newDocumentBuilder();
DOCUMENT_BUILDERS.put(loader, builder);
}
return builder;
}
/**
* This function is much like getAttribute, but returns null, not "", for a nonexistent attribute.
*
* @param e
* @param attributeName
*/
public static String getAttributeValueEmptyNull(Element e, String attributeName) {
Attr node = e.getAttributeNode(attributeName);
if (node == null) {
return null;
}
return node.getValue();
}
/**
* Get the trimmed text content of a node or null if there is no text
*/
public static String getContent(Node n) {
String s = getRawContent(n);
if (s != null) {
s = s.trim();
}
return s;
}
/**
* Get the raw text content of a node or null if there is no text
*/
public static String getRawContent(Node n) {
if (n == null) {
return null;
}
StringBuilder b = null;
String s = null;
Node n1 = n.getFirstChild();
while (n1 != null) {
if (n1.getNodeType() == Node.TEXT_NODE) {
if (b != null) {
b.append(((Text)n1).getNodeValue());
} else if (s == null) {
s = ((Text)n1).getNodeValue();
} else {
b = new StringBuilder(s).append(((Text)n1).getNodeValue());
s = null;
}
}
n1 = n1.getNextSibling();
}
if (b != null) {
return b.toString();
}
return s;
}
/**
* Get the first element child.
*
* @param parent lookup direct childs
* @param name name of the element. If null return the first element.
*/
public static Node getChild(Node parent, String name) {
if (parent == null) {
return null;
}
Node first = parent.getFirstChild();
if (first == null) {
return null;
}
for (Node node = first; node != null; node = node.getNextSibling()) {
// System.out.println("getNode: " + name + " " +
// node.getNodeName());
if (node.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
if (name != null && name.equals(node.getNodeName())) {
return node;
}
if (name == null) {
return node;
}
}
return null;
}
public static String getAttribute(Node element, String attName) {
NamedNodeMap attrs = element.getAttributes();
if (attrs == null) {
return null;
}
Node attN = attrs.getNamedItem(attName);
if (attN == null) {
return null;
}
return attN.getNodeValue();
}
public static String getAttribute(Element element, QName attName) {
Attr attr;
if (StringUtils.isEmpty(attName.getNamespaceURI())) {
attr = element.getAttributeNode(attName.getLocalPart());
} else {
attr = element.getAttributeNodeNS(attName.getNamespaceURI(), attName.getLocalPart());
}
return attr == null ? null : attr.getValue();
}
public static void setAttribute(Node node, String attName, String val) {
NamedNodeMap attributes = node.getAttributes();
Node attNode = node.getOwnerDocument().createAttributeNS(null, attName);
attNode.setNodeValue(val);
attributes.setNamedItem(attNode);
}
public static void removeAttribute(Node node, String attName) {
NamedNodeMap attributes = node.getAttributes();
attributes.removeNamedItem(attName);
}
/**
* Set or replace the text value
*/
public static void setText(Node node, String val) {
Node chld = DOMUtils.getChild(node, Node.TEXT_NODE);
if (chld == null) {
Node textN = node.getOwnerDocument().createTextNode(val);
node.appendChild(textN);
return;
}
// change the value
chld.setNodeValue(val);
}
/**
* Find the first direct child with a given attribute.
*
* @param parent
* @param elemName name of the element, or null for any
* @param attName attribute we're looking for
* @param attVal attribute value or null if we just want any
*/
public static Node findChildWithAtt(Node parent, String elemName, String attName, String attVal) {
Node child = DOMUtils.getChild(parent, Node.ELEMENT_NODE);
if (attVal == null) {
while (child != null && (elemName == null || elemName.equals(child.getNodeName()))
&& DOMUtils.getAttribute(child, attName) != null) {
child = getNext(child, elemName, Node.ELEMENT_NODE);
}
} else {
while (child != null && (elemName == null || elemName.equals(child.getNodeName()))
&& !attVal.equals(DOMUtils.getAttribute(child, attName))) {
child = getNext(child, elemName, Node.ELEMENT_NODE);
}
}
return child;
}
/**
* Get the first child's content ( ie it's included TEXT node ).
*/
public static String getChildContent(Node parent, String name) {
Node first = parent.getFirstChild();
if (first == null) {
return null;
}
for (Node node = first; node != null; node = node.getNextSibling()) {
// System.out.println("getNode: " + name + " " +
// node.getNodeName());
if (name.equals(node.getNodeName())) {
return getRawContent(node);
}
}
return null;
}
public static QName getElementQName(Element el) {
return new QName(el.getNamespaceURI(), el.getLocalName());
}
/**
* Get the first direct child with a given type
*/
public static Element getFirstElement(Node parent) {
Node n = parent.getFirstChild();
while (n != null && Node.ELEMENT_NODE != n.getNodeType()) {
n = n.getNextSibling();
}
if (n == null) {
return null;
}
return (Element)n;
}
public static Element getNextElement(Element el) {
Node nd = el.getNextSibling();
while (nd != null) {
if (nd.getNodeType() == Node.ELEMENT_NODE) {
return (Element)nd;
}
nd = nd.getNextSibling();
}
return null;
}
/**
* Return the first element child with the specified qualified name.
*
* @param parent
* @param q
*/
public static Element getFirstChildWithName(Element parent, QName q) {
String ns = q.getNamespaceURI();
String lp = q.getLocalPart();
return getFirstChildWithName(parent, ns, lp);
}
/**
* Return the first element child with the specified qualified name.
*
* @param parent
* @param ns
* @param lp
*/
public static Element getFirstChildWithName(Element parent, String ns, String lp) {
for (Node n = parent.getFirstChild(); n != null; n = n.getNextSibling()) {
if (n instanceof Element) {
Element e = (Element)n;
String ens = (e.getNamespaceURI() == null) ? "" : e.getNamespaceURI();
if (ns.equals(ens) && lp.equals(e.getLocalName())) {
return e;
}
}
}
return null;
}
/**
* Return child elements with specified name.
*
* @param parent
* @param ns
* @param localName
*/
public static List<Element> getChildrenWithName(Element parent, String ns, String localName) {
List<Element> r = new ArrayList<>();
for (Node n = parent.getFirstChild(); n != null; n = n.getNextSibling()) {
if (n instanceof Element) {
Element e = (Element)n;
String eNs = (e.getNamespaceURI() == null) ? "" : e.getNamespaceURI();
if (ns.equals(eNs) && localName.equals(e.getLocalName())) {
r.add(e);
}
}
}
return r;
}
/**
* Returns all child elements with specified namespace.
*
* @param parent the element to search under
* @param ns the namespace to find elements in
* @return all child elements with specified namespace
*/
public static List<Element> getChildrenWithNamespace(Element parent, String ns) {
List<Element> r = new ArrayList<>();
for (Node n = parent.getFirstChild(); n != null; n = n.getNextSibling()) {
if (n instanceof Element) {
Element e = (Element)n;
String eNs = (e.getNamespaceURI() == null) ? "" : e.getNamespaceURI();
if (ns.equals(eNs)) {
r.add(e);
}
}
}
return r;
}
/**
* Get the first child of the specified type.
*
* @param parent
* @param type
*/
public static Node getChild(Node parent, int type) {
Node n = parent.getFirstChild();
while (n != null && type != n.getNodeType()) {
n = n.getNextSibling();
}
if (n == null) {
return null;
}
return n;
}
/**
* Get the next sibling with the same name and type
*/
public static Node getNext(Node current) {
String name = current.getNodeName();
int type = current.getNodeType();
return getNext(current, name, type);
}
/**
* Return the next sibling with a given name and type
*/
public static Node getNext(Node current, String name, int type) {
Node first = current.getNextSibling();
if (first == null) {
return null;
}
for (Node node = first; node != null; node = node.getNextSibling()) {
if (type >= 0 && node.getNodeType() != type) {
continue;
}
if (name == null) {
return node;
}
if (name.equals(node.getNodeName())) {
return node;
}
}
return null;
}
public static class NullResolver implements EntityResolver {
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
return new InputSource(new StringReader(""));
}
}
private static DocumentBuilderFactory createDocumentBuilderFactory() throws ParserConfigurationException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
dbf.setValidating(false);
dbf.setIgnoringComments(false);
dbf.setIgnoringElementContentWhitespace(true);
dbf.setNamespaceAware(true);
// dbf.setCoalescing(true);
// dbf.setExpandEntityReferences(true);
return dbf;
}
/**
* Read XML as DOM.
*/
public static Document readXml(InputStream is) throws SAXException, IOException,
ParserConfigurationException {
DocumentBuilder db = getBuilder();
return db.parse(is);
}
public static Document readXml(Reader is) throws SAXException, IOException, ParserConfigurationException {
InputSource ips = new InputSource(is);
DocumentBuilder db = getBuilder();
return db.parse(ips);
}
public static Document readXml(StreamSource is) throws SAXException, IOException,
ParserConfigurationException {
InputSource is2 = new InputSource();
is2.setSystemId(is.getSystemId());
is2.setByteStream(is.getInputStream());
is2.setCharacterStream(is.getReader());
DocumentBuilder db = getBuilder();
return db.parse(is2);
}
public static void writeXml(Node n, OutputStream os) throws TransformerException {
TransformerFactory tf = TransformerFactory.newInstance();
tf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
// identity
Transformer t = tf.newTransformer();
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.transform(new DOMSource(n), new StreamResult(os));
}
public static DocumentBuilder createDocumentBuilder() {
try {
return getBuilder();
} catch (ParserConfigurationException e) {
throw new RuntimeException("Couldn't find a DOM parser.", e);
}
}
public static Document createDocument() {
try {
return getBuilder().newDocument();
} catch (ParserConfigurationException e) {
throw new RuntimeException("Couldn't find a DOM parser.", e);
}
}
public static String getPrefixRecursive(Element el, String ns) {
String prefix = getPrefix(el, ns);
if (prefix == null && el.getParentNode() instanceof Element) {
prefix = getPrefixRecursive((Element)el.getParentNode(), ns);
}
return prefix;
}
public static String getPrefix(Element el, String ns) {
NamedNodeMap atts = el.getAttributes();
for (int i = 0; i < atts.getLength(); i++) {
Node node = atts.item(i);
String name = node.getNodeName();
if (ns.equals(node.getNodeValue())
&& (name != null && (XMLNAMESPACE.equals(name) || name.startsWith(XMLNAMESPACE + ":")))) {
return node.getLocalName();
}
}
return null;
}
/**
* Get all prefixes defined, up to the root, for a namespace URI.
*
* @param element
* @param namespaceUri
* @param prefixes
*/
public static void getPrefixesRecursive(Element element, String namespaceUri, List<String> prefixes) {
getPrefixes(element, namespaceUri, prefixes);
Node parent = element.getParentNode();
if (parent instanceof Element) {
getPrefixesRecursive((Element)parent, namespaceUri, prefixes);
}
}
/**
* Get all prefixes defined on this element for the specified namespace.
*
* @param element
* @param namespaceUri
* @param prefixes
*/
public static void getPrefixes(Element element, String namespaceUri, List<String> prefixes) {
NamedNodeMap atts = element.getAttributes();
for (int i = 0; i < atts.getLength(); i++) {
Node node = atts.item(i);
String name = node.getNodeName();
if (namespaceUri.equals(node.getNodeValue())
&& (name != null && (XMLNAMESPACE.equals(name) || name.startsWith(XMLNAMESPACE + ":")))) {
prefixes.add(node.getPrefix());
}
}
}
public static String createNamespace(Element el, String ns) {
String p = "ns1";
int i = 1;
while (getPrefix(el, ns) != null) {
p = "ns" + i;
i++;
}
addNamespacePrefix(el, ns, p);
return p;
}
/**
* Starting from a node, find the namespace declaration for a prefix. for a matching namespace
* declaration.
*
* @param node search up from here to search for namespace definitions
* @param searchPrefix the prefix we are searching for
* @return the namespace if found.
*/
public static String getNamespace(Node node, String searchPrefix) {
Element el;
while (!(node instanceof Element)) {
node = node.getParentNode();
}
el = (Element)node;
NamedNodeMap atts = el.getAttributes();
for (int i = 0; i < atts.getLength(); i++) {
Node currentAttribute = atts.item(i);
String currentLocalName = currentAttribute.getLocalName();
String currentPrefix = currentAttribute.getPrefix();
if (searchPrefix.equals(currentLocalName) && XMLNAMESPACE.equals(currentPrefix)) {
return currentAttribute.getNodeValue();
} else if (StringUtils.isEmpty(searchPrefix) && XMLNAMESPACE.equals(currentLocalName)
&& StringUtils.isEmpty(currentPrefix)) {
return currentAttribute.getNodeValue();
}
}
Node parent = el.getParentNode();
if (parent instanceof Element) {
return getNamespace((Element)parent, searchPrefix);
}
return null;
}
public static List<Element> findAllElementsByTagNameNS(Element elem, String nameSpaceURI, String localName) {
List<Element> ret = new LinkedList<>();
findAllElementsByTagNameNS(elem, nameSpaceURI, localName, ret);
return ret;
}
private static void findAllElementsByTagNameNS(Element el, String nameSpaceURI, String localName,
List<Element> elementList) {
if (localName.equals(el.getLocalName()) && nameSpaceURI.contains(el.getNamespaceURI())) {
elementList.add(el);
}
Element elem = getFirstElement(el);
while (elem != null) {
findAllElementsByTagNameNS(elem, nameSpaceURI, localName, elementList);
elem = getNextElement(elem);
}
}
public static List<Element> findAllElementsByTagName(Element elem, String tagName) {
List<Element> ret = new LinkedList<>();
findAllElementsByTagName(elem, tagName, ret);
return ret;
}
private static void findAllElementsByTagName(Element el, String tagName, List<Element> elementList) {
if (tagName.equals(el.getTagName())) {
elementList.add(el);
}
Element elem = getFirstElement(el);
while (elem != null) {
findAllElementsByTagName(elem, tagName, elementList);
elem = getNextElement(elem);
}
}
public static boolean hasElementInNS(Element el, String namespace) {
if (namespace.equals(el.getNamespaceURI())) {
return true;
}
Element elem = getFirstElement(el);
while (elem != null) {
if (hasElementInNS(elem, namespace)) {
return true;
}
elem = getNextElement(elem);
}
return false;
}
/**
* Set a namespace/prefix on an element if it is not set already. First off, it searches for the element
* for the prefix associated with the specified namespace. If the prefix isn't null, then this is
* returned. Otherwise, it creates a new attribute using the namespace/prefix passed as parameters.
*
* @param element
* @param namespace
* @param prefix
* @return the prefix associated with the set namespace
*/
public static String setNamespace(Element element, String namespace, String prefix) {
String pre = getPrefixRecursive(element, namespace);
if (pre != null) {
return pre;
}
element.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:" + prefix, namespace);
return prefix;
}
/**
* Add a namespace prefix definition to an element.
*
* @param element
* @param namespaceUri
* @param prefix
*/
public static void addNamespacePrefix(Element element, String namespaceUri, String prefix) {
element.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:" + prefix, namespaceUri);
}
}