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