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