001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: DTMTreeWalker.java 468653 2006-10-28 07:07:05Z minchau $
020     */
021    package org.apache.xml.dtm.ref;
022    
023    import org.apache.xml.dtm.DTM;
024    import org.apache.xml.utils.NodeConsumer;
025    import org.apache.xml.utils.XMLString;
026    
027    import org.xml.sax.ContentHandler;
028    import org.xml.sax.ext.LexicalHandler;
029    
030    /**
031     * This class does a pre-order walk of the DTM tree, calling a ContentHandler
032     * interface as it goes. As such, it's more like the Visitor design pattern
033     * than like the DOM's TreeWalker.
034     *
035     * I think normally this class should not be needed, because 
036     * of DTM#dispatchToEvents.
037     * @xsl.usage advanced
038     */
039    public class DTMTreeWalker
040    {
041    
042      /** Local reference to a ContentHandler          */
043      private ContentHandler m_contentHandler = null;
044    
045      /** DomHelper for this TreeWalker          */
046      protected DTM m_dtm;
047      
048      /**
049       * Set the DTM to be traversed.
050       * 
051       * @param dtm The Document Table Model to be used.
052       */
053      public void setDTM(DTM dtm)
054      {
055        m_dtm = dtm;
056      }
057    
058      /**
059       * Get the ContentHandler used for the tree walk.
060       *
061       * @return the ContentHandler used for the tree walk
062       */
063      public ContentHandler getcontentHandler()
064      {
065        return m_contentHandler;
066      }
067      
068      /**
069       * Set the ContentHandler used for the tree walk.
070       *
071       * @param ch the ContentHandler to be the result of the tree walk.
072       */
073      public void setcontentHandler(ContentHandler ch)
074      {
075        m_contentHandler = ch;
076      }
077    
078      
079      /**
080       * Constructor.
081       */
082      public DTMTreeWalker()
083      {
084      }
085      
086      /**
087       * Constructor.
088       * @param   contentHandler The implemention of the
089       * contentHandler operation (toXMLString, digest, ...)
090       */
091      public DTMTreeWalker(ContentHandler contentHandler, DTM dtm)
092      {
093        this.m_contentHandler = contentHandler;
094        m_dtm = dtm;
095      }
096      
097      /** Perform a non-recursive pre-order/post-order traversal,
098       * operating as a Visitor. startNode (preorder) and endNode
099       * (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