001: package net.sf.saxon.dom;
002:
003: import net.sf.saxon.Configuration;
004: import net.sf.saxon.event.PipelineConfiguration;
005: import net.sf.saxon.event.Receiver;
006: import net.sf.saxon.event.SaxonLocator;
007: import net.sf.saxon.om.NameChecker;
008: import net.sf.saxon.om.NamePool;
009: import net.sf.saxon.style.StandardNames;
010: import net.sf.saxon.trans.DynamicError;
011: import net.sf.saxon.trans.XPathException;
012: import org.w3c.dom.*;
013: import org.xml.sax.helpers.AttributesImpl;
014: import org.xml.sax.helpers.NamespaceSupport;
015:
016: import java.util.HashMap;
017: import java.util.Iterator;
018:
019: /**
020: * DOMSender.java: pseudo-SAX driver for a DOM source document.
021: * This class takes an existing
022: * DOM Document and walks around it in a depth-first traversal,
023: * calling a Receiver to process the nodes as it does so
024: */
025:
026: public class DOMSender implements SaxonLocator {
027: private Receiver receiver;
028: private PipelineConfiguration pipe;
029:
030: private NamespaceSupport nsSupport = new NamespaceSupport();
031: private AttributesImpl attlist = new AttributesImpl();
032: private String[] parts = new String[3];
033: private String[] elparts = new String[3];
034: private HashMap nsDeclarations = new HashMap(10);
035: protected Node root = null;
036: protected String systemId;
037:
038: /**
039: * Set the pipeline configuration
040: */
041:
042: public void setPipelineConfiguration(PipelineConfiguration pipe) {
043: this .pipe = pipe;
044: }
045:
046: /**
047: * Set the receiver.
048: * @param receiver The object to receive content events.
049: */
050:
051: public void setReceiver(Receiver receiver) {
052: this .receiver = receiver;
053: }
054:
055: /**
056: * Set the DOM Document that will be walked
057: */
058:
059: public void setStartNode(Node start) {
060: root = start;
061: }
062:
063: /**
064: * Set the systemId of the source document (which will also be
065: * used for the destination)
066: */
067:
068: public void setSystemId(String systemId) {
069: this .systemId = systemId;
070: }
071:
072: /**
073: * Walk a document (traversing the nodes depth first)
074: * @exception net.sf.saxon.trans.XPathException On any error in the document
075: */
076:
077: public void send() throws XPathException {
078: if (root == null) {
079: throw new DynamicError("DOMSender: no start node defined");
080: }
081: if (receiver == null) {
082: throw new DynamicError("DOMSender: no receiver defined");
083: }
084:
085: receiver.setSystemId(systemId);
086: pipe.setLocationProvider(this );
087: receiver.setPipelineConfiguration(pipe);
088:
089: receiver.open();
090: if (root.getNodeType() == Node.ELEMENT_NODE) {
091: sendElement((Element) root);
092: } else {
093: // walk the root node
094: receiver.startDocument(0);
095: walkNode(root);
096: receiver.endDocument();
097: }
098: receiver.close();
099: }
100:
101: /**
102: * Walk a document starting from a particular element node. This has to make
103: * sure that all the namespace declarations in scope for the element are
104: * treated as if they were namespace declarations on the element itself.
105: */
106:
107: private void sendElement(Element startNode) throws XPathException {
108: Element node = startNode;
109: NamedNodeMap topAtts = gatherNamespaces(node, false);
110: while (true) {
111: gatherNamespaces(node, true);
112: Node parent = node.getParentNode();
113: if (parent != null
114: && parent.getNodeType() == Node.ELEMENT_NODE) {
115: node = (Element) parent;
116: } else {
117: break;
118: }
119: }
120: outputElement(startNode, topAtts);
121: }
122:
123: /**
124: * Walk an element of a document (traversing the children depth first)
125: * @param node The DOM Element object to walk
126: * @exception net.sf.saxon.trans.XPathException On any error in the document
127: *
128: */
129:
130: private void walkNode(Node node) throws XPathException {
131: if (node.hasChildNodes()) {
132: NodeList nit = node.getChildNodes();
133: for (int i = 0; i < nit.getLength(); i++) {
134: Node child = nit.item(i);
135: switch (child.getNodeType()) {
136: case Node.DOCUMENT_NODE:
137: break; // should not happen
138: case Node.ELEMENT_NODE:
139: Element element = (Element) child;
140: NamedNodeMap atts = gatherNamespaces(element, false);
141:
142: outputElement(element, atts);
143:
144: nsSupport.popContext();
145: break;
146: case Node.ATTRIBUTE_NODE: // have already dealt with attributes
147: break;
148: case Node.PROCESSING_INSTRUCTION_NODE:
149: receiver
150: .processingInstruction(
151: ((ProcessingInstruction) child)
152: .getTarget(),
153: ((ProcessingInstruction) child)
154: .getData(), 0, 0);
155: break;
156: case Node.COMMENT_NODE: {
157: String text = ((Comment) child).getData();
158: if (text != null) {
159: receiver.comment(text, 0, 0);
160: }
161: break;
162: }
163: case Node.TEXT_NODE:
164: case Node.CDATA_SECTION_NODE: {
165: String text = ((CharacterData) child).getData();
166: if (text != null) {
167: receiver.characters(text, 0, 0);
168: }
169: break;
170: }
171: case Node.ENTITY_REFERENCE_NODE:
172: walkNode(child);
173: break;
174: default:
175: break; // should not happen
176: }
177: }
178: }
179:
180: }
181:
182: private void outputElement(Element element, NamedNodeMap atts)
183: throws XPathException {
184: final Configuration config = pipe.getConfiguration();
185: String[] elparts2 = nsSupport.processName(element.getTagName(),
186: elparts, false);
187: if (elparts2 == null) {
188: throw new DynamicError("Undeclared namespace in "
189: + element.getTagName());
190: }
191: String uri = elparts2[0];
192: String local = elparts2[1];
193: String prefix = NameChecker.getPrefix(elparts2[2]);
194:
195: NamePool namePool = config.getNamePool();
196: int nameCode = namePool.allocate(prefix, uri, local);
197:
198: receiver
199: .startElement(nameCode, StandardNames.XDT_UNTYPED, 0, 0);
200: for (Iterator iter = nsDeclarations.keySet().iterator(); iter
201: .hasNext();) {
202: String nsprefix = (String) iter.next();
203: String nsuri = (String) nsDeclarations.get(nsprefix);
204: receiver.namespace(namePool.allocateNamespaceCode(nsprefix,
205: nsuri), 0);
206: }
207:
208: if (atts != null) {
209: for (int a2 = 0; a2 < atts.getLength(); a2++) {
210: Attr att = (Attr) atts.item(a2);
211: String attname = att.getName();
212: if (attname.startsWith("xmlns")
213: && (attname.equals("xmlns") || attname
214: .startsWith("xmlns:"))) {
215: // do nothing
216: } else {
217: //System.err.println("Processing attribute " + attname);
218: String[] parts2 = nsSupport.processName(attname,
219: parts, true);
220: if (parts2 == null) {
221: throw new DynamicError(
222: "Undeclared namespace in " + attname);
223: }
224: String atturi = parts2[0];
225: String attlocal = parts2[1];
226: String attprefix = NameChecker.getPrefix(parts2[2]);
227:
228: int attCode = namePool.allocate(attprefix, atturi,
229: attlocal);
230:
231: receiver.attribute(attCode,
232: StandardNames.XDT_UNTYPED_ATOMIC, att
233: .getValue(), 0, 0);
234: }
235: }
236: }
237: receiver.startContent();
238:
239: walkNode(element);
240:
241: receiver.endElement();
242: }
243:
244: /**
245: * Collect all the namespace attributes in scope for a given element
246: * @param element
247: * @param cumulative
248: * @return
249: */
250:
251: private NamedNodeMap gatherNamespaces(Element element,
252: boolean cumulative) {
253: if (!cumulative) {
254: nsSupport.pushContext();
255: attlist.clear();
256: nsDeclarations.clear();
257: }
258:
259: // we can't rely on namespace declaration attributes being present -
260: // there may be undeclared namespace prefixes. (If the DOM is a Saxon
261: // tree, there will be no namespace declaration attributes.) So we
262: // declare all namespaces encountered, to be on the safe side.
263:
264: try {
265: String prefix = element.getPrefix();
266: String uri = element.getNamespaceURI();
267: if (prefix == null)
268: prefix = "";
269: if (uri == null)
270: uri = "";
271: //System.err.println("Implicit Namespace: " + prefix + "=" + uri);
272: if (nsDeclarations.get(prefix) == null) {
273: nsSupport.declarePrefix(prefix, uri);
274: nsDeclarations.put(prefix, uri);
275: }
276: } catch (Throwable err) {
277: // it must be a level 1 DOM
278: }
279:
280: NamedNodeMap atts = element.getAttributes();
281:
282: // Apparently the Oracle DOM returns null if there are no attributes:
283: if (atts == null) {
284: return null;
285: }
286: for (int a1 = 0; a1 < atts.getLength(); a1++) {
287: Attr att = (Attr) atts.item(a1);
288: String attname = att.getName();
289: if (attname.equals("xmlns")) {
290: //System.err.println("Default namespace: " + att.getValue());
291: if (nsDeclarations.get("") == null) {
292: String uri = att.getValue();
293: nsSupport.declarePrefix("", uri);
294: nsDeclarations.put("", uri);
295: }
296: } else if (attname.startsWith("xmlns:")) {
297: //System.err.println("Namespace: " + attname.substring(6) + "=" + att.getValue());
298: String prefix = attname.substring(6);
299: if (nsDeclarations.get(prefix) == null) {
300: String uri = att.getValue();
301: nsSupport.declarePrefix(prefix, uri);
302: nsDeclarations.put(prefix, uri);
303: }
304: } else if (attname.indexOf(':') >= 0) {
305: try {
306: String prefix = att.getPrefix();
307: String uri = att.getNamespaceURI();
308: //System.err.println("Implicit Namespace: " + prefix + "=" + uri);
309: if (nsDeclarations.get(prefix) == null) {
310: nsSupport.declarePrefix(prefix, uri);
311: //contentHandler.startPrefixMapping(prefix, uri);
312: nsDeclarations.put(prefix, uri);
313: }
314: } catch (Throwable err) {
315: // it must be a level 1 DOM
316: }
317: }
318: }
319: return atts;
320: }
321:
322: // Implement the SAX Locator interface. This is needed to pass the base URI of nodes
323: // to the receiver. We don't attempt to preserve the original base URI of each individual
324: // node as it is copied, only the base URI of the document as a whole.
325:
326: public int getColumnNumber() {
327: return -1;
328: }
329:
330: public int getLineNumber() {
331: return -1;
332: }
333:
334: public String getPublicId() {
335: return null;
336: }
337:
338: public String getSystemId() {
339: return systemId;
340: }
341:
342: public String getSystemId(int locationId) {
343: return getSystemId();
344: }
345:
346: public int getLineNumber(int locationId) {
347: return getLineNumber();
348: }
349:
350: // public static void main(String[] args) throws Exception {
351: // Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
352: // Element keyInfo = doc.createElement("plughole");
353: // StringWriter sw = new StringWriter();
354: // Transformer transformer = TransformerFactory.newInstance().newTransformer();
355: // transformer.transform(new DOMSource(keyInfo), new StreamResult(sw));
356: // sw.close();
357: // System.out.println(sw.toString());
358: //
359: // }
360:
361: }
362: //
363: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
364: // you may not use this file except in compliance with the License. You may obtain a copy of the
365: // License at http://www.mozilla.org/MPL/
366: //
367: // Software distributed under the License is distributed on an "AS IS" basis,
368: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
369: // See the License for the specific language governing rights and limitations under the License.
370: //
371: // The Original Code is: all this file.
372: //
373: // The Initial Developer of the Original Code is Michael H. Kay.
374: //
375: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
376: //
377: // Contributor(s): none.
378: //
|