Home | History | Annotate | Download | only in ref
      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: DTMTreeWalker.java 468653 2006-10-28 07:07:05Z minchau $
     20  */
     21 package org.apache.xml.dtm.ref;
     22 
     23 import org.apache.xml.dtm.DTM;
     24 import org.apache.xml.utils.NodeConsumer;
     25 import org.apache.xml.utils.XMLString;
     26 
     27 import org.xml.sax.ContentHandler;
     28 import org.xml.sax.ext.LexicalHandler;
     29 
     30 /**
     31  * This class does a pre-order walk of the DTM tree, calling a ContentHandler
     32  * interface as it goes. As such, it's more like the Visitor design pattern
     33  * than like the DOM's TreeWalker.
     34  *
     35  * I think normally this class should not be needed, because
     36  * of DTM#dispatchToEvents.
     37  * @xsl.usage advanced
     38  */
     39 public class DTMTreeWalker
     40 {
     41 
     42   /** Local reference to a ContentHandler          */
     43   private ContentHandler m_contentHandler = null;
     44 
     45   /** DomHelper for this TreeWalker          */
     46   protected DTM m_dtm;
     47 
     48   /**
     49    * Set the DTM to be traversed.
     50    *
     51    * @param dtm The Document Table Model to be used.
     52    */
     53   public void setDTM(DTM dtm)
     54   {
     55     m_dtm = dtm;
     56   }
     57 
     58   /**
     59    * Get the ContentHandler used for the tree walk.
     60    *
     61    * @return the ContentHandler used for the tree walk
     62    */
     63   public ContentHandler getcontentHandler()
     64   {
     65     return m_contentHandler;
     66   }
     67 
     68   /**
     69    * Set the ContentHandler used for the tree walk.
     70    *
     71    * @param ch the ContentHandler to be the result of the tree walk.
     72    */
     73   public void setcontentHandler(ContentHandler ch)
     74   {
     75     m_contentHandler = ch;
     76   }
     77 
     78 
     79   /**
     80    * Constructor.
     81    */
     82   public DTMTreeWalker()
     83   {
     84   }
     85 
     86   /**
     87    * Constructor.
     88    * @param   contentHandler The implemention of the
     89    * contentHandler operation (toXMLString, digest, ...)
     90    */
     91   public DTMTreeWalker(ContentHandler contentHandler, DTM dtm)
     92   {
     93     this.m_contentHandler = contentHandler;
     94     m_dtm = dtm;
     95   }
     96 
     97   /** Perform a non-recursive pre-order/post-order traversal,
     98    * operating as a Visitor. startNode (preorder) and endNode
     99    * (postorder) are invoked for each node as we traverse over them,
    100    * with the result that the node is written out to m_contentHandler.
    101    *
    102    * @param pos Node in the tree at which to start (and end) traversal --
    103    * in other words, the root of the subtree to traverse over.
    104    *
    105    * @throws TransformerException */
    106   public void traverse(int pos) throws org.xml.sax.SAXException
    107   {
    108     // %REVIEW% Why isn't this just traverse(pos,pos)?
    109 
    110     int top = pos;		// Remember the root of this subtree
    111 
    112     while (DTM.NULL != pos)
    113     {
    114       startNode(pos);
    115       int nextNode = m_dtm.getFirstChild(pos);
    116       while (DTM.NULL == nextNode)
    117       {
    118         endNode(pos);
    119 
    120         if (top == pos)
    121           break;
    122 
    123         nextNode = m_dtm.getNextSibling(pos);
    124 
    125         if (DTM.NULL == nextNode)
    126         {
    127           pos = m_dtm.getParent(pos);
    128 
    129           if ((DTM.NULL == pos) || (top == pos))
    130           {
    131             // %REVIEW% This condition isn't tested in traverse(pos,top)
    132             // -- bug?
    133             if (DTM.NULL != pos)
    134               endNode(pos);
    135 
    136             nextNode = DTM.NULL;
    137 
    138             break;
    139           }
    140         }
    141       }
    142 
    143       pos = nextNode;
    144     }
    145   }
    146 
    147   /** Perform a non-recursive pre-order/post-order traversal,
    148    * operating as a Visitor. startNode (preorder) and endNode
    149    * (postorder) are invoked for each node as we traverse over them,
    150    * with the result that the node is written out to m_contentHandler.
    151    *
    152    * @param pos Node in the tree where to start traversal
    153    * @param top Node in the tree where to end traversal.
    154    * If top==DTM.NULL, run through end of document.
    155    *
    156    * @throws TransformerException
    157    */
    158   public void traverse(int pos, int top) throws org.xml.sax.SAXException
    159   {
    160     // %OPT% Can we simplify the loop conditionals by adding:
    161     //		if(top==DTM.NULL) top=0
    162     // -- or by simply ignoring this case and relying on the fact that
    163     // pos will never equal DTM.NULL until we're ready to exit?
    164 
    165     while (DTM.NULL != pos)
    166     {
    167       startNode(pos);
    168       int nextNode = m_dtm.getFirstChild(pos);
    169       while (DTM.NULL == nextNode)
    170       {
    171         endNode(pos);
    172 
    173         if ((DTM.NULL != top) && top == pos)
    174           break;
    175 
    176         nextNode = m_dtm.getNextSibling(pos);
    177 
    178         if (DTM.NULL == nextNode)
    179         {
    180           pos = m_dtm.getParent(pos);
    181 
    182           if ((DTM.NULL == pos) || ((DTM.NULL != top) && (top == pos)))
    183           {
    184             nextNode = DTM.NULL;
    185 
    186             break;
    187           }
    188         }
    189       }
    190 
    191       pos = nextNode;
    192     }
    193   }
    194 
    195   /** Flag indicating whether following text to be processed is raw text          */
    196   boolean nextIsRaw = false;
    197 
    198   /**
    199    * Optimized dispatch of characters.
    200    */
    201   private final void dispatachChars(int node)
    202      throws org.xml.sax.SAXException
    203   {
    204     m_dtm.dispatchCharactersEvents(node, m_contentHandler, false);
    205   }
    206 
    207   /**
    208    * Start processing given node
    209    *
    210    *
    211    * @param node Node to process
    212    *
    213    * @throws org.xml.sax.SAXException
    214    */
    215   protected void startNode(int node) throws org.xml.sax.SAXException
    216   {
    217 
    218     if (m_contentHandler instanceof NodeConsumer)
    219     {
    220       // %TBD%
    221 //      ((NodeConsumer) m_contentHandler).setOriginatingNode(node);
    222     }
    223 
    224     switch (m_dtm.getNodeType(node))
    225     {
    226     case DTM.COMMENT_NODE :
    227     {
    228       XMLString data = m_dtm.getStringValue(node);
    229 
    230       if (m_contentHandler instanceof LexicalHandler)
    231       {
    232         LexicalHandler lh = ((LexicalHandler) this.m_contentHandler);
    233         data.dispatchAsComment(lh);
    234       }
    235     }
    236     break;
    237     case DTM.DOCUMENT_FRAGMENT_NODE :
    238 
    239       // ??;
    240       break;
    241     case DTM.DOCUMENT_NODE :
    242       this.m_contentHandler.startDocument();
    243       break;
    244     case DTM.ELEMENT_NODE :
    245       DTM dtm = m_dtm;
    246 
    247       for (int nsn = dtm.getFirstNamespaceNode(node, true); DTM.NULL != nsn;
    248            nsn = dtm.getNextNamespaceNode(node, nsn, true))
    249       {
    250         // String prefix = dtm.getPrefix(nsn);
    251         String prefix = dtm.getNodeNameX(nsn);
    252 
    253         this.m_contentHandler.startPrefixMapping(prefix, dtm.getNodeValue(nsn));
    254 
    255       }
    256 
    257       // System.out.println("m_dh.getNamespaceOfNode(node): "+m_dh.getNamespaceOfNode(node));
    258       // System.out.println("m_dh.getLocalNameOfNode(node): "+m_dh.getLocalNameOfNode(node));
    259       String ns = dtm.getNamespaceURI(node);
    260       if(null == ns)
    261         ns = "";
    262 
    263       // %OPT% !!
    264       org.xml.sax.helpers.AttributesImpl attrs =
    265                             new org.xml.sax.helpers.AttributesImpl();
    266 
    267       for (int i = dtm.getFirstAttribute(node);
    268            i != DTM.NULL;
    269            i = dtm.getNextAttribute(i))
    270       {
    271         attrs.addAttribute(dtm.getNamespaceURI(i),
    272                            dtm.getLocalName(i),
    273                            dtm.getNodeName(i),
    274                            "CDATA",
    275                            dtm.getNodeValue(i));
    276       }
    277 
    278 
    279       this.m_contentHandler.startElement(ns,
    280                                          m_dtm.getLocalName(node),
    281                                          m_dtm.getNodeName(node),
    282                                          attrs);
    283       break;
    284     case DTM.PROCESSING_INSTRUCTION_NODE :
    285     {
    286       String name = m_dtm.getNodeName(node);
    287 
    288       // String data = pi.getData();
    289       if (name.equals("xslt-next-is-raw"))
    290       {
    291         nextIsRaw = true;
    292       }
    293       else
    294       {
    295         this.m_contentHandler.processingInstruction(name,
    296                                                     m_dtm.getNodeValue(node));
    297       }
    298     }
    299     break;
    300     case DTM.CDATA_SECTION_NODE :
    301     {
    302       boolean isLexH = (m_contentHandler instanceof LexicalHandler);
    303       LexicalHandler lh = isLexH
    304                           ? ((LexicalHandler) this.m_contentHandler) : null;
    305 
    306       if (isLexH)
    307       {
    308         lh.startCDATA();
    309       }
    310 
    311       dispatachChars(node);
    312 
    313       {
    314         if (isLexH)
    315         {
    316           lh.endCDATA();
    317         }
    318       }
    319     }
    320     break;
    321     case DTM.TEXT_NODE :
    322     {
    323       if (nextIsRaw)
    324       {
    325         nextIsRaw = false;
    326 
    327         m_contentHandler.processingInstruction(javax.xml.transform.Result.PI_DISABLE_OUTPUT_ESCAPING, "");
    328         dispatachChars(node);
    329         m_contentHandler.processingInstruction(javax.xml.transform.Result.PI_ENABLE_OUTPUT_ESCAPING, "");
    330       }
    331       else
    332       {
    333         dispatachChars(node);
    334       }
    335     }
    336     break;
    337     case DTM.ENTITY_REFERENCE_NODE :
    338     {
    339       if (m_contentHandler instanceof LexicalHandler)
    340       {
    341         ((LexicalHandler) this.m_contentHandler).startEntity(
    342           m_dtm.getNodeName(node));
    343       }
    344       else
    345       {
    346 
    347         // warning("Can not output entity to a pure SAX ContentHandler");
    348       }
    349     }
    350     break;
    351     default :
    352     }
    353   }
    354 
    355   /**
    356    * End processing of given node
    357    *
    358    *
    359    * @param node Node we just finished processing
    360    *
    361    * @throws org.xml.sax.SAXException
    362    */
    363   protected void endNode(int node) throws org.xml.sax.SAXException
    364   {
    365 
    366     switch (m_dtm.getNodeType(node))
    367     {
    368     case DTM.DOCUMENT_NODE :
    369       this.m_contentHandler.endDocument();
    370       break;
    371     case DTM.ELEMENT_NODE :
    372       String ns = m_dtm.getNamespaceURI(node);
    373       if(null == ns)
    374         ns = "";
    375       this.m_contentHandler.endElement(ns,
    376                                          m_dtm.getLocalName(node),
    377                                          m_dtm.getNodeName(node));
    378 
    379       for (int nsn = m_dtm.getFirstNamespaceNode(node, true); DTM.NULL != nsn;
    380            nsn = m_dtm.getNextNamespaceNode(node, nsn, true))
    381       {
    382         // String prefix = m_dtm.getPrefix(nsn);
    383         String prefix = m_dtm.getNodeNameX(nsn);
    384 
    385         this.m_contentHandler.endPrefixMapping(prefix);
    386       }
    387       break;
    388     case DTM.CDATA_SECTION_NODE :
    389       break;
    390     case DTM.ENTITY_REFERENCE_NODE :
    391     {
    392       if (m_contentHandler instanceof LexicalHandler)
    393       {
    394         LexicalHandler lh = ((LexicalHandler) this.m_contentHandler);
    395 
    396         lh.endEntity(m_dtm.getNodeName(node));
    397       }
    398     }
    399     break;
    400     default :
    401     }
    402   }
    403 }  //TreeWalker
    404 
    405