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
22
23package com.sun.org.apache.xalan.internal.xsltc.trax;
24
25import java.util.Stack;
26import java.util.Vector;
27import javax.xml.parsers.DocumentBuilder;
28
29import javax.xml.parsers.DocumentBuilderFactory;
30import javax.xml.parsers.ParserConfigurationException;
31
32import com.sun.org.apache.xalan.internal.xsltc.runtime.Constants;
33
34import org.w3c.dom.Comment;
35import org.w3c.dom.Document;
36import org.w3c.dom.Element;
37import org.w3c.dom.Node;
38import org.w3c.dom.Text;
39import org.w3c.dom.ProcessingInstruction;
40import org.xml.sax.Attributes;
41import org.xml.sax.ContentHandler;
42import org.xml.sax.Locator;
43import org.xml.sax.SAXException;
44import org.xml.sax.ext.LexicalHandler;
45import org.xml.sax.ext.Locator2;
46
47/**
48 * @author G. Todd Miller
49 * @author Sunitha Reddy
50 * @author Huizhe Wang
51 */
52public class SAX2DOM implements ContentHandler, LexicalHandler, Constants {
53
54    private Node _root = null;
55    private Document _document = null;
56    private Node _nextSibling = null;
57    private Stack _nodeStk = new Stack();
58    private Vector _namespaceDecls = null;
59    private Node _lastSibling = null;
60    private Locator locator = null;
61    private boolean needToSetDocumentInfo = true;
62
63    //Replace StringBuffer with StringBuilder now that we no long support jdk1.4
64    private StringBuilder _textBuffer = new StringBuilder();
65    private Node _nextSiblingCache = null;
66    /**
67     * JAXP document builder factory. Create a single instance and use
68     * synchronization because the Javadoc is not explicit about
69     * thread safety.
70     */
71    private DocumentBuilderFactory _factory =
72            DocumentBuilderFactory.newInstance();
73    private boolean _internal = true;
74
75    public SAX2DOM(boolean useServicesMechanism) throws ParserConfigurationException {
76        _document = createDocument(useServicesMechanism);
77        _root = _document;
78    }
79
80    public SAX2DOM(Node root, Node nextSibling, boolean useServicesMechanism) throws ParserConfigurationException {
81        _root = root;
82        if (root instanceof Document) {
83          _document = (Document)root;
84        }
85        else if (root != null) {
86          _document = root.getOwnerDocument();
87        }
88        else {
89          _document = createDocument(useServicesMechanism);
90          _root = _document;
91        }
92
93        _nextSibling = nextSibling;
94    }
95
96    public SAX2DOM(Node root, boolean useServicesMechanism) throws ParserConfigurationException {
97        this(root, null, useServicesMechanism);
98    }
99
100    public Node getDOM() {
101        return _root;
102    }
103
104    public void characters(char[] ch, int start, int length) {
105        // Ignore text nodes of length 0
106        if (length == 0) {
107            return;
108        }
109
110        final Node last = (Node)_nodeStk.peek();
111
112        // No text nodes can be children of root (DOM006 exception)
113        if (last != _document) {
114            _nextSiblingCache = _nextSibling;
115            _textBuffer.append(ch, start, length);
116        }
117    }
118    private void appendTextNode() {
119        if (_textBuffer.length() > 0) {
120            final Node last = (Node)_nodeStk.peek();
121            if (last == _root && _nextSiblingCache != null) {
122                _lastSibling = last.insertBefore(_document.createTextNode(_textBuffer.toString()), _nextSiblingCache);
123            }
124            else {
125                _lastSibling = last.appendChild(_document.createTextNode(_textBuffer.toString()));
126            }
127            _textBuffer.setLength(0);
128        }
129    }
130    public void startDocument() {
131        _nodeStk.push(_root);
132    }
133
134    public void endDocument() {
135        _nodeStk.pop();
136    }
137
138    private void setDocumentInfo() {
139        //try to set document version
140        if (locator == null) return;
141        try{
142            _document.setXmlVersion(((Locator2)locator).getXMLVersion());
143        }catch(ClassCastException e){}
144
145    }
146
147    public void startElement(String namespace, String localName, String qName,
148        Attributes attrs)
149    {
150        appendTextNode();
151        if (needToSetDocumentInfo) {
152            setDocumentInfo();
153            needToSetDocumentInfo = false;
154        }
155
156        final Element tmp = (Element)_document.createElementNS(namespace, qName);
157
158        // Add namespace declarations first
159        if (_namespaceDecls != null) {
160            final int nDecls = _namespaceDecls.size();
161            for (int i = 0; i < nDecls; i++) {
162                final String prefix = (String) _namespaceDecls.elementAt(i++);
163
164                if (prefix == null || prefix.equals(EMPTYSTRING)) {
165                    tmp.setAttributeNS(XMLNS_URI, XMLNS_PREFIX,
166                        (String) _namespaceDecls.elementAt(i));
167                }
168                else {
169                    tmp.setAttributeNS(XMLNS_URI, XMLNS_STRING + prefix,
170                        (String) _namespaceDecls.elementAt(i));
171                }
172            }
173            _namespaceDecls.clear();
174        }
175
176        // Add attributes to element
177/*      final int nattrs = attrs.getLength();
178        for (int i = 0; i < nattrs; i++) {
179            if (attrs.getLocalName(i) == null) {
180                tmp.setAttribute(attrs.getQName(i), attrs.getValue(i));
181            }
182            else {
183                tmp.setAttributeNS(attrs.getURI(i), attrs.getQName(i),
184                    attrs.getValue(i));
185            }
186        } */
187
188
189        // Add attributes to element
190        final int nattrs = attrs.getLength();
191        for (int i = 0; i < nattrs; i++) {
192            // checking if Namespace processing is being done
193            String attQName = attrs.getQName(i);
194            String attURI = attrs.getURI(i);
195            if (attrs.getLocalName(i).equals("")) {
196                tmp.setAttribute(attQName, attrs.getValue(i));
197                if (attrs.getType(i).equals("ID")) {
198                    tmp.setIdAttribute(attQName, true);
199                }
200            } else {
201                tmp.setAttributeNS(attURI, attQName, attrs.getValue(i));
202                if (attrs.getType(i).equals("ID")) {
203                    tmp.setIdAttributeNS(attURI, attrs.getLocalName(i), true);
204                }
205            }
206        }
207
208
209        // Append this new node onto current stack node
210        Node last = (Node)_nodeStk.peek();
211
212        // If the SAX2DOM is created with a non-null next sibling node,
213        // insert the result nodes before the next sibling under the root.
214        if (last == _root && _nextSibling != null)
215            last.insertBefore(tmp, _nextSibling);
216        else
217            last.appendChild(tmp);
218
219        // Push this node onto stack
220        _nodeStk.push(tmp);
221        _lastSibling = null;
222    }
223
224    public void endElement(String namespace, String localName, String qName) {
225        appendTextNode();
226        _nodeStk.pop();
227        _lastSibling = null;
228    }
229
230    public void startPrefixMapping(String prefix, String uri) {
231        if (_namespaceDecls == null) {
232            _namespaceDecls = new Vector(2);
233        }
234        _namespaceDecls.addElement(prefix);
235        _namespaceDecls.addElement(uri);
236    }
237
238    public void endPrefixMapping(String prefix) {
239        // do nothing
240    }
241
242    /**
243     * This class is only used internally so this method should never
244     * be called.
245     */
246    public void ignorableWhitespace(char[] ch, int start, int length) {
247    }
248
249    /**
250     * adds processing instruction node to DOM.
251     */
252    public void processingInstruction(String target, String data) {
253        appendTextNode();
254        final Node last = (Node)_nodeStk.peek();
255        ProcessingInstruction pi = _document.createProcessingInstruction(
256                target, data);
257        if (pi != null){
258          if (last == _root && _nextSibling != null)
259              last.insertBefore(pi, _nextSibling);
260          else
261              last.appendChild(pi);
262
263          _lastSibling = pi;
264        }
265    }
266
267    /**
268     * This class is only used internally so this method should never
269     * be called.
270     */
271    public void setDocumentLocator(Locator locator) {
272        this.locator = locator;
273    }
274
275    /**
276     * This class is only used internally so this method should never
277     * be called.
278     */
279    public void skippedEntity(String name) {
280    }
281
282
283    /**
284     * Lexical Handler method to create comment node in DOM tree.
285     */
286    public void comment(char[] ch, int start, int length) {
287        appendTextNode();
288        final Node last = (Node)_nodeStk.peek();
289        Comment comment = _document.createComment(new String(ch,start,length));
290        if (comment != null){
291          if (last == _root && _nextSibling != null)
292              last.insertBefore(comment, _nextSibling);
293          else
294              last.appendChild(comment);
295
296          _lastSibling = comment;
297        }
298    }
299
300    // Lexical Handler methods- not implemented
301    public void startCDATA() { }
302    public void endCDATA() { }
303    public void startEntity(java.lang.String name) { }
304    public void endDTD() { }
305    public void endEntity(String name) { }
306    public void startDTD(String name, String publicId, String systemId)
307        throws SAXException {}
308
309    private Document createDocument(boolean useServicesMechanism) throws ParserConfigurationException {
310        if (_factory == null) {
311            if (useServicesMechanism) {
312                _factory = DocumentBuilderFactory.newInstance();
313                if (!(_factory instanceof com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl)) {
314                    _internal = false;
315                }
316            } else {
317                _factory = DocumentBuilderFactory.newInstance(
318                  "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl",
319                  SAX2DOM.class.getClassLoader()
320                  );
321            }
322        }
323        Document doc;
324        if (_internal) {
325            //default implementation is thread safe
326            doc = _factory.newDocumentBuilder().newDocument();
327        } else {
328            synchronized(SAX2DOM.class) {
329                doc = _factory.newDocumentBuilder().newDocument();
330            }
331        }
332        return doc;
333    }
334
335}
336