/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* 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 li.strolch.utils.helper;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.text.MessageFormat;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import li.strolch.utils.exceptions.XmlException;
/**
* Helper class for performing XML based tasks
*
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class XmlHelper {
/**
* PROP_LINE_SEPARATOR = "line.separator" : the system property to fetch defined line separator
*/
public static final String PROP_LINE_SEPARATOR = "line.separator"; //$NON-NLS-1$
/**
* UNIX_LINE_SEP = "\n" : mostly we want this line separator, instead of the windows version
*/
public static final String UNIX_LINE_SEP = "\n"; //$NON-NLS-1$
/**
* DEFAULT_ENCODING = "utf-8" : defines the default UTF-8 encoding expected of XML files
*/
public static final String DEFAULT_ENCODING = "UTF-8"; //$NON-NLS-1$
private static final Logger logger = LoggerFactory.getLogger(XmlHelper.class);
/**
* Parses an XML file on the file system and returns the resulting {@link Document} object
*
* @param xmlFile
* the {@link File} which has the path to the XML file to read
*/
public static void parseDocument(File xmlFile, DefaultHandler xmlHandler) {
try (FileInputStream xmlFileInputStream = new FileInputStream(xmlFile);) {
parseDocument(xmlFileInputStream, xmlHandler);
String msg = "SAX parsed file {0}"; //$NON-NLS-1$
logger.info(MessageFormat.format(msg, xmlFile.getAbsolutePath()));
} catch (IOException e) {
String msg = "Failed to parse XML file: {0} due to: {1}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, xmlFile.getAbsolutePath(), e.getMessage());
throw new XmlException(msg, e);
}
}
/**
* Parses an XML file on the file system and returns the resulting {@link Document} object
*
* @param xmlFileInputStream
* the XML {@link InputStream} which is to be parsed
*/
public static void parseDocument(InputStream xmlFileInputStream, DefaultHandler xmlHandler) {
try {
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
sp.parse(xmlFileInputStream, xmlHandler);
} catch (ParserConfigurationException e) {
throw new XmlException("Failed to initialize a SAX Parser: " + e.getMessage(), e); //$NON-NLS-1$
} catch (SAXException e) {
throw new XmlException("The XML stream is not parseable: " + e.getMessage(), e); //$NON-NLS-1$
} catch (IOException e) {
throw new XmlException("The XML stream not be read: " + e.getMessage(), e); //$NON-NLS-1$
}
}
/**
* Writes an {@link Element} to an XML file on the file system
*
* @param rootElement
* the {@link Element} to write to the file system
* @param file
* the {@link File} describing the path on the file system where the XML file should be written to
*
* @throws RuntimeException
* if something went wrong while creating the XML configuration, or writing the element
*/
public static void writeElement(Element rootElement, File file) throws RuntimeException {
Document document = createDocument();
document.appendChild(rootElement);
XmlHelper.writeDocument(document, file, DEFAULT_ENCODING);
}
/**
* Writes a {@link Document} to an XML file on the file system
*
* @param document
* the {@link Document} to write to the file system
* @param file
* the {@link File} describing the path on the file system where the XML file should be written to
* @param encoding
* encoding to use to write the file
*
* @throws RuntimeException
* if something went wrong while creating the XML configuration, or writing the element
*/
public static void writeDocument(Document document, File file) throws RuntimeException {
writeDocument(document, file, DEFAULT_ENCODING);
}
/**
* Writes a {@link Document} to an XML file on the file system
*
* @param document
* the {@link Document} to write to the file system
* @param file
* the {@link File} describing the path on the file system where the XML file should be written to
* @param encoding
* encoding to use to write the file
*
* @throws RuntimeException
* if something went wrong while creating the XML configuration, or writing the element
*/
public static void writeDocument(Document document, File file, String encoding) throws RuntimeException {
String msg = "Exporting document element {0} to {1}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, document.getNodeName(), file.getAbsolutePath());
XmlHelper.logger.info(msg);
writeDocument(document, new StreamResult(file), encoding);
}
/**
* Writes a {@link Document} to an XML file on the file system
*
* @param document
* the {@link Document} to write to the file system
* @param outputStream
* stream to write document to
*
* @throws RuntimeException
* if something went wrong while creating the XML configuration, or writing the element
*/
public static void writeDocument(Document document, OutputStream outputStream) throws RuntimeException {
writeDocument(document, new StreamResult(outputStream), DEFAULT_ENCODING);
}
/**
* Writes a {@link Document} to an XML file on the file system
*
* @param document
* the {@link Document} to write to the file system
* @param outputStream
* stream to write document to
*
* @throws RuntimeException
* if something went wrong while creating the XML configuration, or writing the element
*/
public static String writeToString(Document document) throws RuntimeException {
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
writeDocument(document, new StreamResult(out), DEFAULT_ENCODING);
return out.toString(DEFAULT_ENCODING);
} catch (UnsupportedEncodingException e) {
throw new XmlException("Failed to create Document: " + e.getLocalizedMessage(), e); //$NON-NLS-1$
}
}
/**
* Writes a {@link Document} to an XML file on the file system
*
* @param document
* the {@link Document} to write to the file system
* @param file
* the {@link File} describing the path on the file system where the XML file should be written to
* @param encoding
* encoding to use to write the file
*
* @throws RuntimeException
* if something went wrong while creating the XML configuration, or writing the element
*/
public static void writeDocument(Document document, StreamResult streamResult, String encoding)
throws RuntimeException {
String lineSep = System.getProperty(PROP_LINE_SEPARATOR);
try {
String docEncoding = document.getInputEncoding();
if (StringHelper.isEmpty(docEncoding)) {
docEncoding = encoding;
}
if (!lineSep.equals("\n")) { //$NON-NLS-1$
XmlHelper.logger.info("Overriding line separator to \\n"); //$NON-NLS-1$
System.setProperty(PROP_LINE_SEPARATOR, UNIX_LINE_SEP);
}
// Set up a transformer
TransformerFactory transfac = TransformerFactory.newInstance();
Transformer transformer = transfac.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); //$NON-NLS-1$
transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$
transformer.setOutputProperty(OutputKeys.ENCODING, docEncoding);
transformer.setOutputProperty("{http://xml.apache.org/xalan}indent-amount", "2"); //$NON-NLS-1$ //$NON-NLS-2$
// transformer.setOutputProperty("{http://xml.apache.org/xalan}line-separator", "\t");
// Transform to file
Source xmlSource = new DOMSource(document);
transformer.transform(xmlSource, streamResult);
} catch (Exception e) {
throw new XmlException("Exception while exporting to file: " + e, e); //$NON-NLS-1$
} finally {
System.setProperty(PROP_LINE_SEPARATOR, lineSep);
}
}
/**
* Returns a new document instance
*
* @return a new document instance
*
* @throws RuntimeException
* if something went wrong while creating the XML configuration
*/
public static Document createDocument() throws RuntimeException {
try {
DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
Document document = docBuilder.newDocument();
return document;
} catch (DOMException e) {
throw new XmlException("Failed to create Document: " + e.getLocalizedMessage(), e); //$NON-NLS-1$
} catch (ParserConfigurationException e) {
throw new XmlException("Failed to create Document: " + e.getLocalizedMessage(), e); //$NON-NLS-1$
}
}
}