View Javadoc

1   /*
2    * Copyright 2001-2004 (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    * $Id: SAXModifier.java,v 1.1 2004/08/02 18:44:07 maartenc Exp $
8    */
9   
10  package org.dom4j.io;
11  
12  import java.io.File;
13  import java.io.InputStream;
14  import java.io.Reader;
15  import java.net.URL;
16  import java.util.HashMap;
17  import java.util.Iterator;
18  import java.util.Map;
19  
20  import org.dom4j.Document;
21  import org.dom4j.DocumentException;
22  import org.dom4j.DocumentFactory;
23  import org.xml.sax.InputSource;
24  import org.xml.sax.SAXException;
25  import org.xml.sax.XMLReader;
26  
27  /***
28   * The SAXModifier reads, modifies and writes XML documents using SAX. <br>
29   * Registered {@link ElementModifier} objects can provide modifications to (part of) the xml tree,
30   * while the document is still being processed. This makes it possible to change large xml documents
31   * without having them in memory.<br>
32   * The modified document is written when the {@link XMLWriter} is specified.
33   * 
34   * @see org.dom4j.io.SAXReader
35   * @see org.dom4j.io.XMLWriter
36   * @author Wonne Keysers (Realsoftware.be)
37   */
38  public class SAXModifier {
39  
40     private XMLWriter xmlWriter;
41     private XMLReader xmlReader;
42  
43     private boolean pruneElements;
44     private SAXModifyReader modifyReader;
45     private HashMap modifiers = new HashMap();
46  
47     /***
48      * Creates a new modifier.<br>
49      * The XMLReader to parse the source will be created via the org.xml.sax.driver system property
50      * or JAXP if the system property is not set.
51      */
52     public SAXModifier() {
53     }
54  
55     /***
56      * Creates a new modifier.<br>
57      * The XMLReader to parse the source will be created via the org.xml.sax.driver system property
58      * or JAXP if the system property is not set.
59      * 
60      * @param pruneElements Set to true when the modified document must NOT be kept in memory.
61      */
62     public SAXModifier(boolean pruneElements) {
63        this.pruneElements = pruneElements;
64     }
65  
66     /***
67      * Creates a new modifier that will the specified {@link org.xml.sax.XMLReader}
68      * to parse the source.
69      * 
70      * @param xmlReader The XMLReader to use
71      */
72     public SAXModifier(XMLReader xmlReader) {
73        this.xmlReader = xmlReader;
74     }
75  
76     /***
77      * Creates a new modifier that will the specified {@link org.xml.sax.XMLReader}
78      * to parse the source.
79      * 
80      * @param xmlReader The XMLReader to use
81      * @param pruneElements Set to true when the modified document must NOT be kept in memory.
82      */
83     public SAXModifier(XMLReader xmlReader, boolean pruneElements) {
84        this.xmlReader = xmlReader;
85     }
86  
87     /*** 
88      * Reads a Document from the given {@link java.io.File}
89      * and writes it to the specified {@link XMLWriter} using SAX.
90      * Registered {@link ElementModifier} objects are invoked on the fly.
91      *
92      * @param source is the <code>File</code> to read from.
93      * @return the newly created Document instance
94      * @throws {@link org.dom4j.DocumentException} if an error occurs during parsing.
95      */
96     public Document modify(File source) throws DocumentException {
97        try {
98           return installModifyReader().read(source);
99        }
100       catch (SAXModifyException ex) {
101          Throwable cause = ex.getCause();
102          throw new DocumentException(cause.getMessage(), cause);
103       }
104    }
105 
106    /*** 
107     * Reads a Document from the given {@link org.xml.sax.InputSource}
108     * and writes it to the specified {@link XMLWriter} using SAX.
109     * Registered {@link ElementModifier} objects are invoked on the fly.
110     *
111     * @param source is the <code>org.xml.sax.InputSource</code> to read from.
112     * @return the newly created Document instance
113     * @throws {@link org.dom4j.DocumentException} if an error occurs during parsing.
114     */
115    public Document modify(InputSource source) throws DocumentException {
116       try {
117          return installModifyReader().read(source);
118       }
119       catch (SAXModifyException ex) {
120          Throwable cause = ex.getCause();
121          throw new DocumentException(cause.getMessage(), cause);
122       }
123    }
124 
125    /*** 
126     * Reads a Document from the given {@link java.io.InputStream}
127     * and writes it to the specified {@link XMLWriter} using SAX.
128     * Registered {@link ElementModifier} objects are invoked on the fly.
129     *
130     * @param source is the <code>java.io.InputStream</code> to read from.
131     * @return the newly created Document instance
132     * @throws {@link org.dom4j.DocumentException} if an error occurs during parsing.
133     */
134    public Document modify(InputStream source) throws DocumentException {
135       try {
136          return installModifyReader().read(source);
137       }
138       catch (SAXModifyException ex) {
139          Throwable cause = ex.getCause();
140          throw new DocumentException(cause.getMessage(), cause);
141       }
142    }
143 
144    /*** 
145     * Reads a Document from the given {@link java.io.InputStream}
146     * and writes it to the specified {@link XMLWriter} using SAX.
147     * Registered {@link ElementModifier} objects are invoked on the fly.
148     *
149     * @param source is the <code>java.io.InputStream</code> to read from.
150     * @return the newly created Document instance
151     * @throws {@link org.dom4j.DocumentException} if an error occurs during parsing.
152     */
153    public Document modify(InputStream source, String systemId) throws DocumentException {
154       try {
155          return installModifyReader().read(source);
156       }
157       catch (SAXModifyException ex) {
158          Throwable cause = ex.getCause();
159          throw new DocumentException(cause.getMessage(), cause);
160       }
161    }
162 
163    /*** 
164     * Reads a Document from the given {@link java.io.Reader}
165     * and writes it to the specified {@link XMLWriter} using SAX.
166     * Registered {@link ElementModifier} objects are invoked on the fly.
167     *
168     * @param source is the <code>java.io.Reader</code> to read from.
169     * @return the newly created Document instance
170     * @throws {@link org.dom4j.DocumentException} if an error occurs during parsing.
171     */
172    public Document modify(Reader source) throws DocumentException {
173       try {
174          return installModifyReader().read(source);
175       }
176       catch (SAXModifyException ex) {
177          Throwable cause = ex.getCause();
178          throw new DocumentException(cause.getMessage(), cause);
179       }
180    }
181 
182    /*** 
183     * Reads a Document from the given {@link java.io.Reader}
184     * and writes it to the specified {@link XMLWriter} using SAX.
185     * Registered {@link ElementModifier} objects are invoked on the fly.
186     *
187     * @param source is the <code>java.io.Reader</code> to read from.
188     * @return the newly created Document instance
189     * @throws {@link org.dom4j.DocumentException} if an error occurs during parsing.
190     */
191    public Document modify(Reader source, String systemId) throws DocumentException {
192       try {
193          return installModifyReader().read(source);
194       }
195       catch (SAXModifyException ex) {
196          Throwable cause = ex.getCause();
197          throw new DocumentException(cause.getMessage(), cause);
198       }
199    }
200 
201    /*** 
202     * Reads a Document from the given {@link java.net.URL}
203     * and writes it to the specified {@link XMLWriter} using SAX.
204     * Registered {@link ElementModifier} objects are invoked on the fly.
205     *
206     * @param source is the <code>java.net.URL</code> to read from.
207     * @return the newly created Document instance
208     * @throws {@link org.dom4j.DocumentException} if an error occurs during parsing.
209     */
210    public Document modify(URL source) throws DocumentException {
211       try {
212          return installModifyReader().read(source);
213       }
214       catch (SAXModifyException ex) {
215          Throwable cause = ex.getCause();
216          throw new DocumentException(cause.getMessage(), cause);
217       }
218    }
219 
220    /*** 
221     * Reads a Document from the given URL or filename
222     * and writes it to the specified {@link XMLWriter} using SAX.
223     * Registered {@link ElementModifier} objects are invoked on the fly.
224     *
225     * @param source is the URL or filename to read from.
226     * @return the newly created Document instance
227     * @throws {@link org.dom4j.DocumentException} if an error occurs during parsing.
228     */
229    public Document modify(String source) throws DocumentException {
230       try {
231          return installModifyReader().read(source);
232       }
233       catch (SAXModifyException ex) {
234          Throwable cause = ex.getCause();
235          throw new DocumentException(cause.getMessage(), cause);
236       }
237    }
238 
239    /*** 
240     * Adds the {@link ElementModifier} to be called
241     * when the specified element path is encounted while parsing the source.
242     *
243     * @param path The element path to be handled
244     * @param handler The {@link ElementModifier} to be called by the event based processor.
245     */
246    public void addModifier(String path, ElementModifier modifier) {
247       this.modifiers.put(path, modifier);
248    }
249 
250    /***
251     * Removes all registered {@link ElementModifier} instances from the event based processor.
252     */
253    public void resetModifiers() {
254       this.modifiers.clear();
255       getSAXModifyReader().resetHandlers();
256    }
257 
258    /***
259     * Removes the {@link ElementModifier} from the event based processor,
260     * for the specified element path.
261     *
262     * @param path The path to remove the {@link ElementModifier} for.
263     */
264    public void removeModifier(String path) {
265       this.modifiers.remove(path);
266       getSAXModifyReader().removeHandler(path);
267    }
268 
269    /***
270     * Get the {@link org.dom4j.DocumentFactory} used to create the DOM4J document structure
271     * 
272     * @return <code>DocumentFactory</code> that will be used
273     */
274    public DocumentFactory getDocumentFactory() {
275       return getSAXModifyReader().getDocumentFactory();
276    }
277 
278    /***
279     * Sets the {@link org.dom4j.DocumentFactory} used to create the DOM4J document tree.
280     * 
281     * @param factory <code>DocumentFactory</code> to be used
282     */
283    public void setDocumentFactory(DocumentFactory factory) {
284       getSAXModifyReader().setDocumentFactory(factory);
285    }
286 
287    /***
288     * Returns the current {@link XMLWriter}.  
289     * @return XMLWriter
290     */
291    public XMLWriter getXMLWriter() {
292       return this.xmlWriter;
293    }
294 
295    /***
296     * Sets the {@link XMLWriter} used to write the modified document.
297     * @param xmlWriter The writer to use.
298     */
299    public void setXMLWriter(XMLWriter xmlWriter) {
300       this.xmlWriter = xmlWriter;
301    }
302 
303    /***
304     * Returns true when xml elements are not kept in memory while parsing.
305     * The {@link org.dom4j.Document} returned by the modify methods will be null.
306     * @return Returns the pruneElements.
307     */
308    public boolean isPruneElements() {
309       return pruneElements;
310    }
311 
312    private SAXReader installModifyReader() throws DocumentException {
313       try {
314          SAXModifyReader reader = getSAXModifyReader();
315          
316          if (isPruneElements()) {
317             modifyReader.setDispatchHandler(new PruningDispatchHandler());
318          }
319          
320          reader.resetHandlers();
321          
322          Iterator modifierIt = this.modifiers.entrySet().iterator();
323          while(modifierIt.hasNext()){
324             Map.Entry entry = (Map.Entry)modifierIt.next();
325             
326 	        SAXModifyElementHandler modifierHandler
327 	        	= new SAXModifyElementHandler((ElementModifier)entry.getValue());
328 	        reader.addHandler((String)entry.getKey(), modifierHandler);
329          }
330 
331          reader.setXMLWriter(getXMLWriter());
332          reader.setXMLReader(getXMLReader());
333 
334          return reader;
335       }
336       catch (SAXException ex) {
337          throw new DocumentException(ex.getMessage(), ex);
338       }
339    }
340 
341    private XMLReader getXMLReader() throws SAXException {
342       if (this.xmlReader == null) {
343          xmlReader = SAXHelper.createXMLReader(false);
344       }
345       return this.xmlReader;
346    }
347 
348    private SAXModifyReader getSAXModifyReader() {
349       if (modifyReader == null) {
350          modifyReader = new SAXModifyReader();
351       }
352 
353       return modifyReader;
354    }
355 
356 }
357 
358 
359 
360 
361 /*
362  * Redistribution and use of this software and associated documentation
363  * ("Software"), with or without modification, are permitted provided
364  * that the following conditions are met:
365  *
366  * 1. Redistributions of source code must retain copyright
367  *    statements and notices.  Redistributions must also contain a
368  *    copy of this document.
369  *
370  * 2. Redistributions in binary form must reproduce the
371  *    above copyright notice, this list of conditions and the
372  *    following disclaimer in the documentation and/or other
373  *    materials provided with the distribution.
374  *
375  * 3. The name "DOM4J" must not be used to endorse or promote
376  *    products derived from this Software without prior written
377  *    permission of MetaStuff, Ltd.  For written permission,
378  *    please contact dom4j-info@metastuff.com.
379  *
380  * 4. Products derived from this Software may not be called "DOM4J"
381  *    nor may "DOM4J" appear in their names without prior written
382  *    permission of MetaStuff, Ltd. DOM4J is a registered
383  *    trademark of MetaStuff, Ltd.
384  *
385  * 5. Due credit should be given to the DOM4J Project - 
386  *    http://www.dom4j.org
387  *
388  * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS
389  * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
390  * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
391  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
392  * METASTUFF, LTD. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
393  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
394  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
395  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
396  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
397  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
398  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
399  * OF THE POSSIBILITY OF SUCH DAMAGE.
400  *
401  * Copyright 2001-2004 (C) MetaStuff, Ltd. All Rights Reserved.
402  *
403  * $Id: SAXModifier.java,v 1.1 2004/08/02 18:44:07 maartenc Exp $
404  */