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    */
setDTM(DTM dtm)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    */
getcontentHandler()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    */
setcontentHandler(ContentHandler ch)73   public void setcontentHandler(ContentHandler ch)
74   {
75     m_contentHandler = ch;
76   }
77 
78 
79   /**
80    * Constructor.
81    */
DTMTreeWalker()82   public DTMTreeWalker()
83   {
84   }
85 
86   /**
87    * Constructor.
88    * @param   contentHandler The implemention of the
89    * contentHandler operation (toXMLString, digest, ...)
90    */
DTMTreeWalker(ContentHandler contentHandler, DTM dtm)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 */
traverse(int pos)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    */
traverse(int pos, int top)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    */
dispatachChars(int node)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    */
startNode(int node)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    */
endNode(int node)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