View Javadoc

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  */