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 }