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 */
21package org.apache.xml.dtm.ref;
22
23import org.apache.xml.dtm.DTM;
24import org.apache.xml.utils.NodeConsumer;
25import org.apache.xml.utils.XMLString;
26
27import org.xml.sax.ContentHandler;
28import 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 */
39public 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