1
2
3
4
5
6
7
8 package org.dom4j.io;
9
10 import java.io.Externalizable;
11 import java.io.IOException;
12 import java.io.ObjectInput;
13 import java.io.ObjectOutput;
14 import java.util.ArrayList;
15 import java.util.HashMap;
16 import java.util.Iterator;
17 import java.util.List;
18 import java.util.Map;
19
20 import org.dom4j.Namespace;
21 import org.dom4j.QName;
22 import org.xml.sax.Attributes;
23 import org.xml.sax.ContentHandler;
24 import org.xml.sax.DTDHandler;
25 import org.xml.sax.SAXException;
26 import org.xml.sax.ext.DeclHandler;
27 import org.xml.sax.ext.LexicalHandler;
28 import org.xml.sax.helpers.AttributesImpl;
29 import org.xml.sax.helpers.DefaultHandler;
30
31 /***
32 * <p>
33 * Records SAX events such that they may be "replayed" at a later time. Provides
34 * an alternative serialization approach when externalizing a DOM4J document.
35 * Rather than serializing a document as text and re-parsing, the sax events may
36 * be serialized instead.
37 * </p>
38 * Example usage:
39 *
40 * <pre>
41 *
42 *
43 *
44 * SAXEventRecorder recorder = new SAXEventRecorder();
45 * SAXWriter saxWriter = new SAXWriter(recorder, recorder);
46 * saxWriter.write(document);
47 * out.writeObject(recorder);
48 * ...
49 * SAXEventRecorder recorder = (SAXEventRecorder)in.readObject();
50 * SAXContentHandler saxContentHandler = new SAXContentHandler();
51 * recorder.replay(saxContentHandler);
52 * Document document = saxContentHandler.getDocument();
53 *
54 *
55 *
56 * </pre>
57 *
58 * @author Todd Wolff (Bluestem Software)
59 */
60 public class SAXEventRecorder extends DefaultHandler implements LexicalHandler,
61 DeclHandler, DTDHandler, Externalizable {
62 public static final long serialVersionUID = 1;
63
64 private static final byte STRING = 0;
65
66 private static final byte OBJECT = 1;
67
68 private static final byte NULL = 2;
69
70 private List events = new ArrayList();
71
72 private Map prefixMappings = new HashMap();
73
74 private static final String XMLNS = "xmlns";
75
76 private static final String EMPTY_STRING = "";
77
78 public SAXEventRecorder() {
79 }
80
81 public void replay(ContentHandler handler) throws SAXException {
82 SAXEvent saxEvent;
83 Iterator itr = events.iterator();
84
85 while (itr.hasNext()) {
86 saxEvent = (SAXEvent) itr.next();
87
88 switch (saxEvent.event) {
89
90 case SAXEvent.PROCESSING_INSTRUCTION:
91 handler.processingInstruction((String) saxEvent.getParm(0),
92 (String) saxEvent.getParm(1));
93
94 break;
95
96 case SAXEvent.START_PREFIX_MAPPING:
97 handler.startPrefixMapping((String) saxEvent.getParm(0),
98 (String) saxEvent.getParm(1));
99
100 break;
101
102 case SAXEvent.END_PREFIX_MAPPING:
103 handler.endPrefixMapping((String) saxEvent.getParm(0));
104
105 break;
106
107 case SAXEvent.START_DOCUMENT:
108 handler.startDocument();
109
110 break;
111
112 case SAXEvent.END_DOCUMENT:
113 handler.endDocument();
114
115 break;
116
117 case SAXEvent.START_ELEMENT:
118
119 AttributesImpl attributes = new AttributesImpl();
120 List attParmList = (List) saxEvent.getParm(3);
121
122 if (attParmList != null) {
123 Iterator attsItr = attParmList.iterator();
124
125 while (attsItr.hasNext()) {
126 String[] attParms = (String[]) attsItr.next();
127 attributes.addAttribute(attParms[0], attParms[1],
128 attParms[2], attParms[3], attParms[4]);
129 }
130 }
131
132 handler.startElement((String) saxEvent.getParm(0),
133 (String) saxEvent.getParm(1), (String) saxEvent
134 .getParm(2), attributes);
135
136 break;
137
138 case SAXEvent.END_ELEMENT:
139 handler.endElement((String) saxEvent.getParm(0),
140 (String) saxEvent.getParm(1), (String) saxEvent
141 .getParm(2));
142
143 break;
144
145 case SAXEvent.CHARACTERS:
146
147 char[] chars = (char[]) saxEvent.getParm(0);
148 int start = ((Integer) saxEvent.getParm(1)).intValue();
149 int end = ((Integer) saxEvent.getParm(2)).intValue();
150 handler.characters(chars, start, end);
151
152 break;
153
154
155 case SAXEvent.START_DTD:
156 ((LexicalHandler) handler).startDTD((String) saxEvent
157 .getParm(0), (String) saxEvent.getParm(1),
158 (String) saxEvent.getParm(2));
159
160 break;
161
162 case SAXEvent.END_DTD:
163 ((LexicalHandler) handler).endDTD();
164
165 break;
166
167 case SAXEvent.START_ENTITY:
168 ((LexicalHandler) handler).startEntity((String) saxEvent
169 .getParm(0));
170
171 break;
172
173 case SAXEvent.END_ENTITY:
174 ((LexicalHandler) handler).endEntity((String) saxEvent
175 .getParm(0));
176
177 break;
178
179 case SAXEvent.START_CDATA:
180 ((LexicalHandler) handler).startCDATA();
181
182 break;
183
184 case SAXEvent.END_CDATA:
185 ((LexicalHandler) handler).endCDATA();
186
187 break;
188
189 case SAXEvent.COMMENT:
190
191 char[] cchars = (char[]) saxEvent.getParm(0);
192 int cstart = ((Integer) saxEvent.getParm(1)).intValue();
193 int cend = ((Integer) saxEvent.getParm(2)).intValue();
194 ((LexicalHandler) handler).comment(cchars, cstart, cend);
195
196 break;
197
198
199 case SAXEvent.ELEMENT_DECL:
200 ((DeclHandler) handler).elementDecl((String) saxEvent
201 .getParm(0), (String) saxEvent.getParm(1));
202
203 break;
204
205 case SAXEvent.ATTRIBUTE_DECL:
206 ((DeclHandler) handler).attributeDecl((String) saxEvent
207 .getParm(0), (String) saxEvent.getParm(1),
208 (String) saxEvent.getParm(2), (String) saxEvent
209 .getParm(3), (String) saxEvent.getParm(4));
210
211 break;
212
213 case SAXEvent.INTERNAL_ENTITY_DECL:
214 ((DeclHandler) handler).internalEntityDecl(
215 (String) saxEvent.getParm(0), (String) saxEvent
216 .getParm(1));
217
218 break;
219
220 case SAXEvent.EXTERNAL_ENTITY_DECL:
221 ((DeclHandler) handler).externalEntityDecl(
222 (String) saxEvent.getParm(0), (String) saxEvent
223 .getParm(1), (String) saxEvent.getParm(2));
224
225 break;
226
227 default:
228 throw new SAXException("Unrecognized event: "
229 + saxEvent.event);
230 }
231 }
232 }
233
234
235
236 public void processingInstruction(String target, String data)
237 throws SAXException {
238 SAXEvent saxEvent = new SAXEvent(SAXEvent.PROCESSING_INSTRUCTION);
239 saxEvent.addParm(target);
240 saxEvent.addParm(data);
241 events.add(saxEvent);
242 }
243
244 public void startPrefixMapping(String prefix, String uri)
245 throws SAXException {
246 SAXEvent saxEvent = new SAXEvent(SAXEvent.START_PREFIX_MAPPING);
247 saxEvent.addParm(prefix);
248 saxEvent.addParm(uri);
249 events.add(saxEvent);
250 }
251
252 public void endPrefixMapping(String prefix) throws SAXException {
253 SAXEvent saxEvent = new SAXEvent(SAXEvent.END_PREFIX_MAPPING);
254 saxEvent.addParm(prefix);
255 events.add(saxEvent);
256 }
257
258 public void startDocument() throws SAXException {
259 SAXEvent saxEvent = new SAXEvent(SAXEvent.START_DOCUMENT);
260 events.add(saxEvent);
261 }
262
263 public void endDocument() throws SAXException {
264 SAXEvent saxEvent = new SAXEvent(SAXEvent.END_DOCUMENT);
265 events.add(saxEvent);
266 }
267
268 public void startElement(String namespaceURI, String localName,
269 String qualifiedName, Attributes attributes) throws SAXException {
270 SAXEvent saxEvent = new SAXEvent(SAXEvent.START_ELEMENT);
271 saxEvent.addParm(namespaceURI);
272 saxEvent.addParm(localName);
273 saxEvent.addParm(qualifiedName);
274
275 QName qName = null;
276 if (namespaceURI != null) {
277 qName = new QName(localName, Namespace.get(namespaceURI));
278 } else {
279 qName = new QName(localName);
280 }
281
282 if ((attributes != null) && (attributes.getLength() > 0)) {
283 List attParmList = new ArrayList(attributes.getLength());
284 String[] attParms = null;
285
286 for (int i = 0; i < attributes.getLength(); i++) {
287
288 String attLocalName = attributes.getLocalName(i);
289
290 if (attLocalName.startsWith(XMLNS)) {
291
292
293
294
295 String prefix = null;
296 if (attLocalName.length() > 5) {
297 prefix = attLocalName.substring(6);
298 } else {
299 prefix = EMPTY_STRING;
300 }
301
302 SAXEvent prefixEvent = new SAXEvent(
303 SAXEvent.START_PREFIX_MAPPING);
304 prefixEvent.addParm(prefix);
305 prefixEvent.addParm(attributes.getValue(i));
306 events.add(prefixEvent);
307
308
309
310 List prefixes = (List) prefixMappings.get(qName);
311 if (prefixes == null) {
312 prefixes = new ArrayList();
313 prefixMappings.put(qName, prefixes);
314 }
315 prefixes.add(prefix);
316
317 } else {
318
319 attParms = new String[5];
320 attParms[0] = attributes.getURI(i);
321 attParms[1] = attLocalName;
322 attParms[2] = attributes.getQName(i);
323 attParms[3] = attributes.getType(i);
324 attParms[4] = attributes.getValue(i);
325 attParmList.add(attParms);
326
327 }
328
329 }
330
331 saxEvent.addParm(attParmList);
332 }
333
334 events.add(saxEvent);
335 }
336
337 public void endElement(String namespaceURI, String localName, String qName)
338 throws SAXException {
339
340 SAXEvent saxEvent = new SAXEvent(SAXEvent.END_ELEMENT);
341 saxEvent.addParm(namespaceURI);
342 saxEvent.addParm(localName);
343 saxEvent.addParm(qName);
344 events.add(saxEvent);
345
346
347
348
349 QName elementName = null;
350 if (namespaceURI != null) {
351 elementName = new QName(localName, Namespace.get(namespaceURI));
352 } else {
353 elementName = new QName(localName);
354 }
355
356 List prefixes = (List) prefixMappings.get(elementName);
357 if (prefixes != null) {
358 Iterator itr = prefixes.iterator();
359 while (itr.hasNext()) {
360 SAXEvent prefixEvent =
361 new SAXEvent(SAXEvent.END_PREFIX_MAPPING);
362 prefixEvent.addParm(itr.next());
363 events.add(prefixEvent);
364 }
365 }
366
367 }
368
369 public void characters(char[] ch, int start, int end) throws SAXException {
370 SAXEvent saxEvent = new SAXEvent(SAXEvent.CHARACTERS);
371 saxEvent.addParm(ch);
372 saxEvent.addParm(new Integer(start));
373 saxEvent.addParm(new Integer(end));
374 events.add(saxEvent);
375 }
376
377
378
379 public void startDTD(String name, String publicId, String systemId)
380 throws SAXException {
381 SAXEvent saxEvent = new SAXEvent(SAXEvent.START_DTD);
382 saxEvent.addParm(name);
383 saxEvent.addParm(publicId);
384 saxEvent.addParm(systemId);
385 events.add(saxEvent);
386 }
387
388 public void endDTD() throws SAXException {
389 SAXEvent saxEvent = new SAXEvent(SAXEvent.END_DTD);
390 events.add(saxEvent);
391 }
392
393 public void startEntity(String name) throws SAXException {
394 SAXEvent saxEvent = new SAXEvent(SAXEvent.START_ENTITY);
395 saxEvent.addParm(name);
396 events.add(saxEvent);
397 }
398
399 public void endEntity(String name) throws SAXException {
400 SAXEvent saxEvent = new SAXEvent(SAXEvent.END_ENTITY);
401 saxEvent.addParm(name);
402 events.add(saxEvent);
403 }
404
405 public void startCDATA() throws SAXException {
406 SAXEvent saxEvent = new SAXEvent(SAXEvent.START_CDATA);
407 events.add(saxEvent);
408 }
409
410 public void endCDATA() throws SAXException {
411 SAXEvent saxEvent = new SAXEvent(SAXEvent.END_CDATA);
412 events.add(saxEvent);
413 }
414
415 public void comment(char[] ch, int start, int end) throws SAXException {
416 SAXEvent saxEvent = new SAXEvent(SAXEvent.COMMENT);
417 saxEvent.addParm(ch);
418 saxEvent.addParm(new Integer(start));
419 saxEvent.addParm(new Integer(end));
420 events.add(saxEvent);
421 }
422
423
424
425 public void elementDecl(String name, String model) throws SAXException {
426 SAXEvent saxEvent = new SAXEvent(SAXEvent.ELEMENT_DECL);
427 saxEvent.addParm(name);
428 saxEvent.addParm(model);
429 events.add(saxEvent);
430 }
431
432 public void attributeDecl(String eName, String aName, String type,
433 String valueDefault, String value) throws SAXException {
434 SAXEvent saxEvent = new SAXEvent(SAXEvent.ATTRIBUTE_DECL);
435 saxEvent.addParm(eName);
436 saxEvent.addParm(aName);
437 saxEvent.addParm(type);
438 saxEvent.addParm(valueDefault);
439 saxEvent.addParm(value);
440 events.add(saxEvent);
441 }
442
443 public void internalEntityDecl(String name, String value)
444 throws SAXException {
445 SAXEvent saxEvent = new SAXEvent(SAXEvent.INTERNAL_ENTITY_DECL);
446 saxEvent.addParm(name);
447 saxEvent.addParm(value);
448 events.add(saxEvent);
449 }
450
451 public void externalEntityDecl(String name, String publicId, String sysId)
452 throws SAXException {
453 SAXEvent saxEvent = new SAXEvent(SAXEvent.EXTERNAL_ENTITY_DECL);
454 saxEvent.addParm(name);
455 saxEvent.addParm(publicId);
456 saxEvent.addParm(sysId);
457 events.add(saxEvent);
458 }
459
460 public void writeExternal(ObjectOutput out) throws IOException {
461 if (events == null) {
462 out.writeByte(NULL);
463 } else {
464 out.writeByte(OBJECT);
465 out.writeObject(events);
466 }
467 }
468
469 public void readExternal(ObjectInput in) throws ClassNotFoundException,
470 IOException {
471 if (in.readByte() != NULL) {
472 events = (List) in.readObject();
473 }
474 }
475
476
477
478 static class SAXEvent implements Externalizable {
479 public static final long serialVersionUID = 1;
480
481 static final byte PROCESSING_INSTRUCTION = 1;
482
483 static final byte START_PREFIX_MAPPING = 2;
484
485 static final byte END_PREFIX_MAPPING = 3;
486
487 static final byte START_DOCUMENT = 4;
488
489 static final byte END_DOCUMENT = 5;
490
491 static final byte START_ELEMENT = 6;
492
493 static final byte END_ELEMENT = 7;
494
495 static final byte CHARACTERS = 8;
496
497 static final byte START_DTD = 9;
498
499 static final byte END_DTD = 10;
500
501 static final byte START_ENTITY = 11;
502
503 static final byte END_ENTITY = 12;
504
505 static final byte START_CDATA = 13;
506
507 static final byte END_CDATA = 14;
508
509 static final byte COMMENT = 15;
510
511 static final byte ELEMENT_DECL = 16;
512
513 static final byte ATTRIBUTE_DECL = 17;
514
515 static final byte INTERNAL_ENTITY_DECL = 18;
516
517 static final byte EXTERNAL_ENTITY_DECL = 19;
518
519 protected byte event;
520
521 protected List parms;
522
523 public SAXEvent() {
524 }
525
526 SAXEvent(byte event) {
527 this.event = event;
528 }
529
530 void addParm(Object parm) {
531 if (parms == null) {
532 parms = new ArrayList(3);
533 }
534
535 parms.add(parm);
536 }
537
538 Object getParm(int index) {
539 if ((parms != null) && (index < parms.size())) {
540 return parms.get(index);
541 } else {
542 return null;
543 }
544 }
545
546 public void writeExternal(ObjectOutput out) throws IOException {
547 out.writeByte(event);
548
549 if (parms == null) {
550 out.writeByte(NULL);
551 } else {
552 out.writeByte(OBJECT);
553 out.writeObject(parms);
554 }
555 }
556
557 public void readExternal(ObjectInput in) throws ClassNotFoundException,
558 IOException {
559 event = in.readByte();
560
561 if (in.readByte() != NULL) {
562 parms = (List) in.readObject();
563 }
564 }
565 }
566 }
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603