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 }