001: /*
002: * Copyright 1999-2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: /*
017: * $Id: DTMTreeWalker.java,v 1.7 2005/01/24 00:34:35 mcnamara Exp $
018: */
019: package org.apache.xml.dtm.ref;
020:
021: import org.apache.xml.dtm.DTM;
022: import org.apache.xml.utils.NodeConsumer;
023: import org.apache.xml.utils.XMLString;
024:
025: import org.xml.sax.ContentHandler;
026: import org.xml.sax.ext.LexicalHandler;
027:
028: /**
029: * This class does a pre-order walk of the DTM tree, calling a ContentHandler
030: * interface as it goes. As such, it's more like the Visitor design pattern
031: * than like the DOM's TreeWalker.
032: *
033: * I think normally this class should not be needed, because
034: * of DTM#dispatchToEvents.
035: * @xsl.usage advanced
036: */
037: public class DTMTreeWalker {
038:
039: /** Local reference to a ContentHandler */
040: private ContentHandler m_contentHandler = null;
041:
042: /** DomHelper for this TreeWalker */
043: protected DTM m_dtm;
044:
045: /**
046: * Set the DTM to be traversed.
047: *
048: * @param dtm The Document Table Model to be used.
049: */
050: public void setDTM(DTM dtm) {
051: m_dtm = dtm;
052: }
053:
054: /**
055: * Get the ContentHandler used for the tree walk.
056: *
057: * @return the ContentHandler used for the tree walk
058: */
059: public ContentHandler getcontentHandler() {
060: return m_contentHandler;
061: }
062:
063: /**
064: * Set the ContentHandler used for the tree walk.
065: *
066: * @param ch the ContentHandler to be the result of the tree walk.
067: */
068: public void setcontentHandler(ContentHandler ch) {
069: m_contentHandler = ch;
070: }
071:
072: /**
073: * Constructor.
074: */
075: public DTMTreeWalker() {
076: }
077:
078: /**
079: * Constructor.
080: * @param contentHandler The implemention of the
081: * contentHandler operation (toXMLString, digest, ...)
082: */
083: public DTMTreeWalker(ContentHandler contentHandler, DTM dtm) {
084: this .m_contentHandler = contentHandler;
085: m_dtm = dtm;
086: }
087:
088: /** Perform a non-recursive pre-order/post-order traversal,
089: * operating as a Visitor. startNode (preorder) and endNode
090: * (postorder) are invoked for each node as we traverse over them,
091: * with the result that the node is written out to m_contentHandler.
092: *
093: * @param pos Node in the tree at which to start (and end) traversal --
094: * in other words, the root of the subtree to traverse over.
095: *
096: * @throws TransformerException */
097: public void traverse(int pos) throws org.xml.sax.SAXException {
098: // %REVIEW% Why isn't this just traverse(pos,pos)?
099:
100: int top = pos; // Remember the root of this subtree
101:
102: while (DTM.NULL != pos) {
103: startNode(pos);
104: int nextNode = m_dtm.getFirstChild(pos);
105: while (DTM.NULL == nextNode) {
106: endNode(pos);
107:
108: if (top == pos)
109: break;
110:
111: nextNode = m_dtm.getNextSibling(pos);
112:
113: if (DTM.NULL == nextNode) {
114: pos = m_dtm.getParent(pos);
115:
116: if ((DTM.NULL == pos) || (top == pos)) {
117: // %REVIEW% This condition isn't tested in traverse(pos,top)
118: // -- bug?
119: if (DTM.NULL != pos)
120: endNode(pos);
121:
122: nextNode = DTM.NULL;
123:
124: break;
125: }
126: }
127: }
128:
129: pos = nextNode;
130: }
131: }
132:
133: /** Perform a non-recursive pre-order/post-order traversal,
134: * operating as a Visitor. startNode (preorder) and endNode
135: * (postorder) are invoked for each node as we traverse over them,
136: * with the result that the node is written out to m_contentHandler.
137: *
138: * @param pos Node in the tree where to start traversal
139: * @param top Node in the tree where to end traversal.
140: * If top==DTM.NULL, run through end of document.
141: *
142: * @throws TransformerException
143: */
144: public void traverse(int pos, int top)
145: throws org.xml.sax.SAXException {
146: // %OPT% Can we simplify the loop conditionals by adding:
147: // if(top==DTM.NULL) top=0
148: // -- or by simply ignoring this case and relying on the fact that
149: // pos will never equal DTM.NULL until we're ready to exit?
150:
151: while (DTM.NULL != pos) {
152: startNode(pos);
153: int nextNode = m_dtm.getFirstChild(pos);
154: while (DTM.NULL == nextNode) {
155: endNode(pos);
156:
157: if ((DTM.NULL != top) && top == pos)
158: break;
159:
160: nextNode = m_dtm.getNextSibling(pos);
161:
162: if (DTM.NULL == nextNode) {
163: pos = m_dtm.getParent(pos);
164:
165: if ((DTM.NULL == pos)
166: || ((DTM.NULL != top) && (top == pos))) {
167: nextNode = DTM.NULL;
168:
169: break;
170: }
171: }
172: }
173:
174: pos = nextNode;
175: }
176: }
177:
178: /** Flag indicating whether following text to be processed is raw text */
179: boolean nextIsRaw = false;
180:
181: /**
182: * Optimized dispatch of characters.
183: */
184: private final void dispatachChars(int node)
185: throws org.xml.sax.SAXException {
186: m_dtm.dispatchCharactersEvents(node, m_contentHandler, false);
187: }
188:
189: /**
190: * Start processing given node
191: *
192: *
193: * @param node Node to process
194: *
195: * @throws org.xml.sax.SAXException
196: */
197: protected void startNode(int node) throws org.xml.sax.SAXException {
198:
199: if (m_contentHandler instanceof NodeConsumer) {
200: // %TBD%
201: // ((NodeConsumer) m_contentHandler).setOriginatingNode(node);
202: }
203:
204: switch (m_dtm.getNodeType(node)) {
205: case DTM.COMMENT_NODE: {
206: XMLString data = m_dtm.getStringValue(node);
207:
208: if (m_contentHandler instanceof LexicalHandler) {
209: LexicalHandler lh = ((LexicalHandler) this .m_contentHandler);
210: data.dispatchAsComment(lh);
211: }
212: }
213: break;
214: case DTM.DOCUMENT_FRAGMENT_NODE:
215:
216: // ??;
217: break;
218: case DTM.DOCUMENT_NODE:
219: this .m_contentHandler.startDocument();
220: break;
221: case DTM.ELEMENT_NODE:
222: DTM dtm = m_dtm;
223:
224: for (int nsn = dtm.getFirstNamespaceNode(node, true); DTM.NULL != nsn; nsn = dtm
225: .getNextNamespaceNode(node, nsn, true)) {
226: // String prefix = dtm.getPrefix(nsn);
227: String prefix = dtm.getNodeNameX(nsn);
228:
229: this .m_contentHandler.startPrefixMapping(prefix, dtm
230: .getNodeValue(nsn));
231:
232: }
233:
234: // System.out.println("m_dh.getNamespaceOfNode(node): "+m_dh.getNamespaceOfNode(node));
235: // System.out.println("m_dh.getLocalNameOfNode(node): "+m_dh.getLocalNameOfNode(node));
236: String ns = dtm.getNamespaceURI(node);
237: if (null == ns)
238: ns = "";
239:
240: // %OPT% !!
241: org.xml.sax.helpers.AttributesImpl attrs = new org.xml.sax.helpers.AttributesImpl();
242:
243: for (int i = dtm.getFirstAttribute(node); i != DTM.NULL; i = dtm
244: .getNextAttribute(i)) {
245: attrs.addAttribute(dtm.getNamespaceURI(i), dtm
246: .getLocalName(i), dtm.getNodeName(i), "CDATA",
247: dtm.getNodeValue(i));
248: }
249:
250: this .m_contentHandler
251: .startElement(ns, m_dtm.getLocalName(node), m_dtm
252: .getNodeName(node), attrs);
253: break;
254: case DTM.PROCESSING_INSTRUCTION_NODE: {
255: String name = m_dtm.getNodeName(node);
256:
257: // String data = pi.getData();
258: if (name.equals("xslt-next-is-raw")) {
259: nextIsRaw = true;
260: } else {
261: this .m_contentHandler.processingInstruction(name, m_dtm
262: .getNodeValue(node));
263: }
264: }
265: break;
266: case DTM.CDATA_SECTION_NODE: {
267: boolean isLexH = (m_contentHandler instanceof LexicalHandler);
268: LexicalHandler lh = isLexH ? ((LexicalHandler) this .m_contentHandler)
269: : null;
270:
271: if (isLexH) {
272: lh.startCDATA();
273: }
274:
275: dispatachChars(node);
276:
277: {
278: if (isLexH) {
279: lh.endCDATA();
280: }
281: }
282: }
283: break;
284: case DTM.TEXT_NODE: {
285: if (nextIsRaw) {
286: nextIsRaw = false;
287:
288: m_contentHandler
289: .processingInstruction(
290: javax.xml.transform.Result.PI_DISABLE_OUTPUT_ESCAPING,
291: "");
292: dispatachChars(node);
293: m_contentHandler
294: .processingInstruction(
295: javax.xml.transform.Result.PI_ENABLE_OUTPUT_ESCAPING,
296: "");
297: } else {
298: dispatachChars(node);
299: }
300: }
301: break;
302: case DTM.ENTITY_REFERENCE_NODE: {
303: if (m_contentHandler instanceof LexicalHandler) {
304: ((LexicalHandler) this .m_contentHandler)
305: .startEntity(m_dtm.getNodeName(node));
306: } else {
307:
308: // warning("Can not output entity to a pure SAX ContentHandler");
309: }
310: }
311: break;
312: default:
313: }
314: }
315:
316: /**
317: * End processing of given node
318: *
319: *
320: * @param node Node we just finished processing
321: *
322: * @throws org.xml.sax.SAXException
323: */
324: protected void endNode(int node) throws org.xml.sax.SAXException {
325:
326: switch (m_dtm.getNodeType(node)) {
327: case DTM.DOCUMENT_NODE:
328: this .m_contentHandler.endDocument();
329: break;
330: case DTM.ELEMENT_NODE:
331: String ns = m_dtm.getNamespaceURI(node);
332: if (null == ns)
333: ns = "";
334: this .m_contentHandler.endElement(ns, m_dtm
335: .getLocalName(node), m_dtm.getNodeName(node));
336:
337: for (int nsn = m_dtm.getFirstNamespaceNode(node, true); DTM.NULL != nsn; nsn = m_dtm
338: .getNextNamespaceNode(node, nsn, true)) {
339: // String prefix = m_dtm.getPrefix(nsn);
340: String prefix = m_dtm.getNodeNameX(nsn);
341:
342: this .m_contentHandler.endPrefixMapping(prefix);
343: }
344: break;
345: case DTM.CDATA_SECTION_NODE:
346: break;
347: case DTM.ENTITY_REFERENCE_NODE: {
348: if (m_contentHandler instanceof LexicalHandler) {
349: LexicalHandler lh = ((LexicalHandler) this .m_contentHandler);
350:
351: lh.endEntity(m_dtm.getNodeName(node));
352: }
353: }
354: break;
355: default:
356: }
357: }
358: } //TreeWalker
|