/*********************************************************************************** * * Copyright (c) 2014 Kamil Baczkowicz * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v1.0 which accompany this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * * The Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * * Kamil Baczkowicz - initial API and implementation and/or initial documentation * */ package pl.baczkowicz.spy.xml; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.io.StringWriter; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.stream.StreamSource; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import pl.baczkowicz.spy.exceptions.XMLException; /** * Simplifies XML marshalling and unmarshalling. */ @SuppressWarnings({ "rawtypes", "unchecked" }) public class XMLParser { /** XML marshaller. */ private final Marshaller marshaller; /** XML unmarshaller. */ private final Unmarshaller unmarshaller; /** * Creates the XMLParser with the namespace and schema file for validation. * * @param namespace The context path / namespace * @param schema The schema file to be used for validation * * @throws XMLException Thrown when cannot instantiate the marshaller/unmarshaller */ public XMLParser(final String namespace, final String schema) throws XMLException { try { JAXBContext jc = JAXBContext.newInstance(namespace); marshaller = jc.createMarshaller(); marshaller.setSchema(XMLSchemaUtils.createSchema(schema)); unmarshaller = jc.createUnmarshaller(); unmarshaller.setSchema(XMLSchemaUtils.createSchema(schema)); } catch (JAXBException e) { throw new XMLException("Cannot instantiate marshaller/unmarshaller for " + namespace, e); } } /** * Creates the XMLParser with the given class as root and schema file for validation. * * @param classToBeBound The class to be bound * @param schema The schema file to be used for validation * * @throws XMLException Thrown when cannot instantiate the marshaller/unmarshaller */ public XMLParser(final Class classToBeBound, final String schema) throws XMLException { try { JAXBContext jc = JAXBContext.newInstance(classToBeBound); marshaller = jc.createMarshaller(); marshaller.setSchema(XMLSchemaUtils.createSchema(schema)); unmarshaller = jc.createUnmarshaller(); unmarshaller.setSchema(XMLSchemaUtils.createSchema(schema)); } catch (JAXBException e) { throw new XMLException("Cannot instantiate marshaller/unmarshaller for " + classToBeBound, e); } } /** * Creates the XMLParser with the namespace and schema files for validation. * * @param namespace The context path / namespace * @param schemas The schema files to be used for validation * * @throws XMLException Thrown when cannot instantiate the marshaller/unmarshaller */ public XMLParser(final String namespace, final String[] schemas) throws XMLException { try { JAXBContext jc = JAXBContext.newInstance(namespace); marshaller = jc.createMarshaller(); marshaller.setSchema(XMLSchemaUtils.createSchema(schemas)); unmarshaller = jc.createUnmarshaller(); unmarshaller.setSchema(XMLSchemaUtils.createSchema(schemas)); } catch (JAXBException e) { throw new XMLException("Cannot instantiate marshaller/unmarshaller for " + namespace, e); } } /** * Creates the XMLParser with the given class as root and schema files for validation. * * @param classToBeBound The class to be bound * * @throws XMLException Thrown when cannot instantiate the marshaller/unmarshaller */ public XMLParser(final Class classToBeBound, final String[] schemas) throws XMLException { try { JAXBContext jc = JAXBContext.newInstance(classToBeBound); marshaller = jc.createMarshaller(); marshaller.setSchema(XMLSchemaUtils.createSchema(schemas)); unmarshaller = jc.createUnmarshaller(); unmarshaller.setSchema(XMLSchemaUtils.createSchema(schemas)); } catch (JAXBException e) { throw new XMLException("Cannot instantiate marshaller/unmarshaller for " + classToBeBound, e); } } /** * Creates the XMLParser with the given class as root. No schema validation. * * @param classToBeBound The class to be bound * * @throws XMLException Thrown when cannot instantiate the marshaller/unmarshaller */ public XMLParser(final Class classToBeBound) throws XMLException { try { JAXBContext jc = JAXBContext.newInstance(classToBeBound.getPackage().getName()); marshaller = jc.createMarshaller(); unmarshaller = jc.createUnmarshaller(); } catch (JAXBException e) { throw new XMLException("Cannot instantiate marshaller/unmarshaller for " + classToBeBound, e); } } /** * Unmarshals the given XML. * * @param xml The XML to unmarshal * * @return The unmarshalled XML document * * @throws XMLException When cannot unmarshal the XML document */ public Object unmarshal(final String xml) throws XMLException { Object readObject = null; try { readObject = unmarshaller.unmarshal(new StreamSource(xml)); if (readObject instanceof JAXBElement) { readObject = ((JAXBElement) readObject).getValue(); } } catch (JAXBException e) { throw new XMLException("Cannot read the XML ", e); } catch (IllegalArgumentException e) { throw new XMLException("Cannot read the XML ", e); } return readObject; } /** * Unmarshals the given XML with the given root class. * * @param xml The XML to unmarshal * @param rootClass The root class * * @return The unmarshalled XML document * * @throws XMLException When cannot unmarshal the XML document */ public Object unmarshal(final String xml, final Class rootClass) throws XMLException { Object readObject = null; try { final DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); final InputSource is = new InputSource(); is.setCharacterStream(new StringReader(xml)); readObject = unmarshaller.unmarshal(db.parse(is).getFirstChild(), rootClass); if (readObject instanceof JAXBElement) { readObject = ((JAXBElement) readObject).getValue(); } } catch (JAXBException e) { throw new XMLException("Cannot read the XML ", e); } catch (IllegalArgumentException e) { throw new XMLException("Cannot read the XML ", e); } catch (SAXException e) { throw new XMLException("Cannot read the XML ", e); } catch (IOException e) { throw new XMLException("Cannot read the XML ", e); } catch (ParserConfigurationException e) { throw new XMLException("Cannot read the XML ", e); } return readObject; } /** * Loads an XML document from a stream and unmarshals it. * * @param inputStream The stream to load from * * @return The unmarshalled XML document * * @throws XMLException When cannot unmarshal the XML document */ public Object loadFromInputStream(final InputStream inputStream) throws XMLException { Object readObject = null; try { readObject = unmarshaller.unmarshal(inputStream); if (readObject instanceof JAXBElement) { readObject = ((JAXBElement) readObject).getValue(); } } catch (JAXBException e) { throw new XMLException("Cannot unmarshal the XML ", e); } catch (IllegalArgumentException e) { throw new XMLException("Cannot unmarshal the XML ", e); } return readObject; } /** * Loads an XML document from a file and unmarshals it. * * @param file The file to load from * * @return The unmarshalled XML document * * @throws XMLException When cannot unmarshal the XML document * @throws FileNotFoundException When cannot read from the given file */ public Object loadFromFile(final File file) throws XMLException, FileNotFoundException { if (file == null) { throw new FileNotFoundException("Cannot load a null file"); } else if (!file.exists()) { throw new FileNotFoundException("Cannot load the file from " + file.getAbsolutePath()); } Object readObject = null; try { readObject = unmarshaller.unmarshal(file); if (readObject instanceof JAXBElement) { readObject = ((JAXBElement) readObject).getValue(); } } catch (JAXBException e) { throw new XMLException("Cannot unmarshal the XML from " + file.getAbsolutePath(), e); } catch (IllegalArgumentException e) { throw new XMLException("Cannot unmarshal the XML from " + file.getAbsolutePath(), e); } return readObject; } /** * Marshals and saves the given object to a file. The generated XML is formatted. * * @param file The file to write to * @param objectToSave The object to save * * @throws XMLException Thrown if any errors occur */ public void saveToFile(final File file, final Object objectToSave) throws XMLException { try { // Format the output marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); // Test write - if we cannot marshal, we won't destroy the config file // TODO: take the write output and then write it to a file, rather than marshalling twice StringWriter writer = new StringWriter(); marshaller.marshal(objectToSave, writer); // Convert the object to XML, and save to given file marshaller.marshal(objectToSave, file); } catch (Exception e) { throw new XMLException("Cannot save to " + file.getAbsolutePath(), e); } } }