View Javadoc
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 }