1/*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
4 */
5/*
6 * Licensed to the Apache Software Foundation (ASF) under one or more
7 * contributor license agreements.  See the NOTICE file distributed with
8 * this work for additional information regarding copyright ownership.
9 * The ASF licenses this file to You under the Apache License, Version 2.0
10 * (the "License"); you may not use this file except in compliance with
11 * the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 */
21
22package com.sun.org.apache.xalan.internal.xsltc.trax;
23
24import java.io.IOException;
25
26import org.w3c.dom.NamedNodeMap;
27import org.w3c.dom.Node;
28import org.w3c.dom.Document;
29import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
30import org.xml.sax.ContentHandler;
31import org.xml.sax.DTDHandler;
32import org.xml.sax.EntityResolver;
33import org.xml.sax.ErrorHandler;
34import org.xml.sax.InputSource;
35import org.xml.sax.ext.Locator2;
36import org.xml.sax.SAXException;
37import org.xml.sax.SAXNotRecognizedException;
38import org.xml.sax.SAXNotSupportedException;
39import org.xml.sax.XMLReader;
40import com.sun.org.apache.xml.internal.serializer.NamespaceMappings;
41
42/**
43 * @author Santiago Pericas-Geertsen
44 * @author Sunitha Reddy
45 */
46public 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
67    public DOM2TO(Node root, SerializationHandler handler) {
68        _dom = root;
69        _handler = handler;
70    }
71
72    public ContentHandler getContentHandler() {
73        return null;
74    }
75
76    public void setContentHandler(ContentHandler handler) {
77        // Empty
78    }
79
80    public void parse(InputSource unused) throws IOException, SAXException {
81        parse(_dom);
82    }
83
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     */
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 = new NamespaceMappings();
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                        String newPrefix = nm.lookupPrefix(uriAttr);
191                        if (newPrefix == null)
192                            newPrefix = nm.generateNextPrefix();
193                        prefix = (colon > 0) ? qnameAttr.substring(0, colon)
194                            : newPrefix;
195                        _handler.namespaceAfterStartElement(prefix, uriAttr);
196                        _handler.addAttribute((prefix + ":" + qnameAttr),
197                            attr.getNodeValue());
198                    } else {
199                         _handler.addAttribute(qnameAttr, attr.getNodeValue());
200                    }
201                }
202            }
203
204            // Now element namespace and children
205            final String uri = node.getNamespaceURI();
206            final String localName = node.getLocalName();
207
208            // Uri may be implicitly declared
209            if (uri != null) {
210                colon = qname.lastIndexOf(':');
211                prefix = (colon > 0) ? qname.substring(0, colon) : EMPTYSTRING;
212                _handler.namespaceAfterStartElement(prefix, uri);
213            }else {
214                  // Fix for bug 26319
215                  // If an element foo is created using
216                  // createElementNS(null,locName)
217                  // then the  element should be serialized
218                  // <foo xmlns=" "/>
219                  if (uri == null  && localName != null) {
220                     prefix = EMPTYSTRING;
221                     _handler.namespaceAfterStartElement(prefix, EMPTYSTRING);
222                 }
223            }
224
225            // Traverse all child nodes of the element (if any)
226            next = node.getFirstChild();
227            while (next != null) {
228                parse(next);
229                next = next.getNextSibling();
230            }
231
232            // Generate SAX event to close element
233            _handler.endElement(qname);
234            break;
235
236        case Node.PROCESSING_INSTRUCTION_NODE:
237            _handler.processingInstruction(node.getNodeName(),
238                                           node.getNodeValue());
239            break;
240
241        case Node.TEXT_NODE:
242            _handler.characters(node.getNodeValue());
243            break;
244        }
245    }
246
247    /**
248     * This class is only used internally so this method should never
249     * be called.
250     */
251    public DTDHandler getDTDHandler() {
252        return null;
253    }
254
255    /**
256     * This class is only used internally so this method should never
257     * be called.
258     */
259    public ErrorHandler getErrorHandler() {
260        return null;
261    }
262
263    /**
264     * This class is only used internally so this method should never
265     * be called.
266     */
267    public boolean getFeature(String name) throws SAXNotRecognizedException,
268        SAXNotSupportedException
269    {
270        return false;
271    }
272
273    /**
274     * This class is only used internally so this method should never
275     * be called.
276     */
277    public void setFeature(String name, boolean value) throws
278        SAXNotRecognizedException, SAXNotSupportedException
279    {
280    }
281
282    /**
283     * This class is only used internally so this method should never
284     * be called.
285     */
286    public void parse(String sysId) throws IOException, SAXException {
287        throw new IOException("This method is not yet implemented.");
288    }
289
290    /**
291     * This class is only used internally so this method should never
292     * be called.
293     */
294    public void setDTDHandler(DTDHandler handler) throws NullPointerException {
295    }
296
297    /**
298     * This class is only used internally so this method should never
299     * be called.
300     */
301    public void setEntityResolver(EntityResolver resolver) throws
302        NullPointerException
303    {
304    }
305
306    /**
307     * This class is only used internally so this method should never
308     * be called.
309     */
310    public EntityResolver getEntityResolver() {
311        return null;
312    }
313
314    /**
315     * This class is only used internally so this method should never
316     * be called.
317     */
318    public void setErrorHandler(ErrorHandler handler) throws
319        NullPointerException
320    {
321    }
322
323    /**
324     * This class is only used internally so this method should never
325     * be called.
326     */
327    public void setProperty(String name, Object value) throws
328        SAXNotRecognizedException, SAXNotSupportedException {
329    }
330
331    /**
332     * This class is only used internally so this method should never
333     * be called.
334     */
335    public Object getProperty(String name) throws SAXNotRecognizedException,
336        SAXNotSupportedException
337    {
338        return null;
339    }
340
341    /**
342     * This class is only used internally so this method should never
343     * be called.
344     */
345    public int getColumnNumber() {
346        return 0;
347    }
348
349    /**
350     * This class is only used internally so this method should never
351     * be called.
352     */
353    public int getLineNumber() {
354        return 0;
355    }
356
357    /**
358     * This class is only used internally so this method should never
359     * be called.
360     */
361    public String getPublicId() {
362        return null;
363    }
364
365    /**
366     * This class is only used internally so this method should never
367     * be called.
368     */
369    public String getSystemId() {
370        return null;
371    }
372
373
374    private void setDocumentInfo(Document document) {
375        if (!document.getXmlStandalone())
376            _handler.setStandalone(Boolean.toString(document.getXmlStandalone()));
377        setXMLVersion(document.getXmlVersion());
378        setEncoding(document.getXmlEncoding());
379    }
380
381    public String getXMLVersion() {
382        return xmlVersion;
383    }
384
385    private void setXMLVersion(String version) {
386        if (version != null) {
387            xmlVersion = version;
388            _handler.setVersion(xmlVersion);
389        }
390    }
391
392    public String getEncoding() {
393        return xmlEncoding;
394    }
395
396    private void setEncoding(String encoding) {
397        if (encoding != null) {
398            xmlEncoding = encoding;
399            _handler.setEncoding(encoding);
400        }
401    }
402
403    // Debugging
404    private String getNodeTypeFromCode(short code) {
405        String retval = null;
406        switch (code) {
407        case Node.ATTRIBUTE_NODE :
408            retval = "ATTRIBUTE_NODE"; break;
409        case Node.CDATA_SECTION_NODE :
410            retval = "CDATA_SECTION_NODE"; break;
411        case Node.COMMENT_NODE :
412            retval = "COMMENT_NODE"; break;
413        case Node.DOCUMENT_FRAGMENT_NODE :
414            retval = "DOCUMENT_FRAGMENT_NODE"; break;
415        case Node.DOCUMENT_NODE :
416            retval = "DOCUMENT_NODE"; break;
417        case Node.DOCUMENT_TYPE_NODE :
418            retval = "DOCUMENT_TYPE_NODE"; break;
419        case Node.ELEMENT_NODE :
420            retval = "ELEMENT_NODE"; break;
421        case Node.ENTITY_NODE :
422            retval = "ENTITY_NODE"; break;
423        case Node.ENTITY_REFERENCE_NODE :
424            retval = "ENTITY_REFERENCE_NODE"; break;
425        case Node.NOTATION_NODE :
426            retval = "NOTATION_NODE"; break;
427        case Node.PROCESSING_INSTRUCTION_NODE :
428            retval = "PROCESSING_INSTRUCTION_NODE"; break;
429        case Node.TEXT_NODE:
430            retval = "TEXT_NODE"; break;
431        }
432        return retval;
433    }
434}
435