001: package org.springframework.oxm.xmlbeans;
002:
003: import java.io.IOException;
004: import java.io.InputStream;
005: import java.io.OutputStream;
006: import java.io.Reader;
007: import java.io.Writer;
008: import java.util.ArrayList;
009: import java.util.Iterator;
010: import java.util.List;
011: import javax.xml.stream.XMLEventReader;
012: import javax.xml.stream.XMLEventWriter;
013: import javax.xml.stream.XMLStreamReader;
014: import javax.xml.stream.XMLStreamWriter;
015:
016: import org.apache.xmlbeans.XmlError;
017: import org.apache.xmlbeans.XmlException;
018: import org.apache.xmlbeans.XmlObject;
019: import org.apache.xmlbeans.XmlOptions;
020: import org.apache.xmlbeans.XmlSaxHandler;
021: import org.apache.xmlbeans.XmlValidationError;
022: import org.springframework.oxm.AbstractMarshaller;
023: import org.springframework.oxm.Marshaller;
024: import org.springframework.oxm.XmlMappingException;
025: import org.springframework.xml.stream.StaxEventContentHandler;
026: import org.springframework.xml.stream.StaxEventXmlReader;
027: import org.springframework.xml.stream.StaxStreamContentHandler;
028: import org.w3c.dom.Document;
029: import org.w3c.dom.Node;
030: import org.w3c.dom.NodeList;
031: import org.xml.sax.ContentHandler;
032: import org.xml.sax.InputSource;
033: import org.xml.sax.SAXException;
034: import org.xml.sax.SAXNotRecognizedException;
035: import org.xml.sax.SAXNotSupportedException;
036: import org.xml.sax.XMLReader;
037: import org.xml.sax.ext.LexicalHandler;
038:
039: /**
040: * Implementation of the {@link Marshaller} interface for XMLBeans. Further options can be set by setting the
041: * <code>xmlOptions</code> property. The {@link XmlOptionsFactoryBean} is provided to easily wire up {@link XmlOptions}
042: * instances.
043: * <p/>
044: * Unmarshalled objects can be validated by setting the <code>validating</code> property, or by calling the {@link
045: * #validate(XmlObject)} method directly. Invalid objects will result in an {@link XmlBeansValidationFailureException}.
046: * <p/>
047: * <strong>Note</strong> that due to the nature of XMLBeans, this marshaller requires all passed objects to be of type
048: * {@link XmlObject}.
049: *
050: * @author Arjen Poutsma
051: * @see #setXmlOptions(org.apache.xmlbeans.XmlOptions)
052: * @see XmlOptionsFactoryBean
053: * @see #setValidating(boolean)
054: * @since 1.0.0
055: */
056: public class XmlBeansMarshaller extends AbstractMarshaller {
057:
058: private XmlOptions xmlOptions;
059:
060: private boolean validating = false;
061:
062: /** Returns the <code>XmlOptions</code>. */
063: public XmlOptions getXmlOptions() {
064: return xmlOptions;
065: }
066:
067: /**
068: * Sets the <code>XmlOptions</code>.
069: *
070: * @see XmlOptionsFactoryBean
071: */
072: public void setXmlOptions(XmlOptions xmlOptions) {
073: this .xmlOptions = xmlOptions;
074: }
075:
076: /** Returns whether this marshaller should validate in- and outgoing documents. */
077: public boolean isValidating() {
078: return validating;
079: }
080:
081: /** Sets whether this marshaller should validate in- and outgoing documents. Default is <code>false</code>. */
082: public void setValidating(boolean validating) {
083: this .validating = validating;
084: }
085:
086: /** Returns true if the given class is an implementation of {@link XmlObject}. */
087: public boolean supports(Class clazz) {
088: return XmlObject.class.isAssignableFrom(clazz);
089: }
090:
091: protected final void marshalDomNode(Object graph, Node node)
092: throws XmlMappingException {
093: Document document = node.getNodeType() == Node.DOCUMENT_NODE ? (Document) node
094: : node.getOwnerDocument();
095: Node xmlBeansNode = ((XmlObject) graph)
096: .newDomNode(getXmlOptions());
097: NodeList xmlBeansChildNodes = xmlBeansNode.getChildNodes();
098: for (int i = 0; i < xmlBeansChildNodes.getLength(); i++) {
099: Node xmlBeansChildNode = xmlBeansChildNodes.item(i);
100: Node importedNode = document.importNode(xmlBeansChildNode,
101: true);
102: node.appendChild(importedNode);
103: }
104: }
105:
106: protected final void marshalOutputStream(Object graph,
107: OutputStream outputStream) throws XmlMappingException,
108: IOException {
109: ((XmlObject) graph).save(outputStream, getXmlOptions());
110: }
111:
112: protected final void marshalSaxHandlers(Object graph,
113: ContentHandler contentHandler, LexicalHandler lexicalHandler)
114: throws XmlMappingException {
115: try {
116: ((XmlObject) graph).save(contentHandler, lexicalHandler,
117: getXmlOptions());
118: } catch (SAXException ex) {
119: throw convertXmlBeansException(ex, true);
120: }
121: }
122:
123: protected final void marshalWriter(Object graph, Writer writer)
124: throws XmlMappingException, IOException {
125: ((XmlObject) graph).save(writer, getXmlOptions());
126: }
127:
128: protected final void marshalXmlEventWriter(Object graph,
129: XMLEventWriter eventWriter) {
130: ContentHandler contentHandler = new StaxEventContentHandler(
131: eventWriter);
132: marshalSaxHandlers(graph, contentHandler, null);
133: }
134:
135: protected final void marshalXmlStreamWriter(Object graph,
136: XMLStreamWriter streamWriter) throws XmlMappingException {
137: ContentHandler contentHandler = new StaxStreamContentHandler(
138: streamWriter);
139: marshalSaxHandlers(graph, contentHandler, null);
140: }
141:
142: protected final Object unmarshalDomNode(Node node)
143: throws XmlMappingException {
144: try {
145: XmlObject object = XmlObject.Factory.parse(node,
146: getXmlOptions());
147: validate(object);
148: return object;
149: } catch (XmlException ex) {
150: throw convertXmlBeansException(ex, false);
151: }
152: }
153:
154: protected final Object unmarshalInputStream(InputStream inputStream)
155: throws XmlMappingException, IOException {
156: try {
157: XmlObject object = XmlObject.Factory.parse(inputStream,
158: getXmlOptions());
159: validate(object);
160: return object;
161: } catch (XmlException ex) {
162: throw convertXmlBeansException(ex, false);
163: }
164: }
165:
166: protected final Object unmarshalReader(Reader reader)
167: throws XmlMappingException, IOException {
168: try {
169: XmlObject object = XmlObject.Factory.parse(reader,
170: getXmlOptions());
171: validate(object);
172: return object;
173: } catch (XmlException ex) {
174: throw convertXmlBeansException(ex, false);
175: }
176: }
177:
178: protected final Object unmarshalSaxReader(XMLReader xmlReader,
179: InputSource inputSource) throws XmlMappingException,
180: IOException {
181: XmlSaxHandler saxHandler = XmlObject.Factory
182: .newXmlSaxHandler(getXmlOptions());
183: xmlReader.setContentHandler(saxHandler.getContentHandler());
184: try {
185: xmlReader.setProperty(
186: "http://xml.org/sax/properties/lexical-handler",
187: saxHandler.getLexicalHandler());
188: } catch (SAXNotRecognizedException e) {
189: // ignore
190: } catch (SAXNotSupportedException e) {
191: // ignore
192: }
193: try {
194: xmlReader.parse(inputSource);
195: XmlObject object = saxHandler.getObject();
196: validate(object);
197: return object;
198: } catch (SAXException ex) {
199: throw convertXmlBeansException(ex, false);
200: } catch (XmlException ex) {
201: throw convertXmlBeansException(ex, false);
202: }
203: }
204:
205: protected final Object unmarshalXmlEventReader(
206: XMLEventReader eventReader) throws XmlMappingException {
207: XMLReader reader = new StaxEventXmlReader(eventReader);
208: try {
209: return unmarshalSaxReader(reader, new InputSource());
210: } catch (IOException ex) {
211: throw convertXmlBeansException(ex, false);
212: }
213: }
214:
215: protected final Object unmarshalXmlStreamReader(
216: XMLStreamReader streamReader) throws XmlMappingException {
217: try {
218: XmlObject object = XmlObject.Factory.parse(streamReader,
219: getXmlOptions());
220: validate(object);
221: return object;
222: } catch (XmlException ex) {
223: throw convertXmlBeansException(ex, false);
224: }
225: }
226:
227: /**
228: * Converts the given XMLBeans exception to an appropriate exception from the <code>org.springframework.oxm</code>
229: * hierarchy.
230: * <p/>
231: * The default implementation delegates to <code>XmlBeansUtils</code>. Can be overridden in subclasses.
232: * <p/>
233: * A boolean flag is used to indicate whether this exception occurs during marshalling or unmarshalling, since
234: * XMLBeans itself does not make this distinction in its exception hierarchy.
235: *
236: * @param ex XMLBeans Exception that occured
237: * @param marshalling indicates whether the exception occurs during marshalling (<code>true</code>), or
238: * unmarshalling (<code>false</code>)
239: * @return the corresponding <code>XmlMappingException</code>
240: * @see XmlBeansUtils#convertXmlBeansException(Exception,boolean)
241: */
242: public XmlMappingException convertXmlBeansException(Exception ex,
243: boolean marshalling) {
244: return XmlBeansUtils.convertXmlBeansException(ex, marshalling);
245: }
246:
247: /**
248: * Validates the given <code>XmlObject</code>.
249: *
250: * @param object the xml object to validate
251: * @throws XmlBeansValidationFailureException
252: * if the given object is not valid
253: */
254: public void validate(XmlObject object)
255: throws XmlBeansValidationFailureException {
256: if (isValidating() && object != null) {
257: // create a temporary xmlOptions just for validation
258: XmlOptions validateOptions = getXmlOptions() != null ? getXmlOptions()
259: : new XmlOptions();
260: List errorsList = new ArrayList();
261: validateOptions.setErrorListener(errorsList);
262: if (!object.validate(validateOptions)) {
263: StringBuffer buffer = new StringBuffer(
264: "Could not validate XmlObject :");
265: for (Iterator iterator = errorsList.iterator(); iterator
266: .hasNext();) {
267: XmlError xmlError = (XmlError) iterator.next();
268: if (xmlError instanceof XmlValidationError) {
269: buffer.append(xmlError.toString());
270: }
271: }
272: XmlException ex = new XmlException(buffer.toString(),
273: null, errorsList);
274: throw new XmlBeansValidationFailureException(ex);
275: }
276: }
277: }
278: }
|