1
2
3
4
5
6
7
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
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404