ingo@102: /* ingo@102: * Copyright (c) 2010 by Intevation GmbH ingo@102: * ingo@102: * This program is free software under the LGPL (>=v2.1) ingo@102: * Read the file LGPL.txt coming with the software for details ingo@102: * or visit http://www.gnu.org/licenses/ if it does not exist. ingo@102: */ ingo@102: ingo@103: package de.intevation.artifacts.common.utils; ingo@102: sascha@135: import java.io.ByteArrayOutputStream; ingo@102: import java.io.File; ingo@102: import java.io.IOException; ingo@102: import java.io.OutputStream; ingo@102: ingo@102: import javax.xml.namespace.NamespaceContext; ingo@102: import javax.xml.namespace.QName; ingo@102: ingo@102: import javax.xml.parsers.DocumentBuilderFactory; ingo@102: import javax.xml.parsers.ParserConfigurationException; ingo@102: ingo@102: import javax.xml.transform.Transformer; ingo@102: import javax.xml.transform.TransformerConfigurationException; ingo@102: import javax.xml.transform.TransformerException; ingo@102: import javax.xml.transform.TransformerFactory; ingo@102: import javax.xml.transform.TransformerFactoryConfigurationError; ingo@102: ingo@102: import javax.xml.transform.dom.DOMSource; ingo@102: ingo@102: import javax.xml.transform.stream.StreamResult; ingo@102: ingo@102: import javax.xml.xpath.XPath; ingo@102: import javax.xml.xpath.XPathConstants; ingo@102: import javax.xml.xpath.XPathExpressionException; ingo@102: import javax.xml.xpath.XPathFactory; ingo@102: ingo@102: import org.apache.log4j.Logger; ingo@102: ingo@102: import org.w3c.dom.Attr; ingo@102: import org.w3c.dom.Document; ingo@102: import org.w3c.dom.Element; ingo@102: ingo@102: import org.xml.sax.SAXException; ingo@102: ingo@102: /** ingo@102: * Some helper functions to ease work with XML concering namespaces, XPATH ingo@102: * and so on. ingo@102: * @author Sascha L. Teichmann ingo@102: */ ingo@102: public final class XMLUtils ingo@102: { ingo@102: /** ingo@102: * W3C URL of XForms ingo@102: */ ingo@102: public static final String XFORM_URL = "http://www.w3.org/2002/xforms"; ingo@102: /** ingo@102: * W3C prefix of XForms ingo@102: */ ingo@102: public static final String XFORM_PREFIX = "xform"; ingo@102: ingo@102: private static Logger logger = Logger.getLogger(XMLUtils.class); ingo@102: ingo@102: private XMLUtils() { ingo@102: } ingo@102: ingo@102: /** ingo@102: * Helper class to generate elements and attributes with ingo@102: * namespaces. ingo@102: */ ingo@102: public static class ElementCreator ingo@102: { ingo@102: /** ingo@102: * owner document of the elements to be created ingo@102: */ ingo@102: protected Document document; ingo@102: /** ingo@102: * namespace to be used ingo@102: */ ingo@102: protected String ns; ingo@102: /** ingo@102: * prefix to be used ingo@102: */ ingo@102: protected String prefix; ingo@102: ingo@102: /** ingo@102: * Constructor to create an element/attribute creator ingo@102: * with a given namespace and namespace prefix using a ingo@102: * given owner document. ingo@102: * @param document The owning document ingo@102: * @param ns The namespace ingo@102: * @param prefix The namespace prefix ingo@102: */ ingo@102: public ElementCreator(Document document, String ns, String prefix) { ingo@102: this.document = document; ingo@102: this.ns = ns; ingo@102: this.prefix = prefix; ingo@102: } ingo@102: ingo@102: /** ingo@102: * Creates a new element using the owning document with ingo@102: * the this creators namespace and namespace prefix. ingo@102: * @param name The name of the element ingo@102: * @return The new element ingo@102: */ ingo@102: public Element create(String name) { ingo@102: Element element = document.createElementNS(ns, name); ingo@102: element.setPrefix(prefix); ingo@102: return element; ingo@102: } ingo@102: ingo@102: /** ingo@102: * Adds a new attribute and its value to a given element. ingo@102: * It does not set the namespace prefix. ingo@102: * @param element The element to add the attribute to ingo@102: * @param name The name of the attribute ingo@102: * @param value The value of the attribute ingo@102: */ ingo@102: public void addAttr(Element element, String name, String value) { ingo@102: addAttr(element, name, value, false); ingo@102: } ingo@102: ingo@102: /** ingo@102: * Adds a new attribute and its value to a given element. ingo@102: * If the namespace prefix is used is decided by the 'addPrefix' flag. ingo@102: * @param element The element to add the attribute to ingo@102: * @param name The name of the attribute ingo@102: * @param value The value of the attribute ingo@102: * @param addPrefix If true the creators namespace prefix is ingo@102: * set on the attribute. ingo@102: */ ingo@102: public void addAttr( ingo@102: Element element, ingo@102: String name, ingo@102: String value, ingo@102: boolean addPrefix ingo@102: ) { ingo@102: Attr attr = document.createAttributeNS(ns, name); ingo@102: attr.setValue(value); ingo@102: ingo@102: if (addPrefix) ingo@102: attr.setPrefix(prefix); ingo@102: ingo@102: element.setAttributeNode(attr); ingo@102: } ingo@102: } // class ElementCreator ingo@102: ingo@102: /** ingo@102: * Creates a new XML document ingo@102: * @return the new XML document ot null if something went wrong during ingo@102: * creation. ingo@102: */ ingo@102: public static final Document newDocument() { ingo@102: DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); ingo@102: factory.setNamespaceAware(true); ingo@102: ingo@102: try { ingo@102: return factory.newDocumentBuilder().newDocument(); ingo@102: } ingo@102: catch (ParserConfigurationException pce) { ingo@102: logger.error(pce.getLocalizedMessage(), pce); ingo@102: } ingo@102: return null; ingo@102: } ingo@102: ingo@102: /** ingo@102: * Loads a XML document namespace aware from a file ingo@102: * @param file The file to load. ingo@102: * @return the XML document or null if something went wrong ingo@102: * during loading. ingo@102: */ ingo@102: public static final Document parseDocument(File file) { ingo@102: DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); ingo@102: factory.setNamespaceAware(true); ingo@102: ingo@102: try { ingo@102: return factory.newDocumentBuilder().parse(file); ingo@102: } ingo@102: catch (ParserConfigurationException pce) { ingo@102: logger.error(pce.getLocalizedMessage(), pce); ingo@102: } ingo@102: catch (SAXException se) { ingo@102: logger.error(se.getLocalizedMessage(), se); ingo@102: } ingo@102: catch (IOException ioe) { ingo@102: logger.error(ioe.getLocalizedMessage(), ioe); ingo@102: } ingo@102: return null; ingo@102: } ingo@102: ingo@102: /** ingo@102: * Creates a new XPath without a namespace context. ingo@102: * @return the new XPath. ingo@102: */ ingo@102: public static final XPath newXPath() { ingo@102: return newXPath(null); ingo@102: } ingo@102: ingo@102: /** ingo@102: * Creates a new XPath with a given namespace context. ingo@102: * @param namespaceContext The namespace context to be used or null ingo@102: * if none should be used. ingo@102: * @return The new XPath ingo@102: */ ingo@102: public static final XPath newXPath(NamespaceContext namespaceContext) { ingo@102: XPathFactory factory = XPathFactory.newInstance(); ingo@102: XPath xpath = factory.newXPath(); ingo@102: if (namespaceContext != null) { ingo@102: xpath.setNamespaceContext(namespaceContext); ingo@102: } ingo@102: return xpath; ingo@102: } ingo@102: ingo@102: /** ingo@102: * Evaluates an XPath query on a given object and returns the result ingo@102: * as a given type. No namespace context is used. ingo@102: * @param root The object which is used as the root of the tree to ingo@102: * be searched in. ingo@102: * @param query The XPath query ingo@102: * @param returnTyp The type of the result. ingo@102: * @return The result of type 'returnTyp' or null if something ingo@102: * went wrong during XPath evaluation. ingo@102: */ ingo@102: public static final Object xpath( ingo@102: Object root, ingo@102: String query, ingo@102: QName returnTyp ingo@102: ) { ingo@102: return xpath(root, query, returnTyp, null); ingo@102: } ingo@102: ingo@102: /** ingo@102: * Evaluates an XPath query on a given object and returns the result ingo@102: * as a string. A given namespace context is used. ingo@102: * @param root The object which is used as the root of the tree to ingo@102: * be searched in. ingo@102: * @param query The XPath query ingo@102: * @param namespaceContext The namespace context to be used or null ingo@102: * if none should be used. ingo@102: * @return The result of the query or null if something went wrong ingo@102: * during XPath evaluation. ingo@102: */ ingo@102: public static final String xpathString( ingo@102: Object root, String query, NamespaceContext namespaceContext ingo@102: ) { ingo@102: return (String)xpath( ingo@102: root, query, XPathConstants.STRING, namespaceContext); ingo@102: } ingo@102: ingo@102: /** ingo@102: * Evaluates an XPath query on a given object and returns the result ingo@102: * as a given type. Optionally a namespace context is used. ingo@102: * @param root The object which is used as the root of the tree to ingo@102: * be searched in. ingo@102: * @param query The XPath query ingo@102: * @param returnType The type of the result. ingo@102: * @param namespaceContext The namespace context to be used or null ingo@102: * if none should be used. ingo@102: * @return The result of type 'returnTyp' or null if something ingo@102: * went wrong during XPath evaluation. ingo@102: */ ingo@102: public static final Object xpath( ingo@102: Object root, ingo@102: String query, ingo@102: QName returnType, ingo@102: NamespaceContext namespaceContext ingo@102: ) { ingo@102: if (root == null) { ingo@102: return null; ingo@102: } ingo@102: ingo@102: try { ingo@102: XPath xpath = newXPath(namespaceContext); ingo@102: if (xpath != null) { ingo@102: return xpath.evaluate(query, root, returnType); ingo@102: } ingo@102: } ingo@102: catch (XPathExpressionException xpee) { ingo@102: logger.error(xpee.getLocalizedMessage(), xpee); ingo@102: } ingo@102: ingo@102: return null; ingo@102: } ingo@102: ingo@102: /** ingo@102: * Streams out an XML document to a given output stream. ingo@102: * @param document The document to be streamed out. ingo@102: * @param out The output stream to be used. ingo@102: * @return true if operation succeeded else false. ingo@102: */ ingo@102: public static boolean toStream(Document document, OutputStream out) { ingo@102: try { ingo@102: Transformer transformer = ingo@102: TransformerFactory.newInstance().newTransformer(); ingo@102: DOMSource source = new DOMSource(document); ingo@102: StreamResult result = new StreamResult(out); ingo@102: transformer.transform(source, result); ingo@102: return true; ingo@102: } ingo@102: catch (TransformerConfigurationException tce) { ingo@102: logger.error(tce.getLocalizedMessage(), tce); ingo@102: } ingo@102: catch (TransformerFactoryConfigurationError tfce) { ingo@102: logger.error(tfce.getLocalizedMessage(), tfce); ingo@102: } ingo@102: catch (TransformerException te) { ingo@102: logger.error(te.getLocalizedMessage(), te); ingo@102: } ingo@102: ingo@102: return false; ingo@102: } sascha@135: sascha@135: /** sascha@135: * Transforms an XML document into a byte array. sascha@135: * @param document The document to be streamed out. sascha@135: * @return the byte array or null if operation failed or sascha@135: * document is null. sascha@135: */ sascha@135: public static byte [] toByteArray(Document document) { sascha@135: if (document == null) { sascha@135: return null; sascha@135: } sascha@135: ByteArrayOutputStream baos = new ByteArrayOutputStream(); sascha@135: return toStream(document, baos) sascha@135: ? baos.toByteArray() sascha@135: : null; sascha@135: } ingo@102: } ingo@102: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :