Home | History | Annotate | Download | only in serializer
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one
      3  * or more contributor license agreements. See the NOTICE file
      4  * distributed with this work for additional information
      5  * regarding copyright ownership. The ASF licenses this file
      6  * to you under the Apache License, Version 2.0 (the  "License");
      7  * you may not use this file except in compliance with the License.
      8  * You may obtain a copy of the License at
      9  *
     10  *     http://www.apache.org/licenses/LICENSE-2.0
     11  *
     12  * Unless required by applicable law or agreed to in writing, software
     13  * distributed under the License is distributed on an "AS IS" BASIS,
     14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15  * See the License for the specific language governing permissions and
     16  * limitations under the License.
     17  */
     18 /*
     19  * $Id: TreeWalker.java 468654 2006-10-28 07:09:23Z minchau $
     20  */
     21 package org.apache.xml.serializer;
     22 
     23 import java.io.File;
     24 
     25 import org.apache.xml.serializer.utils.AttList;
     26 import org.apache.xml.serializer.utils.DOM2Helper;
     27 import org.w3c.dom.Comment;
     28 import org.w3c.dom.Element;
     29 import org.w3c.dom.EntityReference;
     30 import org.w3c.dom.NamedNodeMap;
     31 import org.w3c.dom.Node;
     32 import org.w3c.dom.ProcessingInstruction;
     33 import org.w3c.dom.Text;
     34 import org.xml.sax.ContentHandler;
     35 import org.xml.sax.Locator;
     36 import org.xml.sax.ext.LexicalHandler;
     37 import org.xml.sax.helpers.LocatorImpl;
     38 
     39 
     40 /**
     41  * This class does a pre-order walk of the DOM tree, calling a ContentHandler
     42  * interface as it goes.
     43  *
     44  * This class is a copy of the one in org.apache.xml.utils.
     45  * It exists to cut the serializers dependancy on that package.
     46  *
     47  * @xsl.usage internal
     48  */
     49 
     50 public final class TreeWalker
     51 {
     52 
     53   /** Local reference to a ContentHandler          */
     54   final private ContentHandler m_contentHandler;
     55   /**
     56    * If m_contentHandler is a SerializationHandler, then this is
     57    * a reference to the same object.
     58    */
     59   final private SerializationHandler m_Serializer;
     60 
     61   // ARGHH!!  JAXP Uses Xerces without setting the namespace processing to ON!
     62   // DOM2Helper m_dh = new DOM2Helper();
     63 
     64   /** DomHelper for this TreeWalker          */
     65   final protected DOM2Helper m_dh;
     66 
     67   /** Locator object for this TreeWalker          */
     68   final private LocatorImpl m_locator = new LocatorImpl();
     69 
     70   /**
     71    * Get the ContentHandler used for the tree walk.
     72    *
     73    * @return the ContentHandler used for the tree walk
     74    */
     75   public ContentHandler getContentHandler()
     76   {
     77     return m_contentHandler;
     78   }
     79 
     80   public TreeWalker(ContentHandler ch) {
     81       this(ch,null);
     82   }
     83   /**
     84    * Constructor.
     85    * @param   contentHandler The implemention of the
     86    * contentHandler operation (toXMLString, digest, ...)
     87    */
     88   public TreeWalker(ContentHandler contentHandler, String systemId)
     89   {
     90       // Set the content handler
     91       m_contentHandler = contentHandler;
     92       if (m_contentHandler instanceof SerializationHandler) {
     93           m_Serializer = (SerializationHandler) m_contentHandler;
     94       }
     95       else
     96           m_Serializer = null;
     97 
     98       // Set the system ID, if it is given
     99       m_contentHandler.setDocumentLocator(m_locator);
    100       if (systemId != null)
    101           m_locator.setSystemId(systemId);
    102       else {
    103           try {
    104             // Bug see Bugzilla  26741
    105             m_locator.setSystemId(System.getProperty("user.dir") + File.separator + "dummy.xsl");
    106            }
    107            catch (SecurityException se) {// user.dir not accessible from applet
    108            }
    109       }
    110 
    111       // Set the document locator
    112                 if (m_contentHandler != null)
    113                         m_contentHandler.setDocumentLocator(m_locator);
    114                 try {
    115                    // Bug see Bugzilla  26741
    116                   m_locator.setSystemId(System.getProperty("user.dir") + File.separator + "dummy.xsl");
    117                 }
    118                 catch (SecurityException se){// user.dir not accessible from applet
    119 
    120     }
    121     m_dh = new DOM2Helper();
    122   }
    123 
    124   /**
    125    * Perform a pre-order traversal non-recursive style.
    126    *
    127    * Note that TreeWalker assumes that the subtree is intended to represent
    128    * a complete (though not necessarily well-formed) document and, during a
    129    * traversal, startDocument and endDocument will always be issued to the
    130    * SAX listener.
    131    *
    132    * @param pos Node in the tree where to start traversal
    133    *
    134    * @throws TransformerException
    135    */
    136   public void traverse(Node pos) throws org.xml.sax.SAXException
    137   {
    138 
    139     this.m_contentHandler.startDocument();
    140 
    141     Node top = pos;
    142 
    143     while (null != pos)
    144     {
    145       startNode(pos);
    146 
    147       Node nextNode = pos.getFirstChild();
    148 
    149       while (null == nextNode)
    150       {
    151         endNode(pos);
    152 
    153         if (top.equals(pos))
    154           break;
    155 
    156         nextNode = pos.getNextSibling();
    157 
    158         if (null == nextNode)
    159         {
    160           pos = pos.getParentNode();
    161 
    162           if ((null == pos) || (top.equals(pos)))
    163           {
    164             if (null != pos)
    165               endNode(pos);
    166 
    167             nextNode = null;
    168 
    169             break;
    170           }
    171         }
    172       }
    173 
    174       pos = nextNode;
    175     }
    176     this.m_contentHandler.endDocument();
    177   }
    178 
    179   /**
    180    * Perform a pre-order traversal non-recursive style.
    181 
    182    * Note that TreeWalker assumes that the subtree is intended to represent
    183    * a complete (though not necessarily well-formed) document and, during a
    184    * traversal, startDocument and endDocument will always be issued to the
    185    * SAX listener.
    186    *
    187    * @param pos Node in the tree where to start traversal
    188    * @param top Node in the tree where to end traversal
    189    *
    190    * @throws TransformerException
    191    */
    192   public void traverse(Node pos, Node top) throws org.xml.sax.SAXException
    193   {
    194 
    195     this.m_contentHandler.startDocument();
    196 
    197     while (null != pos)
    198     {
    199       startNode(pos);
    200 
    201       Node nextNode = pos.getFirstChild();
    202 
    203       while (null == nextNode)
    204       {
    205         endNode(pos);
    206 
    207         if ((null != top) && top.equals(pos))
    208           break;
    209 
    210         nextNode = pos.getNextSibling();
    211 
    212         if (null == nextNode)
    213         {
    214           pos = pos.getParentNode();
    215 
    216           if ((null == pos) || ((null != top) && top.equals(pos)))
    217           {
    218             nextNode = null;
    219 
    220             break;
    221           }
    222         }
    223       }
    224 
    225       pos = nextNode;
    226     }
    227     this.m_contentHandler.endDocument();
    228   }
    229 
    230   /** Flag indicating whether following text to be processed is raw text          */
    231   boolean nextIsRaw = false;
    232 
    233   /**
    234    * Optimized dispatch of characters.
    235    */
    236   private final void dispatachChars(Node node)
    237      throws org.xml.sax.SAXException
    238   {
    239     if(m_Serializer != null)
    240     {
    241       this.m_Serializer.characters(node);
    242     }
    243     else
    244     {
    245       String data = ((Text) node).getData();
    246       this.m_contentHandler.characters(data.toCharArray(), 0, data.length());
    247     }
    248   }
    249 
    250   /**
    251    * Start processing given node
    252    *
    253    *
    254    * @param node Node to process
    255    *
    256    * @throws org.xml.sax.SAXException
    257    */
    258   protected void startNode(Node node) throws org.xml.sax.SAXException
    259   {
    260 
    261 //   TODO: <REVIEW>
    262 //    A Serializer implements ContentHandler, but not NodeConsumer
    263 //    so drop this reference to NodeConsumer which would otherwise
    264 //    pull in all sorts of things
    265 //    if (m_contentHandler instanceof NodeConsumer)
    266 //    {
    267 //      ((NodeConsumer) m_contentHandler).setOriginatingNode(node);
    268 //    }
    269 //    TODO: </REVIEW>
    270 
    271                 if (node instanceof Locator)
    272                 {
    273                         Locator loc = (Locator)node;
    274                         m_locator.setColumnNumber(loc.getColumnNumber());
    275                         m_locator.setLineNumber(loc.getLineNumber());
    276                         m_locator.setPublicId(loc.getPublicId());
    277                         m_locator.setSystemId(loc.getSystemId());
    278                 }
    279                 else
    280                 {
    281                         m_locator.setColumnNumber(0);
    282       m_locator.setLineNumber(0);
    283                 }
    284 
    285     switch (node.getNodeType())
    286     {
    287     case Node.COMMENT_NODE :
    288     {
    289       String data = ((Comment) node).getData();
    290 
    291       if (m_contentHandler instanceof LexicalHandler)
    292       {
    293         LexicalHandler lh = ((LexicalHandler) this.m_contentHandler);
    294 
    295         lh.comment(data.toCharArray(), 0, data.length());
    296       }
    297     }
    298     break;
    299     case Node.DOCUMENT_FRAGMENT_NODE :
    300 
    301       // ??;
    302       break;
    303     case Node.DOCUMENT_NODE :
    304 
    305       break;
    306     case Node.ELEMENT_NODE :
    307       Element elem_node = (Element) node;
    308       {
    309           // Make sure the namespace node
    310           // for the element itself is declared
    311           // to the ContentHandler
    312           String uri = elem_node.getNamespaceURI();
    313           if (uri != null) {
    314               String prefix = elem_node.getPrefix();
    315               if (prefix==null)
    316                 prefix="";
    317               this.m_contentHandler.startPrefixMapping(prefix,uri);
    318           }
    319       }
    320       NamedNodeMap atts = elem_node.getAttributes();
    321       int nAttrs = atts.getLength();
    322       // System.out.println("TreeWalker#startNode: "+node.getNodeName());
    323 
    324 
    325       // Make sure the namespace node of
    326       // each attribute is declared to the ContentHandler
    327       for (int i = 0; i < nAttrs; i++)
    328       {
    329         final Node attr = atts.item(i);
    330         final String attrName = attr.getNodeName();
    331         final int colon = attrName.indexOf(':');
    332         final String prefix;
    333 
    334         // System.out.println("TreeWalker#startNode: attr["+i+"] = "+attrName+", "+attr.getNodeValue());
    335         if (attrName.equals("xmlns") || attrName.startsWith("xmlns:"))
    336         {
    337           // Use "" instead of null, as Xerces likes "" for the
    338           // name of the default namespace.  Fix attributed
    339           // to "Steven Murray" <smurray (at) ebt.com>.
    340           if (colon < 0)
    341             prefix = "";
    342           else
    343             prefix = attrName.substring(colon + 1);
    344 
    345           this.m_contentHandler.startPrefixMapping(prefix,
    346                                                    attr.getNodeValue());
    347         }
    348         else if (colon > 0) {
    349             prefix = attrName.substring(0,colon);
    350             String uri = attr.getNamespaceURI();
    351             if (uri != null)
    352                 this.m_contentHandler.startPrefixMapping(prefix,uri);
    353         }
    354       }
    355 
    356       String ns = m_dh.getNamespaceOfNode(node);
    357       if(null == ns)
    358         ns = "";
    359       this.m_contentHandler.startElement(ns,
    360                                          m_dh.getLocalNameOfNode(node),
    361                                          node.getNodeName(),
    362                                          new AttList(atts, m_dh));
    363       break;
    364     case Node.PROCESSING_INSTRUCTION_NODE :
    365     {
    366       ProcessingInstruction pi = (ProcessingInstruction) node;
    367       String name = pi.getNodeName();
    368 
    369       // String data = pi.getData();
    370       if (name.equals("xslt-next-is-raw"))
    371       {
    372         nextIsRaw = true;
    373       }
    374       else
    375       {
    376         this.m_contentHandler.processingInstruction(pi.getNodeName(),
    377                                                     pi.getData());
    378       }
    379     }
    380     break;
    381     case Node.CDATA_SECTION_NODE :
    382     {
    383       boolean isLexH = (m_contentHandler instanceof LexicalHandler);
    384       LexicalHandler lh = isLexH
    385                           ? ((LexicalHandler) this.m_contentHandler) : null;
    386 
    387       if (isLexH)
    388       {
    389         lh.startCDATA();
    390       }
    391 
    392       dispatachChars(node);
    393 
    394       {
    395         if (isLexH)
    396         {
    397           lh.endCDATA();
    398         }
    399       }
    400     }
    401     break;
    402     case Node.TEXT_NODE :
    403     {
    404       //String data = ((Text) node).getData();
    405 
    406       if (nextIsRaw)
    407       {
    408         nextIsRaw = false;
    409 
    410         m_contentHandler.processingInstruction(javax.xml.transform.Result.PI_DISABLE_OUTPUT_ESCAPING, "");
    411         dispatachChars(node);
    412         m_contentHandler.processingInstruction(javax.xml.transform.Result.PI_ENABLE_OUTPUT_ESCAPING, "");
    413       }
    414       else
    415       {
    416         dispatachChars(node);
    417       }
    418     }
    419     break;
    420     case Node.ENTITY_REFERENCE_NODE :
    421     {
    422       EntityReference eref = (EntityReference) node;
    423 
    424       if (m_contentHandler instanceof LexicalHandler)
    425       {
    426         ((LexicalHandler) this.m_contentHandler).startEntity(
    427           eref.getNodeName());
    428       }
    429       else
    430       {
    431 
    432         // warning("Can not output entity to a pure SAX ContentHandler");
    433       }
    434     }
    435     break;
    436     default :
    437     }
    438   }
    439 
    440   /**
    441    * End processing of given node
    442    *
    443    *
    444    * @param node Node we just finished processing
    445    *
    446    * @throws org.xml.sax.SAXException
    447    */
    448   protected void endNode(Node node) throws org.xml.sax.SAXException
    449   {
    450 
    451     switch (node.getNodeType())
    452     {
    453     case Node.DOCUMENT_NODE :
    454       break;
    455 
    456     case Node.ELEMENT_NODE :
    457       String ns = m_dh.getNamespaceOfNode(node);
    458       if(null == ns)
    459         ns = "";
    460       this.m_contentHandler.endElement(ns,
    461                                          m_dh.getLocalNameOfNode(node),
    462                                          node.getNodeName());
    463 
    464       if (m_Serializer == null) {
    465       // Don't bother with endPrefixMapping calls if the ContentHandler is a
    466       // SerializationHandler because SerializationHandler's ignore the
    467       // endPrefixMapping() calls anyways. . . .  This is an optimization.
    468       Element elem_node = (Element) node;
    469       NamedNodeMap atts = elem_node.getAttributes();
    470       int nAttrs = atts.getLength();
    471 
    472       // do the endPrefixMapping calls in reverse order
    473       // of the startPrefixMapping calls
    474       for (int i = (nAttrs-1); 0 <= i; i--)
    475       {
    476         final Node attr = atts.item(i);
    477         final String attrName = attr.getNodeName();
    478         final int colon = attrName.indexOf(':');
    479         final String prefix;
    480 
    481         if (attrName.equals("xmlns") || attrName.startsWith("xmlns:"))
    482         {
    483           // Use "" instead of null, as Xerces likes "" for the
    484           // name of the default namespace.  Fix attributed
    485           // to "Steven Murray" <smurray (at) ebt.com>.
    486           if (colon < 0)
    487             prefix = "";
    488           else
    489             prefix = attrName.substring(colon + 1);
    490 
    491           this.m_contentHandler.endPrefixMapping(prefix);
    492         }
    493         else if (colon > 0) {
    494             prefix = attrName.substring(0, colon);
    495             this.m_contentHandler.endPrefixMapping(prefix);
    496         }
    497       }
    498       {
    499           String uri = elem_node.getNamespaceURI();
    500           if (uri != null) {
    501               String prefix = elem_node.getPrefix();
    502               if (prefix==null)
    503                 prefix="";
    504               this.m_contentHandler.endPrefixMapping(prefix);
    505           }
    506       }
    507       }
    508       break;
    509     case Node.CDATA_SECTION_NODE :
    510       break;
    511     case Node.ENTITY_REFERENCE_NODE :
    512     {
    513       EntityReference eref = (EntityReference) node;
    514 
    515       if (m_contentHandler instanceof LexicalHandler)
    516       {
    517         LexicalHandler lh = ((LexicalHandler) this.m_contentHandler);
    518 
    519         lh.endEntity(eref.getNodeName());
    520       }
    521     }
    522     break;
    523     default :
    524     }
    525   }
    526 }  //TreeWalker
    527 
    528