package com.voxeo.moho.common.util;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
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.transform.OutputKeys;
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.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.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* A utility class to cover up the rough bits of xml parsing
*
* @author Kyle Liu
*/
public class XmlUtils {
private static ThreadLocal<Document> documentThreadLocal = new ThreadLocal<Document>();
private static ThreadLocal<DocumentBuilder> builderThreadLocal = new ThreadLocal<DocumentBuilder>() {
@Override
protected DocumentBuilder initialValue() {
return getNewDocumentBuilder();
}
};
private static DocumentBuilder getNewDocumentBuilder() {
try {
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setAttribute("http://xml.org/sax/features/namespaces", true);
factory.setAttribute("http://xml.org/sax/features/validation", false);
factory.setAttribute("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
factory.setAttribute("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
factory.setNamespaceAware(true);
factory.setIgnoringElementContentWhitespace(true);
factory.setValidating(false);
factory.setIgnoringComments(false);
final DocumentBuilder builder = factory.newDocumentBuilder();
return builder;
}
catch (final ParserConfigurationException e) {
throw new RuntimeException("Failed to create DocumentBuilder", e);
}
}
private static DocumentBuilder getOwnerDocumentBuilder() {
return builderThreadLocal.get();
}
public static Document createDocument() {
return getNewDocumentBuilder().newDocument();
}
public static Document getOwnerDocument() {
Document doc = documentThreadLocal.get();
if (doc == null) {
doc = getOwnerDocumentBuilder().newDocument();
documentThreadLocal.set(doc);
}
return doc;
}
public static Element parse(final String xmlString) throws IOException {
return parse(new ByteArrayInputStream(xmlString.getBytes(LanguageUtils.DEFAULT_ENCODING)));
}
public static Element parse(final InputStream xmlStream) throws IOException {
try {
return getOwnerDocumentBuilder().parse(xmlStream).getDocumentElement();
}
catch (final SAXException e) {
throw new IOException(e.toString());
}
}
public static Element parse(final InputSource source) throws IOException {
try {
return getOwnerDocumentBuilder().parse(source).getDocumentElement();
}
catch (final SAXException e) {
throw new IOException(e.toString());
}
}
public static String render(final Node node) {
try {
final StringWriter writer = new StringWriter();
final DOMSource domSource = new DOMSource(node);
final StreamResult streamResult = new StreamResult(writer);
final TransformerFactory tf = TransformerFactory.newInstance();
final Transformer serializer = tf.newTransformer();
serializer.setOutputProperty(OutputKeys.INDENT, "yes");
serializer.transform(domSource, streamResult);
return writer.toString();
}
catch (final Exception e) {
throw new RuntimeException("Error serializing DOM", e);
}
}
public static String normalize(final String s, final boolean canonical) {
final StringBuffer str = new StringBuffer();
final int len = s != null ? s.length() : 0;
for (int i = 0; i < len; i++) {
final char ch = s.charAt(i);
switch (ch) {
case '<': {
str.append("<");
break;
}
case '>': {
str.append(">");
break;
}
case '&': {
str.append("&");
break;
}
case '"': {
str.append(""");
break;
}
case '\r':
case '\n': {
if (canonical) {
str.append("");
str.append(Integer.toString(ch));
str.append(';');
break;
}
// else, default append char
}
default: {
str.append(ch);
}
}
}
return str.toString();
}
public static Element createElement(final String localPart) {
return createElement(new QName(localPart));
}
public static Element createElement(final String localPart, final Document doc) {
return createElement(new QName(localPart), doc);
}
public static Element createElement(final QName qname) {
return createElement(qname.getLocalPart(), qname.getPrefix(), qname.getNamespaceURI(), getOwnerDocument());
}
public static Element createElement(final QName qname, final Document doc) {
return createElement(qname.getLocalPart(), qname.getPrefix(), qname.getNamespaceURI(), doc);
}
public static Element createElement(final String localPart, final String prefix, final String uri, final Document doc) {
if (prefix == null || prefix.length() == 0) {
return doc.createElementNS(uri, localPart);
}
return doc.createElementNS(uri, prefix + ":" + localPart);
}
public static Text createTextNode(final String value) {
final Document doc = getOwnerDocument();
return doc.createTextNode(value);
}
public static Element copyElement(final Element element) {
return copyElement(element, null);
}
public static Element copyElement(final Element element, final Document doc) {
Element retval = null;
if (doc == null) {
retval = createElement(getElementQName(element));
}
else {
retval = createElement(getElementQName(element), doc);
}
copyAttributes(retval, element);
final String text = getTextContent(element);
if (text != null) {
retval.setTextContent(text);
}
for (final Element child : getChildElements(element)) {
final Element childCopy = copyElement(child, doc);
retval.appendChild(childCopy);
}
return retval;
}
public static Element getParentElement(final Node node) {
final Node parent = node.getParentNode();
return parent instanceof Element ? (Element) parent : null;
}
public static boolean hasChildElements(final Node node) {
final NodeList nlist = node.getChildNodes();
for (int i = 0; i < nlist.getLength(); i++) {
final Node child = nlist.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE) {
return true;
}
}
return false;
}
public static List<Element> getChildElements(final Node node) {
final ArrayList<Element> list = new ArrayList<Element>();
final NodeList nlist = node.getChildNodes();
for (int i = 0; i < nlist.getLength(); i++) {
final Node child = nlist.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE) {
list.add((Element) child);
}
}
return list;
}
public static Element getFirstChildElement(final Node node) {
return getFirstChildElementIntern(node, null);
}
public static Element getFirstChildElement(final Node node, final String nodeName) {
return getFirstChildElementIntern(node, new QName(nodeName));
}
public static Element getFirstChildElement(final Node node, final QName nodeName) {
return getFirstChildElementIntern(node, nodeName);
}
private static Element getFirstChildElementIntern(final Node node, final QName nodeName) {
Element childElement = null;
final Iterator it = getChildElementsIntern(node, nodeName, false).iterator();
if (it.hasNext()) {
childElement = (Element) it.next();
}
return childElement;
}
public static List<Element> getChildElements(final Node node, final String nodeName, final boolean recurse) {
return getChildElementsIntern(node, new QName(nodeName), recurse);
}
public static List<Element> getChildElements(final Node node, final boolean recurse, final String... nodeNames) {
final List<Element> retval = new ArrayList<Element>();
for (final String nodeName : nodeNames) {
retval.addAll(getChildElementsIntern(node, new QName(nodeName), recurse));
}
return retval;
}
private static List<Element> getChildElementsIntern(final Node node, final QName nodeName, final boolean recurse) {
final ArrayList<Element> list = new ArrayList<Element>();
if (node == null) {
return list;
}
final NodeList nlist = node.getChildNodes();
for (int i = 0; i < nlist.getLength(); i++) {
final Node child = nlist.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE) {
if (nodeName == null) {
list.add((Element) child);
}
else {
QName qname;
if (nodeName.getNamespaceURI().length() > 0) {
qname = new QName(child.getNamespaceURI(), child.getLocalName());
}
else {
qname = new QName(child.getLocalName() == null ? child.getNodeName() : child.getLocalName());
}
if (qname.equals(nodeName)) {
list.add((Element) child);
}
}
if (recurse) {
list.addAll(getChildElementsIntern(child, nodeName, true));
}
}
}
return Collections.unmodifiableList(list);
}
public static String getTextContent(final Node node) {
boolean hasTextContent = false;
final StringBuffer buffer = new StringBuffer();
final NodeList nlist = node.getChildNodes();
for (int i = 0; i < nlist.getLength(); i++) {
final Node child = nlist.item(i);
if (child.getNodeType() == Node.TEXT_NODE) {
buffer.append(child.getNodeValue());
hasTextContent = true;
}
}
return hasTextContent ? buffer.toString() : null;
}
public static String getElementContent(final Element element, final boolean trim) {
final NodeList nl = element.getChildNodes();
String attributeText = "";
for (int i = 0; i < nl.getLength(); i++) {
final Node n = nl.item(i);
if (n instanceof Text) {
attributeText += ((Text) n).getData();
}
}
if (trim) {
attributeText = attributeText.trim();
}
return attributeText;
}
public static QName getElementQName(final Element el) {
final String qualifiedName = el.getNodeName();
return resolveQName(el, qualifiedName);
}
public static QName resolveQName(final Element el, final String qualifiedName) {
QName qname;
String prefix = "";
String namespaceURI = "";
String localPart = qualifiedName;
final int colIndex = qualifiedName.indexOf(":");
if (colIndex > 0) {
prefix = qualifiedName.substring(0, colIndex);
localPart = qualifiedName.substring(colIndex + 1);
if ("xmlns".equals(prefix)) {
namespaceURI = "URI:XML_PREDEFINED_NAMESPACE";
}
else {
Element nsElement = el;
while (namespaceURI.equals("") && nsElement != null) {
namespaceURI = nsElement.getAttribute("xmlns:" + prefix);
if (namespaceURI.equals("")) {
nsElement = getParentElement(nsElement);
}
}
}
if (namespaceURI.equals("")) {
throw new IllegalArgumentException("Cannot find namespace uri for: " + qualifiedName);
}
}
qname = new QName(namespaceURI, localPart, prefix);
return qname;
}
public static String getAttributeValue(final Element el, final String attrName) {
return getAttributeValue(el, new QName(attrName));
}
public static String getAttributeValue(final Element el, final String attrName, final String defaultVal) {
final String retval = getAttributeValue(el, new QName(attrName));
return retval == null ? defaultVal : retval;
}
public static String getAttributeValue(final Element el, final QName attrName) {
String attr = null;
if ("".equals(attrName.getNamespaceURI())) {
attr = el.getAttribute(attrName.getLocalPart());
}
else {
attr = el.getAttributeNS(attrName.getNamespaceURI(), attrName.getLocalPart());
}
if ("".equals(attr)) {
attr = null;
}
return attr;
}
public static QName getAttributeValueAsQName(final Element el, final String attrName) {
return getAttributeValueAsQName(el, new QName(attrName));
}
public static QName getAttributeValueAsQName(final Element el, final QName attrName) {
QName qname = null;
final String qualifiedName = getAttributeValue(el, attrName);
if (qualifiedName != null) {
qname = resolveQName(el, qualifiedName);
}
return qname;
}
public static boolean getAttributeValueAsBoolean(final Element el, final String attrName) {
return getAttributeValueAsBoolean(el, new QName(attrName));
}
public static boolean getAttributeValueAsBoolean(final Element el, final QName attrName) {
return StringUtils.toBoolean(getAttributeValue(el, attrName));
}
public static boolean getAttributeValueAsBoolean(final Element el, final String attrName, final boolean defaultVal) {
return getAttributeValueAsBoolean(el, new QName(attrName), defaultVal);
}
public static boolean getAttributeValueAsBoolean(final Element el, final QName attrName, final boolean defaultVal) {
final String ret = getAttributeValue(el, attrName);
if (ret == null) {
return defaultVal;
}
return StringUtils.toBoolean(ret);
}
public static int getAttributeValueAsInteger(final Element el, final String attrName, final int defaultVal) {
return getAttributeValueAsInteger(el, new QName(attrName), defaultVal);
}
public static int getAttributeValueAsInteger(final Element el, final QName attrName, final int defaultVal) {
final String attrVal = getAttributeValue(el, attrName);
return attrVal != null ? new Integer(attrVal) : defaultVal;
}
public static long getAttributeValueAsLong(final Element el, final String attrName, final long defaultVal) {
return getAttributeValueAsLong(el, new QName(attrName), defaultVal);
}
public static long getAttributeValueAsLong(final Element el, final QName attrName, final long defaultVal) {
final String attrVal = getAttributeValue(el, attrName);
return attrVal != null ? new Long(attrVal) : defaultVal;
}
public static Map<QName, String> getAttributes(final Element el) {
final Map<QName, String> attmap = new HashMap<QName, String>();
final NamedNodeMap attribs = el.getAttributes();
for (int i = 0; i < attribs.getLength(); i++) {
final Attr attr = (Attr) attribs.item(i);
final String name = attr.getName();
final QName qname = resolveQName(el, name);
final String value = attr.getNodeValue();
attmap.put(qname, value);
}
return attmap;
}
public static Map<String, String> getAttributeMap(final Element el) {
final Map<String, String> attmap = new HashMap<String, String>();
final NamedNodeMap attribs = el.getAttributes();
for (int i = 0; i < attribs.getLength(); i++) {
final Attr attr = (Attr) attribs.item(i);
final String name = attr.getName();
final String value = attr.getNodeValue();
attmap.put(name, value);
}
return attmap;
}
public static void copyAttributes(final Element destElement, final Element srcElement) {
final NamedNodeMap attribs = srcElement.getAttributes();
for (int i = 0; i < attribs.getLength(); i++) {
final Attr attr = (Attr) attribs.item(i);
final String uri = attr.getNamespaceURI();
final String qname = attr.getName();
final String value = attr.getNodeValue();
if (uri == null && qname.startsWith("xmlns")) {
}
else {
destElement.setAttributeNS(uri, qname, value);
}
}
}
public static void copyElementAttributes(final Element e, final Map<String, String> attributes, String[] filters) {
final Map<QName, String> map = getAttributes(e);
if (filters == null) {
filters = new String[0];
}
for (final QName qname : map.keySet()) {
boolean ignore = false;
final String name = qname.getLocalPart();
for (final String filter : filters) {
if (name.equals(filter)) {
ignore = true;
break;
}
}
if (!ignore) {
attributes.put(name, map.get(qname));
}
}
}
public static final String CATEGORY = "category";
public static final String ITEM = "item";
public static final String NAME = "name";
public static Element toElement(final Object o, final Document ctx) {
if (o == null) {
return null;
}
Element retval = null;
if (o instanceof Collection) {
retval = XmlUtils.createElement(CATEGORY, ctx);
for (final Iterator it = ((Collection) o).iterator(); it.hasNext();) {
retval.appendChild(toElement(it.next(), ctx));
}
}
else if (o instanceof Map) {
retval = XmlUtils.createElement(CATEGORY, ctx);
for (final Object entry : ((Map) o).entrySet()) {
retval.appendChild(toElement(entry, ctx));
}
}
else if (o instanceof Map.Entry) {
retval = toElement(((Map.Entry) o).getValue(), ctx);
retval.setAttribute(NAME, StringUtils.toString(((Map.Entry) o).getKey()));
}
else {
retval = XmlUtils.createElement(ITEM, ctx);
retval.setTextContent(StringUtils.toString(o));
}
return retval;
}
}