/* * Copyright (c) 2010-2011 Ardesco Solutions - http://www.ardescosolutions.com * * 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. */ package com.lazerycode.ebselen.handlers; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.*; import org.xml.sax.InputSource; import javax.xml.XMLConstants; import javax.xml.namespace.NamespaceContext; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.*; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.xpath.*; import java.io.*; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.Map; public class XMLHandler { private static final Logger LOGGER = LoggerFactory.getLogger(XMLHandler.class); private Document xmlDocument = null; private LocalNamespaceContext namespaceMappings = new LocalNamespaceContext(); public XMLHandler(File absoluteFile) throws Exception { if (absoluteFile.exists()) { createDocumentAndCollectPrefixes(new InputSource(new FileReader(absoluteFile)), false); } else { throw new IOException("File does not exist!"); } } public XMLHandler(File absoluteFile, boolean scanEntireDocumentForNamespaces) throws Exception { if (absoluteFile.exists()) { createDocumentAndCollectPrefixes(new InputSource(new FileReader(absoluteFile)), scanEntireDocumentForNamespaces); } else { throw new IOException("File does not exist!"); } } public XMLHandler(String sourceXML) throws Exception { createDocumentAndCollectPrefixes(new InputSource(new StringReader(sourceXML.trim().replaceFirst("^([\\W]+)<", "<"))), false); } public XMLHandler(String sourceXML, boolean scanEntireDocumentForNamespaces) throws Exception { createDocumentAndCollectPrefixes(new InputSource(new StringReader(sourceXML.trim().replaceFirst("^([\\W]+)<", "<"))), scanEntireDocumentForNamespaces); } private void createDocumentAndCollectPrefixes(InputSource documentSource, boolean scanEntireDocument) throws Exception { DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(); domFactory.setNamespaceAware(true); this.xmlDocument = domFactory.newDocumentBuilder().parse(documentSource); this.namespaceMappings.findNamespaces(this.xmlDocument.getFirstChild(), scanEntireDocument); } class LocalNamespaceContext implements NamespaceContext { private final String defaultNamespace = ""; private Map<String, String> prefixMappedToUri = new HashMap<String, String>(); private Map<String, String> uriMappedToPrefix = new HashMap<String, String>(); public void findNamespaces(Node node, boolean attributesOnly) { NamedNodeMap attributes = node.getAttributes(); if (attributes == null) { return; } for (int currentAttribute = 0; currentAttribute < attributes.getLength(); currentAttribute++) { storeAttribute((Attr) attributes.item(currentAttribute)); } if (!attributesOnly) { NodeList childNodes = node.getChildNodes(); for (int child = 0; child < childNodes.getLength(); child++) { if (childNodes.item(child).getNodeType() == Node.ELEMENT_NODE) { findNamespaces(childNodes.item(child), false); } } } } private void storeAttribute(Attr attribute) { if (attribute.getNamespaceURI() != null && attribute.getNamespaceURI().equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) { if (attribute.getNodeName().equals(XMLConstants.XMLNS_ATTRIBUTE)) { addToContext(attribute.getNodeValue()); } else { addToContext(attribute.getLocalName(), attribute.getNodeValue()); } } } private void addToContext(String prefix, String uri) { this.prefixMappedToUri.put(prefix, uri); this.uriMappedToPrefix.put(uri, prefix); } private void addToContext(String uri) { this.prefixMappedToUri.put(this.defaultNamespace, uri); this.uriMappedToPrefix.put(uri, this.defaultNamespace); } public String getNamespaceURI(String prefix) { if (prefix == null || prefix.equals(XMLConstants.DEFAULT_NS_PREFIX)) { return this.prefixMappedToUri.get(this.defaultNamespace); } else { return this.prefixMappedToUri.get(prefix); } } public String getPrefix(String namespaceURI) { return uriMappedToPrefix.get(namespaceURI); } public Iterator getPrefixes(String namespaceURI) { ArrayList<String> prefixes = new ArrayList<String>(); for (String URI : this.uriMappedToPrefix.keySet()) { if (URI.equals(namespaceURI)) { prefixes.add(URI); } } return prefixes.iterator(); } } /** * Create an XPath Expression. * * @param xPathLocator XPath location to build the Expression from. * @return XPathExpression - Created Expression. * @throws XPathExpressionException */ private XPathExpression constructAnXPathExpression(String xPathLocator) throws XPathExpressionException { XPath xPath = XPathFactory.newInstance().newXPath(); xPath.setNamespaceContext(namespaceMappings); return xPath.compile(xPathLocator); } /** * Create an Element reference. * * @param locator - XPath location of the element. * @return Element - Element object referenced by the XPath locator. * @throws XPathExpressionException */ private Element getElement(String locator) throws XPathExpressionException { return (Element) constructAnXPathExpression(locator).evaluate(this.xmlDocument, XPathConstants.NODE); } /** * Return the result of an XPath query on an XML Document in int format * * @param query - The XPath query * @return int - Result of the query * @throws Exception */ public int performXPathQueryReturnInteger(String query) throws Exception { return Integer.parseInt(performXPathQueryReturnString(query)); } /** * Return the result of an XPath query on an XML Document in String format * * @param query - The XPath query * @return String - Result of the query * @throws Exception */ public String performXPathQueryReturnString(String query) throws Exception { return constructAnXPathExpression(query).evaluate(this.xmlDocument, XPathConstants.STRING).toString(); } /** * Add text to an element. * * @param text - Text to add to the element. * @param locator - XPath location of the element. * @throws Exception */ public void addTextToElement(String text, String locator) throws Exception { Element element = getElement(locator); Node textNode = xmlDocument.createTextNode(text); element.appendChild(textNode); } /** * Add a child element to an existing element (Will go to the end of the list) * * @param elementType - Type of child element to add (e.g. div) * @param locator - XPath location of the element. * @throws Exception */ public void addChildElement(String elementType, String locator) throws Exception { Element element = getElement(locator); Node childNode = xmlDocument.createElement(elementType); element.appendChild(childNode); } /** * Add an attribute to an element. * * @param attributeType - Attribute to add (e.g. class). * @param value - Value to assign to the attribute. * @param locator - XPath location of the attribute. * @throws Exception */ public void addAttribute(String attributeType, String value, String locator) throws Exception { Element element = getElement(locator); element.setAttribute(attributeType, value); } /** * Return the current XML as a string * * @return String - Current XML * @throws Exception */ public String returnXML() throws Exception { StringWriter xmlAsString = new StringWriter(); TransformerFactory.newInstance().newTransformer().transform(new DOMSource(this.xmlDocument), new StreamResult(xmlAsString)); return xmlAsString.toString().trim(); } /** * Write the current XML object to file. * * @param absoluteFileName - Absolute filename to write XML object to * @return String - Directory that file has been written to * @throws Exception */ public String writeXMLFile(String absoluteFileName) throws Exception { if (this.xmlDocument == null) { LOGGER.error("The Document object is null, unable to generate a file!"); return null; } Source source = new DOMSource(this.xmlDocument); FileHandler outputFile = new FileHandler(absoluteFileName, true); try { Result output = new StreamResult(outputFile.getWriteableFile()); TransformerFactory.newInstance().newTransformer().transform(source, output); } catch (TransformerConfigurationException Ex) { LOGGER.error(" Error creating file: " + Ex); } catch (TransformerException Ex) { LOGGER.error(" Error creating file: " + Ex); } outputFile.close(); return outputFile.getAbsoluteFile(); } }