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: SAX2DOM.java 468653 2006-10-28 07:07:05Z minchau $
020     */
021    
022    
023    package org.apache.xalan.xsltc.trax;
024    
025    import java.util.Stack;
026    import java.util.Vector;
027    
028    
029    import javax.xml.parsers.DocumentBuilderFactory;
030    import javax.xml.parsers.ParserConfigurationException;
031    
032    import org.apache.xalan.xsltc.runtime.Constants;
033    
034    import org.w3c.dom.Comment;
035    import org.w3c.dom.Document;
036    import org.w3c.dom.Element;
037    import org.w3c.dom.Node;
038    import org.w3c.dom.Text;
039    import org.w3c.dom.ProcessingInstruction;
040    import org.xml.sax.Attributes;
041    import org.xml.sax.ContentHandler;
042    import org.xml.sax.Locator;
043    import org.xml.sax.SAXException;
044    import org.xml.sax.ext.LexicalHandler;
045    
046    /**
047     * @author G. Todd Miller 
048     */
049    public class SAX2DOM implements ContentHandler, LexicalHandler, Constants {
050    
051        private Node _root = null;
052        private Document _document = null;
053        private Node _nextSibling = null;
054        private Stack _nodeStk = new Stack();
055        private Vector _namespaceDecls = null;
056        private Node _lastSibling = null;
057    
058        public SAX2DOM() throws ParserConfigurationException {
059            final DocumentBuilderFactory factory = 
060                    DocumentBuilderFactory.newInstance();
061            _document = factory.newDocumentBuilder().newDocument();
062            _root = _document;
063        }
064    
065        public SAX2DOM(Node root, Node nextSibling) throws ParserConfigurationException {
066            _root = root;
067            if (root instanceof Document) {
068              _document = (Document)root;
069            }
070            else if (root != null) {
071              _document = root.getOwnerDocument();
072            }
073            else {
074              final DocumentBuilderFactory factory = 
075                    DocumentBuilderFactory.newInstance();
076              _document = factory.newDocumentBuilder().newDocument();
077              _root = _document;
078            }
079            
080            _nextSibling = nextSibling;
081        }
082        
083        public SAX2DOM(Node root) throws ParserConfigurationException {
084            this(root, null);
085        }
086    
087        public Node getDOM() {
088            return _root;
089        }
090    
091        public void characters(char[] ch, int start, int length) {
092            final Node last = (Node)_nodeStk.peek();
093    
094            // No text nodes can be children of root (DOM006 exception)
095            if (last != _document) {
096                final String text = new String(ch, start, length);
097                if( _lastSibling != null && _lastSibling.getNodeType() == Node.TEXT_NODE ){
098                      ((Text)_lastSibling).appendData(text);
099                }
100                else if (last == _root && _nextSibling != null) {
101                    _lastSibling = last.insertBefore(_document.createTextNode(text), _nextSibling);
102                }
103                else {
104                    _lastSibling = last.appendChild(_document.createTextNode(text));
105                }
106                
107            }
108        }
109    
110        public void startDocument() {
111            _nodeStk.push(_root);
112        }
113    
114        public void endDocument() {
115            _nodeStk.pop();
116        }
117    
118        public void startElement(String namespace, String localName, String qName,
119            Attributes attrs) 
120        {
121            final Element tmp = (Element)_document.createElementNS(namespace, qName);
122    
123            // Add namespace declarations first
124            if (_namespaceDecls != null) {
125                final int nDecls = _namespaceDecls.size();
126                for (int i = 0; i < nDecls; i++) {
127                    final String prefix = (String) _namespaceDecls.elementAt(i++);
128    
129                    if (prefix == null || prefix.equals(EMPTYSTRING)) {
130                        tmp.setAttributeNS(XMLNS_URI, XMLNS_PREFIX,
131                            (String) _namespaceDecls.elementAt(i));
132                    }
133                    else {
134                        tmp.setAttributeNS(XMLNS_URI, XMLNS_STRING + prefix, 
135                            (String) _namespaceDecls.elementAt(i));
136                    }
137                }
138                _namespaceDecls.clear();
139            }
140    
141            // Add attributes to element
142            final int nattrs = attrs.getLength();
143            for (int i = 0; i < nattrs; i++) {
144                if (attrs.getLocalName(i) == null) {
145                    tmp.setAttribute(attrs.getQName(i), attrs.getValue(i));
146                }
147                else {
148                    tmp.setAttributeNS(attrs.getURI(i), attrs.getQName(i), 
149                        attrs.getValue(i));
150                }
151            }
152    
153            // Append this new node onto current stack node
154            Node last = (Node)_nodeStk.peek();
155            
156            // If the SAX2DOM is created with a non-null next sibling node,
157            // insert the result nodes before the next sibling under the root.
158            if (last == _root && _nextSibling != null)
159                last.insertBefore(tmp, _nextSibling);
160            else
161                last.appendChild(tmp);
162    
163            // Push this node onto stack
164            _nodeStk.push(tmp);
165            _lastSibling = null;
166        }
167    
168        public void endElement(String namespace, String localName, String qName) {
169            _nodeStk.pop();  
170            _lastSibling = null;
171        }
172    
173        public void startPrefixMapping(String prefix, String uri) {
174            if (_namespaceDecls == null) {
175                _namespaceDecls = new Vector(2);
176            }
177            _namespaceDecls.addElement(prefix);
178            _namespaceDecls.addElement(uri);
179        }
180    
181        public void endPrefixMapping(String prefix) {
182            // do nothing
183        }
184    
185        /**
186         * This class is only used internally so this method should never 
187         * be called.
188         */
189        public void ignorableWhitespace(char[] ch, int start, int length) {
190        }
191    
192        /**
193         * adds processing instruction node to DOM.
194         */
195        public void processingInstruction(String target, String data) {
196            final Node last = (Node)_nodeStk.peek();
197            ProcessingInstruction pi = _document.createProcessingInstruction(
198                    target, data);
199            if (pi != null){
200              if (last == _root && _nextSibling != null)
201                  last.insertBefore(pi, _nextSibling);
202              else
203                  last.appendChild(pi);
204              
205              _lastSibling = pi;
206            }
207        }
208    
209        /**
210         * This class is only used internally so this method should never 
211         * be called.
212         */
213        public void setDocumentLocator(Locator locator) {
214        }
215    
216        /**
217         * This class is only used internally so this method should never 
218         * be called.
219         */
220        public void skippedEntity(String name) {
221        }
222    
223    
224        /**
225         * Lexical Handler method to create comment node in DOM tree.
226         */
227        public void comment(char[] ch, int start, int length) {
228            final Node last = (Node)_nodeStk.peek();
229            Comment comment = _document.createComment(new String(ch,start,length));
230            if (comment != null){
231              if (last == _root && _nextSibling != null)
232                  last.insertBefore(comment, _nextSibling);
233              else
234                  last.appendChild(comment);
235              
236              _lastSibling = comment;
237            }
238        }
239    
240        // Lexical Handler methods- not implemented
241        public void startCDATA() { }
242        public void endCDATA() { }
243        public void startEntity(java.lang.String name) { }
244        public void endDTD() { }
245        public void endEntity(String name) { }
246        public void startDTD(String name, String publicId, String systemId)
247            throws SAXException { }
248    
249    }