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