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: TreeWalker.java 468655 2006-10-28 07:12:06Z minchau $
020 */
021 package org.apache.xml.utils;
022
023 import java.io.File;
024
025 import org.w3c.dom.Comment;
026 import org.w3c.dom.Element;
027 import org.w3c.dom.EntityReference;
028 import org.w3c.dom.NamedNodeMap;
029 import org.w3c.dom.Node;
030 import org.w3c.dom.ProcessingInstruction;
031 import org.w3c.dom.Text;
032
033 import org.xml.sax.ContentHandler;
034 import org.xml.sax.Locator;
035 import org.xml.sax.ext.LexicalHandler;
036 import org.xml.sax.helpers.LocatorImpl;
037
038 /**
039 * This class does a pre-order walk of the DOM tree, calling a ContentHandler
040 * interface as it goes.
041 * @xsl.usage advanced
042 */
043
044 public class TreeWalker
045 {
046
047 /** Local reference to a ContentHandler */
048 private ContentHandler m_contentHandler = null;
049
050 // ARGHH!! JAXP Uses Xerces without setting the namespace processing to ON!
051 // DOM2Helper m_dh = new DOM2Helper();
052
053 /** DomHelper for this TreeWalker */
054 protected DOMHelper m_dh;
055
056 /** Locator object for this TreeWalker */
057 private LocatorImpl m_locator = new LocatorImpl();
058
059 /**
060 * Get the ContentHandler used for the tree walk.
061 *
062 * @return the ContentHandler used for the tree walk
063 */
064 public ContentHandler getContentHandler()
065 {
066 return m_contentHandler;
067 }
068
069 /**
070 * Get the ContentHandler used for the tree walk.
071 *
072 * @return the ContentHandler used for the tree walk
073 */
074 public void setContentHandler(ContentHandler ch)
075 {
076 m_contentHandler = ch;
077 }
078
079 /**
080 * Constructor.
081 * @param contentHandler The implemention of the
082 * @param systemId System identifier for the document.
083 * contentHandler operation (toXMLString, digest, ...)
084 */
085 public TreeWalker(ContentHandler contentHandler, DOMHelper dh, String systemId)
086 {
087 this.m_contentHandler = contentHandler;
088 m_contentHandler.setDocumentLocator(m_locator);
089 if (systemId != null)
090 m_locator.setSystemId(systemId);
091 else {
092 try {
093 // Bug see Bugzilla 26741
094 m_locator.setSystemId(System.getProperty("user.dir") + File.separator + "dummy.xsl");
095 }
096 catch (SecurityException se) {// user.dir not accessible from applet
097 }
098 }
099 m_dh = dh;
100 }
101
102 /**
103 * Constructor.
104 * @param contentHandler The implemention of the
105 * contentHandler operation (toXMLString, digest, ...)
106 */
107 public TreeWalker(ContentHandler contentHandler, DOMHelper dh)
108 {
109 this.m_contentHandler = contentHandler;
110 m_contentHandler.setDocumentLocator(m_locator);
111 try {
112 // Bug see Bugzilla 26741
113 m_locator.setSystemId(System.getProperty("user.dir") + File.separator + "dummy.xsl");
114 }
115 catch (SecurityException se){// user.dir not accessible from applet
116 }
117 m_dh = dh;
118 }
119
120 /**
121 * Constructor.
122 * @param contentHandler The implemention of the
123 * contentHandler operation (toXMLString, digest, ...)
124 */
125 public TreeWalker(ContentHandler contentHandler)
126 {
127 this.m_contentHandler = contentHandler;
128 if (m_contentHandler != null)
129 m_contentHandler.setDocumentLocator(m_locator);
130 try {
131 // Bug see Bugzilla 26741
132 m_locator.setSystemId(System.getProperty("user.dir") + File.separator + "dummy.xsl");
133 }
134 catch (SecurityException se){// user.dir not accessible from applet
135
136 }
137 m_dh = new DOM2Helper();
138 }
139
140 /**
141 * Perform a pre-order traversal non-recursive style.
142 *
143 * Note that TreeWalker assumes that the subtree is intended to represent
144 * a complete (though not necessarily well-formed) document and, during a
145 * traversal, startDocument and endDocument will always be issued to the
146 * SAX listener.
147 *
148 * @param pos Node in the tree where to start traversal
149 *
150 * @throws TransformerException
151 */
152 public void traverse(Node pos) throws org.xml.sax.SAXException
153 {
154 this.m_contentHandler.startDocument();
155
156 traverseFragment(pos);
157
158 this.m_contentHandler.endDocument();
159 }
160
161 /**
162 * Perform a pre-order traversal non-recursive style.
163 *
164 * In contrast to the traverse() method this method will not issue
165 * startDocument() and endDocument() events to the SAX listener.
166 *
167 * @param pos Node in the tree where to start traversal
168 *
169 * @throws TransformerException
170 */
171 public void traverseFragment(Node pos) throws org.xml.sax.SAXException
172 {
173 Node top = pos;
174
175 while (null != pos)
176 {
177 startNode(pos);
178
179 Node nextNode = pos.getFirstChild();
180
181 while (null == nextNode)
182 {
183 endNode(pos);
184
185 if (top.equals(pos))
186 break;
187
188 nextNode = pos.getNextSibling();
189
190 if (null == nextNode)
191 {
192 pos = pos.getParentNode();
193
194 if ((null == pos) || (top.equals(pos)))
195 {
196 if (null != pos)
197 endNode(pos);
198
199 nextNode = null;
200
201 break;
202 }
203 }
204 }
205
206 pos = nextNode;
207 }
208 }
209
210 /**
211 * Perform a pre-order traversal non-recursive style.
212
213 * Note that TreeWalker assumes that the subtree is intended to represent
214 * a complete (though not necessarily well-formed) document and, during a
215 * traversal, startDocument and endDocument will always be issued to the
216 * SAX listener.
217 *
218 * @param pos Node in the tree where to start traversal
219 * @param top Node in the tree where to end traversal
220 *
221 * @throws TransformerException
222 */
223 public void traverse(Node pos, Node top) throws org.xml.sax.SAXException
224 {
225
226 this.m_contentHandler.startDocument();
227
228 while (null != pos)
229 {
230 startNode(pos);
231
232 Node nextNode = pos.getFirstChild();
233
234 while (null == nextNode)
235 {
236 endNode(pos);
237
238 if ((null != top) && top.equals(pos))
239 break;
240
241 nextNode = pos.getNextSibling();
242
243 if (null == nextNode)
244 {
245 pos = pos.getParentNode();
246
247 if ((null == pos) || ((null != top) && top.equals(pos)))
248 {
249 nextNode = null;
250
251 break;
252 }
253 }
254 }
255
256 pos = nextNode;
257 }
258 this.m_contentHandler.endDocument();
259 }
260
261 /** Flag indicating whether following text to be processed is raw text */
262 boolean nextIsRaw = false;
263
264 /**
265 * Optimized dispatch of characters.
266 */
267 private final void dispatachChars(Node node)
268 throws org.xml.sax.SAXException
269 {
270 if(m_contentHandler instanceof org.apache.xml.dtm.ref.dom2dtm.DOM2DTM.CharacterNodeHandler)
271 {
272 ((org.apache.xml.dtm.ref.dom2dtm.DOM2DTM.CharacterNodeHandler)m_contentHandler).characters(node);
273 }
274 else
275 {
276 String data = ((Text) node).getData();
277 this.m_contentHandler.characters(data.toCharArray(), 0, data.length());
278 }
279 }
280
281 /**
282 * Start processing given node
283 *
284 *
285 * @param node Node to process
286 *
287 * @throws org.xml.sax.SAXException
288 */
289 protected void startNode(Node node) throws org.xml.sax.SAXException
290 {
291
292 if (m_contentHandler instanceof NodeConsumer)
293 {
294 ((NodeConsumer) m_contentHandler).setOriginatingNode(node);
295 }
296
297 if (node instanceof Locator)
298 {
299 Locator loc = (Locator)node;
300 m_locator.setColumnNumber(loc.getColumnNumber());
301 m_locator.setLineNumber(loc.getLineNumber());
302 m_locator.setPublicId(loc.getPublicId());
303 m_locator.setSystemId(loc.getSystemId());
304 }
305 else
306 {
307 m_locator.setColumnNumber(0);
308 m_locator.setLineNumber(0);
309 }
310
311 switch (node.getNodeType())
312 {
313 case Node.COMMENT_NODE :
314 {
315 String data = ((Comment) node).getData();
316
317 if (m_contentHandler instanceof LexicalHandler)
318 {
319 LexicalHandler lh = ((LexicalHandler) this.m_contentHandler);
320
321 lh.comment(data.toCharArray(), 0, data.length());
322 }
323 }
324 break;
325 case Node.DOCUMENT_FRAGMENT_NODE :
326
327 // ??;
328 break;
329 case Node.DOCUMENT_NODE :
330
331 break;
332 case Node.ELEMENT_NODE :
333 NamedNodeMap atts = ((Element) node).getAttributes();
334 int nAttrs = atts.getLength();
335 // System.out.println("TreeWalker#startNode: "+node.getNodeName());
336
337 for (int i = 0; i < nAttrs; i++)
338 {
339 Node attr = atts.item(i);
340 String attrName = attr.getNodeName();
341
342 // System.out.println("TreeWalker#startNode: attr["+i+"] = "+attrName+", "+attr.getNodeValue());
343 if (attrName.equals("xmlns") || attrName.startsWith("xmlns:"))
344 {
345 // System.out.println("TreeWalker#startNode: attr["+i+"] = "+attrName+", "+attr.getNodeValue());
346 int index;
347 // Use "" instead of null, as Xerces likes "" for the
348 // name of the default namespace. Fix attributed
349 // to "Steven Murray" <smurray@ebt.com>.
350 String prefix = (index = attrName.indexOf(":")) < 0
351 ? "" : attrName.substring(index + 1);
352
353 this.m_contentHandler.startPrefixMapping(prefix,
354 attr.getNodeValue());
355 }
356
357 }
358
359 // System.out.println("m_dh.getNamespaceOfNode(node): "+m_dh.getNamespaceOfNode(node));
360 // System.out.println("m_dh.getLocalNameOfNode(node): "+m_dh.getLocalNameOfNode(node));
361 String ns = m_dh.getNamespaceOfNode(node);
362 if(null == ns)
363 ns = "";
364 this.m_contentHandler.startElement(ns,
365 m_dh.getLocalNameOfNode(node),
366 node.getNodeName(),
367 new AttList(atts, m_dh));
368 break;
369 case Node.PROCESSING_INSTRUCTION_NODE :
370 {
371 ProcessingInstruction pi = (ProcessingInstruction) node;
372 String name = pi.getNodeName();
373
374 // String data = pi.getData();
375 if (name.equals("xslt-next-is-raw"))
376 {
377 nextIsRaw = true;
378 }
379 else
380 {
381 this.m_contentHandler.processingInstruction(pi.getNodeName(),
382 pi.getData());
383 }
384 }
385 break;
386 case Node.CDATA_SECTION_NODE :
387 {
388 boolean isLexH = (m_contentHandler instanceof LexicalHandler);
389 LexicalHandler lh = isLexH
390 ? ((LexicalHandler) this.m_contentHandler) : null;
391
392 if (isLexH)
393 {
394 lh.startCDATA();
395 }
396
397 dispatachChars(node);
398
399 {
400 if (isLexH)
401 {
402 lh.endCDATA();
403 }
404 }
405 }
406 break;
407 case Node.TEXT_NODE :
408 {
409 //String data = ((Text) node).getData();
410
411 if (nextIsRaw)
412 {
413 nextIsRaw = false;
414
415 m_contentHandler.processingInstruction(javax.xml.transform.Result.PI_DISABLE_OUTPUT_ESCAPING, "");
416 dispatachChars(node);
417 m_contentHandler.processingInstruction(javax.xml.transform.Result.PI_ENABLE_OUTPUT_ESCAPING, "");
418 }
419 else
420 {
421 dispatachChars(node);
422 }
423 }
424 break;
425 case Node.ENTITY_REFERENCE_NODE :
426 {
427 EntityReference eref = (EntityReference) node;
428
429 if (m_contentHandler instanceof LexicalHandler)
430 {
431 ((LexicalHandler) this.m_contentHandler).startEntity(
432 eref.getNodeName());
433 }
434 else
435 {
436
437 // warning("Can not output entity to a pure SAX ContentHandler");
438 }
439 }
440 break;
441 default :
442 }
443 }
444
445 /**
446 * End processing of given node
447 *
448 *
449 * @param node Node we just finished processing
450 *
451 * @throws org.xml.sax.SAXException
452 */
453 protected void endNode(Node node) throws org.xml.sax.SAXException
454 {
455
456 switch (node.getNodeType())
457 {
458 case Node.DOCUMENT_NODE :
459 break;
460
461 case Node.ELEMENT_NODE :
462 String ns = m_dh.getNamespaceOfNode(node);
463 if(null == ns)
464 ns = "";
465 this.m_contentHandler.endElement(ns,
466 m_dh.getLocalNameOfNode(node),
467 node.getNodeName());
468
469 NamedNodeMap atts = ((Element) node).getAttributes();
470 int nAttrs = atts.getLength();
471
472 for (int i = 0; i < nAttrs; i++)
473 {
474 Node attr = atts.item(i);
475 String attrName = attr.getNodeName();
476
477 if (attrName.equals("xmlns") || attrName.startsWith("xmlns:"))
478 {
479 int index;
480 // Use "" instead of null, as Xerces likes "" for the
481 // name of the default namespace. Fix attributed
482 // to "Steven Murray" <smurray@ebt.com>.
483 String prefix = (index = attrName.indexOf(":")) < 0
484 ? "" : attrName.substring(index + 1);
485
486 this.m_contentHandler.endPrefixMapping(prefix);
487 }
488 }
489 break;
490 case Node.CDATA_SECTION_NODE :
491 break;
492 case Node.ENTITY_REFERENCE_NODE :
493 {
494 EntityReference eref = (EntityReference) node;
495
496 if (m_contentHandler instanceof LexicalHandler)
497 {
498 LexicalHandler lh = ((LexicalHandler) this.m_contentHandler);
499
500 lh.endEntity(eref.getNodeName());
501 }
502 }
503 break;
504 default :
505 }
506 }
507 } //TreeWalker
508