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