001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: DOM2TO.java 468653 2006-10-28 07:07:05Z minchau $
020     */
021    
022    package org.apache.xalan.xsltc.trax;
023    
024    import java.io.IOException;
025    
026    import org.w3c.dom.NamedNodeMap;
027    import org.w3c.dom.Node;
028    import org.apache.xml.serializer.SerializationHandler;
029    import org.xml.sax.ContentHandler;
030    import org.xml.sax.DTDHandler;
031    import org.xml.sax.EntityResolver;
032    import org.xml.sax.ErrorHandler;
033    import org.xml.sax.InputSource;
034    import org.xml.sax.Locator;
035    import org.xml.sax.SAXException;
036    import org.xml.sax.SAXNotRecognizedException;
037    import org.xml.sax.SAXNotSupportedException;
038    import org.xml.sax.XMLReader;
039    import org.apache.xml.serializer.NamespaceMappings;
040    
041    /**
042     * @author Santiago Pericas-Geertsen
043     */
044    public class DOM2TO implements XMLReader, Locator {
045    
046        private final static String EMPTYSTRING = "";
047        private static final String XMLNS_PREFIX = "xmlns";
048    
049        /**
050         * A reference to the DOM to be traversed.
051         */
052        private Node _dom;
053    
054        /**
055         * A reference to the output handler receiving the events.
056         */
057        private SerializationHandler _handler;
058    
059        public DOM2TO(Node root, SerializationHandler handler) {
060            _dom = root;
061            _handler = handler;
062        }
063    
064        public ContentHandler getContentHandler() { 
065            return null;
066        }
067    
068        public void setContentHandler(ContentHandler handler) {
069            // Empty
070        }
071    
072        public void parse(InputSource unused) throws IOException, SAXException {
073            parse(_dom);
074        }
075    
076        public void parse() throws IOException, SAXException {
077            if (_dom != null) {
078                boolean isIncomplete = 
079                    (_dom.getNodeType() != org.w3c.dom.Node.DOCUMENT_NODE);
080    
081                if (isIncomplete) {
082                    _handler.startDocument();
083                    parse(_dom);
084                    _handler.endDocument();
085                }
086                else {
087                    parse(_dom);
088                }
089            }
090        }
091    
092        /**
093         * Traverse the DOM and generate TO events for a handler. Notice that 
094         * we need to handle implicit namespace declarations too.
095         */
096        private void parse(Node node) 
097            throws IOException, SAXException 
098        {
099            if (node == null) return;
100    
101            switch (node.getNodeType()) {
102            case Node.ATTRIBUTE_NODE:         // handled by ELEMENT_NODE
103            case Node.DOCUMENT_TYPE_NODE :
104            case Node.ENTITY_NODE :
105            case Node.ENTITY_REFERENCE_NODE:
106            case Node.NOTATION_NODE :
107                // These node types are ignored!!!
108                break;
109            case Node.CDATA_SECTION_NODE:
110                _handler.startCDATA();
111                _handler.characters(node.getNodeValue());
112                _handler.endCDATA();
113                break;
114    
115            case Node.COMMENT_NODE:           // should be handled!!!
116                _handler.comment(node.getNodeValue());
117                break;
118    
119            case Node.DOCUMENT_NODE:
120                _handler.startDocument();
121                Node next = node.getFirstChild();
122                while (next != null) {
123                    parse(next);
124                    next = next.getNextSibling();
125                }
126                _handler.endDocument();
127                break;
128    
129            case Node.DOCUMENT_FRAGMENT_NODE:
130                next = node.getFirstChild();
131                while (next != null) {
132                    parse(next);
133                    next = next.getNextSibling();
134                }
135                break;
136    
137            case Node.ELEMENT_NODE:
138                // Generate SAX event to start element
139                final String qname = node.getNodeName();
140                _handler.startElement(null, null, qname);
141    
142                int colon;
143                String prefix;
144                final NamedNodeMap map = node.getAttributes();
145                final int length = map.getLength();
146    
147                // Process all namespace attributes first
148                for (int i = 0; i < length; i++) {
149                    final Node attr = map.item(i);
150                    final String qnameAttr = attr.getNodeName();
151    
152                    // Is this a namespace declaration?
153                    if (qnameAttr.startsWith(XMLNS_PREFIX)) {
154                        final String uriAttr = attr.getNodeValue();
155                        colon = qnameAttr.lastIndexOf(':');
156                        prefix = (colon > 0) ? qnameAttr.substring(colon + 1) 
157                                             : EMPTYSTRING;
158                        _handler.namespaceAfterStartElement(prefix, uriAttr);
159                    }
160                }
161                
162                // Process all non-namespace attributes next
163                NamespaceMappings nm = new NamespaceMappings();
164                for (int i = 0; i < length; i++) {
165                    final Node attr = map.item(i);
166                    final String qnameAttr = attr.getNodeName();
167    
168                    // Is this a regular attribute?
169                    if (!qnameAttr.startsWith(XMLNS_PREFIX)) {
170                        final String uriAttr = attr.getNamespaceURI();
171                        // Uri may be implicitly declared
172                        if (uriAttr != null && !uriAttr.equals(EMPTYSTRING) ) {     
173                            colon = qnameAttr.lastIndexOf(':');
174                               
175                            // Fix for bug 26319
176                            // For attributes not given an prefix explictly
177                            // but having a namespace uri we need
178                            // to explicitly generate the prefix
179                            String newPrefix = nm.lookupPrefix(uriAttr);
180                            if (newPrefix == null) 
181                                newPrefix = nm.generateNextPrefix();
182                            prefix = (colon > 0) ? qnameAttr.substring(0, colon) 
183                                : newPrefix;
184                            _handler.namespaceAfterStartElement(prefix, uriAttr);
185                            _handler.addAttribute((prefix + ":" + qnameAttr),
186                                attr.getNodeValue());
187                        } else {
188                             _handler.addAttribute(qnameAttr, attr.getNodeValue());
189                        }
190                    }
191                }
192    
193                // Now element namespace and children
194                final String uri = node.getNamespaceURI();
195                final String localName = node.getLocalName();
196    
197                // Uri may be implicitly declared
198                if (uri != null) {  
199                    colon = qname.lastIndexOf(':');
200                    prefix = (colon > 0) ? qname.substring(0, colon) : EMPTYSTRING;
201                    _handler.namespaceAfterStartElement(prefix, uri);
202                }else {
203                      // Fix for bug 26319
204                      // If an element foo is created using
205                      // createElementNS(null,locName)
206                      // then the  element should be serialized
207                      // <foo xmlns=" "/> 
208                      if (uri == null  && localName != null) {
209                         prefix = EMPTYSTRING;
210                         _handler.namespaceAfterStartElement(prefix, EMPTYSTRING);
211                     }
212                }
213    
214                // Traverse all child nodes of the element (if any)
215                next = node.getFirstChild();
216                while (next != null) {
217                    parse(next);
218                    next = next.getNextSibling();
219                }
220    
221                // Generate SAX event to close element
222                _handler.endElement(qname);
223                break;
224    
225            case Node.PROCESSING_INSTRUCTION_NODE:
226                _handler.processingInstruction(node.getNodeName(),
227                                               node.getNodeValue());
228                break;
229    
230            case Node.TEXT_NODE:
231                _handler.characters(node.getNodeValue());
232                break;
233            }
234        }
235    
236        /**
237         * This class is only used internally so this method should never 
238         * be called.
239         */
240        public DTDHandler getDTDHandler() { 
241            return null;
242        }
243    
244        /**
245         * This class is only used internally so this method should never 
246         * be called.
247         */
248        public ErrorHandler getErrorHandler() {
249            return null;
250        }
251    
252        /**
253         * This class is only used internally so this method should never 
254         * be called.
255         */
256        public boolean getFeature(String name) throws SAXNotRecognizedException,
257            SAXNotSupportedException
258        {
259            return false;
260        }
261    
262        /**
263         * This class is only used internally so this method should never 
264         * be called.
265         */
266        public void setFeature(String name, boolean value) throws 
267            SAXNotRecognizedException, SAXNotSupportedException 
268        {
269        }
270    
271        /**
272         * This class is only used internally so this method should never 
273         * be called.
274         */
275        public void parse(String sysId) throws IOException, SAXException {
276            throw new IOException("This method is not yet implemented.");
277        }
278    
279        /**
280         * This class is only used internally so this method should never 
281         * be called.
282         */
283        public void setDTDHandler(DTDHandler handler) throws NullPointerException {
284        }
285    
286        /**
287         * This class is only used internally so this method should never 
288         * be called.
289         */
290        public void setEntityResolver(EntityResolver resolver) throws 
291            NullPointerException 
292        {
293        }
294    
295        /**
296         * This class is only used internally so this method should never 
297         * be called.
298         */
299        public EntityResolver getEntityResolver() {
300            return null;
301        }
302    
303        /**
304         * This class is only used internally so this method should never 
305         * be called.
306         */
307        public void setErrorHandler(ErrorHandler handler) throws 
308            NullPointerException
309        {
310        }
311    
312        /**
313         * This class is only used internally so this method should never 
314         * be called.
315         */
316        public void setProperty(String name, Object value) throws
317            SAXNotRecognizedException, SAXNotSupportedException {
318        }
319    
320        /**
321         * This class is only used internally so this method should never 
322         * be called.
323         */
324        public Object getProperty(String name) throws SAXNotRecognizedException,
325            SAXNotSupportedException
326        {
327            return null;
328        }
329    
330        /**
331         * This class is only used internally so this method should never 
332         * be called.
333         */
334        public int getColumnNumber() { 
335            return 0; 
336        }
337        
338        /**
339         * This class is only used internally so this method should never 
340         * be called.
341         */
342        public int getLineNumber() { 
343            return 0; 
344        }
345    
346        /**
347         * This class is only used internally so this method should never 
348         * be called.
349         */
350        public String getPublicId() { 
351            return null; 
352        }
353    
354        /**
355         * This class is only used internally so this method should never 
356         * be called.
357         */
358        public String getSystemId() { 
359            return null; 
360        }
361    
362        // Debugging 
363        private String getNodeTypeFromCode(short code) {
364            String retval = null;
365            switch (code) {
366            case Node.ATTRIBUTE_NODE : 
367                retval = "ATTRIBUTE_NODE"; break; 
368            case Node.CDATA_SECTION_NODE :
369                retval = "CDATA_SECTION_NODE"; break; 
370            case Node.COMMENT_NODE :
371                retval = "COMMENT_NODE"; break; 
372            case Node.DOCUMENT_FRAGMENT_NODE :
373                retval = "DOCUMENT_FRAGMENT_NODE"; break; 
374            case Node.DOCUMENT_NODE :
375                retval = "DOCUMENT_NODE"; break; 
376            case Node.DOCUMENT_TYPE_NODE :
377                retval = "DOCUMENT_TYPE_NODE"; break; 
378            case Node.ELEMENT_NODE :
379                retval = "ELEMENT_NODE"; break; 
380            case Node.ENTITY_NODE :
381                retval = "ENTITY_NODE"; break; 
382            case Node.ENTITY_REFERENCE_NODE :
383                retval = "ENTITY_REFERENCE_NODE"; break; 
384            case Node.NOTATION_NODE :
385                retval = "NOTATION_NODE"; break; 
386            case Node.PROCESSING_INSTRUCTION_NODE :
387                retval = "PROCESSING_INSTRUCTION_NODE"; break; 
388            case Node.TEXT_NODE:
389                retval = "TEXT_NODE"; break; 
390            }
391            return retval;
392        }
393    }