1   
2   
3   
4   
5   
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 
503 
504 
505 
506 
507 
508 
509 
510 
511 
512 
513 
514 
515 
516 
517 
518 
519 
520 
521 
522 
523 
524 
525 
526 
527 
528 
529 
530 
531 
532 
533 
534 
535 
536