Manipulate w3c DOM trees
//
// Copyright 2007 Requea.
// ------------------------------------------------------------------------
// Licensed 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.
//
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.Stack;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
/**
* Utilities to manipulate w3c DOM trees.
* @author Pierre Dubois
*/
public class XMLUtils {
private static DocumentBuilderFactory fDocumentFactory;
private static Stack fParsersPool = new Stack();
/**
* Public Id and the Resource path (of the cached copy)
* of the DTDs for tag library descriptors.
*/
public static final String TAGLIB_DTD_PUBLIC_ID_11 =
"-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN";
public static final String TAGLIB_DTD_RESOURCE_PATH_11 =
"/javax/servlet/jsp/resources/web-jsptaglibrary_1_1.dtd";
public static final String TAGLIB_DTD_PUBLIC_ID_12 =
"-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN";
public static final String TAGLIB_DTD_RESOURCE_PATH_12 =
"/javax/servlet/jsp/resources/web-jsptaglibrary_1_2.dtd";
/**
* Public Id and the Resource path (of the cached copy)
* of the DTDs for web application deployment descriptors
*/
public static final String WEBAPP_DTD_PUBLIC_ID_22 =
"-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN";
public static final String WEBAPP_DTD_RESOURCE_PATH_22 =
"/javax/servlet/resources/web-app_2_2.dtd";
public static final String WEBAPP_DTD_PUBLIC_ID_23 =
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN";
public static final String WEBAPP_DTD_RESOURCE_PATH_23 =
"/javax/servlet/resources/web-app_2_3.dtd";
/**
* List of the Public IDs that we cache, and their
* associated location. This is used by
* an EntityResolver to return the location of the
* cached copy of a DTD.
*/
public static final String[] CACHED_DTD_PUBLIC_IDS = {
TAGLIB_DTD_PUBLIC_ID_11,
TAGLIB_DTD_PUBLIC_ID_12,
WEBAPP_DTD_PUBLIC_ID_22,
WEBAPP_DTD_PUBLIC_ID_23,
};
public static final String[] CACHED_DTD_RESOURCE_PATHS = {
TAGLIB_DTD_RESOURCE_PATH_11,
TAGLIB_DTD_RESOURCE_PATH_12,
WEBAPP_DTD_RESOURCE_PATH_22,
WEBAPP_DTD_RESOURCE_PATH_23,
};
private static synchronized void initFactory() {
if(fDocumentFactory != null)
return;
fDocumentFactory =
DocumentBuilderFactory.newInstance();
fDocumentFactory.setNamespaceAware(true);
fDocumentFactory.setIgnoringElementContentWhitespace(true);
fDocumentFactory.setValidating(false);
}
public static synchronized DocumentBuilder getParser() throws ParserConfigurationException {
if(fParsersPool.isEmpty()) {
// create a new parser
initFactory();
DocumentBuilder builder = fDocumentFactory.newDocumentBuilder();
builder.setEntityResolver(entityResolver);
return builder;
} else {
return (DocumentBuilder)fParsersPool.pop();
}
}
public static synchronized void releaseParser(DocumentBuilder parser) {
if(parser != null)
fParsersPool.push(parser);
}
/**
* Parse an xml document as an imput stream.
* @param is
* @return
* @throws XMLException
*/
public static Document parse(InputStream is) {
DocumentBuilder builder = null;
try {
builder = getParser();
// parse the document
Document doc = builder.parse(is);
return doc;
} catch (Exception e) {
} finally {
releaseParser(builder);
}
}
/**
* Parses an xml document as a string.
* @param xml
* @return
* @throws XMLException
*/
public static Document parse(String xml) {
DocumentBuilder builder = null;
try {
builder = getParser();
// parse the document
try {
InputStream is = new ByteArrayInputStream(xml.getBytes());
Document doc = builder.parse(is);
return doc;
} catch (SAXException e) {
} catch (IOException e) {
}
} catch (ParserConfigurationException e) {
} finally {
releaseParser(builder);
}
}
/**
* Creates a new and empty document.
* @return
*/
public static Document newDocument() {
DocumentBuilder builder = null;
try {
builder = getParser();
return builder.newDocument();
} catch (ParserConfigurationException e) {
throw new RuntimeException(e);
} finally {
releaseParser(builder);
}
}
/**
* Creates a new document with a document root element.
* @param name
* @return
*/
public static Element newElement(String name) {
DocumentBuilder builder = null;
try {
builder = getParser();
Document doc = builder.newDocument();
Element el = doc.createElement(name);
doc.appendChild(el);
return el;
} catch (ParserConfigurationException e) {
throw new RuntimeException(e);
} finally {
releaseParser(builder);
}
}
/**
* Serialize an element to a string.
* @param element
* @return
* @throws XMLException
*/
public static String ElementToString(Element element) {
return privateElementToString(element, true, true);
}
public static String ElementToString(Element element, boolean pretty) {
return privateElementToString(element, true, pretty);
}
/**
* Serialize a document to a string
* @param doc
* @return
*/
public static String DocumentToString(Document doc) {
return privateElementToString(doc.getDocumentElement(), false, true);
}
public static String DocumentToString(Document doc, boolean pretty) {
return privateElementToString(doc.getDocumentElement(), false, pretty);
}
/**
* Get the first child element to match a given name.
* Look for a child element in the same namespace as the parent.
*
* @param parent
* @param name
* @return
*/
public static Element getChild(Element parent, String name) {
Element child = getFirstChild(parent);
while(child != null) {
String tagName = child.getTagName();
if(tagName != null && tagName.equals(name)) {
return child;
}
if(child.getPrefix() != null && child.getPrefix().equals(parent.getPrefix()) && child.getLocalName() != null && child.getLocalName().equals(name)) {
return child;
}
child = getNext(child);
}
return child;
}
/**
* Get the first child element to match a given name.
* Look for a child element in the same namespace as the parent.
*
* @param parent
* @param name
* @return
*/
public static Element getChild(Element parent, String ns, String name) {
Element child = getFirstChild(parent);
while(child != null) {
if(child.getLocalName().equals(name)) {
if(ns == null && child.getNamespaceURI() == null) {
return child;
} else if(ns != null && ns.equals(child.getNamespaceURI())) {
return child;
}
}
child = getNext(child);
}
return child;
}
/**
* Get the first child element of an element.
* @param el
* @return
*/
public static Element getFirstChild(Element el) {
if(el == null) {
return null;
}
NodeList lst = el.getChildNodes();
int len = lst.getLength();
for (int i = 0; i < len; i++) {
Node n = lst.item(i);
if (n instanceof Element)
return (Element) n;
}
return null;
}
/**
* Get the next sibling element of a given element.
* @param el
* @return
*/
public static Element getNext(Element el) {
Node n = el.getNextSibling();
while (n != null && !(n instanceof Element)) {
// get the next one
n = n.getNextSibling();
}
if (n instanceof Element) {
return (Element) n;
}
// else, nothing to return
return null;
}
/**
* Get the next sibling element of a given element.
* @param el
* @return
*/
public static Element getNextSibling(Element el) {
String tagName = el.getTagName();
if(tagName == null) {
return null;
}
Node n = el.getNextSibling();
while (n != null && (
!(n instanceof Element) ||
!tagName.equals(((Element)n).getTagName()))) {
// get the next one
n = n.getNextSibling();
}
if (n instanceof Element) {
return (Element) n;
} else {
// else, nothing to return
return null;
}
}
/**
* Get the previous sibling element of a given element.
* @param el
* @return
*/
public static Element getPrevious(Element el) {
Node n = el.getPreviousSibling();
while (n != null && !(n instanceof Element)) {
// get the next one
n = n.getPreviousSibling();
}
if (n instanceof Element) {
return (Element) n;
} else {
// else, nothing to return
return null;
}
}
/**
* Get the previous sibling element of a given element.
* @param el
* @return
*/
public static Element getPreviousSibling(Element el) {
Node n = el.getPreviousSibling();
while (n != null && (
!(n instanceof Element) ||
!el.getTagName().equals(((Element)n).getTagName()))) {
// get the next one
n = n.getPreviousSibling();
}
if (n instanceof Element) {
return (Element) n;
} else {
// else, nothing to return
return null;
}
}
/**
* Returns the text value of an element.
* @param el
* @return
*/
public static String getTextValue(Element el) {
StringBuffer b = new StringBuffer();
// retrieve the text node child
NodeList nl = el.getChildNodes();
int len = nl.getLength();
for (int i = 0; i < len; i++) {
Node n = nl.item(i);
if (n instanceof Text) {
Text t = (Text) n;
b.append(t.getData());
}
}
// trim the result, ignoring the first spaces and cariage return
int iFirst =0;
for(; iFirst<b.length(); iFirst++) {
char c = b.charAt(iFirst);
if(c != ' ' && c != '\r' && c != '\n' && c != '\t') {
break;
}
}
// start by the end as well
int iLast = b.length()-1;
for(; iLast>=0; iLast--) {
char c = b.charAt(iLast);
if(c != ' ' && c != '\r' && c != '\n' && c != '\t') {
break;
}
}
return b.substring(iFirst, iLast+1);
}
/**
* Get the text value of a child element with a given name.
* @param parent
* @param name
* @return
*/
public static String getChildText(Element parent, String name) {
Element child = getChild(parent, name);
if (child != null) {
return getTextValue(child);
}
return null;
}
/**
* Get the text value of a child element with a given name.
* @param parent
* @param name
* @return
*/
public static String getChildText(Element parent, String ns, String name) {
Element child = getChild(parent, ns, name);
if (child != null) {
return getTextValue(child);
}
return null;
}
/**
* Adds an element as a child of a given element.
* The child is created with the same namespace as the parent.
* @param parent
* @param name
* @return
*/
public static Element addElement(Element parent, String name) {
Document doc = parent.getOwnerDocument();
String qname;
if(parent.getPrefix() != null) {
qname = parent.getPrefix() + ":" + name;
} else {
qname = name;
}
Element child = doc.createElementNS(parent.getNamespaceURI(), qname);
parent.appendChild(child);
return child;
}
/**
* Adds an element as a child of a given element and sets the text value.
* The child is created with the same namespace as the parent.
*
* @param parent
* @param name
* @param textValue
* @return
*/
public static Element addElement(
Element parent,
String name,
String textValue) {
Element child = addElement(parent, name);
// create a text node
if(textValue == null) {
textValue = "";
}
Text txt = child.getOwnerDocument().createTextNode(textValue);
child.appendChild(txt);
return child;
}
/**
* Sets the text value for a given element.
* @param el
* @param value
*/
public static void setText(Element el, String value) {
// remove the children if already exist
while (el.getFirstChild() != null) {
el.removeChild(el.getFirstChild());
}
if(value == null) {
value = "";
}
Text txt = el.getOwnerDocument().createTextNode(value);
el.appendChild(txt);
}
/**
* Sets the text value for a given element as a CDATA section
* @param el
* @param value
*/
public static void setCDATA(Element el, String value) {
// remove the children if already exist
while (el.getFirstChild() != null) {
el.removeChild(el.getFirstChild());
}
if(value == null) {
value = "";
}
CDATASection txt = el.getOwnerDocument().createCDATASection(value);
el.appendChild(txt);
}
/**
* Retrieve the namespace for a given prefix.
* Does a lookup into the parent hierarchy.
* @param el
* @param prefix
* @return
*/
public static String getNamespace(Element el, String prefix) {
Element parent = el;
while (parent != null) {
String ns = parent.getAttribute("xmlns:" + prefix);
if (ns != null && ns.length() > 0) {
return ns;
}
// get the parent
Node n = parent.getParentNode();
if (n instanceof Element) {
parent = (Element) n;
} else {
parent = null;
}
}
// nothing found
return null;
}
/*
* serialize an element to a string.
*/
private static String privateElementToString(
Element element,
boolean omitXMLDecl,
boolean pretty) {
try {
Source source = new DOMSource(element);
StringWriter out = new StringWriter();
StreamResult result = new StreamResult(out);
Transformer xformer = TransformerFactory.newInstance().newTransformer();
xformer.setOutputProperty("indent", "yes");
xformer.transform(source, result);
return out.toString();
} catch(Exception e) {
}
}
public static String getAttribute(Element el, String att) {
String str = el.getAttribute(att);
if(str == null || str.length() == 0) {
return null;
} else {
return str;
}
}
static EntityResolver entityResolver = new MyEntityResolver();
static private class MyEntityResolver implements EntityResolver {
public InputSource resolveEntity(String publicId, String systemId)
throws SAXException {
for (int i = 0; i < CACHED_DTD_PUBLIC_IDS.length; i++) {
String cachedDtdPublicId = CACHED_DTD_PUBLIC_IDS[i];
if (cachedDtdPublicId.equals(publicId)) {
String resourcePath = CACHED_DTD_RESOURCE_PATHS[i];
InputStream input = this.getClass().getResourceAsStream(
resourcePath);
if (input == null) {
throw new SAXException("file.not.found"+resourcePath);
}
InputSource isrc = new InputSource(input);
return isrc;
}
}
return null;
}
}
}
Related examples in the same category