1 /*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
4 */
5 /*
6 * Copyright 2001-2004 The Apache Software Foundation.
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20 /*
21 * $Id: DOM2SAX.java,v 1.2.4.1 2005/09/06 11:52:46 pvedula Exp $
22 */
23
24
25 package com.sun.org.apache.xalan.internal.xsltc.trax;
26
27 import java.io.IOException;
28 import java.util.Hashtable;
29 import java.util.Stack;
30 import java.util.Vector;
31
32 import org.w3c.dom.NamedNodeMap;
33 import org.w3c.dom.Node;
34
35 import org.xml.sax.ContentHandler;
36 import org.xml.sax.DTDHandler;
37 import org.xml.sax.EntityResolver;
38 import org.xml.sax.ErrorHandler;
39 import org.xml.sax.InputSource;
40 import org.xml.sax.Locator;
41 import org.xml.sax.SAXException;
42 import org.xml.sax.SAXNotRecognizedException;
43 import org.xml.sax.SAXNotSupportedException;
44 import org.xml.sax.XMLReader;
45 import org.xml.sax.ext.LexicalHandler;
46 import org.xml.sax.helpers.AttributesImpl;
47 import com.sun.org.apache.xalan.internal.xsltc.dom.SAXImpl;
48 import com.sun.org.apache.xalan.internal.xsltc.runtime.BasisLibrary;
49
50 /**
51 * @author G. Todd Miller
52 */
53 public class DOM2SAX implements XMLReader, Locator {
54
55 private final static String EMPTYSTRING = "";
56 private static final String XMLNS_PREFIX = "xmlns";
57
58 private Node _dom = null;
59 private ContentHandler _sax = null;
60 private LexicalHandler _lex = null;
61 private SAXImpl _saxImpl = null;
62 private Hashtable _nsPrefixes = new Hashtable();
63
64 public DOM2SAX(Node root) {
65 _dom = root;
66 }
67
68 public ContentHandler getContentHandler() {
69 return _sax;
70 }
71
72 public void setContentHandler(ContentHandler handler) throws
73 NullPointerException
74 {
75 _sax = handler;
76 if (handler instanceof LexicalHandler) {
77 _lex = (LexicalHandler) handler;
78 }
79
80 if (handler instanceof SAXImpl) {
81 _saxImpl = (SAXImpl)handler;
82 }
83 }
84
85 /**
86 * Begin the scope of namespace prefix. Forward the event to the
87 * SAX handler only if the prefix is unknown or it is mapped to a
88 * different URI.
89 */
90 private boolean startPrefixMapping(String prefix, String uri)
91 throws SAXException
92 {
93 boolean pushed = true;
94 Stack uriStack = (Stack) _nsPrefixes.get(prefix);
95
96 if (uriStack != null) {
97 if (uriStack.isEmpty()) {
98 _sax.startPrefixMapping(prefix, uri);
99 uriStack.push(uri);
100 }
101 else {
102 final String lastUri = (String) uriStack.peek();
103 if (!lastUri.equals(uri)) {
104 _sax.startPrefixMapping(prefix, uri);
105 uriStack.push(uri);
106 }
107 else {
108 pushed = false;
109 }
110 }
111 }
112 else {
113 _sax.startPrefixMapping(prefix, uri);
114 _nsPrefixes.put(prefix, uriStack = new Stack());
115 uriStack.push(uri);
116 }
117 return pushed;
118 }
119
120 /*
121 * End the scope of a name prefix by popping it from the stack and
122 * passing the event to the SAX Handler.
123 */
124 private void endPrefixMapping(String prefix)
125 throws SAXException
126 {
127 final Stack uriStack = (Stack) _nsPrefixes.get(prefix);
128
129 if (uriStack != null) {
130 _sax.endPrefixMapping(prefix);
131 uriStack.pop();
132 }
133 }
134
135 /**
136 * If the DOM was created using a DOM 1.0 API, the local name may be
137 * null. If so, get the local name from the qualified name before
138 * generating the SAX event.
139 */
140 private static String getLocalName(Node node) {
141 final String localName = node.getLocalName();
142
143 if (localName == null) {
144 final String qname = node.getNodeName();
145 final int col = qname.lastIndexOf(':');
146 return (col > 0) ? qname.substring(col + 1) : qname;
147 }
148 return localName;
149 }
150
151 public void parse(InputSource unused) throws IOException, SAXException {
152 parse(_dom);
153 }
154
155 public void parse() throws IOException, SAXException {
156 if (_dom != null) {
157 boolean isIncomplete =
158 (_dom.getNodeType() != org.w3c.dom.Node.DOCUMENT_NODE);
159
160 if (isIncomplete) {
161 _sax.startDocument();
162 parse(_dom);
163 _sax.endDocument();
164 }
165 else {
166 parse(_dom);
167 }
168 }
169 }
170
171 /**
172 * Traverse the DOM and generate SAX events for a handler. A
173 * startElement() event passes all attributes, including namespace
174 * declarations.
175 */
176 private void parse(Node node) throws IOException, SAXException {
177 Node first = null;
178 if (node == null) return;
179
180 switch (node.getNodeType()) {
181 case Node.ATTRIBUTE_NODE: // handled by ELEMENT_NODE
182 case Node.DOCUMENT_FRAGMENT_NODE:
183 case Node.DOCUMENT_TYPE_NODE :
184 case Node.ENTITY_NODE :
185 case Node.ENTITY_REFERENCE_NODE:
186 case Node.NOTATION_NODE :
187 // These node types are ignored!!!
188 break;
189 case Node.CDATA_SECTION_NODE:
190 final String cdata = node.getNodeValue();
191 if (_lex != null) {
192 _lex.startCDATA();
193 _sax.characters(cdata.toCharArray(), 0, cdata.length());
194 _lex.endCDATA();
195 }
196 else {
197 // in the case where there is no lex handler, we still
198 // want the text of the cdate to make its way through.
199 _sax.characters(cdata.toCharArray(), 0, cdata.length());
200 }
201 break;
202
203 case Node.COMMENT_NODE: // should be handled!!!
204 if (_lex != null) {
205 final String value = node.getNodeValue();
206 _lex.comment(value.toCharArray(), 0, value.length());
207 }
208 break;
209 case Node.DOCUMENT_NODE:
210 _sax.setDocumentLocator(this);
211
212 _sax.startDocument();
213 Node next = node.getFirstChild();
214 while (next != null) {
215 parse(next);
216 next = next.getNextSibling();
217 }
218 _sax.endDocument();
219 break;
220
221 case Node.ELEMENT_NODE:
222 String prefix;
223 Vector pushedPrefixes = new Vector();
224 final AttributesImpl attrs = new AttributesImpl();
225 final NamedNodeMap map = node.getAttributes();
226 final int length = map.getLength();
227
228 // Process all namespace declarations
229 for (int i = 0; i < length; i++) {
230 final Node attr = map.item(i);
231 final String qnameAttr = attr.getNodeName();
232
233 // Ignore everything but NS declarations here
234 if (qnameAttr.startsWith(XMLNS_PREFIX)) {
235 final String uriAttr = attr.getNodeValue();
236 final int colon = qnameAttr.lastIndexOf(':');
237 prefix = (colon > 0) ? qnameAttr.substring(colon + 1) : EMPTYSTRING;
238 if (startPrefixMapping(prefix, uriAttr)) {
239 pushedPrefixes.addElement(prefix);
240 }
241 }
242 }
243
244 // Process all other attributes
245 for (int i = 0; i < length; i++) {
246 final Node attr = map.item(i);
247 String qnameAttr = attr.getNodeName();
248
249 // Ignore NS declarations here
250 if (!qnameAttr.startsWith(XMLNS_PREFIX)) {
251 final String uriAttr = attr.getNamespaceURI();
252 final String localNameAttr = getLocalName(attr);
253
254 // Uri may be implicitly declared
255 if (uriAttr != null) {
256 final int colon = qnameAttr.lastIndexOf(':');
257 if (colon > 0) {
258 prefix = qnameAttr.substring(0, colon);
259 }
260 else {
261 // If no prefix for this attr, we need to create
262 // one because we cannot use the default ns
263 prefix = BasisLibrary.generatePrefix();
264 qnameAttr = prefix + ':' + qnameAttr;
265 }
266 if (startPrefixMapping(prefix, uriAttr)) {
267 pushedPrefixes.addElement(prefix);
268 }
269 }
270
271 // Add attribute to list
272 attrs.addAttribute(attr.getNamespaceURI(), getLocalName(attr),
273 qnameAttr, "CDATA", attr.getNodeValue());
274 }
275 }
276
277 // Now process the element itself
278 final String qname = node.getNodeName();
279 final String uri = node.getNamespaceURI();
280 final String localName = getLocalName(node);
281
282 // Uri may be implicitly declared
283 if (uri != null) {
284 final int colon = qname.lastIndexOf(':');
285 prefix = (colon > 0) ? qname.substring(0, colon) : EMPTYSTRING;
286 if (startPrefixMapping(prefix, uri)) {
287 pushedPrefixes.addElement(prefix);
288 }
289 }
290
291 // Generate SAX event to start element
292 if (_saxImpl != null) {
293 _saxImpl.startElement(uri, localName, qname, attrs, node);
294 }
295 else {
296 _sax.startElement(uri, localName, qname, attrs);
297 }
298
299 // Traverse all child nodes of the element (if any)
300 next = node.getFirstChild();
301 while (next != null) {
302 parse(next);
303 next = next.getNextSibling();
304 }
305
306 // Generate SAX event to close element
307 _sax.endElement(uri, localName, qname);
308
309 // Generate endPrefixMapping() for all pushed prefixes
310 final int nPushedPrefixes = pushedPrefixes.size();
311 for (int i = 0; i < nPushedPrefixes; i++) {
312 endPrefixMapping((String) pushedPrefixes.elementAt(i));
313 }
314 break;
315
316 case Node.PROCESSING_INSTRUCTION_NODE:
317 _sax.processingInstruction(node.getNodeName(),
318 node.getNodeValue());
319 break;
320
321 case Node.TEXT_NODE:
322 final String data = node.getNodeValue();
323 _sax.characters(data.toCharArray(), 0, data.length());
324 break;
325 }
326 }
327
328 /**
329 * This class is only used internally so this method should never
330 * be called.
331 */
332 public DTDHandler getDTDHandler() {
333 return null;
334 }
335
336 /**
337 * This class is only used internally so this method should never
338 * be called.
339 */
340 public ErrorHandler getErrorHandler() {
341 return null;
342 }
343
344 /**
345 * This class is only used internally so this method should never
346 * be called.
347 */
348 public boolean getFeature(String name) throws SAXNotRecognizedException,
349 SAXNotSupportedException
350 {
351 return false;
352 }
353
354 /**
355 * This class is only used internally so this method should never
356 * be called.
357 */
358 public void setFeature(String name, boolean value) throws
359 SAXNotRecognizedException, SAXNotSupportedException
360 {
361 }
362
363 /**
364 * This class is only used internally so this method should never
365 * be called.
366 */
367 public void parse(String sysId) throws IOException, SAXException {
368 throw new IOException("This method is not yet implemented.");
369 }
370
371 /**
372 * This class is only used internally so this method should never
373 * be called.
374 */
375 public void setDTDHandler(DTDHandler handler) throws NullPointerException {
376 }
377
378 /**
379 * This class is only used internally so this method should never
380 * be called.
381 */
382 public void setEntityResolver(EntityResolver resolver) throws
383 NullPointerException
384 {
385 }
386
387 /**
388 * This class is only used internally so this method should never
389 * be called.
390 */
391 public EntityResolver getEntityResolver() {
392 return null;
393 }
394
395 /**
396 * This class is only used internally so this method should never
397 * be called.
398 */
399 public void setErrorHandler(ErrorHandler handler) throws
400 NullPointerException
401 {
402 }
403
404 /**
405 * This class is only used internally so this method should never
406 * be called.
407 */
408 public void setProperty(String name, Object value) throws
409 SAXNotRecognizedException, SAXNotSupportedException {
410 }
411
412 /**
413 * This class is only used internally so this method should never
414 * be called.
415 */
416 public Object getProperty(String name) throws SAXNotRecognizedException,
417 SAXNotSupportedException
418 {
419 return null;
420 }
421
422 /**
423 * This class is only used internally so this method should never
424 * be called.
425 */
426 public int getColumnNumber() {
427 return 0;
428 }
429
430 /**
431 * This class is only used internally so this method should never
432 * be called.
433 */
434 public int getLineNumber() {
435 return 0;
436 }
437
438 /**
439 * This class is only used internally so this method should never
440 * be called.
441 */
442 public String getPublicId() {
443 return null;
444 }
445
446 /**
447 * This class is only used internally so this method should never
448 * be called.
449 */
450 public String getSystemId() {
451 return null;
452 }
453
454 // Debugging
455 private String getNodeTypeFromCode(short code) {
456 String retval = null;
457 switch (code) {
458 case Node.ATTRIBUTE_NODE :
459 retval = "ATTRIBUTE_NODE"; break;
460 case Node.CDATA_SECTION_NODE :
461 retval = "CDATA_SECTION_NODE"; break;
462 case Node.COMMENT_NODE :
463 retval = "COMMENT_NODE"; break;
464 case Node.DOCUMENT_FRAGMENT_NODE :
465 retval = "DOCUMENT_FRAGMENT_NODE"; break;
466 case Node.DOCUMENT_NODE :
467 retval = "DOCUMENT_NODE"; break;
468 case Node.DOCUMENT_TYPE_NODE :
469 retval = "DOCUMENT_TYPE_NODE"; break;
470 case Node.ELEMENT_NODE :
471 retval = "ELEMENT_NODE"; break;
472 case Node.ENTITY_NODE :
473 retval = "ENTITY_NODE"; break;
474 case Node.ENTITY_REFERENCE_NODE :
475 retval = "ENTITY_REFERENCE_NODE"; break;
476 case Node.NOTATION_NODE :
477 retval = "NOTATION_NODE"; break;
478 case Node.PROCESSING_INSTRUCTION_NODE :
479 retval = "PROCESSING_INSTRUCTION_NODE"; break;
480 case Node.TEXT_NODE:
481 retval = "TEXT_NODE"; break;
482 }
483 return retval;
484 }
485 }