001: /*
002: * Copyright 2006 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.oxm.jibx;
018:
019: import java.io.ByteArrayInputStream;
020: import java.io.ByteArrayOutputStream;
021: import java.io.IOException;
022: import java.io.InputStream;
023: import java.io.OutputStream;
024: import java.io.Reader;
025: import java.io.Writer;
026: import javax.xml.stream.XMLEventReader;
027: import javax.xml.stream.XMLEventWriter;
028: import javax.xml.stream.XMLStreamException;
029: import javax.xml.stream.XMLStreamReader;
030: import javax.xml.stream.XMLStreamWriter;
031: import javax.xml.transform.Transformer;
032: import javax.xml.transform.TransformerException;
033: import javax.xml.transform.TransformerFactory;
034: import javax.xml.transform.dom.DOMResult;
035: import javax.xml.transform.dom.DOMSource;
036: import javax.xml.transform.sax.SAXResult;
037: import javax.xml.transform.sax.SAXSource;
038: import javax.xml.transform.stream.StreamResult;
039: import javax.xml.transform.stream.StreamSource;
040:
041: import org.jibx.runtime.BindingDirectory;
042: import org.jibx.runtime.IBindingFactory;
043: import org.jibx.runtime.IMarshallingContext;
044: import org.jibx.runtime.IUnmarshallingContext;
045: import org.jibx.runtime.IXMLReader;
046: import org.jibx.runtime.IXMLWriter;
047: import org.jibx.runtime.JiBXException;
048: import org.jibx.runtime.impl.MarshallingContext;
049: import org.jibx.runtime.impl.StAXReaderWrapper;
050: import org.jibx.runtime.impl.StAXWriter;
051: import org.jibx.runtime.impl.UnmarshallingContext;
052: import org.springframework.beans.factory.InitializingBean;
053: import org.springframework.oxm.AbstractMarshaller;
054: import org.springframework.oxm.XmlMappingException;
055: import org.springframework.util.Assert;
056: import org.springframework.util.StringUtils;
057: import org.springframework.xml.stream.StaxEventContentHandler;
058: import org.springframework.xml.stream.XmlEventStreamReader;
059: import org.w3c.dom.Node;
060: import org.xml.sax.ContentHandler;
061: import org.xml.sax.InputSource;
062: import org.xml.sax.XMLReader;
063: import org.xml.sax.ext.LexicalHandler;
064:
065: /**
066: * Implementation of the <code>Marshaller</code> and <code>Unmarshaller</code> interfaces for JiBX.
067: * <p/>
068: * The typical usage will be to set the <code>targetClass</code> and optionally the <code>bindingName</code> property on
069: * this bean, and to refer to it.
070: *
071: * @author Arjen Poutsma
072: * @see org.jibx.runtime.IMarshallingContext
073: * @see org.jibx.runtime.IUnmarshallingContext
074: * @since 1.0.0
075: */
076: public class JibxMarshaller extends AbstractMarshaller implements
077: InitializingBean {
078:
079: private Class targetClass;
080:
081: private String bindingName;
082:
083: private IBindingFactory bindingFactory;
084:
085: private TransformerFactory transfomerFactory;
086:
087: private int indent = -1;
088:
089: private String encoding;
090:
091: private Boolean standalone;
092:
093: /** Sets the optional binding name for this instance. */
094: public void setBindingName(String bindingName) {
095: this .bindingName = bindingName;
096: }
097:
098: /** Sets the target class for this instance. This property is required. */
099: public void setTargetClass(Class targetClass) {
100: this .targetClass = targetClass;
101: }
102:
103: /** Sets the number of nesting indent spaces. Default is <code>-1</code>, i.e. no indentation. */
104: public void setIndent(int indent) {
105: this .indent = indent;
106: }
107:
108: /** Sets the document encoding using for marshalling. Default is UTF-8. */
109: public void setEncoding(String encoding) {
110: this .encoding = encoding;
111: }
112:
113: /** Sets the document standalone flag for marshalling. By default, this flag is not present. */
114: public void setStandalone(Boolean standalone) {
115: this .standalone = standalone;
116: }
117:
118: public void afterPropertiesSet() throws Exception {
119: Assert.notNull(targetClass, "targetClass is required");
120: if (logger.isInfoEnabled()) {
121: if (StringUtils.hasLength(bindingName)) {
122: logger.info("Configured for target class ["
123: + targetClass + "] using binding ["
124: + bindingName + "]");
125: } else {
126: logger.info("Configured for target class ["
127: + targetClass + "]");
128: }
129: }
130: try {
131: if (StringUtils.hasLength(bindingName)) {
132: bindingFactory = BindingDirectory.getFactory(
133: bindingName, targetClass);
134: } else {
135: bindingFactory = BindingDirectory
136: .getFactory(targetClass);
137: }
138: } catch (JiBXException ex) {
139: throw new JibxSystemException(ex);
140: }
141: transfomerFactory = TransformerFactory.newInstance();
142: }
143:
144: public boolean supports(Class clazz) {
145: return targetClass.isAssignableFrom(clazz);
146: }
147:
148: /**
149: * Convert the given <code>JiBXException</code> to an appropriate exception from the
150: * <code>org.springframework.oxm</code> hierarchy.
151: * <p/>
152: * The default implementation delegates to <code>JibxUtils</code>. Can be overridden in subclasses.
153: * <p/>
154: * A boolean flag is used to indicate whether this exception occurs during marshalling or unmarshalling, since JiBX
155: * itself does not make this distinction in its exception hierarchy.
156: *
157: * @param ex <code>JiBXException</code> that occured
158: * @param marshalling indicates whether the exception occurs during marshalling (<code>true</code>), or
159: * unmarshalling (<code>false</code>)
160: * @return the corresponding <code>XmlMappingException</code> instance
161: * @see JibxUtils#convertJibxException(org.jibx.runtime.JiBXException,boolean)
162: */
163: public XmlMappingException convertJibxException(JiBXException ex,
164: boolean marshalling) {
165: return JibxUtils.convertJibxException(ex, marshalling);
166: }
167:
168: //
169: // Supported Marshalling
170: //
171:
172: protected void marshalOutputStream(Object graph,
173: OutputStream outputStream) throws XmlMappingException,
174: IOException {
175: try {
176: IMarshallingContext marshallingContext = createMarshallingContext();
177: marshallingContext.marshalDocument(graph, encoding,
178: standalone, outputStream);
179: } catch (JiBXException ex) {
180: throw convertJibxException(ex, true);
181: }
182: }
183:
184: protected void marshalWriter(Object graph, Writer writer)
185: throws XmlMappingException, IOException {
186: try {
187: IMarshallingContext marshallingContext = createMarshallingContext();
188: marshallingContext.marshalDocument(graph, encoding,
189: standalone, writer);
190: } catch (JiBXException ex) {
191: throw convertJibxException(ex, true);
192: }
193: }
194:
195: protected void marshalXmlStreamWriter(Object graph,
196: XMLStreamWriter streamWriter) throws XmlMappingException {
197: try {
198: MarshallingContext marshallingContext = (MarshallingContext) createMarshallingContext();
199: IXMLWriter xmlWriter = new StAXWriter(marshallingContext
200: .getNamespaces(), streamWriter);
201: marshallingContext.setXmlWriter(xmlWriter);
202: marshallingContext.marshalDocument(graph);
203: } catch (JiBXException ex) {
204: throw convertJibxException(ex, false);
205: }
206: }
207:
208: //
209: // Unsupported Marshalling
210: //
211:
212: protected void marshalDomNode(Object graph, Node node)
213: throws XmlMappingException {
214: try {
215: // JiBX does not support DOM natively, so we write to a buffer first, and transform that to the Node
216: ByteArrayOutputStream os = new ByteArrayOutputStream();
217: marshalOutputStream(graph, os);
218: ByteArrayInputStream is = new ByteArrayInputStream(os
219: .toByteArray());
220: Transformer transformer = transfomerFactory
221: .newTransformer();
222: transformer.transform(new StreamSource(is), new DOMResult(
223: node));
224: } catch (IOException ex) {
225: throw new JibxSystemException(ex);
226: } catch (TransformerException ex) {
227: throw new JibxSystemException(ex);
228: }
229: }
230:
231: protected void marshalSaxHandlers(Object graph,
232: ContentHandler contentHandler, LexicalHandler lexicalHandler)
233: throws XmlMappingException {
234: try {
235: // JiBX does not support SAX natively, so we write to a buffer first, and transform that to the handlers
236: ByteArrayOutputStream os = new ByteArrayOutputStream();
237: marshalOutputStream(graph, os);
238: ByteArrayInputStream is = new ByteArrayInputStream(os
239: .toByteArray());
240: Transformer transformer = transfomerFactory
241: .newTransformer();
242: SAXResult saxResult = new SAXResult(contentHandler);
243: saxResult.setLexicalHandler(lexicalHandler);
244: transformer.transform(new StreamSource(is), saxResult);
245: } catch (IOException ex) {
246: throw new JibxSystemException(ex);
247: } catch (TransformerException ex) {
248: throw new JibxSystemException(ex);
249: }
250: }
251:
252: protected void marshalXmlEventWriter(Object graph,
253: XMLEventWriter eventWriter) {
254: ContentHandler contentHandler = new StaxEventContentHandler(
255: eventWriter);
256: marshalSaxHandlers(graph, contentHandler, null);
257: }
258:
259: //
260: // Unmarshalling
261: //
262:
263: protected Object unmarshalInputStream(InputStream inputStream)
264: throws XmlMappingException, IOException {
265: try {
266: IUnmarshallingContext unmarshallingContext = createUnmarshallingContext();
267: return unmarshallingContext.unmarshalDocument(inputStream,
268: null);
269: } catch (JiBXException ex) {
270: throw convertJibxException(ex, false);
271: }
272: }
273:
274: protected Object unmarshalReader(Reader reader)
275: throws XmlMappingException, IOException {
276: try {
277: IUnmarshallingContext unmarshallingContext = createUnmarshallingContext();
278: return unmarshallingContext.unmarshalDocument(reader);
279: } catch (JiBXException ex) {
280: throw convertJibxException(ex, false);
281: }
282: }
283:
284: protected Object unmarshalXmlStreamReader(
285: XMLStreamReader streamReader) {
286: try {
287: UnmarshallingContext unmarshallingContext = (UnmarshallingContext) createUnmarshallingContext();
288: IXMLReader xmlReader = new StAXReaderWrapper(streamReader,
289: null, true);
290: unmarshallingContext.setDocument(xmlReader);
291: return unmarshallingContext.unmarshalElement();
292: } catch (JiBXException ex) {
293: throw convertJibxException(ex, false);
294: }
295: }
296:
297: protected Object unmarshalXmlEventReader(XMLEventReader eventReader) {
298: try {
299: XMLStreamReader streamReader = new XmlEventStreamReader(
300: eventReader);
301: return unmarshalXmlStreamReader(streamReader);
302: } catch (XMLStreamException ex) {
303: throw new JibxSystemException(ex);
304: }
305: }
306:
307: //
308: // Unsupported Unmarshalling
309: //
310:
311: protected Object unmarshalDomNode(Node node)
312: throws XmlMappingException {
313: try {
314: Transformer transformer = transfomerFactory
315: .newTransformer();
316: ByteArrayOutputStream os = new ByteArrayOutputStream();
317: transformer.transform(new DOMSource(node),
318: new StreamResult(os));
319: ByteArrayInputStream is = new ByteArrayInputStream(os
320: .toByteArray());
321: return unmarshalInputStream(is);
322: } catch (IOException ex) {
323: throw new JibxSystemException(ex);
324: } catch (TransformerException ex) {
325: throw new JibxSystemException(ex);
326: }
327: }
328:
329: protected Object unmarshalSaxReader(XMLReader xmlReader,
330: InputSource inputSource) throws XmlMappingException,
331: IOException {
332: try {
333: Transformer transformer = transfomerFactory
334: .newTransformer();
335: ByteArrayOutputStream os = new ByteArrayOutputStream();
336: transformer.transform(
337: new SAXSource(xmlReader, inputSource),
338: new StreamResult(os));
339: ByteArrayInputStream is = new ByteArrayInputStream(os
340: .toByteArray());
341: return unmarshalInputStream(is);
342: } catch (IOException ex) {
343: throw new JibxSystemException(ex);
344: } catch (TransformerException ex) {
345: throw new JibxSystemException(ex);
346: }
347: }
348:
349: /**
350: * Creates a new <code>IMarshallingContext</code>, set with the correct indentation.
351: *
352: * @return the created marshalling context
353: * @throws JiBXException in case of errors
354: */
355: protected IMarshallingContext createMarshallingContext()
356: throws JiBXException {
357: IMarshallingContext marshallingContext = bindingFactory
358: .createMarshallingContext();
359: marshallingContext.setIndent(indent);
360: return marshallingContext;
361: }
362:
363: /**
364: * Creates a new <code>IUnmarshallingContext</code>, set with the correct indentation.
365: *
366: * @return the created unmarshalling context
367: * @throws JiBXException in case of errors
368: */
369: protected IUnmarshallingContext createUnmarshallingContext()
370: throws JiBXException {
371: return bindingFactory.createUnmarshallingContext();
372: }
373:
374: }
|