001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.xml.dom;
018:
019: import org.apache.avalon.excalibur.pool.Recyclable;
020:
021: import org.apache.cocoon.xml.AbstractXMLProducer;
022: import org.apache.cocoon.xml.EmbeddedXMLPipe;
023: import org.apache.cocoon.xml.XMLConsumer;
024: import org.apache.cocoon.xml.XMLProducer;
025:
026: import org.apache.commons.lang.StringUtils;
027: import org.w3c.dom.Attr;
028: import org.w3c.dom.Comment;
029: import org.w3c.dom.Element;
030: import org.w3c.dom.EntityReference;
031: import org.w3c.dom.NamedNodeMap;
032: import org.w3c.dom.Node;
033: import org.w3c.dom.ProcessingInstruction;
034: import org.w3c.dom.Text;
035: import org.xml.sax.ContentHandler;
036: import org.xml.sax.SAXException;
037: import org.xml.sax.ext.LexicalHandler;
038: import org.xml.sax.helpers.AttributesImpl;
039:
040: import javax.xml.transform.Transformer;
041: import javax.xml.transform.TransformerConfigurationException;
042: import javax.xml.transform.TransformerException;
043: import javax.xml.transform.TransformerFactory;
044: import javax.xml.transform.dom.DOMSource;
045: import javax.xml.transform.sax.SAXResult;
046: import java.util.HashMap;
047: import java.util.Iterator;
048: import java.util.Map;
049:
050: /**
051: * The <code>DOMStreamer</code> is a utility class that will generate SAX
052: * events from a W3C DOM Document.
053: *
054: * <p>The DOMStreamer uses a different strategy based on the value of the
055: * normalizeNamespaces property:
056: * <ul>
057: * <li>if true (the default), the DOMStreamer will normalize namespace
058: * declarations (i.e. add missing xmlns attributes or correct them). See
059: * also {@link NamespaceNormalizingDOMStreamer}.
060: * <li>if false, the standard JAXP identity transformer is used.
061: * </ul>
062: *
063: * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
064: * @author <a href="mailto:pier@apache.org">Pierpaolo Fumagalli</a>
065: * (Apache Software Foundation)
066: * @version $Id: DOMStreamer.java 433543 2006-08-22 06:22:54Z crossley $
067: */
068: public class DOMStreamer implements XMLProducer, Recyclable {
069:
070: /** The transformer factory shared by all instances (only used by DefaultDOMStreamer) */
071: private static final TransformerFactory FACTORY = TransformerFactory
072: .newInstance();
073:
074: /** Default value for normalizeNamespaces. */
075: private static final boolean DEFAULT_NORMALIZE_NAMESPACES = true;
076:
077: /** Indicates whether namespace normalization should happen. */
078: protected boolean normalizeNamespaces = DEFAULT_NORMALIZE_NAMESPACES;
079:
080: /** DOMStreamer used in case of namespace normalization. */
081: protected NamespaceNormalizingDOMStreamer namespaceNormalizingDOMStreamer = new NamespaceNormalizingDOMStreamer();
082:
083: /** DOMStreamer used when namespace normalization should not explicitely happen. */
084: protected DefaultDOMStreamer defaultDOMStreamer = new DefaultDOMStreamer();
085:
086: /**
087: * Create a new <code>DOMStreamer</code> instance.
088: */
089: public DOMStreamer() {
090: super ();
091: }
092:
093: /**
094: * Create a new <code>DOMStreamer</code> instance.
095: */
096: public DOMStreamer(ContentHandler content, LexicalHandler lexical) {
097: this ();
098: setContentHandler(content);
099: setLexicalHandler(lexical);
100: }
101:
102: /**
103: * Create a new <code>DOMStreamer</code> instance.
104: */
105: public DOMStreamer(XMLConsumer consumer) {
106: this (consumer, consumer);
107: }
108:
109: /**
110: * Create a new <code>DOMStreamer</code> instance.
111: */
112: public DOMStreamer(ContentHandler content) {
113: this (
114: content,
115: content instanceof LexicalHandler ? (LexicalHandler) content
116: : null);
117: }
118:
119: /**
120: * Set the <code>XMLConsumer</code> that will receive XML data.
121: */
122: public void setConsumer(XMLConsumer consumer) {
123: setContentHandler(consumer);
124: setLexicalHandler(consumer);
125: }
126:
127: /**
128: * Set the <code>ContentHandler</code> that will receive XML data.
129: */
130: public void setContentHandler(ContentHandler handler) {
131: defaultDOMStreamer.setContentHandler(handler);
132: namespaceNormalizingDOMStreamer.setContentHandler(handler);
133: }
134:
135: /**
136: * Set the <code>LexicalHandler</code> that will receive XML data.
137: */
138: public void setLexicalHandler(LexicalHandler handler) {
139: defaultDOMStreamer.setLexicalHandler(handler);
140: namespaceNormalizingDOMStreamer.setLexicalHandler(handler);
141: }
142:
143: /**
144: * Start the production of SAX events.
145: */
146: public void stream(Node node) throws SAXException {
147: if (normalizeNamespaces) {
148: namespaceNormalizingDOMStreamer.stream(node);
149: } else {
150: defaultDOMStreamer.stream(node);
151: }
152: }
153:
154: public boolean isNormalizeNamespaces() {
155: return normalizeNamespaces;
156: }
157:
158: public void setNormalizeNamespaces(boolean normalizeNamespaces) {
159: this .normalizeNamespaces = normalizeNamespaces;
160: }
161:
162: public void recycle() {
163: defaultDOMStreamer.recycle();
164: namespaceNormalizingDOMStreamer.recycle();
165: normalizeNamespaces = DEFAULT_NORMALIZE_NAMESPACES;
166: }
167:
168: /**
169: * Streams a DOM tree to SAX events and normalizes namespace declarations on the way.
170: *
171: * <p>The code in this class is based on the org.apache.xml.utils.TreeWalker class from Xalan,
172: * though it differs in some important ways.
173: *
174: * <p>This class will automatically fix up ("normalize") namespace declarations
175: * while streaming to SAX. The original DOM-tree is not modified. The algorithm
176: * used is described in
177: * <a href="http://www.w3.org/TR/2002/WD-DOM-Level-3-Core-20021022/namespaces-algorithms.html#normalizeDocumentAlgo">an appendix of the DOM Level 3 spec</a>.
178: *
179: * <p>This class will NOT check the correctness of namespaces, e.g. it will not
180: * check that the "xml" prefix is not misused etc.
181: *
182: * @author Bruno Dumon (bruno at outerthought dot org)
183: * @author Xalan team
184: */
185: public static class NamespaceNormalizingDOMStreamer extends
186: AbstractXMLProducer {
187: /**
188: * Information about the current element. Used to remember the localName, qName
189: * and namespaceURI for generating the endElement event, and holds the namespaces
190: * declared on the element. This extra class is needed because we don't want to
191: * modify the DOM-tree itself. The currentElementInfo has a pointer to its parent
192: * elementInfo.
193: */
194: protected NamespaceNormalizingDOMStreamer.ElementInfo currentElementInfo;
195:
196: /** Counter used when generating new namespace prefixes. */
197: protected int newPrefixCounter;
198:
199: public void recycle() {
200: super .recycle();
201: currentElementInfo = null;
202: newPrefixCounter = 0;
203: }
204:
205: /**
206: * Start the production of SAX events.
207: *
208: * <p>Perform a pre-order traversal non-recursive style.
209: *
210: * <p>Note that TreeWalker assumes that the subtree is intended to represent
211: * a complete (though not necessarily well-formed) document and, during a
212: * traversal, startDocument and endDocument will always be issued to the
213: * SAX listener.
214: *
215: * @param pos Node in the tree where to start traversal
216: */
217: protected void stream(Node pos) throws SAXException {
218:
219: // Start document only if we're streaming a document
220: boolean isDoc = (pos.getNodeType() == Node.DOCUMENT_NODE);
221: if (isDoc) {
222: contentHandler.startDocument();
223: }
224:
225: Node top = pos;
226: while (null != pos) {
227: startNode(pos);
228:
229: Node nextNode = pos.getFirstChild();
230: while (null == nextNode) {
231: endNode(pos);
232:
233: if (top.equals(pos)) {
234: break;
235: }
236:
237: nextNode = pos.getNextSibling();
238: if (null == nextNode) {
239: pos = pos.getParentNode();
240:
241: if ((null == pos) || (top.equals(pos))) {
242: if (null != pos) {
243: endNode(pos);
244: }
245: nextNode = null;
246:
247: break;
248: }
249: }
250: }
251:
252: pos = nextNode;
253: }
254:
255: if (isDoc) {
256: contentHandler.endDocument();
257: }
258: }
259:
260: private final void dispatchChars(Node node) throws SAXException {
261: final String data = ((Text) node).getData();
262: if (data != null) {
263: contentHandler.characters(data.toCharArray(), 0, data
264: .length());
265: }
266: }
267:
268: /**
269: * Start processing given node
270: *
271: * @param node Node to process
272: */
273: protected void startNode(Node node) throws SAXException {
274:
275: switch (node.getNodeType()) {
276: case Node.COMMENT_NODE: {
277: if (lexicalHandler != null) {
278: final String data = ((Comment) node).getData();
279: if (data != null) {
280: lexicalHandler.comment(data.toCharArray(), 0,
281: data.length());
282: }
283: }
284: }
285: break;
286: case Node.DOCUMENT_FRAGMENT_NODE:
287: // ??;
288: case Node.DOCUMENT_NODE:
289: break;
290: case Node.ELEMENT_NODE:
291: NamedNodeMap atts = node.getAttributes();
292: int nAttrs = atts.getLength();
293:
294: // create a list of localy declared namespace prefixes
295: currentElementInfo = new NamespaceNormalizingDOMStreamer.ElementInfo(
296: currentElementInfo);
297: for (int i = 0; i < nAttrs; i++) {
298: Node attr = atts.item(i);
299: String attrName = attr.getNodeName();
300:
301: if (attrName.equals("xmlns")
302: || attrName.startsWith("xmlns:")) {
303: int index;
304: String prefix = (index = attrName.indexOf(":")) < 0 ? ""
305: : attrName.substring(index + 1);
306:
307: currentElementInfo.put(prefix, attr
308: .getNodeValue());
309: }
310: }
311:
312: String namespaceURI = node.getNamespaceURI();
313: String prefix = node.getPrefix();
314: String localName = node.getLocalName();
315:
316: if (localName == null) {
317: // this is an element created with createElement instead of createElementNS
318: String[] prefixAndLocalName = getPrefixAndLocalName(node
319: .getNodeName());
320: prefix = prefixAndLocalName[0];
321: localName = prefixAndLocalName[1];
322: // note: if prefix is null, there can still be a default namespace...
323: namespaceURI = getNamespaceForPrefix(prefix,
324: (Element) node);
325: }
326:
327: if (namespaceURI != null) {
328: // no prefix means: make this the default namespace
329: if (prefix == null) {
330: prefix = "";
331: }
332: // check that is declared
333: String uri = currentElementInfo
334: .findNamespaceURI(prefix);
335: if (StringUtils.equals(uri, namespaceURI)) {
336: // System.out.println("namespace is declared");
337: // prefix is declared correctly, do nothing
338: //} else if (uri != null) {
339: // System.out.println("prefix is declared with other namespace, overwriting it");
340: // prefix exists but is bound to another namespace, overwrite it
341: // currentElementInfo.put(prefix, namespaceURI);
342: } else {
343: // System.out.println("prefix is not yet declared, declaring it now");
344: currentElementInfo.put(prefix, namespaceURI);
345: }
346: } else {
347: // element has no namespace
348: // check if there is a default namespace, if so undeclare it
349: String uri = currentElementInfo
350: .findNamespaceURI("");
351: if (StringUtils.isNotEmpty(uri)) {
352: // System.out.println("undeclaring default namespace");
353: currentElementInfo.put("", "");
354: }
355: }
356:
357: // SAX uses empty string to denote no namespace, while DOM uses null.
358: if (namespaceURI == null)
359: namespaceURI = "";
360:
361: String qName;
362: if (StringUtils.isNotEmpty(prefix)) {
363: qName = prefix + ":" + localName;
364: } else {
365: qName = localName;
366: }
367:
368: // make the attributes
369: AttributesImpl newAttrs = new AttributesImpl();
370: for (int i = 0; i < nAttrs; i++) {
371: Node attr = atts.item(i);
372: String attrName = attr.getNodeName();
373: String assignedAttrPrefix = null;
374:
375: // only do non-namespace attributes
376: if (!(attrName.equals("xmlns") || attrName
377: .startsWith("xmlns:"))) {
378: String attrPrefix;
379: String attrLocalName;
380: String attrNsURI;
381:
382: if (attr.getLocalName() == null) {
383: // this is an attribute created with setAttribute instead of setAttributeNS
384: String[] prefixAndLocalName = getPrefixAndLocalName(attrName);
385: attrPrefix = prefixAndLocalName[0];
386: // the statement below causes the attribute to keep its prefix even if it is not
387: // bound to a namespace (to support pre-namespace XML).
388: assignedAttrPrefix = attrPrefix;
389: attrLocalName = prefixAndLocalName[1];
390: // note: if prefix is null, the attribute has no namespace (namespace defaulting
391: // does not apply to attributes)
392: if (attrPrefix != null)
393: attrNsURI = getNamespaceForPrefix(
394: attrPrefix, (Element) node);
395: else
396: attrNsURI = null;
397: } else {
398: attrLocalName = attr.getLocalName();
399: attrPrefix = attr.getPrefix();
400: attrNsURI = attr.getNamespaceURI();
401: }
402:
403: if (attrNsURI != null) {
404: String declaredUri = currentElementInfo
405: .findNamespaceURI(attrPrefix);
406: // if the prefix is null, or the prefix has not been declared, or conflicts with an in-scope binding
407: if (declaredUri == null
408: || !declaredUri.equals(attrNsURI)) {
409: String availablePrefix = currentElementInfo
410: .findPrefix(attrNsURI);
411: if (availablePrefix != null
412: && !availablePrefix.equals(""))
413: assignedAttrPrefix = availablePrefix;
414: else {
415: if (attrPrefix != null
416: && declaredUri == null) {
417: // prefix is not null and is not yet declared: declare it
418: assignedAttrPrefix = attrPrefix;
419: currentElementInfo.put(
420: assignedAttrPrefix,
421: attrNsURI);
422: } else {
423: // attribute has no prefix (which is not allowed for namespaced attributes) or
424: // the prefix is already bound to something else: generate a new prefix
425: newPrefixCounter++;
426: assignedAttrPrefix = "NS"
427: + newPrefixCounter;
428: currentElementInfo.put(
429: assignedAttrPrefix,
430: attrNsURI);
431: }
432: }
433: } else {
434: assignedAttrPrefix = attrPrefix;
435: }
436: }
437:
438: String assignedAttrNsURI = attrNsURI != null ? attrNsURI
439: : "";
440: String attrQName;
441: if (assignedAttrPrefix != null) {
442: attrQName = assignedAttrPrefix + ":"
443: + attrLocalName;
444: } else {
445: attrQName = attrLocalName;
446: }
447: newAttrs.addAttribute(assignedAttrNsURI,
448: attrLocalName, attrQName, "CDATA", attr
449: .getNodeValue());
450: }
451: }
452:
453: // add local namespace declaration and fire startPrefixMapping events
454: if (currentElementInfo.namespaceDeclarations != null
455: && currentElementInfo.namespaceDeclarations
456: .size() > 0) {
457: Iterator localNsDeclIt = currentElementInfo.namespaceDeclarations
458: .entrySet().iterator();
459: while (localNsDeclIt.hasNext()) {
460: Map.Entry entry = (Map.Entry) localNsDeclIt
461: .next();
462: String pr = (String) entry.getKey();
463: String ns = (String) entry.getValue();
464: // the following lines enable the creation of explicit xmlns attributes
465: //String pr1 = pr.equals("") ? "xmlns" : pr;
466: //String qn = pr.equals("") ? "xmlns" : "xmlns:" + pr;
467: //newAttrs.addAttribute("", pr1, qn, "CDATA", ns);
468: // System.out.println("starting prefix mapping for prefix " + pr + " for " + ns);
469: contentHandler.startPrefixMapping(pr, ns);
470: }
471: }
472:
473: contentHandler.startElement(namespaceURI, localName,
474: qName, newAttrs);
475:
476: currentElementInfo.localName = localName;
477: currentElementInfo.namespaceURI = namespaceURI;
478: currentElementInfo.qName = qName;
479: break;
480: case Node.PROCESSING_INSTRUCTION_NODE: {
481: ProcessingInstruction pi = (ProcessingInstruction) node;
482: contentHandler.processingInstruction(pi.getNodeName(),
483: pi.getData());
484: }
485: break;
486: case Node.CDATA_SECTION_NODE: {
487: if (lexicalHandler != null)
488: lexicalHandler.startCDATA();
489:
490: dispatchChars(node);
491:
492: if (lexicalHandler != null)
493: lexicalHandler.endCDATA();
494: }
495: break;
496: case Node.TEXT_NODE: {
497: dispatchChars(node);
498: }
499: break;
500: case Node.ENTITY_REFERENCE_NODE: {
501: EntityReference eref = (EntityReference) node;
502:
503: if (lexicalHandler != null) {
504: lexicalHandler.startEntity(eref.getNodeName());
505: } else {
506: // warning("Can not output entity to a pure SAX ContentHandler");
507: }
508: }
509: break;
510: default:
511: }
512: }
513:
514: /**
515: * Searches the namespace for a given namespace prefix starting from a
516: * given Element.
517: *
518: * <p>Note that this resolves the prefix in the orginal DOM-tree, not in
519: * the {@link ElementInfo} objects. This is used to resolve prefixes
520: * of elements or attributes created with createElement or setAttribute
521: * instead of createElementNS or setAttributeNS.
522: *
523: * <p>The code in this method is largely based on
524: * org.apache.xml.utils.DOMHelper.getNamespaceForPrefix() (from Xalan).
525: *
526: * @param prefix the prefix to look for, can be empty or null to find the
527: * default namespace
528: *
529: * @return the namespace, or null if not found.
530: */
531: public String getNamespaceForPrefix(String prefix,
532: Element namespaceContext) {
533: int type;
534: Node parent = namespaceContext;
535: String namespace = null;
536:
537: if (prefix == null)
538: prefix = "";
539:
540: if (prefix.equals("xml")) {
541: namespace = "http://www.w3.org/XML/1998/namespace";
542: } else if (prefix.equals("xmlns")) {
543: namespace = "http://www.w3.org/2000/xmlns/";
544: } else {
545: // Attribute name for this prefix's declaration
546: String declname = (prefix.length() == 0) ? "xmlns"
547: : "xmlns:" + prefix;
548:
549: // Scan until we run out of Elements or have resolved the namespace
550: while ((null != parent)
551: && (((type = parent.getNodeType()) == Node.ELEMENT_NODE) || (type == Node.ENTITY_REFERENCE_NODE))) {
552: if (type == Node.ELEMENT_NODE) {
553: Attr attr = ((Element) parent)
554: .getAttributeNode(declname);
555: if (attr != null) {
556: namespace = attr.getNodeValue();
557: break;
558: }
559: }
560: parent = parent.getParentNode();
561: }
562: }
563: return namespace;
564: }
565:
566: /**
567: * Splits a nodeName into a prefix and a localName
568: *
569: * @return an array containing two elements, the first one is the prefix (can be null), the
570: * second one is the localName
571: */
572: private String[] getPrefixAndLocalName(String nodeName) {
573: String prefix, localName;
574: int colonPos = nodeName.indexOf(":");
575: if (colonPos != -1) {
576: prefix = nodeName.substring(0, colonPos);
577: localName = nodeName.substring(colonPos + 1, nodeName
578: .length());
579: } else {
580: prefix = null;
581: localName = nodeName;
582: }
583: return new String[] { prefix, localName };
584: }
585:
586: /**
587: * End processing of given node
588: *
589: * @param node Node we just finished processing
590: */
591: protected void endNode(Node node)
592: throws org.xml.sax.SAXException {
593:
594: switch (node.getNodeType()) {
595: case Node.ELEMENT_NODE:
596: contentHandler.endElement(
597: currentElementInfo.namespaceURI,
598: currentElementInfo.localName,
599: currentElementInfo.qName);
600:
601: // generate endPrefixMapping events if needed
602: if (currentElementInfo.namespaceDeclarations != null
603: && currentElementInfo.namespaceDeclarations
604: .size() > 0) {
605: Iterator namespaceIt = currentElementInfo.namespaceDeclarations
606: .entrySet().iterator();
607: while (namespaceIt.hasNext()) {
608: Map.Entry entry = (Map.Entry) namespaceIt
609: .next();
610: contentHandler.endPrefixMapping((String) entry
611: .getKey());
612: //System.out.println("ending prefix mapping " + (String) entry.getKey());
613: }
614: }
615: currentElementInfo = currentElementInfo.parent;
616: break;
617: case Node.DOCUMENT_NODE:
618: case Node.CDATA_SECTION_NODE:
619: break;
620: case Node.ENTITY_REFERENCE_NODE: {
621: EntityReference eref = (EntityReference) node;
622:
623: if (lexicalHandler != null) {
624: lexicalHandler.endEntity(eref.getNodeName());
625: }
626: }
627: break;
628: default:
629: }
630: }
631:
632: public static class ElementInfo {
633: public String localName;
634: public String namespaceURI;
635: public String qName;
636: public Map namespaceDeclarations = null;
637: public ElementInfo parent;
638:
639: public ElementInfo(ElementInfo parent) {
640: this .parent = parent;
641: }
642:
643: /**
644: * Declare a new namespace prefix on this element, possibly overriding
645: * an existing one.
646: */
647: public void put(String prefix, String namespaceURI) {
648: if (namespaceDeclarations == null)
649: namespaceDeclarations = new HashMap();
650: namespaceDeclarations.put(prefix, namespaceURI);
651: }
652:
653: /**
654: * Finds a prefix declared on this element.
655: */
656: public String getPrefix(String namespaceURI) {
657: if (namespaceDeclarations == null
658: || namespaceDeclarations.size() == 0)
659: return null;
660: // note: there could be more than one prefix for the same namespaceURI, but
661: // we return the first found one.
662: Iterator it = namespaceDeclarations.entrySet()
663: .iterator();
664: while (it.hasNext()) {
665: Map.Entry entry = (Map.Entry) it.next();
666: if (entry.getValue().equals(namespaceURI))
667: return (String) entry.getKey();
668: }
669: return null;
670: }
671:
672: /**
673: * Finds a namespace URI declared on this element.
674: */
675: public String getNamespaceURI(String prefix) {
676: if (namespaceDeclarations == null
677: || namespaceDeclarations.size() == 0)
678: return null;
679:
680: return (String) namespaceDeclarations.get(prefix);
681: }
682:
683: /**
684: * Finds a prefix declaration on this element or containing elements.
685: */
686: public String findPrefix(String namespaceURI) {
687: if (namespaceDeclarations != null
688: && namespaceDeclarations.size() != 0) {
689: String prefix = getPrefix(namespaceURI);
690: if (prefix != null) {
691: return prefix;
692: }
693: }
694: if (parent != null) {
695: return parent.findPrefix(namespaceURI);
696: }
697: return null;
698: }
699:
700: /**
701: * Finds a namespace declaration on this element or containing elements.
702: */
703: public String findNamespaceURI(String prefix) {
704: if (namespaceDeclarations != null
705: && namespaceDeclarations.size() != 0) {
706: String uri = (String) namespaceDeclarations
707: .get(prefix);
708: if (uri != null) {
709: return uri;
710: }
711: }
712: if (parent != null) {
713: return parent.findNamespaceURI(prefix);
714: }
715: return null;
716: }
717: }
718: }
719:
720: /**
721: * The <code>DefaultDOMStreamer</code> is a utility class that will generate SAX
722: * events from a W3C DOM Document.
723: *
724: * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
725: * @author <a href="mailto:pier@apache.org">Pierpaolo Fumagalli</a>
726: * (Apache Software Foundation)
727: */
728: public static class DefaultDOMStreamer extends AbstractXMLProducer {
729:
730: /** The private transformer for this instance */
731: protected Transformer transformer;
732:
733: /**
734: * Start the production of SAX events.
735: */
736: public void stream(Node node) throws SAXException {
737: if (this .transformer == null) {
738: try {
739: this .transformer = FACTORY.newTransformer();
740: } catch (TransformerConfigurationException e) {
741: throw new SAXException(e);
742: }
743: }
744: DOMSource source = new DOMSource(node);
745:
746: ContentHandler handler;
747: if (node.getNodeType() == Node.DOCUMENT_NODE) {
748: // Pass all SAX events
749: handler = contentHandler;
750: } else {
751: // Strip start/endDocument
752: handler = new EmbeddedXMLPipe(contentHandler);
753: }
754:
755: SAXResult result = new SAXResult(handler);
756: result.setLexicalHandler(lexicalHandler);
757:
758: try {
759: transformer.transform(source, result);
760: } catch (TransformerException e) {
761: throw new SAXException(e);
762: }
763: }
764: }
765: }
|