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