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