org.springframework.oxm.xmlbeans.XmlBeansMarshaller.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.oxm.xmlbeans.XmlBeansMarshaller.java

Source

/*
 * Copyright 2002-2009 the original author or authors.
 *
 * 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 org.springframework.oxm.xmlbeans;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.io.FilterInputStream;
import java.util.ArrayList;
import java.util.List;
import java.lang.ref.WeakReference;
import java.nio.CharBuffer;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;

import org.apache.xmlbeans.XMLStreamValidationException;
import org.apache.xmlbeans.XmlError;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.XmlSaxHandler;
import org.apache.xmlbeans.XmlValidationError;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.XMLReader;
import org.xml.sax.ext.LexicalHandler;

import org.springframework.oxm.Marshaller;
import org.springframework.oxm.MarshallingFailureException;
import org.springframework.oxm.UncategorizedMappingException;
import org.springframework.oxm.UnmarshallingFailureException;
import org.springframework.oxm.ValidationFailureException;
import org.springframework.oxm.XmlMappingException;
import org.springframework.oxm.support.AbstractMarshaller;
import org.springframework.util.xml.StaxUtils;

/**
 * Implementation of the {@link Marshaller} interface for Apache XMLBeans.
 *
 * <p>Options can be set by setting the <code>xmlOptions</code> property.
 * The {@link XmlOptionsFactoryBean} is provided to easily set up an {@link XmlOptions} instance.
 *
 * <p>Unmarshalled objects can be validated by setting the <code>validating</code> property,
 * or by calling the {@link #validate(XmlObject)} method directly. Invalid objects will
 * result in an {@link ValidationFailureException}.
 *
 * <p><b>NOTE:</b> Due to the nature of XMLBeans, this marshaller requires
 * all passed objects to be of type {@link XmlObject}.
 *
 * @author Arjen Poutsma
 * @since 3.0
 * @see #setValidating
 * @see #setXmlOptions
 * @see XmlOptionsFactoryBean
 */
public class XmlBeansMarshaller extends AbstractMarshaller {

    private XmlOptions xmlOptions;

    private boolean validating = false;

    /**
     * Set the <code>XmlOptions</code>.
     * @see XmlOptionsFactoryBean
     */
    public void setXmlOptions(XmlOptions xmlOptions) {
        this.xmlOptions = xmlOptions;
    }

    /**
     * Return the <code>XmlOptions</code>.
     */
    public XmlOptions getXmlOptions() {
        return this.xmlOptions;
    }

    /**
     * Set whether this marshaller should validate in- and outgoing documents.
     * Default is <code>false</code>.
     */
    public void setValidating(boolean validating) {
        this.validating = validating;
    }

    /**
     * Return whether this marshaller should validate in- and outgoing documents.
     */
    public boolean isValidating() {
        return this.validating;
    }

    /**
     * This implementation returns true if the given class is an implementation of {@link XmlObject}.
     */
    public boolean supports(Class<?> clazz) {
        return XmlObject.class.isAssignableFrom(clazz);
    }

    @Override
    protected final void marshalDomNode(Object graph, Node node) throws XmlMappingException {
        Document document = node.getNodeType() == Node.DOCUMENT_NODE ? (Document) node : node.getOwnerDocument();
        Node xmlBeansNode = ((XmlObject) graph).newDomNode(getXmlOptions());
        NodeList xmlBeansChildNodes = xmlBeansNode.getChildNodes();
        for (int i = 0; i < xmlBeansChildNodes.getLength(); i++) {
            Node xmlBeansChildNode = xmlBeansChildNodes.item(i);
            Node importedNode = document.importNode(xmlBeansChildNode, true);
            node.appendChild(importedNode);
        }
    }

    @Override
    protected final void marshalOutputStream(Object graph, OutputStream outputStream)
            throws XmlMappingException, IOException {

        ((XmlObject) graph).save(outputStream, getXmlOptions());
    }

    @Override
    protected final void marshalSaxHandlers(Object graph, ContentHandler contentHandler,
            LexicalHandler lexicalHandler) throws XmlMappingException {
        try {
            ((XmlObject) graph).save(contentHandler, lexicalHandler, getXmlOptions());
        } catch (SAXException ex) {
            throw convertXmlBeansException(ex, true);
        }
    }

    @Override
    protected final void marshalWriter(Object graph, Writer writer) throws XmlMappingException, IOException {
        ((XmlObject) graph).save(writer, getXmlOptions());
    }

    @Override
    protected final void marshalXmlEventWriter(Object graph, XMLEventWriter eventWriter) {
        ContentHandler contentHandler = StaxUtils.createContentHandler(eventWriter);
        marshalSaxHandlers(graph, contentHandler, null);
    }

    @Override
    protected final void marshalXmlStreamWriter(Object graph, XMLStreamWriter streamWriter)
            throws XmlMappingException {
        ContentHandler contentHandler = StaxUtils.createContentHandler(streamWriter);
        marshalSaxHandlers(graph, contentHandler, null);
    }

    @Override
    protected final Object unmarshalDomNode(Node node) throws XmlMappingException {
        try {
            XmlObject object = XmlObject.Factory.parse(node, getXmlOptions());
            validate(object);
            return object;
        } catch (XmlException ex) {
            throw convertXmlBeansException(ex, false);
        }
    }

    @Override
    protected final Object unmarshalInputStream(InputStream inputStream) throws XmlMappingException, IOException {
        try {
            InputStream nonClosingInputStream = new NonClosingInputStream(inputStream);
            XmlObject object = XmlObject.Factory.parse(nonClosingInputStream, getXmlOptions());
            validate(object);
            return object;
        } catch (XmlException ex) {
            throw convertXmlBeansException(ex, false);
        }
    }

    @Override
    protected final Object unmarshalReader(Reader reader) throws XmlMappingException, IOException {
        try {
            Reader nonClosingReader = new NonClosingReader(reader);
            XmlObject object = XmlObject.Factory.parse(nonClosingReader, getXmlOptions());
            validate(object);
            return object;
        } catch (XmlException ex) {
            throw convertXmlBeansException(ex, false);
        }
    }

    @Override
    protected final Object unmarshalSaxReader(XMLReader xmlReader, InputSource inputSource)
            throws XmlMappingException, IOException {
        XmlSaxHandler saxHandler = XmlObject.Factory.newXmlSaxHandler(getXmlOptions());
        xmlReader.setContentHandler(saxHandler.getContentHandler());
        try {
            xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", saxHandler.getLexicalHandler());
        } catch (SAXNotRecognizedException e) {
            // ignore
        } catch (SAXNotSupportedException e) {
            // ignore
        }
        try {
            xmlReader.parse(inputSource);
            XmlObject object = saxHandler.getObject();
            validate(object);
            return object;
        } catch (SAXException ex) {
            throw convertXmlBeansException(ex, false);
        } catch (XmlException ex) {
            throw convertXmlBeansException(ex, false);
        }
    }

    @Override
    protected final Object unmarshalXmlEventReader(XMLEventReader eventReader) throws XmlMappingException {
        XMLReader reader = StaxUtils.createXMLReader(eventReader);
        try {
            return unmarshalSaxReader(reader, new InputSource());
        } catch (IOException ex) {
            throw convertXmlBeansException(ex, false);
        }
    }

    @Override
    protected final Object unmarshalXmlStreamReader(XMLStreamReader streamReader) throws XmlMappingException {
        try {
            XmlObject object = XmlObject.Factory.parse(streamReader, getXmlOptions());
            validate(object);
            return object;
        } catch (XmlException ex) {
            throw convertXmlBeansException(ex, false);
        }
    }

    /**
     * Validate the given <code>XmlObject</code>.
     * @param object the xml object to validate
     * @throws ValidationFailureException if the given object is not valid
     */
    protected void validate(XmlObject object) throws ValidationFailureException {
        if (isValidating() && object != null) {
            // create a temporary xmlOptions just for validation
            XmlOptions validateOptions = getXmlOptions() != null ? getXmlOptions() : new XmlOptions();
            List errorsList = new ArrayList();
            validateOptions.setErrorListener(errorsList);
            if (!object.validate(validateOptions)) {
                StringBuilder builder = new StringBuilder("Could not validate XmlObject :");
                for (Object anErrorsList : errorsList) {
                    XmlError xmlError = (XmlError) anErrorsList;
                    if (xmlError instanceof XmlValidationError) {
                        builder.append(xmlError.toString());
                    }
                }
                throw new ValidationFailureException("XMLBeans validation failure",
                        new XmlException(builder.toString(), null, errorsList));
            }
        }
    }

    /**
     * Convert the given XMLBeans exception to an appropriate exception from the
     * <code>org.springframework.oxm</code> hierarchy.
     * <p>A boolean flag is used to indicate whether this exception occurs during marshalling or
     * unmarshalling, since XMLBeans itself does not make this distinction in its exception hierarchy.
     * @param ex XMLBeans Exception that occured
     * @param marshalling indicates whether the exception occurs during marshalling (<code>true</code>),
     * or unmarshalling (<code>false</code>)
     * @return the corresponding <code>XmlMappingException</code>
     */
    protected XmlMappingException convertXmlBeansException(Exception ex, boolean marshalling) {
        if (ex instanceof XMLStreamValidationException) {
            return new ValidationFailureException("XmlBeans validation exception", ex);
        } else if (ex instanceof XmlException || ex instanceof SAXException) {
            if (marshalling) {
                return new MarshallingFailureException("XMLBeans marshalling exception", ex);
            } else {
                return new UnmarshallingFailureException("XMLBeans unmarshalling exception", ex);
            }
        } else {
            // fallback
            return new UncategorizedMappingException("Unknown XMLBeans exception", ex);
        }
    }

    /**
     * See SPR-7034
     */
    private static class NonClosingInputStream extends InputStream {

        private final WeakReference<InputStream> in;

        private NonClosingInputStream(InputStream in) {
            this.in = new WeakReference<InputStream>(in);
        }

        private InputStream getInputStream() {
            return this.in.get();
        }

        @Override
        public int read() throws IOException {
            InputStream in = getInputStream();
            return in != null ? in.read() : -1;
        }

        @Override
        public int read(byte[] b) throws IOException {
            InputStream in = getInputStream();
            return in != null ? in.read(b) : -1;
        }

        @Override
        public int read(byte[] b, int off, int len) throws IOException {
            InputStream in = getInputStream();
            return in != null ? in.read(b, off, len) : -1;
        }

        @Override
        public long skip(long n) throws IOException {
            InputStream in = getInputStream();
            return in != null ? in.skip(n) : 0;
        }

        @Override
        public boolean markSupported() {
            InputStream in = getInputStream();
            return in != null && in.markSupported();
        }

        @Override
        public void mark(int readlimit) {
            InputStream in = getInputStream();
            if (in != null) {
                in.mark(readlimit);
            }
        }

        @Override
        public void reset() throws IOException {
            InputStream in = getInputStream();
            if (in != null) {
                in.reset();
            }
        }

        @Override
        public int available() throws IOException {
            InputStream in = getInputStream();
            return in != null ? in.available() : 0;
        }

        @Override
        public void close() throws IOException {
            InputStream in = getInputStream();
            if (in != null) {
                this.in.clear();
            }
        }
    }

    private static class NonClosingReader extends Reader {

        private final WeakReference<Reader> reader;

        private NonClosingReader(Reader reader) {
            this.reader = new WeakReference<Reader>(reader);
        }

        private Reader getReader() {
            return this.reader.get();
        }

        @Override
        public int read(CharBuffer target) throws IOException {
            Reader rdr = getReader();
            return rdr != null ? rdr.read(target) : -1;
        }

        @Override
        public int read() throws IOException {
            Reader rdr = getReader();
            return rdr != null ? rdr.read() : -1;
        }

        @Override
        public int read(char[] cbuf) throws IOException {
            Reader rdr = getReader();
            return rdr != null ? rdr.read(cbuf) : -1;
        }

        @Override
        public int read(char[] cbuf, int off, int len) throws IOException {
            Reader rdr = getReader();
            return rdr != null ? rdr.read(cbuf, off, len) : -1;
        }

        @Override
        public long skip(long n) throws IOException {
            Reader rdr = getReader();
            return rdr != null ? rdr.skip(n) : 0;
        }

        @Override
        public boolean ready() throws IOException {
            Reader rdr = getReader();
            return rdr != null && rdr.ready();
        }

        @Override
        public boolean markSupported() {
            Reader rdr = getReader();
            return rdr != null && rdr.markSupported();
        }

        @Override
        public void mark(int readAheadLimit) throws IOException {
            Reader rdr = getReader();
            if (rdr != null) {
                rdr.mark(readAheadLimit);
            }
        }

        @Override
        public void reset() throws IOException {
            Reader rdr = getReader();
            if (rdr != null) {
                rdr.reset();
            }
        }

        @Override
        public void close() throws IOException {
            Reader rdr = getReader();
            if (rdr != null) {
                this.reader.clear();
            }
        }

    }

}