001: /*
002: * Copyright (C) 2004 Joe Walnes.
003: * Copyright (C) 2006, 2007 XStream Committers.
004: * All rights reserved.
005: *
006: * The software in this package is published under the terms of the BSD
007: * style license a copy of which has been included with this distribution in
008: * the LICENSE.txt file.
009: *
010: * Created on 14. August 2004 by Joe Walnes
011: */
012: package com.thoughtworks.xstream.io.xml;
013:
014: import com.thoughtworks.xstream.XStream;
015:
016: import org.xml.sax.InputSource;
017: import org.xml.sax.SAXException;
018: import org.xml.sax.XMLFilter;
019: import org.xml.sax.XMLReader;
020:
021: import javax.xml.transform.sax.SAXSource;
022:
023: import java.util.ArrayList;
024: import java.util.List;
025:
026: /**
027: * A {@link SAXSource JAXP TrAX Source} that enables using XStream object serialization as
028: * direct input for XSLT processors without resorting to an intermediate representation such as
029: * text XML, DOM or DOM4J. <p/> The following example shows how to apply an XSL Transformation
030: * to a set of Java objects gathered into a List (<code>source</code>):
031: * </p>
032: *
033: * <pre><code>
034: * public static String transform(List source, String stylesheet) {
035: * try {
036: * Transformer transformer = TransformerFactory.newInstance().newTransformer(
037: * new StreamSource(stylesheet));
038: * TraxSource in = new TraxSource(source);
039: * Writer out = new StringWriter();
040: * transformer.transform(in, new StreamResult(out));
041: * return out.toString();
042: * } catch (TransformerException e) {
043: * throw new RuntimeException("XSLT Transformation failed", e);
044: * }
045: * }
046: * </code></pre>
047: *
048: * @author Laurent Bihanic
049: */
050: public class TraxSource extends SAXSource {
051:
052: /**
053: * If {@link javax.xml.transform.TransformerFactory#getFeature} returns <code>true</code>
054: * when passed this value as an argument, the Transformer natively supports XStream. <p/>
055: * <strong>Note</strong>: This implementation does not override the
056: * {@link SAXSource#FEATURE} value defined by its superclass to be considered as a SAXSource
057: * by Transformer implementations not natively supporting this XStream-specific source
058: * </p>
059: */
060: public final static String XSTREAM_FEATURE = "http://com.thoughtworks.xstream/XStreamSource/feature";
061:
062: /**
063: * The XMLReader object associated to this source or <code>null</code> if no XMLReader has
064: * yet been requested.
065: *
066: * @see #getXMLReader
067: */
068: private XMLReader xmlReader = null;
069:
070: /**
071: * The configured XStream facade to use for serializing objects.
072: */
073: private XStream xstream = null;
074:
075: /**
076: * The list of Java objects to be serialized.
077: */
078: private List source = null;
079:
080: // -------------------------------------------------------------------------
081: // Constructors
082: // -------------------------------------------------------------------------
083:
084: /**
085: * Creates a XStream TrAX source.
086: */
087: public TraxSource() {
088: super (new InputSource());
089: }
090:
091: /**
092: * Creates a XStream TrAX source, specifying the object to marshal.
093: *
094: * @param source the object to marshal.
095: * @throws IllegalArgumentException if <code>source</code> is <code>null</code>.
096: * @see #setSource(java.lang.Object)
097: */
098: public TraxSource(Object source) {
099: super (new InputSource());
100:
101: this .setSource(source);
102: }
103:
104: /**
105: * Creates a XStream TrAX source, specifying the object to marshal and a configured (with
106: * aliases) XStream facade.
107: *
108: * @param source the object to marshal.
109: * @param xstream a configured XStream facade.
110: * @throws IllegalArgumentException if <code>source</code> or <code>xstream</code> is
111: * <code>null</code>.
112: * @see #setSource(java.lang.Object)
113: * @see #setXStream(com.thoughtworks.xstream.XStream)
114: */
115: public TraxSource(Object source, XStream xstream) {
116: super (new InputSource());
117:
118: this .setSource(source);
119: this .setXStream(xstream);
120: }
121:
122: /**
123: * Creates a XStream TrAX source, setting the objects to marshal.
124: *
125: * @param source the list of objects to marshal.
126: * @throws IllegalArgumentException if <code>source</code> is <code>null</code> or
127: * empty.
128: * @see #setSourceAsList(java.util.List)
129: */
130: public TraxSource(List source) {
131: super (new InputSource());
132:
133: this .setSourceAsList(source);
134: }
135:
136: /**
137: * Creates a XStream TrAX source, setting the objects to marshal and a configured (with
138: * aliases) XStream facade.
139: *
140: * @param source the list of objects to marshal.
141: * @param xstream a configured XStream facade.
142: * @throws IllegalArgumentException if <code>source</code> or <code>xstream</code> is
143: * <code>null</code> or <code>source</code> is empty.
144: * @see #setSourceAsList(java.util.List)
145: * @see #setXStream(com.thoughtworks.xstream.XStream)
146: */
147: public TraxSource(List source, XStream xstream) {
148: super (new InputSource());
149:
150: this .setSourceAsList(source);
151: this .setXStream(xstream);
152: }
153:
154: // -------------------------------------------------------------------------
155: // SAXSource overwritten methods
156: // -------------------------------------------------------------------------
157:
158: /**
159: * Sets the SAX InputSource to be used for the Source. <p/> As this implementation only
160: * supports object lists as data source, this method always throws an
161: * {@link UnsupportedOperationException}.
162: * </p>
163: *
164: * @param inputSource a valid InputSource reference.
165: * @throws UnsupportedOperationException always!
166: */
167: public void setInputSource(InputSource inputSource) {
168: throw new UnsupportedOperationException();
169: }
170:
171: /**
172: * Set the XMLReader to be used for the Source. <p/> As this implementation only supports
173: * object lists as data source, this method throws an {@link UnsupportedOperationException}
174: * if the provided reader object does not implement the SAX {@link XMLFilter} interface.
175: * Otherwise, a {@link SaxWriter} instance will be attached as parent of the filter chain.
176: * </p>
177: *
178: * @param reader a valid XMLReader or XMLFilter reference.
179: * @throws UnsupportedOperationException if <code>reader</code> is not a SAX
180: * {@link XMLFilter}.
181: * @see #getXMLReader
182: */
183: public void setXMLReader(XMLReader reader) {
184: this .createXMLReader(reader);
185: }
186:
187: /**
188: * Returns the XMLReader to be used for the Source. <p/> This implementation returns a
189: * specific XMLReader ({@link SaxWriter}) generating the XML from a list of input objects.
190: * </p>
191: *
192: * @return an XMLReader generating the XML from a list of input objects.
193: */
194: public XMLReader getXMLReader() {
195: if (this .xmlReader == null) {
196: this .createXMLReader(null);
197: }
198: return this .xmlReader;
199: }
200:
201: // -------------------------------------------------------------------------
202: // Specific implementation
203: // -------------------------------------------------------------------------
204:
205: /**
206: * Sets the XStream facade to use when marshalling objects.
207: *
208: * @param xstream a configured XStream facade.
209: * @throws IllegalArgumentException if <code>xstream</code> is <code>null</code>.
210: */
211: public void setXStream(XStream xstream) {
212: if (xstream == null) {
213: throw new IllegalArgumentException("xstream");
214: }
215: this .xstream = xstream;
216:
217: this .configureXMLReader();
218: }
219:
220: /**
221: * Sets the object to marshal.
222: *
223: * @param obj the object to marshal.
224: * @throws IllegalArgumentException if <code>source</code> is <code>null</code>.
225: */
226: public void setSource(Object obj) {
227: if (obj == null) {
228: throw new IllegalArgumentException("obj");
229: }
230: List list = new ArrayList(1);
231: list.add(obj);
232:
233: this .setSourceAsList(list);
234: }
235:
236: /**
237: * Sets the list of objects to marshal. <p/> When dealing with non-text input (such as SAX
238: * or DOM), XSLT processors support multiple root node children for the source tree (see <a
239: * href="http://www.w3.org/TR/xslt#root-node-children">section 3.1</a> of the "XSL
240: * Transformations (XSLT) Version 1.0" specification. Using a list of objects as source
241: * makes use of this feature and allows creating XML documents merging the XML serialization
242: * of several Java objects.
243: *
244: * @param list the list of objects to marshal.
245: * @throws IllegalArgumentException if <code>source</code> is <code>null</code> or
246: * empty.
247: */
248: public void setSourceAsList(List list) {
249: if ((list == null) || (list.isEmpty())) {
250: throw new IllegalArgumentException("list");
251: }
252: this .source = list;
253:
254: this .configureXMLReader();
255: }
256:
257: private void createXMLReader(XMLReader filterChain) {
258: if (filterChain == null) {
259: this .xmlReader = new SaxWriter();
260: } else {
261: if (filterChain instanceof XMLFilter) {
262: // Connect the filter chain to a document reader.
263: XMLFilter filter = (XMLFilter) filterChain;
264: while (filter.getParent() instanceof XMLFilter) {
265: filter = (XMLFilter) (filter.getParent());
266: }
267: if (!(filter.getParent() instanceof SaxWriter)) {
268: filter.setParent(new SaxWriter());
269: }
270:
271: // Read XML data from filter chain.
272: this .xmlReader = filterChain;
273: } else {
274: throw new UnsupportedOperationException();
275: }
276: }
277: this .configureXMLReader();
278: }
279:
280: private void configureXMLReader() {
281: if (this .xmlReader != null) {
282: try {
283: if (this .xstream != null) {
284: this .xmlReader.setProperty(
285: SaxWriter.CONFIGURED_XSTREAM_PROPERTY,
286: this .xstream);
287: }
288: if (this .source != null) {
289: this .xmlReader.setProperty(
290: SaxWriter.SOURCE_OBJECT_LIST_PROPERTY,
291: this .source);
292: }
293: } catch (SAXException e) {
294: throw new IllegalArgumentException(e.getMessage());
295: }
296: }
297: }
298: }
|