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