org.plasma.sdo.helper.PlasmaXMLHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.plasma.sdo.helper.PlasmaXMLHelper.java

Source

/**
 *         PlasmaSDO License
 * 
 * This is a community release of PlasmaSDO, a dual-license 
 * Service Data Object (SDO) 2.1 implementation. 
 * This particular copy of the software is released under the 
 * version 2 of the GNU General Public License. PlasmaSDO was developed by 
 * TerraMeta Software, Inc.
 * 
 * Copyright (c) 2013, TerraMeta Software, Inc. All rights reserved.
 * 
 * General License information can be found below.
 * 
 * This distribution may include materials developed by third
 * parties. For license and attribution notices for these
 * materials, please refer to the documentation that accompanies
 * this distribution (see the "Licenses for Third-Party Components"
 * appendix) or view the online documentation at 
 * <http://plasma-sdo.org/licenses/>.
 *  
 */
package org.plasma.sdo.helper;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringBufferInputStream;
import java.io.Writer;

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.stream.XMLStreamException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.modeldriven.fuml.xmi.stream.StreamReader;
import org.plasma.common.exception.PlasmaRuntimeException;
import org.plasma.common.io.StreamCopier;
import org.plasma.sdo.PlasmaDataObject;
import org.plasma.sdo.PlasmaDataObjectException;
import org.plasma.sdo.core.CoreXMLDocument;
import org.plasma.sdo.xml.DefaultErrorHandler;
import org.plasma.sdo.xml.DocumentMarshaller;
import org.plasma.sdo.xml.MarshallerException;
import org.plasma.sdo.xml.StreamMarshaller;
import org.plasma.sdo.xml.StreamMarshaller;
import org.plasma.sdo.xml.StreamUnmarshaller;
import org.plasma.sdo.xml.UnmarshallerException;
import org.plasma.sdo.xml.UnmarshallerRuntimeException;
import org.plasma.sdo.xml.XMLConstants;
import org.plasma.sdo.xml.XMLOptions;
import org.plasma.xml.schema.SchemaConstants;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;

import commonj.sdo.DataObject;
import commonj.sdo.helper.XMLDocument;
import commonj.sdo.helper.XMLHelper;

/**
 * A helper to convert XML documents into DataObects and 
 * DataObjects into XML documents.
 */
public class PlasmaXMLHelper implements XMLHelper {

    private static Log log = LogFactory.getLog(PlasmaXMLHelper.class);

    static public PlasmaXMLHelper INSTANCE = initializeInstance();

    private PlasmaXMLHelper() {
    }

    private static synchronized PlasmaXMLHelper initializeInstance() {
        if (INSTANCE == null)
            INSTANCE = new PlasmaXMLHelper();
        return INSTANCE;
    }

    /**
     * Creates an XMLDocument with the specified XML rootElement for the DataObject.
     * @param dataObject specifies DataObject to be saved
     * @param rootElementURI the Target Namespace URI of the root XML element
     * @param rootElementName the Name of the root XML element
     * @return XMLDocument a new XMLDocument set with the specified parameters.
     */
    public XMLDocument createDocument(DataObject dataObject, String rootElementURI, String rootElementName) {
        return new CoreXMLDocument(dataObject, rootElementURI, rootElementName);
    }

    /**
     * Creates and returns an XMLDocument from the input String.
     * By default does not perform XSD validation.
     * Same as
     *   load(new StringReader(inputString), null, null);
     * 
     * @param inputString specifies the String to read from
     * @return the new XMLDocument loaded
     * @throws RuntimeException for errors in XML parsing or 
     *   implementation-specific validation.
     */
    public XMLDocument load(String inputString) {
        try {
            return load(new ByteArrayInputStream(inputString.getBytes("UTF-8")), null, null);
        } catch (IOException e) {
            throw new PlasmaDataObjectException(e);
        }
    }

    /**
     * Creates and returns an XMLDocument from the inputStream.
     * The InputStream will be closed after reading.
     * By default does not perform XSD validation.
     * Same as
     *   load(inputStream, null, null);
     * 
     * @param inputStream specifies the InputStream to read from
     * @return the new XMLDocument loaded
     * @throws IOException for stream exceptions.
     * @throws RuntimeException for errors in XML parsing or 
     *   implementation-specific validation.
     */
    public XMLDocument load(InputStream inputStream) throws IOException {
        return load(inputStream, null, null);
    }

    /**
     * Creates and returns an XMLDocument from the inputStream.
     * The InputStream will be closed after reading.
     * By default does not perform XSD validation.
     * @param inputStream specifies the InputStream to read from
     * @param locationURI specifies the URI of the document for relative schema locations
     * @param options implementation-specific options.
     * @return the new XMLDocument loaded
     * @throws IOException for stream exceptions.
     * @throws RuntimeException for errors in XML parsing or 
     *   implementation-specific validation.
     */
    public XMLDocument load(InputStream inputStream, String locationURI, Object options) throws IOException {
        if (options != null)
            if (!(options instanceof XMLOptions))
                throw new IllegalArgumentException(
                        "expected 'options' as instance of " + XMLOptions.class.getName());

        XMLOptions xmlOptions = (XMLOptions) options;
        StreamUnmarshaller unmarshaler = new StreamUnmarshaller(xmlOptions, locationURI);

        InputStream unmarshalStream = inputStream;
        if (xmlOptions != null && xmlOptions.isValidate()) {
            // XSD validation using StAX at least at the same time
            // it is reading or if multiple schemas are involved is not
            // currently possible with current StAX libs. Can explore Woodstix from
            // codehaus.org. Hence, creates a temp stream in order to 
            // execute the validation before unmarshaling in a new stream. 
            File tempFile = File.createTempFile(PlasmaXMLHelper.class.getSimpleName(), "xml");
            StreamCopier.copy(inputStream, new FileOutputStream(tempFile));
            validateDOM(new FileInputStream(tempFile), locationURI, (XMLOptions) options);
            unmarshalStream = new FileInputStream(tempFile);
        }

        try {
            unmarshaler.unmarshal(unmarshalStream);
        } catch (UnmarshallerException e) {
            throw new PlasmaDataObjectException(e);
        } catch (XMLStreamException e) {
            throw new PlasmaDataObjectException(e);
        }

        XMLDocument result = unmarshaler.getResult();
        result.setNoNamespaceSchemaLocation(locationURI);
        return result;
    }

    /**
     * Creates and returns an XMLDocument from the inputReader.
     * The InputStream will be closed after reading.
     * By default does not perform XSD validation.
     * @param inputReader specifies the Reader to read from
     * @param locationURI specifies the URI of the document for relative schema locations
     * @param options implementation-specific options.
     * @return the new XMLDocument loaded
     * @throws IOException for stream exceptions.
     * @throws RuntimeException for errors in XML parsing or 
     *   implementation-specific validation.
     */
    public XMLDocument load(Reader inputReader, String locationURI, Object options) throws IOException {
        if (options != null)
            if (!(options instanceof XMLOptions))
                throw new IllegalArgumentException(
                        "expected 'options' as instance of " + XMLOptions.class.getName());
        XMLOptions xmlOptions = (XMLOptions) options;
        StreamUnmarshaller unmarshaler = new StreamUnmarshaller(xmlOptions, locationURI);
        try {
            unmarshaler.unmarshal(inputReader);
        } catch (UnmarshallerException e) {
            throw new PlasmaDataObjectException(e);
        } catch (XMLStreamException e) {
            throw new PlasmaDataObjectException(e);
        }

        return unmarshaler.getResult();
    }

    /**
     * Creates and returns an XMLDocument from the inputSource.
     * The InputSource will be closed after reading.
     * By default does not perform XSD validation.
     * @param inputSource specifies the Source to read from
     * @param locationURI specifies the URI of the document for relative schema locations
     * @param options implementation-specific options.
     * @return the new XMLDocument loaded
     * @throws IOException for stream exceptions.
     * @throws RuntimeException for errors in XML parsing or 
     *   implementation-specific validation.
     */
    public XMLDocument load(Source inputSource, String locationURI, Object options) throws IOException {
        if (options != null)
            if (!(options instanceof XMLOptions))
                throw new IllegalArgumentException(
                        "expected 'options' as instance of " + XMLOptions.class.getName());
        XMLOptions xmlOptions = (XMLOptions) options;
        StreamUnmarshaller unmarshaler = new StreamUnmarshaller((XMLOptions) options, locationURI);
        try {
            unmarshaler.unmarshal(inputSource);
        } catch (UnmarshallerException e) {
            throw new PlasmaDataObjectException(e);
        } catch (XMLStreamException e) {
            throw new PlasmaDataObjectException(e);
        }
        return unmarshaler.getResult();
    }

    /**
     * Returns the DataObject saved as an XML document with the specified root element.
     * Same as
     *   StringWriter stringWriter = new StringWriter();
     *   save(createDocument(dataObject, rootElementURI, rootElementName), 
     *     stringWriter, null);
     *   stringWriter.toString();
     *
     * @param dataObject specifies DataObject to be saved
     * @param rootElementURI the Target Namespace URI of the root XML element
     * @param rootElementName the Name of the root XML element
     * @return the saved XML document as a string
     * @throws IllegalArgumentException if the dataObject tree
     *   is not closed or has no container.
     */
    public String save(DataObject dataObject, String rootElementURI, String rootElementName) {
        String result = null;
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        try {
            save(dataObject, rootElementURI, rootElementName, os);
            os.flush();
            result = os.toString();
        } catch (IOException e) {
            throw new PlasmaDataObjectException(e);
        }
        return result;
    }

    /**
     * Saves the DataObject as an XML document with the specified root element.
     * Same as
     *   save(createDocument(dataObject, rootElementURI, rootElementName),
     *     outputStream, null);
     * 
     * @param dataObject specifies DataObject to be saved
     * @param rootElementURI the Target Namespace URI of the root XML element
     * @param rootElementName the Name of the root XML element
     * @param outputStream specifies the OutputStream to write to.
     * @throws IOException for stream exceptions.
     * @throws IllegalArgumentException if the dataObject tree
     *   is not closed or has no container.
     */
    public void save(DataObject dataObject, String rootElementURI, String rootElementName,
            OutputStream outputStream) throws IOException {

        XMLDocument doc = new CoreXMLDocument(dataObject, rootElementURI, rootElementName);
        StreamMarshaller marshaler = new StreamMarshaller(doc, (XMLOptions) null);
        try {
            marshaler.marshal(outputStream);
        } catch (XMLStreamException e) {
            throw new IOException(e);
        } catch (MarshallerException e) {
            throw new IOException(e);
        }
    }

    /**
     * Serializes an XMLDocument as an XML document into the outputStream.
     * If the DataObject's Type was defined by an XSD, the serialization
     *   will follow the XSD.
     * Otherwise the serialization will follow the format as if an XSD
     *   were generated as defined by the SDO specification.
     * The OutputStream will be flushed after writing.
     * Does not perform validation to ensure compliance with an XSD.
     * @param xmlDocument specifies XMLDocument to be saved
     * @param outputStream specifies the OutputStream to write to.
     * @param options implementation-specific options.
     * @throws IOException for stream exceptions.
     * @throws IllegalArgumentException if the dataObject tree
     *   is not closed or has no container.
     */
    public void save(XMLDocument xmlDocument, OutputStream outputStream, Object options) throws IOException {
        if (options != null)
            if (!(options instanceof XMLOptions))
                throw new IllegalArgumentException(
                        "expected 'options' as instance of " + XMLOptions.class.getName());
        XMLOptions xmlOptions = (XMLOptions) options;
        StreamMarshaller marshaler = new StreamMarshaller(xmlDocument);
        marshaler.setOptions(xmlOptions);
        try {
            marshaler.marshal(outputStream);
        } catch (XMLStreamException e) {
            throw new IOException(e);
        } catch (MarshallerException e) {
            throw new IOException(e);
        }
    }

    /**
     * Serializes an XMLDocument as an XML document into the outputWriter.
     * If the DataObject's Type was defined by an XSD, the serialization
     *   will follow the XSD.
     * Otherwise the serialization will follow the format as if an XSD
     *   were generated as defined by the SDO specification.
     * The OutputStream will be flushed after writing.
     * Does not perform validation to ensure compliance with an XSD.
     * @param xmlDocument specifies XMLDocument to be saved
     * @param outputWriter specifies the Writer to write to.
     * @param options implementation-specific options.
     * @throws IOException for stream exceptions.
     * @throws IllegalArgumentException if the dataObject tree
     *   is not closed or has no container.
     */
    public void save(XMLDocument xmlDocument, Writer outputWriter, Object options) throws IOException {
        if (options != null)
            if (!(options instanceof XMLOptions))
                throw new IllegalArgumentException(
                        "expected 'options' as instance of " + XMLOptions.class.getName());
        XMLOptions xmlOptions = (XMLOptions) options;
        StreamMarshaller marshaler = new StreamMarshaller(xmlDocument);
        marshaler.setOptions(xmlOptions);
        try {
            marshaler.marshal(outputWriter);
        } catch (XMLStreamException e) {
            throw new IOException(e);
        } catch (MarshallerException e) {
            throw new IOException(e);
        }
    }

    /**
     * Serializes an XMLDocument as an XML document into the outputResult in a 
     * serialization technology independent format (as specified in 
     * javax.xml.transform).
     * The OutputResult will be flushed after writing.
     * Does not perform validation to ensure compliance with an XSD.
     * @param xmlDocument specifies XMLDocument to be saved
     * @param outputResult specifies Result to be saved
     * @param options implementation-specific options.
     * @throws IOException for stream exceptions.
     * @throws IllegalArgumentException if the dataObject tree
     *   is not closed or has no container.
     */
    public void save(XMLDocument xmlDocument, Result outputResult, Object options) throws IOException {
        if (options != null)
            if (!(options instanceof XMLOptions))
                throw new IllegalArgumentException(
                        "expected 'options' as instance of " + XMLOptions.class.getName());
        XMLOptions xmlOptions = (XMLOptions) options;
        StreamMarshaller marshaler = new StreamMarshaller(xmlDocument);
        marshaler.setOptions(xmlOptions);
        try {
            marshaler.marshal(outputResult);
        } catch (XMLStreamException e) {
            throw new IOException(e);
        } catch (MarshallerException e) {
            throw new IOException(e);
        }
    }

    private void validateDOM(InputStream inputStream, String locationURI, XMLOptions options) throws IOException {

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        factory.setValidating(true);
        try {
            factory.setAttribute(XMLConstants.JAXP_SCHEMA_LANGUAGE, SchemaConstants.XMLSCHEMA_NAMESPACE_URI);
            factory.setValidating(true);
            //factory.setNamespaceAware(true);
            //factory.setFeature("http://apache.org/xml/features/validation/schema",true);
            //factory.setFeature("http://apache.org/xml/features/validation/schema-full-checking", true);
        } catch (IllegalArgumentException e) {
            log.warn("parser does not support JAXP 1.2");
        } /*catch (ParserConfigurationException e) {
           throw new PlasmaDataObjectException(e);
          }*/

        if (locationURI != null) {
            factory.setAttribute(XMLConstants.JAXP_NO_NAMESPACE_SCHEMA_SOURCE, locationURI);
        }

        try {
            DocumentBuilder builder = factory.newDocumentBuilder();
            if (options.getErrorHandler() == null)
                builder.setErrorHandler(new DefaultErrorHandler(options));
            else
                builder.setErrorHandler(options.getErrorHandler());
            if (log.isDebugEnabled())
                log.debug("validating...");
            builder.parse(inputStream);

        } catch (ParserConfigurationException e) {
            throw new PlasmaDataObjectException(e);
        } catch (SAXException e) {
            throw new PlasmaDataObjectException(e);
        }

    }

    private void validateSAX(InputStream inputStream, String locationURI, XMLOptions options) throws IOException {

        SAXParserFactory factory = SAXParserFactory.newInstance();
        factory.setValidating(true);
        factory.setNamespaceAware(true);
        SAXParser parser;

        try {
            factory.setFeature("http://xml.org/sax/features/validation", true);
            factory.setFeature("http://apache.org/xml/features/validation/schema", true);
            factory.setFeature("http://apache.org/xml/features/validation/schema-full-checking", true);
            parser = factory.newSAXParser();
            try {
                parser.setProperty(XMLConstants.JAXP_SCHEMA_LANGUAGE, SchemaConstants.XMLSCHEMA_NAMESPACE_URI);
            } catch (SAXNotRecognizedException e) {
                log.warn("parses does not support JAXP 1.2");
            }
            if (locationURI != null) {
                parser.setProperty(XMLConstants.JAXP_NO_NAMESPACE_SCHEMA_SOURCE, locationURI);
            }
            XMLReader xmlReader = parser.getXMLReader();
            //xmlReader.setEntityResolver(new SchemaLoader());
            if (options.getErrorHandler() == null)
                xmlReader.setErrorHandler(new DefaultErrorHandler(options));
            else
                xmlReader.setErrorHandler(options.getErrorHandler());
            if (log.isDebugEnabled())
                log.debug("validating...");
            xmlReader.parse(new InputSource(inputStream));

        } catch (SAXNotRecognizedException e) {
            throw new PlasmaDataObjectException(e);
        } catch (SAXNotSupportedException e) {
            throw new PlasmaDataObjectException(e);
        } catch (ParserConfigurationException e) {
            throw new PlasmaDataObjectException(e);
        } catch (SAXException e) {
            throw new PlasmaDataObjectException(e);
        }
    }

}