1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.oxm.xstream;
18
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.InputStreamReader;
22 import java.io.OutputStream;
23 import java.io.OutputStreamWriter;
24 import java.io.Reader;
25 import java.io.Writer;
26 import java.util.Iterator;
27 import java.util.Map;
28 import javax.xml.stream.XMLEventReader;
29 import javax.xml.stream.XMLEventWriter;
30 import javax.xml.stream.XMLStreamException;
31 import javax.xml.stream.XMLStreamReader;
32 import javax.xml.stream.XMLStreamWriter;
33
34 import com.thoughtworks.xstream.XStream;
35 import com.thoughtworks.xstream.converters.Converter;
36 import com.thoughtworks.xstream.converters.ConverterMatcher;
37 import com.thoughtworks.xstream.converters.SingleValueConverter;
38 import com.thoughtworks.xstream.io.HierarchicalStreamDriver;
39 import com.thoughtworks.xstream.io.HierarchicalStreamReader;
40 import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
41 import com.thoughtworks.xstream.io.xml.CompactWriter;
42 import com.thoughtworks.xstream.io.xml.DomReader;
43 import com.thoughtworks.xstream.io.xml.DomWriter;
44 import com.thoughtworks.xstream.io.xml.QNameMap;
45 import com.thoughtworks.xstream.io.xml.SaxWriter;
46 import com.thoughtworks.xstream.io.xml.StaxReader;
47 import com.thoughtworks.xstream.io.xml.StaxWriter;
48 import com.thoughtworks.xstream.io.xml.XmlFriendlyReplacer;
49 import com.thoughtworks.xstream.io.xml.XppReader;
50 import org.w3c.dom.Document;
51 import org.w3c.dom.Element;
52 import org.w3c.dom.Node;
53 import org.xml.sax.ContentHandler;
54 import org.xml.sax.InputSource;
55 import org.xml.sax.XMLReader;
56 import org.xml.sax.ext.LexicalHandler;
57
58 import org.springframework.beans.propertyeditors.ClassEditor;
59 import org.springframework.oxm.AbstractMarshaller;
60 import org.springframework.oxm.XmlMappingException;
61 import org.springframework.util.ObjectUtils;
62 import org.springframework.util.StringUtils;
63 import org.springframework.xml.stream.StaxEventContentHandler;
64 import org.springframework.xml.stream.XmlEventStreamReader;
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84 public class XStreamMarshaller extends AbstractMarshaller {
85
86
87 public static final String DEFAULT_ENCODING = "UTF-8";
88
89 private XStream xstream = new XStream();
90
91 private String encoding;
92
93 private Class[] supportedClasses;
94
95
96 private HierarchicalStreamDriver streamDriver;
97
98
99
100
101
102
103 public String getEncoding() {
104 return encoding != null ? encoding : DEFAULT_ENCODING;
105 }
106
107
108
109
110
111
112 public void setEncoding(String encoding) {
113 this.encoding = encoding;
114 }
115
116
117 public XStream getXStream() {
118 return xstream;
119 }
120
121
122
123
124
125
126
127
128 public void setMode(int mode) {
129 getXStream().setMode(mode);
130 }
131
132
133
134
135
136
137
138 public void setSupportedClasses(Class[] supportedClasses) {
139 this.supportedClasses = supportedClasses;
140 }
141
142
143
144
145
146
147
148
149 public void setConverters(ConverterMatcher[] converters) {
150 for (int i = 0; i < converters.length; i++) {
151 if (converters[i] instanceof Converter) {
152 getXStream().registerConverter((Converter) converters[i], i);
153 }
154 else if (converters[i] instanceof SingleValueConverter) {
155 getXStream().registerConverter((SingleValueConverter) converters[i], i);
156 }
157 else {
158 throw new IllegalArgumentException("Invalid ConverterMatcher [" + converters[i] + "]");
159 }
160 }
161 }
162
163
164 public void setStreamDriver(HierarchicalStreamDriver streamDriver) {
165 this.streamDriver = streamDriver;
166 }
167
168
169
170
171
172
173
174 public void setAliases(Map aliases) {
175 for (Iterator iterator = aliases.entrySet().iterator(); iterator.hasNext();) {
176 Map.Entry entry = (Map.Entry) iterator.next();
177
178 Class type;
179 if (entry.getValue() instanceof Class) {
180 type = (Class) entry.getValue();
181 }
182 else {
183 ClassEditor editor = new ClassEditor();
184 editor.setAsText(String.valueOf(entry.getValue()));
185 type = (Class) editor.getValue();
186 }
187 addAlias((String) entry.getKey(), type);
188 }
189 }
190
191
192
193
194
195
196
197 public void addAlias(String name, Class type) {
198 getXStream().alias(name, type);
199 }
200
201
202
203
204
205
206 public void setUseAttributeForTypes(Class[] types) {
207 for (int i = 0; i < types.length; i++) {
208 getXStream().useAttributeFor(types[i]);
209 }
210 }
211
212
213
214
215
216
217 public void setUseAttributeFor(Map attributes) {
218 for (Iterator iterator = attributes.entrySet().iterator(); iterator.hasNext();) {
219 Map.Entry entry = (Map.Entry) iterator.next();
220 if (entry.getKey() instanceof String && entry.getValue() instanceof Class) {
221 getXStream().useAttributeFor((String) entry.getKey(), (Class) entry.getValue());
222 }
223 else if (entry.getKey() instanceof Class && entry.getValue() instanceof String) {
224 getXStream().useAttributeFor((Class) entry.getKey(), (String) entry.getValue());
225 }
226 else {
227 throw new IllegalArgumentException("Invalid attribute key and value pair. " +
228 "'useAttributesFor' property takes either a <String, Class> map or a <Class, String> map");
229 }
230 }
231 }
232
233
234
235
236
237
238 public void addImplicitCollection(String name, Class type) {
239 getXStream().addImplicitCollection(type, name);
240 }
241
242
243
244
245
246
247
248 public void setImplicitCollection(Map implicitCollection) {
249 for (Iterator iterator = implicitCollection.entrySet().iterator(); iterator.hasNext();) {
250 Map.Entry entry = (Map.Entry) iterator.next();
251
252 Class type;
253 if (entry.getValue() instanceof Class) {
254 type = (Class) entry.getValue();
255 }
256 else {
257 ClassEditor editor = new ClassEditor();
258 editor.setAsText(String.valueOf(entry.getValue()));
259 type = (Class) editor.getValue();
260 }
261 addImplicitCollection((String) entry.getKey(), type);
262 }
263 }
264
265
266
267
268
269
270
271
272 public void addOmittedField(Class type, String fieldName) {
273 getXStream().omitField(type, fieldName);
274 }
275
276
277
278
279
280
281
282 public void setOmittedFields(Map omittedFields) {
283 for (Iterator iterator = omittedFields.entrySet().iterator(); iterator.hasNext();) {
284 Map.Entry entry = (Map.Entry) iterator.next();
285
286 Class type;
287 if (entry.getKey() instanceof Class) {
288 type = (Class) entry.getKey();
289 }
290 else {
291 ClassEditor editor = new ClassEditor();
292 editor.setAsText(String.valueOf(entry.getKey()));
293 type = (Class) editor.getValue();
294 }
295
296 String fieldsString = (String) entry.getValue();
297 String[] fields = StringUtils.commaDelimitedListToStringArray(fieldsString);
298 for (int i = 0; i < fields.length; i++) {
299 addOmittedField(type, fields[i]);
300 }
301 }
302 }
303
304 public boolean supports(Class clazz) {
305 if (ObjectUtils.isEmpty(supportedClasses)) {
306 return true;
307 }
308 else {
309 for (int i = 0; i < supportedClasses.length; i++) {
310 if (supportedClasses[i].isAssignableFrom(clazz)) {
311 return true;
312 }
313 }
314 return false;
315 }
316 }
317
318
319
320
321
322
323
324
325
326
327
328
329
330 public XmlMappingException convertXStreamException(Exception ex, boolean marshalling) {
331 return XStreamUtils.convertXStreamException(ex, marshalling);
332 }
333
334
335
336
337
338
339
340
341
342 private void marshal(Object graph, HierarchicalStreamWriter streamWriter) {
343 try {
344 getXStream().marshal(graph, streamWriter);
345 }
346 catch (Exception ex) {
347 throw convertXStreamException(ex, true);
348 }
349 }
350
351 protected void marshalDomNode(Object graph, Node node) throws XmlMappingException {
352 HierarchicalStreamWriter streamWriter;
353 if (node instanceof Document) {
354 streamWriter = new DomWriter((Document) node);
355 }
356 else if (node instanceof Element) {
357 streamWriter = new DomWriter((Element) node, node.getOwnerDocument(), new XmlFriendlyReplacer());
358 }
359 else {
360 throw new IllegalArgumentException("DOMResult contains neither Document nor Element");
361 }
362 marshal(graph, streamWriter);
363 }
364
365 protected void marshalXmlEventWriter(Object graph, XMLEventWriter eventWriter) throws XmlMappingException {
366 ContentHandler contentHandler = new StaxEventContentHandler(eventWriter);
367 marshalSaxHandlers(graph, contentHandler, null);
368 }
369
370 protected void marshalXmlStreamWriter(Object graph, XMLStreamWriter streamWriter) throws XmlMappingException {
371 try {
372 marshal(graph, new StaxWriter(new QNameMap(), streamWriter));
373 }
374 catch (XMLStreamException ex) {
375 throw convertXStreamException(ex, true);
376 }
377 }
378
379 protected void marshalOutputStream(Object graph, OutputStream outputStream)
380 throws XmlMappingException, IOException {
381 marshalWriter(graph, new OutputStreamWriter(outputStream, getEncoding()));
382 }
383
384 protected void marshalSaxHandlers(Object graph, ContentHandler contentHandler, LexicalHandler lexicalHandler)
385 throws XmlMappingException {
386 SaxWriter saxWriter = new SaxWriter();
387 saxWriter.setContentHandler(contentHandler);
388 marshal(graph, saxWriter);
389 }
390
391 protected void marshalWriter(Object graph, Writer writer) throws XmlMappingException, IOException {
392 if (streamDriver != null) {
393 marshal(graph, streamDriver.createWriter(writer));
394 }
395 else {
396 marshal(graph, new CompactWriter(writer));
397 }
398 }
399
400
401
402
403
404 private Object unmarshal(HierarchicalStreamReader streamReader) {
405 try {
406 return getXStream().unmarshal(streamReader);
407 }
408 catch (Exception ex) {
409 throw convertXStreamException(ex, false);
410 }
411 }
412
413 protected Object unmarshalDomNode(Node node) throws XmlMappingException {
414 HierarchicalStreamReader streamReader;
415 if (node instanceof Document) {
416 streamReader = new DomReader((Document) node);
417 }
418 else if (node instanceof Element) {
419 streamReader = new DomReader((Element) node);
420 }
421 else {
422 throw new IllegalArgumentException("DOMSource contains neither Document nor Element");
423 }
424 return unmarshal(streamReader);
425 }
426
427 protected Object unmarshalXmlEventReader(XMLEventReader eventReader) throws XmlMappingException {
428 try {
429 XMLStreamReader streamReader = new XmlEventStreamReader(eventReader);
430 return unmarshalXmlStreamReader(streamReader);
431 }
432 catch (XMLStreamException ex) {
433 throw convertXStreamException(ex, false);
434 }
435 }
436
437 protected Object unmarshalXmlStreamReader(XMLStreamReader streamReader) throws XmlMappingException {
438 return unmarshal(new StaxReader(new QNameMap(), streamReader));
439 }
440
441 protected Object unmarshalInputStream(InputStream inputStream) throws XmlMappingException, IOException {
442 return unmarshalReader(new InputStreamReader(inputStream, getEncoding()));
443 }
444
445 protected Object unmarshalReader(Reader reader) throws XmlMappingException, IOException {
446 if (streamDriver != null) {
447 return unmarshal(streamDriver.createReader(reader));
448 }
449 else {
450 return unmarshal(new XppReader(reader));
451 }
452 }
453
454 protected Object unmarshalSaxReader(XMLReader xmlReader, InputSource inputSource)
455 throws XmlMappingException, IOException {
456 throw new UnsupportedOperationException(
457 "XStreamMarshaller does not support unmarshalling using SAX XMLReaders");
458 }
459
460
461 }