Save This Page
Home » dom4j-1.6.1 » org.dom4j.io » [javadoc | source]
    1   /*
    2    * Copyright 2001-2005 (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   
    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                   // replay to ContentHandler
   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                   // replay to LexicalHandler
  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                   // replay to DeclHandler
  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       // ContentHandler interface
  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                       // if SAXWriter is writing a DOMDocument, namespace
  293                       // decls are treated as attributes. record a start
  294                       // prefix mapping event
  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                       // 'register' the prefix so that we can generate
  309                       // an end prefix mapping event within endElement
  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           // check to see if a we issued a start prefix mapping event
  347           // for DOMDocument namespace decls
  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       // LexicalHandler interface
  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       // DeclHandler interface
  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       // SAXEvent inner class
  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    * Redistribution and use of this software and associated documentation
  570    * ("Software"), with or without modification, are permitted provided that the
  571    * following conditions are met:
  572    * 
  573    * 1. Redistributions of source code must retain copyright statements and
  574    * notices. Redistributions must also contain a copy of this document.
  575    * 
  576    * 2. Redistributions in binary form must reproduce the above copyright notice,
  577    * this list of conditions and the following disclaimer in the documentation
  578    * and/or other materials provided with the distribution.
  579    * 
  580    * 3. The name "DOM4J" must not be used to endorse or promote products derived
  581    * from this Software without prior written permission of MetaStuff, Ltd. For
  582    * written permission, please contact dom4j-info@metastuff.com.
  583    * 
  584    * 4. Products derived from this Software may not be called "DOM4J" nor may
  585    * "DOM4J" appear in their names without prior written permission of MetaStuff,
  586    * Ltd. DOM4J is a registered trademark of MetaStuff, Ltd.
  587    * 
  588    * 5. Due credit should be given to the DOM4J Project - http://www.dom4j.org
  589    * 
  590    * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS ``AS IS'' AND
  591    * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  592    * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  593    * ARE DISCLAIMED. IN NO EVENT SHALL METASTUFF, LTD. OR ITS CONTRIBUTORS BE
  594    * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  595    * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  596    * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  597    * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  598    * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  599    * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  600    * POSSIBILITY OF SUCH DAMAGE.
  601    * 
  602    * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
  603    */

Save This Page
Home » dom4j-1.6.1 » org.dom4j.io » [javadoc | source]