1 /*
2 * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
3 *
4 * This software is open source.
5 * See the bottom of this file for the licence.
6 */
7
8 package org.dom4j.jaxb;
9
10 import java.io.File;
11 import java.io.FileInputStream;
12 import java.io.FileNotFoundException;
13 import java.io.FileOutputStream;
14 import java.io.IOException;
15 import java.io.InputStream;
16 import java.io.InputStreamReader;
17 import java.io.OutputStream;
18 import java.io.Reader;
19 import java.io.Writer;
20 import java.net.URL;
21 import java.nio.charset.Charset;
22 import java.util.HashMap;
23 import java.util.Iterator;
24 import java.util.Map;
25
26 import org.dom4j.Document;
27 import org.dom4j.DocumentException;
28 import org.dom4j.io.ElementModifier;
29 import org.dom4j.io.OutputFormat;
30 import org.dom4j.io.SAXModifier;
31 import org.dom4j.io.XMLWriter;
32
33 import org.xml.sax.InputSource;
34
35 /**
36 * Reads an XML document using SAX and writes its content to the provided
37 * {@link org.dom4j.io.XMLWriter}. Modifications must be provided by {@link
38 * org.dom4j.jaxb.JAXBObjectModifier} objects, which are called prior to writing
39 * the XML fragment they are registered for.
40 *
41 * @author Wonne Keysers (Realsoftware.be)
42 *
43 * @see org.dom4j.io.SAXModifier
44 */
45 public class JAXBModifier extends JAXBSupport {
46 private SAXModifier modifier;
47
48 private XMLWriter xmlWriter;
49
50 private boolean pruneElements;
51
52 private OutputFormat outputFormat;
53
54 private HashMap modifiers = new HashMap();
55
56 /**
57 * Creates a new JAXBModifier for the given JAXB context path. This is the
58 * Java package where JAXB can find the generated XML classes. This package
59 * MUST contain jaxb.properties!
60 *
61 * @param contextPath
62 * JAXB context path to be used
63 *
64 * @see javax.xml.bind.JAXBContext
65 */
66 public JAXBModifier(String contextPath) {
67 super(contextPath);
68 this.outputFormat = new OutputFormat();
69 }
70
71 /**
72 * Creates a new JAXBModifier for the given JAXB context path, using the
73 * given {@link java.lang.ClassLoader}. This is the Java package where JAXB
74 * can find the generated XML classes. This package MUST contain
75 * jaxb.properties!
76 *
77 * @param contextPath
78 * JAXB context path to be used
79 * @param classloader
80 * the classloader to use
81 *
82 * @see javax.xml.bind.JAXBContext
83 */
84 public JAXBModifier(String contextPath, ClassLoader classloader) {
85 super(contextPath, classloader);
86 this.outputFormat = new OutputFormat();
87 }
88
89 /**
90 * Creates a new JAXBModifier for the given JAXB context path. The specified
91 * {@link org.dom4j.io.OutputFormat}will be used while writing the XML
92 * stream.
93 *
94 * @param contextPath
95 * JAXB context path to be used
96 * @param outputFormat
97 * the DOM4J {@link org.dom4j.io.OutputFormat}to be used
98 *
99 * @see javax.xml.bind.JAXBContext
100 */
101 public JAXBModifier(String contextPath, OutputFormat outputFormat) {
102 super(contextPath);
103 this.outputFormat = outputFormat;
104 }
105
106 /**
107 * Creates a new JAXBModifier for the given JAXB context path, using the
108 * specified {@link java.lang.Classloader}. The specified {@link
109 * org.dom4j.io.OutputFormat} will be used while writing the XML stream.
110 *
111 * @param contextPath
112 * JAXB context path to be used
113 * @param classloader
114 * the class loader to be used to load JAXB
115 * @param outputFormat
116 * the DOM4J {@link org.dom4j.io.OutputFormat}to be used
117 *
118 * @see javax.xml.bind.JAXBContext
119 */
120 public JAXBModifier(String contextPath, ClassLoader classloader,
121 OutputFormat outputFormat) {
122 super(contextPath, classloader);
123 this.outputFormat = outputFormat;
124 }
125
126 /**
127 * Parses the specified {@link java.io.File}with SAX
128 *
129 * @param source
130 * the file to parse
131 *
132 * @return the resulting DOM4J document
133 *
134 * @throws DocumentException
135 * when an error occurs while parsing
136 * @throws IOException
137 * when an error occurs while writing to the {@link
138 * org.dom4j.io.XMLWriter}
139 */
140 public Document modify(File source) throws DocumentException, IOException {
141 return installModifier().modify(source);
142 }
143
144 /**
145 * Parses the specified {@link java.io.File}with SAX, using the given
146 * {@link java.nio.charset.Charset}.
147 *
148 * @param source
149 * the file to parse
150 * @param charset
151 * the character set to use
152 *
153 * @return the resulting DOM4J document
154 *
155 * @throws DocumentException
156 * when an error occurs while parsing
157 * @throws IOException
158 * when an error occurs while writing to the {@link
159 * org.dom4j.io.XMLWriter}
160 */
161 public Document modify(File source, Charset charset)
162 throws DocumentException, IOException {
163 try {
164 Reader reader = new InputStreamReader(new FileInputStream(source),
165 charset);
166
167 return installModifier().modify(reader);
168 } catch (JAXBRuntimeException ex) {
169 Throwable cause = ex.getCause();
170 throw new DocumentException(cause.getMessage(), cause);
171 } catch (FileNotFoundException ex) {
172 throw new DocumentException(ex.getMessage(), ex);
173 }
174 }
175
176 /**
177 * Parses the specified {@link org.xml.sax.InputSource}with SAX.
178 *
179 * @param source
180 * the input source to parse
181 *
182 * @return the resulting DOM4J document
183 *
184 * @throws DocumentException
185 * when an error occurs while parsing
186 * @throws IOException
187 * when an error occurs while writing to the {@link
188 * org.dom4j.io.XMLWriter}
189 */
190 public Document modify(InputSource source) throws DocumentException,
191 IOException {
192 try {
193 return installModifier().modify(source);
194 } catch (JAXBRuntimeException ex) {
195 Throwable cause = ex.getCause();
196 throw new DocumentException(cause.getMessage(), cause);
197 }
198 }
199
200 /**
201 * Parses the specified {@link java.io.InputStream}with SAX.
202 *
203 * @param source
204 * the inputstream to parse
205 *
206 * @return the resulting DOM4J document
207 *
208 * @throws DocumentException
209 * when an error occurs while parsing
210 * @throws IOException
211 * when an error occurs while writing to the {@link
212 * org.dom4j.io.XMLWriter}
213 */
214 public Document modify(InputStream source) throws DocumentException,
215 IOException {
216 try {
217 return installModifier().modify(source);
218 } catch (JAXBRuntimeException ex) {
219 Throwable cause = ex.getCause();
220 throw new DocumentException(cause.getMessage(), cause);
221 }
222 }
223
224 /**
225 * Parses the specified {@link java.io.InputStream}with SAX.
226 *
227 * @param source
228 * the inputstream to parse
229 * @param systemId
230 * the URI of the given inputstream
231 *
232 * @return the resulting DOM4J document
233 *
234 * @throws DocumentException
235 * when an error occurs while parsing
236 * @throws IOException
237 * when an error occurs while writing to the {@link
238 * org.dom4j.io.XMLWriter}
239 */
240 public Document modify(InputStream source, String systemId)
241 throws DocumentException, IOException {
242 try {
243 return installModifier().modify(source);
244 } catch (JAXBRuntimeException ex) {
245 Throwable cause = ex.getCause();
246 throw new DocumentException(cause.getMessage(), cause);
247 }
248 }
249
250 /**
251 * Parses the specified {@link java.io.Reader}with SAX.
252 *
253 * @param r
254 * the reader to use for parsing
255 *
256 * @return the resulting DOM4J document
257 *
258 * @throws DocumentException
259 * when an error occurs while parsing
260 * @throws IOException
261 * when an error occurs while writing to the {@link
262 * org.dom4j.io.XMLWriter}
263 */
264 public Document modify(Reader r) throws DocumentException, IOException {
265 try {
266 return installModifier().modify(r);
267 } catch (JAXBRuntimeException ex) {
268 Throwable cause = ex.getCause();
269 throw new DocumentException(cause.getMessage(), cause);
270 }
271 }
272
273 /**
274 * Parses the specified {@link java.io.Reader}with SAX.
275 *
276 * @param source
277 * the reader to parse
278 * @param systemId
279 * the URI of the given reader
280 *
281 * @return the resulting DOM4J document
282 *
283 * @throws DocumentException
284 * when an error occurs while parsing
285 * @throws IOException
286 * when an error occurs while writing to the {@link
287 * org.dom4j.io.XMLWriter}
288 */
289 public Document modify(Reader source, String systemId)
290 throws DocumentException, IOException {
291 try {
292 return installModifier().modify(source);
293 } catch (JAXBRuntimeException ex) {
294 Throwable cause = ex.getCause();
295 throw new DocumentException(cause.getMessage(), cause);
296 }
297 }
298
299 /**
300 * Parses the the given URL or filename.
301 *
302 * @param url
303 * the URL or filename to parse
304 *
305 * @return the resulting DOM4J document
306 *
307 * @throws DocumentException
308 * when an error occurs while parsing
309 * @throws IOException
310 * when an error occurs while writing to the {@link
311 * org.dom4j.io.XMLWriter}
312 */
313 public Document modify(String url) throws DocumentException, IOException {
314 try {
315 return installModifier().modify(url);
316 } catch (JAXBRuntimeException ex) {
317 Throwable cause = ex.getCause();
318 throw new DocumentException(cause.getMessage(), cause);
319 }
320 }
321
322 /**
323 * Parses the the given URL.
324 *
325 * @param source
326 * the URL to parse
327 *
328 * @return the resulting DOM4J document
329 *
330 * @throws DocumentException
331 * when an error occurs while parsing
332 * @throws IOException
333 * when an error occurs while writing to the {@link
334 * org.dom4j.io.XMLWriter}
335 */
336 public Document modify(URL source) throws DocumentException, IOException {
337 try {
338 return installModifier().modify(source);
339 } catch (JAXBRuntimeException ex) {
340 Throwable cause = ex.getCause();
341 throw new DocumentException(cause.getMessage(), cause);
342 }
343 }
344
345 /**
346 * Sets the Output to write the (modified) xml document to.
347 *
348 * @param file
349 * the {@link java.io.File}to write to
350 *
351 * @throws IOException
352 * when the file cannot be found or when the outputformat
353 */
354 public void setOutput(File file) throws IOException {
355 createXMLWriter().setOutputStream(new FileOutputStream(file));
356 }
357
358 /**
359 * Sets the Output to write the (modified) xml document to.
360 *
361 * @param outputStream
362 * the {@link java.io.OutputStream}to write to
363 *
364 * @throws IOException
365 * when an error occurs
366 */
367 public void setOutput(OutputStream outputStream) throws IOException {
368 createXMLWriter().setOutputStream(outputStream);
369 }
370
371 /**
372 * Sets the Output to write the (modified) xml document to.
373 *
374 * @param writer
375 * the {@link java.io.Writer}to write to
376 *
377 * @throws IOException
378 * when an error occurs
379 */
380 public void setOutput(Writer writer) throws IOException {
381 createXMLWriter().setWriter(writer);
382 }
383
384 /**
385 * Adds the {@link JAXBObjectModifier}to be called when the specified xml
386 * path is encounted while parsing the source.
387 *
388 * @param path
389 * the element path to listen for
390 * @param mod
391 * the modifier to register
392 */
393 public void addObjectModifier(String path, JAXBObjectModifier mod) {
394 modifiers.put(path, mod);
395 }
396
397 /**
398 * Removes the {@link JAXBObjectModifier}from the event based processor,
399 * for the specified element path.
400 *
401 * @param path
402 * the xml path to remove the modifier for
403 */
404 public void removeObjectModifier(String path) {
405 modifiers.remove(path);
406 getModifier().removeModifier(path);
407 }
408
409 /**
410 * Removes all registered {@link JAXBObjectModifier}instances from the
411 * event based processor.
412 */
413 public void resetObjectModifiers() {
414 modifiers.clear();
415 getModifier().resetModifiers();
416 }
417
418 /**
419 * Returns true when the modified {@link org.dom4j.Document}is not kept in
420 * memory.
421 *
422 * @return Returns true if elements are pruned.
423 */
424 public boolean isPruneElements() {
425 return pruneElements;
426 }
427
428 /**
429 * Define whether the modified {@link org.dom4j.Document}must only be
430 * written to the output and pruned from the DOM4J tree.
431 *
432 * @param pruneElements
433 * When true, elements will not be kept in memory
434 */
435 public void setPruneElements(boolean pruneElements) {
436 this.pruneElements = pruneElements;
437 }
438
439 private SAXModifier installModifier() throws IOException {
440 modifier = new SAXModifier(isPruneElements());
441
442 modifier.resetModifiers();
443
444 Iterator modifierIt = modifiers.entrySet().iterator();
445
446 while (modifierIt.hasNext()) {
447 Map.Entry entry = (Map.Entry) modifierIt.next();
448 ElementModifier mod = new JAXBElementModifier(this,
449 (JAXBObjectModifier) entry.getValue());
450 getModifier().addModifier((String) entry.getKey(), mod);
451 }
452
453 modifier.setXMLWriter(getXMLWriter());
454
455 return modifier;
456 }
457
458 private SAXModifier getModifier() {
459 if (this.modifier == null) {
460 modifier = new SAXModifier(isPruneElements());
461 }
462
463 return modifier;
464 }
465
466 private XMLWriter getXMLWriter() {
467 return xmlWriter;
468 }
469
470 private XMLWriter createXMLWriter() throws IOException {
471 if (this.xmlWriter == null) {
472 xmlWriter = new XMLWriter(outputFormat);
473 }
474
475 return xmlWriter;
476 }
477
478 private class JAXBElementModifier implements ElementModifier {
479 private JAXBModifier jaxbModifier;
480
481 private JAXBObjectModifier objectModifier;
482
483 public JAXBElementModifier(JAXBModifier jaxbModifier,
484 JAXBObjectModifier objectModifier) {
485 this.jaxbModifier = jaxbModifier;
486 this.objectModifier = objectModifier;
487 }
488
489 public org.dom4j.Element modifyElement(org.dom4j.Element element)
490 throws Exception {
491 javax.xml.bind.Element originalObject = jaxbModifier
492 .unmarshal(element);
493 javax.xml.bind.Element modifiedObject = objectModifier
494 .modifyObject(originalObject);
495
496 return jaxbModifier.marshal(modifiedObject);
497 }
498 }
499 }
500
501 /*
502 * Redistribution and use of this software and associated documentation
503 * ("Software"), with or without modification, are permitted provided that the
504 * following conditions are met:
505 *
506 * 1. Redistributions of source code must retain copyright statements and
507 * notices. Redistributions must also contain a copy of this document.
508 *
509 * 2. Redistributions in binary form must reproduce the above copyright notice,
510 * this list of conditions and the following disclaimer in the documentation
511 * and/or other materials provided with the distribution.
512 *
513 * 3. The name "DOM4J" must not be used to endorse or promote products derived
514 * from this Software without prior written permission of MetaStuff, Ltd. For
515 * written permission, please contact dom4j-info@metastuff.com.
516 *
517 * 4. Products derived from this Software may not be called "DOM4J" nor may
518 * "DOM4J" appear in their names without prior written permission of MetaStuff,
519 * Ltd. DOM4J is a registered trademark of MetaStuff, Ltd.
520 *
521 * 5. Due credit should be given to the DOM4J Project - http://www.dom4j.org
522 *
523 * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS ``AS IS'' AND
524 * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
525 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
526 * ARE DISCLAIMED. IN NO EVENT SHALL METASTUFF, LTD. OR ITS CONTRIBUTORS BE
527 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
528 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
529 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
530 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
531 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
532 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
533 * POSSIBILITY OF SUCH DAMAGE.
534 *
535 * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
536 */