View Javadoc

1   package org.springframework.oxm.xmlbeans;
2   
3   import java.io.IOException;
4   import java.io.InputStream;
5   import java.io.OutputStream;
6   import java.io.Reader;
7   import java.io.Writer;
8   import java.util.ArrayList;
9   import java.util.Iterator;
10  import java.util.List;
11  import javax.xml.stream.XMLEventReader;
12  import javax.xml.stream.XMLEventWriter;
13  import javax.xml.stream.XMLStreamReader;
14  import javax.xml.stream.XMLStreamWriter;
15  
16  import org.apache.xmlbeans.XmlError;
17  import org.apache.xmlbeans.XmlException;
18  import org.apache.xmlbeans.XmlObject;
19  import org.apache.xmlbeans.XmlOptions;
20  import org.apache.xmlbeans.XmlSaxHandler;
21  import org.apache.xmlbeans.XmlValidationError;
22  import org.springframework.oxm.AbstractMarshaller;
23  import org.springframework.oxm.Marshaller;
24  import org.springframework.oxm.XmlMappingException;
25  import org.springframework.xml.stream.StaxEventContentHandler;
26  import org.springframework.xml.stream.StaxEventXmlReader;
27  import org.springframework.xml.stream.StaxStreamContentHandler;
28  import org.w3c.dom.Document;
29  import org.w3c.dom.Node;
30  import org.w3c.dom.NodeList;
31  import org.xml.sax.ContentHandler;
32  import org.xml.sax.InputSource;
33  import org.xml.sax.SAXException;
34  import org.xml.sax.SAXNotRecognizedException;
35  import org.xml.sax.SAXNotSupportedException;
36  import org.xml.sax.XMLReader;
37  import org.xml.sax.ext.LexicalHandler;
38  
39  /**
40   * Implementation of the {@link Marshaller} interface for XMLBeans. Further options can be set by setting the
41   * <code>xmlOptions</code> property. The {@link XmlOptionsFactoryBean} is provided to easily wire up {@link XmlOptions}
42   * instances.
43   * <p/>
44   * Unmarshalled objects can be validated by setting the <code>validating</code> property, or by calling the {@link
45   * #validate(XmlObject)} method directly. Invalid objects will result in an {@link XmlBeansValidationFailureException}.
46   * <p/>
47   * <strong>Note</strong> that due to the nature of XMLBeans, this marshaller requires all passed objects to be of type
48   * {@link XmlObject}.
49   *
50   * @author Arjen Poutsma
51   * @see #setXmlOptions(org.apache.xmlbeans.XmlOptions)
52   * @see XmlOptionsFactoryBean
53   * @see #setValidating(boolean)
54   * @since 1.0.0
55   */
56  public class XmlBeansMarshaller extends AbstractMarshaller {
57  
58      private XmlOptions xmlOptions;
59  
60      private boolean validating = false;
61  
62      /** Returns the <code>XmlOptions</code>. */
63      public XmlOptions getXmlOptions() {
64          return xmlOptions;
65      }
66  
67      /**
68       * Sets the <code>XmlOptions</code>.
69       *
70       * @see XmlOptionsFactoryBean
71       */
72      public void setXmlOptions(XmlOptions xmlOptions) {
73          this.xmlOptions = xmlOptions;
74      }
75  
76      /** Returns whether this marshaller should validate in- and outgoing documents. */
77      public boolean isValidating() {
78          return validating;
79      }
80  
81      /** Sets whether this marshaller should validate in- and outgoing documents.  Default is <code>false</code>. */
82      public void setValidating(boolean validating) {
83          this.validating = validating;
84      }
85  
86      /** Returns true if the given class is an implementation of {@link XmlObject}. */
87      public boolean supports(Class clazz) {
88          return XmlObject.class.isAssignableFrom(clazz);
89      }
90  
91      protected final void marshalDomNode(Object graph, Node node) throws XmlMappingException {
92          Document document = node.getNodeType() == Node.DOCUMENT_NODE ? (Document) node : node.getOwnerDocument();
93          Node xmlBeansNode = ((XmlObject) graph).newDomNode(getXmlOptions());
94          NodeList xmlBeansChildNodes = xmlBeansNode.getChildNodes();
95          for (int i = 0; i < xmlBeansChildNodes.getLength(); i++) {
96              Node xmlBeansChildNode = xmlBeansChildNodes.item(i);
97              Node importedNode = document.importNode(xmlBeansChildNode, true);
98              node.appendChild(importedNode);
99          }
100     }
101 
102     protected final void marshalOutputStream(Object graph, OutputStream outputStream)
103             throws XmlMappingException, IOException {
104         ((XmlObject) graph).save(outputStream, getXmlOptions());
105     }
106 
107     protected final void marshalSaxHandlers(Object graph, ContentHandler contentHandler, LexicalHandler lexicalHandler)
108             throws XmlMappingException {
109         try {
110             ((XmlObject) graph).save(contentHandler, lexicalHandler, getXmlOptions());
111         }
112         catch (SAXException ex) {
113             throw convertXmlBeansException(ex, true);
114         }
115     }
116 
117     protected final void marshalWriter(Object graph, Writer writer) throws XmlMappingException, IOException {
118         ((XmlObject) graph).save(writer, getXmlOptions());
119     }
120 
121     protected final void marshalXmlEventWriter(Object graph, XMLEventWriter eventWriter) {
122         ContentHandler contentHandler = new StaxEventContentHandler(eventWriter);
123         marshalSaxHandlers(graph, contentHandler, null);
124     }
125 
126     protected final void marshalXmlStreamWriter(Object graph, XMLStreamWriter streamWriter) throws XmlMappingException {
127         ContentHandler contentHandler = new StaxStreamContentHandler(streamWriter);
128         marshalSaxHandlers(graph, contentHandler, null);
129     }
130 
131     protected final Object unmarshalDomNode(Node node) throws XmlMappingException {
132         try {
133             XmlObject object = XmlObject.Factory.parse(node, getXmlOptions());
134             validate(object);
135             return object;
136         }
137         catch (XmlException ex) {
138             throw convertXmlBeansException(ex, false);
139         }
140     }
141 
142     protected final Object unmarshalInputStream(InputStream inputStream) throws XmlMappingException, IOException {
143         try {
144             XmlObject object = XmlObject.Factory.parse(inputStream, getXmlOptions());
145             validate(object);
146             return object;
147         }
148         catch (XmlException ex) {
149             throw convertXmlBeansException(ex, false);
150         }
151     }
152 
153     protected final Object unmarshalReader(Reader reader) throws XmlMappingException, IOException {
154         try {
155             XmlObject object = XmlObject.Factory.parse(reader, getXmlOptions());
156             validate(object);
157             return object;
158         }
159         catch (XmlException ex) {
160             throw convertXmlBeansException(ex, false);
161         }
162     }
163 
164     protected final Object unmarshalSaxReader(XMLReader xmlReader, InputSource inputSource)
165             throws XmlMappingException, IOException {
166         XmlSaxHandler saxHandler = XmlObject.Factory.newXmlSaxHandler(getXmlOptions());
167         xmlReader.setContentHandler(saxHandler.getContentHandler());
168         try {
169             xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", saxHandler.getLexicalHandler());
170         }
171         catch (SAXNotRecognizedException e) {
172             // ignore
173         }
174         catch (SAXNotSupportedException e) {
175             // ignore
176         }
177         try {
178             xmlReader.parse(inputSource);
179             XmlObject object = saxHandler.getObject();
180             validate(object);
181             return object;
182         }
183         catch (SAXException ex) {
184             throw convertXmlBeansException(ex, false);
185         }
186         catch (XmlException ex) {
187             throw convertXmlBeansException(ex, false);
188         }
189     }
190 
191     protected final Object unmarshalXmlEventReader(XMLEventReader eventReader) throws XmlMappingException {
192         XMLReader reader = new StaxEventXmlReader(eventReader);
193         try {
194             return unmarshalSaxReader(reader, new InputSource());
195         }
196         catch (IOException ex) {
197             throw convertXmlBeansException(ex, false);
198         }
199     }
200 
201     protected final Object unmarshalXmlStreamReader(XMLStreamReader streamReader) throws XmlMappingException {
202         try {
203             XmlObject object = XmlObject.Factory.parse(streamReader, getXmlOptions());
204             validate(object);
205             return object;
206         }
207         catch (XmlException ex) {
208             throw convertXmlBeansException(ex, false);
209         }
210     }
211 
212     /**
213      * Converts the given XMLBeans exception to an appropriate exception from the <code>org.springframework.oxm</code>
214      * hierarchy.
215      * <p/>
216      * The default implementation delegates to <code>XmlBeansUtils</code>. Can be overridden in subclasses.
217      * <p/>
218      * A boolean flag is used to indicate whether this exception occurs during marshalling or unmarshalling, since
219      * XMLBeans itself does not make this distinction in its exception hierarchy.
220      *
221      * @param ex          XMLBeans Exception that occured
222      * @param marshalling indicates whether the exception occurs during marshalling (<code>true</code>), or
223      *                    unmarshalling (<code>false</code>)
224      * @return the corresponding <code>XmlMappingException</code>
225      * @see XmlBeansUtils#convertXmlBeansException(Exception,boolean)
226      */
227     public XmlMappingException convertXmlBeansException(Exception ex, boolean marshalling) {
228         return XmlBeansUtils.convertXmlBeansException(ex, marshalling);
229     }
230 
231     /**
232      * Validates the given <code>XmlObject</code>.
233      *
234      * @param object the xml object to validate
235      * @throws XmlBeansValidationFailureException
236      *          if the given object is not valid
237      */
238     public void validate(XmlObject object) throws XmlBeansValidationFailureException {
239         if (isValidating() && object != null) {
240             // create a temporary xmlOptions just for validation
241             XmlOptions validateOptions = getXmlOptions() != null ? getXmlOptions() : new XmlOptions();
242             List errorsList = new ArrayList();
243             validateOptions.setErrorListener(errorsList);
244             if (!object.validate(validateOptions)) {
245                 StringBuffer buffer = new StringBuffer("Could not validate XmlObject :");
246                 for (Iterator iterator = errorsList.iterator(); iterator.hasNext();) {
247                     XmlError xmlError = (XmlError) iterator.next();
248                     if (xmlError instanceof XmlValidationError) {
249                         buffer.append(xmlError.toString());
250                     }
251                 }
252                 XmlException ex = new XmlException(buffer.toString(), null, errorsList);
253                 throw new XmlBeansValidationFailureException(ex);
254             }
255         }
256     }
257 }