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: DOM2TO.java,v 1.5 2005/09/28 13:48:44 pvedula Exp $
22   */
23  
24  package com.sun.org.apache.xalan.internal.xsltc.trax;
25  
26  import java.io.IOException;
27  
28  import org.w3c.dom.NamedNodeMap;
29  import org.w3c.dom.Node;
30  import org.w3c.dom.Document;
31  import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
32  import org.xml.sax.ContentHandler;
33  import org.xml.sax.DTDHandler;
34  import org.xml.sax.EntityResolver;
35  import org.xml.sax.ErrorHandler;
36  import org.xml.sax.InputSource;
37  import org.xml.sax.ext.Locator2;
38  import org.xml.sax.SAXException;
39  import org.xml.sax.SAXNotRecognizedException;
40  import org.xml.sax.SAXNotSupportedException;
41  import org.xml.sax.XMLReader;
42  import com.sun.org.apache.xml.internal.serializer.NamespaceMappings;
43  
44  /**
45   * @author Santiago Pericas-Geertsen
46   * @author Sunitha Reddy
47   */
48  public class DOM2TO implements XMLReader, Locator2 {
49  
50      private final static String EMPTYSTRING = "";
51      private static final String XMLNS_PREFIX = "xmlns";
52  
53      /**
54       * A reference to the DOM to be traversed.
55       */
56      private Node _dom;
57  
58      /**
59       * A reference to the output handler receiving the events.
60       */
61      private SerializationHandler _handler;
62  
63  
64      private String xmlVersion = null;
65  
66      private String xmlEncoding = null;
67  
68  
69      public DOM2TO(Node root, SerializationHandler handler) {
70          _dom = root;
71          _handler = handler;
72      }
73  
74      public ContentHandler getContentHandler() {
75          return null;
76      }
77  
78      public void setContentHandler(ContentHandler handler) {
79          // Empty
80      }
81  
82      public void parse(InputSource unused) throws IOException, SAXException {
83          parse(_dom);
84      }
85  
86      public void parse() throws IOException, SAXException {
87  
88          if (_dom != null) {
89              boolean isIncomplete =
90                  (_dom.getNodeType() != org.w3c.dom.Node.DOCUMENT_NODE);
91  
92              if (isIncomplete) {
93                  _handler.startDocument();
94                  parse(_dom);
95                  _handler.endDocument();
96              }
97              else {
98                  parse(_dom);
99              }
100         }
101     }
102 
103     /**
104      * Traverse the DOM and generate TO events for a handler. Notice that
105      * we need to handle implicit namespace declarations too.
106      */
107     private void parse(Node node)
108         throws IOException, SAXException
109     {
110         if (node == null) return;
111 
112         switch (node.getNodeType()) {
113         case Node.ATTRIBUTE_NODE:         // handled by ELEMENT_NODE
114         case Node.DOCUMENT_TYPE_NODE :
115         case Node.ENTITY_NODE :
116         case Node.ENTITY_REFERENCE_NODE:
117         case Node.NOTATION_NODE :
118             // These node types are ignored!!!
119             break;
120         case Node.CDATA_SECTION_NODE:
121             _handler.startCDATA();
122             _handler.characters(node.getNodeValue());
123             _handler.endCDATA();
124             break;
125 
126         case Node.COMMENT_NODE:           // should be handled!!!
127             _handler.comment(node.getNodeValue());
128             break;
129 
130         case Node.DOCUMENT_NODE:
131              setDocumentInfo((Document)node);
132              _handler.setDocumentLocator(this);
133              _handler.startDocument();
134             Node next = node.getFirstChild();
135             while (next != null) {
136                 parse(next);
137                 next = next.getNextSibling();
138             }
139             _handler.endDocument();
140             break;
141 
142         case Node.DOCUMENT_FRAGMENT_NODE:
143             next = node.getFirstChild();
144             while (next != null) {
145                 parse(next);
146                 next = next.getNextSibling();
147             }
148             break;
149 
150         case Node.ELEMENT_NODE:
151             // Generate SAX event to start element
152             final String qname = node.getNodeName();
153             _handler.startElement(null, null, qname);
154 
155             int colon;
156             String prefix;
157             final NamedNodeMap map = node.getAttributes();
158             final int length = map.getLength();
159 
160             // Process all namespace attributes first
161             for (int i = 0; i < length; i++) {
162                 final Node attr = map.item(i);
163                 final String qnameAttr = attr.getNodeName();
164 
165                 // Is this a namespace declaration?
166                 if (qnameAttr.startsWith(XMLNS_PREFIX)) {
167                     final String uriAttr = attr.getNodeValue();
168                     colon = qnameAttr.lastIndexOf(':');
169                     prefix = (colon > 0) ? qnameAttr.substring(colon + 1)
170                                          : EMPTYSTRING;
171                     _handler.namespaceAfterStartElement(prefix, uriAttr);
172                 }
173             }
174 
175             // Process all non-namespace attributes next
176             NamespaceMappings nm = new NamespaceMappings();
177             for (int i = 0; i < length; i++) {
178                 final Node attr = map.item(i);
179                 final String qnameAttr = attr.getNodeName();
180 
181                 // Is this a regular attribute?
182                 if (!qnameAttr.startsWith(XMLNS_PREFIX)) {
183                     final String uriAttr = attr.getNamespaceURI();
184                     // Uri may be implicitly declared
185                     if (uriAttr != null && !uriAttr.equals(EMPTYSTRING) ) {
186                         colon = qnameAttr.lastIndexOf(':');
187 
188                         // Fix for bug 26319
189                         // For attributes not given an prefix explictly
190                         // but having a namespace uri we need
191                         // to explicitly generate the prefix
192                         String newPrefix = nm.lookupPrefix(uriAttr);
193                         if (newPrefix == null)
194                             newPrefix = nm.generateNextPrefix();
195                         prefix = (colon > 0) ? qnameAttr.substring(0, colon)
196                             : newPrefix;
197                         _handler.namespaceAfterStartElement(prefix, uriAttr);
198                         _handler.addAttribute((prefix + ":" + qnameAttr),
199                             attr.getNodeValue());
200                     } else {
201                          _handler.addAttribute(qnameAttr, attr.getNodeValue());
202                     }
203                 }
204             }
205 
206             // Now element namespace and children
207             final String uri = node.getNamespaceURI();
208             final String localName = node.getLocalName();
209 
210             // Uri may be implicitly declared
211             if (uri != null) {
212                 colon = qname.lastIndexOf(':');
213                 prefix = (colon > 0) ? qname.substring(0, colon) : EMPTYSTRING;
214                 _handler.namespaceAfterStartElement(prefix, uri);
215             }else {
216                   // Fix for bug 26319
217                   // If an element foo is created using
218                   // createElementNS(null,locName)
219                   // then the  element should be serialized
220                   // <foo xmlns=" "/>
221                   if (uri == null  && localName != null) {
222                      prefix = EMPTYSTRING;
223                      _handler.namespaceAfterStartElement(prefix, EMPTYSTRING);
224                  }
225             }
226 
227             // Traverse all child nodes of the element (if any)
228             next = node.getFirstChild();
229             while (next != null) {
230                 parse(next);
231                 next = next.getNextSibling();
232             }
233 
234             // Generate SAX event to close element
235             _handler.endElement(qname);
236             break;
237 
238         case Node.PROCESSING_INSTRUCTION_NODE:
239             _handler.processingInstruction(node.getNodeName(),
240                                            node.getNodeValue());
241             break;
242 
243         case Node.TEXT_NODE:
244             _handler.characters(node.getNodeValue());
245             break;
246         }
247     }
248 
249     /**
250      * This class is only used internally so this method should never
251      * be called.
252      */
253     public DTDHandler getDTDHandler() {
254         return null;
255     }
256 
257     /**
258      * This class is only used internally so this method should never
259      * be called.
260      */
261     public ErrorHandler getErrorHandler() {
262         return null;
263     }
264 
265     /**
266      * This class is only used internally so this method should never
267      * be called.
268      */
269     public boolean getFeature(String name) throws SAXNotRecognizedException,
270         SAXNotSupportedException
271     {
272         return false;
273     }
274 
275     /**
276      * This class is only used internally so this method should never
277      * be called.
278      */
279     public void setFeature(String name, boolean value) throws
280         SAXNotRecognizedException, SAXNotSupportedException
281     {
282     }
283 
284     /**
285      * This class is only used internally so this method should never
286      * be called.
287      */
288     public void parse(String sysId) throws IOException, SAXException {
289         throw new IOException("This method is not yet implemented.");
290     }
291 
292     /**
293      * This class is only used internally so this method should never
294      * be called.
295      */
296     public void setDTDHandler(DTDHandler handler) throws NullPointerException {
297     }
298 
299     /**
300      * This class is only used internally so this method should never
301      * be called.
302      */
303     public void setEntityResolver(EntityResolver resolver) throws
304         NullPointerException
305     {
306     }
307 
308     /**
309      * This class is only used internally so this method should never
310      * be called.
311      */
312     public EntityResolver getEntityResolver() {
313         return null;
314     }
315 
316     /**
317      * This class is only used internally so this method should never
318      * be called.
319      */
320     public void setErrorHandler(ErrorHandler handler) throws
321         NullPointerException
322     {
323     }
324 
325     /**
326      * This class is only used internally so this method should never
327      * be called.
328      */
329     public void setProperty(String name, Object value) throws
330         SAXNotRecognizedException, SAXNotSupportedException {
331     }
332 
333     /**
334      * This class is only used internally so this method should never
335      * be called.
336      */
337     public Object getProperty(String name) throws SAXNotRecognizedException,
338         SAXNotSupportedException
339     {
340         return null;
341     }
342 
343     /**
344      * This class is only used internally so this method should never
345      * be called.
346      */
347     public int getColumnNumber() {
348         return 0;
349     }
350 
351     /**
352      * This class is only used internally so this method should never
353      * be called.
354      */
355     public int getLineNumber() {
356         return 0;
357     }
358 
359     /**
360      * This class is only used internally so this method should never
361      * be called.
362      */
363     public String getPublicId() {
364         return null;
365     }
366 
367     /**
368      * This class is only used internally so this method should never
369      * be called.
370      */
371     public String getSystemId() {
372         return null;
373     }
374 
375 
376     private void setDocumentInfo(Document document) {
377         if (!document.getXmlStandalone())
378             _handler.setStandalone(Boolean.toString(document.getXmlStandalone()));
379         setXMLVersion(document.getXmlVersion());
380         setEncoding(document.getXmlEncoding());
381     }
382 
383     public String getXMLVersion() {
384         return xmlVersion;
385     }
386 
387     private void setXMLVersion(String version) {
388         if (version != null) {
389             xmlVersion = version;
390             _handler.setVersion(xmlVersion);
391         }
392     }
393 
394     public String getEncoding() {
395         return xmlEncoding;
396     }
397 
398     private void setEncoding(String encoding) {
399         if (encoding != null) {
400             xmlEncoding = encoding;
401             _handler.setEncoding(encoding);
402         }
403     }
404 
405     // Debugging
406     private String getNodeTypeFromCode(short code) {
407         String retval = null;
408         switch (code) {
409         case Node.ATTRIBUTE_NODE :
410             retval = "ATTRIBUTE_NODE"; break;
411         case Node.CDATA_SECTION_NODE :
412             retval = "CDATA_SECTION_NODE"; break;
413         case Node.COMMENT_NODE :
414             retval = "COMMENT_NODE"; break;
415         case Node.DOCUMENT_FRAGMENT_NODE :
416             retval = "DOCUMENT_FRAGMENT_NODE"; break;
417         case Node.DOCUMENT_NODE :
418             retval = "DOCUMENT_NODE"; break;
419         case Node.DOCUMENT_TYPE_NODE :
420             retval = "DOCUMENT_TYPE_NODE"; break;
421         case Node.ELEMENT_NODE :
422             retval = "ELEMENT_NODE"; break;
423         case Node.ENTITY_NODE :
424             retval = "ENTITY_NODE"; break;
425         case Node.ENTITY_REFERENCE_NODE :
426             retval = "ENTITY_REFERENCE_NODE"; break;
427         case Node.NOTATION_NODE :
428             retval = "NOTATION_NODE"; break;
429         case Node.PROCESSING_INSTRUCTION_NODE :
430             retval = "PROCESSING_INSTRUCTION_NODE"; break;
431         case Node.TEXT_NODE:
432             retval = "TEXT_NODE"; break;
433         }
434         return retval;
435     }
436 }