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