All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.solarnetwork.support.XmlSupport Maven / Gradle / Ivy

There is a newer version: 3.27.0
Show newest version
/* ===================================================================
 * XmlSupport.java
 * 
 * This program is free software; you can redistribute it and/or 
 * modify it under the terms of the GNU General Public License as 
 * published by the Free Software Foundation; either version 2 of 
 * the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License 
 * along with this program; if not, write to the Free Software 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
 * 02111-1307 USA
 * ===================================================================
 */

package net.solarnetwork.support;

import java.beans.PropertyDescriptor;
import java.beans.PropertyEditor;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessor;
import org.springframework.core.io.Resource;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

/**
 * A class to support services that use XML.
 * 
 * 

* The configurable properties of this class are: *

* *
*
docBuilderFactory
*
A JAXP {@link DocumentBuilderFactory} to use. If not configured, the * {@link DocumentBuilderFactory#newInstance()} method will be used to create a * default one.
* *
transformerFactory
*
A JAXP {@link TransformerFactory} for handling XSLT transformations with. * If not configured, the {@link TransformerFactory#newInstance()} method will * be used to create a default one.
* *
xpathFactory
*
A JAXP {@link XPathFactory} for handling XPath operations with. If not * configured the {@link XPathFactory#newInstance()} method will be used to * create a default one.
* *
nsContext
*
An optional {@link NamespaceContext} to use for proper XML namespace * handling in some contexts, such as XPath.
*
* * @author matt * @version 1.2 */ public class XmlSupport { /** A class-level logger. */ protected final Logger log = LoggerFactory.getLogger(getClass()); private NamespaceContext nsContext = null; private DocumentBuilderFactory docBuilderFactory = null; private XPathFactory xpathFactory = null; private TransformerFactory transformerFactory = null; /** * Constructor. */ public XmlSupport() { super(); } /** * Compile XPathExpression mappings from String XPath expressions. * * @param xpathMap * the XPath string expressions * @return the XPathExperssion mapping */ public Map getXPathExpressionMap(Map xpathMap) { Map datumXPathMap = new LinkedHashMap(); for ( Map.Entry me : xpathMap.entrySet() ) { try { datumXPathMap.put(me.getKey(), getXPathExpression(me.getValue())); } catch ( XPathExpressionException e ) { throw new RuntimeException(e); } } return datumXPathMap; } /** * Compile a single XPathExpression from a String XPath expression. The * {@link #getNsContext()} will be configured on the resulting * XPathExpression, if available. * * @param xpath * the XPath to compile * @return the compiled XPath * @throws XPathExpressionException * if the XPath cannot be compiled */ public XPathExpression getXPathExpression(String xpath) throws XPathExpressionException { XPath xp = getXpathFactory().newXPath(); if ( getNsContext() != null ) { xp.setNamespaceContext(getNsContext()); } return xp.compile(xpath); } /** * Get an XSLT Templates object from an XSLT Resource. * * @param resource * the XSLT Resource to load * @return the compiled Templates */ public Templates getTemplates(Resource resource) { TransformerFactory tf = TransformerFactory.newInstance(); try { return tf.newTemplates(new StreamSource(resource.getInputStream())); } catch ( TransformerConfigurationException e ) { throw new RuntimeException("Unable to load XSLT from resource [" + resource + ']'); } catch ( IOException e ) { throw new RuntimeException("Unable to load XSLT from resource [" + resource + ']'); } } /** * Turn an object into a simple XML Document, supporting custom property * editors. * *

* The returned XML will be a document with a single element with all * JavaBean properties turned into attributes. For example: *

* *
	 * <powerDatum
	 *   id="123"
	 *   pvVolts="123.123"
	 *   ... />
	 * 
* *

* {@link PropertyEditor} instances can be registered with the supplied * {@link BeanWrapper} for custom handling of properties, e.g. dates. *

* * @param bean * the object to turn into XML * @param elementName * the name of the XML element * @return the element, as an XML DOM Document */ public Document getDocument(BeanWrapper bean, String elementName) { Document dom = null; try { dom = getDocBuilderFactory().newDocumentBuilder().newDocument(); dom.appendChild(getElement(bean, elementName, dom)); } catch ( ParserConfigurationException e ) { throw new RuntimeException(e); } return dom; } /** * Turn an object into a simple XML Element, supporting custom property * editors. * *

* The returned XML will be a single element with all JavaBean properties * turned into attributes and the element named after the bean object's * class name. For example: *

* *
	 * <PowerDatum
	 *   id="123"
	 *   pvVolts="123.123"
	 *   ... />
	 * 
* *

* {@link PropertyEditor} instances can be registered with the supplied * {@link BeanWrapper} for custom handling of properties, e.g. dates. *

* * @param bean * the object to turn into XML * @param dom * the document * @return the element, as an XML DOM Document */ public Element getElement(BeanWrapper bean, Document dom) { String elementName = bean.getWrappedInstance().getClass().getSimpleName(); return getElement(bean, elementName, dom); } /** * Turn an object into a simple XML Element, supporting custom property * editors. * *

* The returned XML will be a single element with all JavaBean properties * turned into attributes. For example: *

* *
	 * <powerDatum
	 *   id="123"
	 *   pvVolts="123.123"
	 *   ... />
	 * 
* *

* {@link PropertyEditor} instances can be registered with the supplied * {@link BeanWrapper} for custom handling of properties, e.g. dates. *

* * @param bean * the object to turn into XML * @param elementName * the name of the XML element * @param dom * the document * @return the element, as an XML DOM Element */ public Element getElement(BeanWrapper bean, String elementName, Document dom) { PropertyDescriptor[] props = bean.getPropertyDescriptors(); Element root = null; root = dom.createElement(elementName); for ( int i = 0; i < props.length; i++ ) { PropertyDescriptor prop = props[i]; if ( prop.getReadMethod() == null ) { continue; } String propName = prop.getName(); if ( "class".equals(propName) ) { continue; } Object propValue = null; PropertyEditor editor = bean.findCustomEditor(prop.getPropertyType(), prop.getName()); if ( editor != null ) { editor.setValue(bean.getPropertyValue(propName)); propValue = editor.getAsText(); } else { propValue = bean.getPropertyValue(propName); } if ( propValue == null ) { continue; } if ( log.isTraceEnabled() ) { log.trace("attribute name: " + propName + " attribute value: " + propValue); } root.setAttribute(propName, propValue.toString()); } return root; } /** * Turn an object into a simple XML Document, supporting custom property * editors. * *

* The returned XML will be a single element with all JavaBean properties * turned into attributed. For example: *

* *
	 * <powerDatum
	 *   id="123"
	 *   pvVolts="123.123"
	 *   ... />
	 * 
* * @param bean * the object to turn into XML * @param elementName * the name of the XML element * @return the element, as XSLT Source * @see #getDocument(BeanWrapper, String) */ public Source getSource(BeanWrapper bean, String elementName) { Document dom = getDocument(bean, elementName); return getSource(dom); } /** * Turn a Document into a Source. * *

* This method will log the XML document at the FINEST level. *

* * @param dom * the Document to turn into XSLT source * @return the document, as XSLT Source */ public Source getSource(Document dom) { DOMSource result = new DOMSource(dom); if ( log.isDebugEnabled() ) { log.debug("XML: " + getXmlAsString(result, true)); } return result; } /** * Turn an XML Source into a String. * * @param source * the XML Source * @param indent * if {@literal true} then indent the result * @return the XML, as a String */ public String getXmlAsString(Source source, boolean indent) { ByteArrayOutputStream byos = new ByteArrayOutputStream(); try { Transformer xform = getTransformerFactory().newTransformer(); if ( indent ) { xform.setOutputProperty(OutputKeys.INDENT, "yes"); xform.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); } xform.transform(source, new StreamResult(byos)); } catch ( TransformerConfigurationException e ) { throw new RuntimeException(e); } catch ( TransformerException e ) { throw new RuntimeException(e); } return byos.toString(); } /** * Populate JavaBean properties via XPath extraction. * * @param bean * the object to set properties on * @param xml * the XML * @param xpathMap * the mapping of JavaBean property names to XPaths */ public void extractBeanDataFromXml(PropertyAccessor bean, Node xml, Map xpathMap) { for ( Map.Entry me : xpathMap.entrySet() ) { String val = extractStringFromXml(xml, me.getValue()); if ( val != null && !"".equals(val) ) { bean.setPropertyValue(me.getKey(), val); } } } /** * Extract a String value via an XPath expression. * * @param xml * the XML * @param xpath * the XPath expression * @return the String */ public String extractStringFromXml(Node xml, XPathExpression xpath) { try { return (String) xpath.evaluate(xml, XPathConstants.STRING); } catch ( XPathExpressionException e ) { throw new RuntimeException("Unable to extract string from XPath [" + xpath.toString() + "]", e); } } /** * Get the namespace context. * * @return the context */ public NamespaceContext getNsContext() { return nsContext; } /** * Set the namespace context. * * @param nsContext * the context to set */ public void setNsContext(NamespaceContext nsContext) { this.nsContext = nsContext; } /** * Get a document builder factory. * * @return the factory */ public DocumentBuilderFactory getDocBuilderFactory() { DocumentBuilderFactory result = this.docBuilderFactory; if ( result == null ) { result = DocumentBuilderFactory.newInstance(); result.setNamespaceAware(true); this.docBuilderFactory = result; } return result; } /** * Set the document builder factor. * * @param docBuilderFactory * the factory to set */ public void setDocBuilderFactory(DocumentBuilderFactory docBuilderFactory) { this.docBuilderFactory = docBuilderFactory; } /** * Get an XPath factory. * * @return the factory */ public XPathFactory getXpathFactory() { XPathFactory result = this.xpathFactory; if ( result == null ) { // work around Oracle JDK issues loading XPathFactory, see // https://java.net/projects/glassfish/lists/users/archive/2012-02/message/371 final ClassLoader origClassLoader = Thread.currentThread().getContextClassLoader(); final ClassLoader newClassLoader = XPathFactory.class.getClassLoader(); if ( newClassLoader != null ) { Thread.currentThread().setContextClassLoader(newClassLoader); } try { result = XPathFactory.newInstance(); this.xpathFactory = result; } finally { if ( newClassLoader != null ) { Thread.currentThread().setContextClassLoader(origClassLoader); } } } return result; } /** * Set the XPath factory. * * @param xpathFactory * the factory to set */ public void setXpathFactory(XPathFactory xpathFactory) { this.xpathFactory = xpathFactory; } /** * Get a transformer factory. * * @return the factory */ public TransformerFactory getTransformerFactory() { TransformerFactory result = this.transformerFactory; if ( result == null ) { // work around Oracle JDK issues loading XPathFactory, see // https://java.net/projects/glassfish/lists/users/archive/2012-02/message/371 final ClassLoader origClassLoader = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(XPathFactory.class.getClassLoader()); try { result = TransformerFactory.newInstance(); this.transformerFactory = result; } finally { Thread.currentThread().setContextClassLoader(origClassLoader); } } return result; } /** * Set the transformer factory. * * @param transformerFactory * the factory to set */ public void setTransformerFactory(TransformerFactory transformerFactory) { this.transformerFactory = transformerFactory; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy