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: DOM2TO.java 468653 2006-10-28 07:07:05Z minchau $ 020 */ 021 022 package org.apache.xalan.xsltc.trax; 023 024 import java.io.IOException; 025 026 import org.w3c.dom.NamedNodeMap; 027 import org.w3c.dom.Node; 028 import org.apache.xml.serializer.SerializationHandler; 029 import org.xml.sax.ContentHandler; 030 import org.xml.sax.DTDHandler; 031 import org.xml.sax.EntityResolver; 032 import org.xml.sax.ErrorHandler; 033 import org.xml.sax.InputSource; 034 import org.xml.sax.Locator; 035 import org.xml.sax.SAXException; 036 import org.xml.sax.SAXNotRecognizedException; 037 import org.xml.sax.SAXNotSupportedException; 038 import org.xml.sax.XMLReader; 039 import org.apache.xml.serializer.NamespaceMappings; 040 041 /** 042 * @author Santiago Pericas-Geertsen 043 */ 044 public class DOM2TO implements XMLReader, Locator { 045 046 private final static String EMPTYSTRING = ""; 047 private static final String XMLNS_PREFIX = "xmlns"; 048 049 /** 050 * A reference to the DOM to be traversed. 051 */ 052 private Node _dom; 053 054 /** 055 * A reference to the output handler receiving the events. 056 */ 057 private SerializationHandler _handler; 058 059 public DOM2TO(Node root, SerializationHandler handler) { 060 _dom = root; 061 _handler = handler; 062 } 063 064 public ContentHandler getContentHandler() { 065 return null; 066 } 067 068 public void setContentHandler(ContentHandler handler) { 069 // Empty 070 } 071 072 public void parse(InputSource unused) throws IOException, SAXException { 073 parse(_dom); 074 } 075 076 public void parse() throws IOException, SAXException { 077 if (_dom != null) { 078 boolean isIncomplete = 079 (_dom.getNodeType() != org.w3c.dom.Node.DOCUMENT_NODE); 080 081 if (isIncomplete) { 082 _handler.startDocument(); 083 parse(_dom); 084 _handler.endDocument(); 085 } 086 else { 087 parse(_dom); 088 } 089 } 090 } 091 092 /** 093 * Traverse the DOM and generate TO events for a handler. Notice that 094 * we need to handle implicit namespace declarations too. 095 */ 096 private void parse(Node node) 097 throws IOException, SAXException 098 { 099 if (node == null) return; 100 101 switch (node.getNodeType()) { 102 case Node.ATTRIBUTE_NODE: // handled by ELEMENT_NODE 103 case Node.DOCUMENT_TYPE_NODE : 104 case Node.ENTITY_NODE : 105 case Node.ENTITY_REFERENCE_NODE: 106 case Node.NOTATION_NODE : 107 // These node types are ignored!!! 108 break; 109 case Node.CDATA_SECTION_NODE: 110 _handler.startCDATA(); 111 _handler.characters(node.getNodeValue()); 112 _handler.endCDATA(); 113 break; 114 115 case Node.COMMENT_NODE: // should be handled!!! 116 _handler.comment(node.getNodeValue()); 117 break; 118 119 case Node.DOCUMENT_NODE: 120 _handler.startDocument(); 121 Node next = node.getFirstChild(); 122 while (next != null) { 123 parse(next); 124 next = next.getNextSibling(); 125 } 126 _handler.endDocument(); 127 break; 128 129 case Node.DOCUMENT_FRAGMENT_NODE: 130 next = node.getFirstChild(); 131 while (next != null) { 132 parse(next); 133 next = next.getNextSibling(); 134 } 135 break; 136 137 case Node.ELEMENT_NODE: 138 // Generate SAX event to start element 139 final String qname = node.getNodeName(); 140 _handler.startElement(null, null, qname); 141 142 int colon; 143 String prefix; 144 final NamedNodeMap map = node.getAttributes(); 145 final int length = map.getLength(); 146 147 // Process all namespace attributes first 148 for (int i = 0; i < length; i++) { 149 final Node attr = map.item(i); 150 final String qnameAttr = attr.getNodeName(); 151 152 // Is this a namespace declaration? 153 if (qnameAttr.startsWith(XMLNS_PREFIX)) { 154 final String uriAttr = attr.getNodeValue(); 155 colon = qnameAttr.lastIndexOf(':'); 156 prefix = (colon > 0) ? qnameAttr.substring(colon + 1) 157 : EMPTYSTRING; 158 _handler.namespaceAfterStartElement(prefix, uriAttr); 159 } 160 } 161 162 // Process all non-namespace attributes next 163 NamespaceMappings nm = new NamespaceMappings(); 164 for (int i = 0; i < length; i++) { 165 final Node attr = map.item(i); 166 final String qnameAttr = attr.getNodeName(); 167 168 // Is this a regular attribute? 169 if (!qnameAttr.startsWith(XMLNS_PREFIX)) { 170 final String uriAttr = attr.getNamespaceURI(); 171 // Uri may be implicitly declared 172 if (uriAttr != null && !uriAttr.equals(EMPTYSTRING) ) { 173 colon = qnameAttr.lastIndexOf(':'); 174 175 // Fix for bug 26319 176 // For attributes not given an prefix explictly 177 // but having a namespace uri we need 178 // to explicitly generate the prefix 179 String newPrefix = nm.lookupPrefix(uriAttr); 180 if (newPrefix == null) 181 newPrefix = nm.generateNextPrefix(); 182 prefix = (colon > 0) ? qnameAttr.substring(0, colon) 183 : newPrefix; 184 _handler.namespaceAfterStartElement(prefix, uriAttr); 185 _handler.addAttribute((prefix + ":" + qnameAttr), 186 attr.getNodeValue()); 187 } else { 188 _handler.addAttribute(qnameAttr, attr.getNodeValue()); 189 } 190 } 191 } 192 193 // Now element namespace and children 194 final String uri = node.getNamespaceURI(); 195 final String localName = node.getLocalName(); 196 197 // Uri may be implicitly declared 198 if (uri != null) { 199 colon = qname.lastIndexOf(':'); 200 prefix = (colon > 0) ? qname.substring(0, colon) : EMPTYSTRING; 201 _handler.namespaceAfterStartElement(prefix, uri); 202 }else { 203 // Fix for bug 26319 204 // If an element foo is created using 205 // createElementNS(null,locName) 206 // then the element should be serialized 207 // <foo xmlns=" "/> 208 if (uri == null && localName != null) { 209 prefix = EMPTYSTRING; 210 _handler.namespaceAfterStartElement(prefix, EMPTYSTRING); 211 } 212 } 213 214 // Traverse all child nodes of the element (if any) 215 next = node.getFirstChild(); 216 while (next != null) { 217 parse(next); 218 next = next.getNextSibling(); 219 } 220 221 // Generate SAX event to close element 222 _handler.endElement(qname); 223 break; 224 225 case Node.PROCESSING_INSTRUCTION_NODE: 226 _handler.processingInstruction(node.getNodeName(), 227 node.getNodeValue()); 228 break; 229 230 case Node.TEXT_NODE: 231 _handler.characters(node.getNodeValue()); 232 break; 233 } 234 } 235 236 /** 237 * This class is only used internally so this method should never 238 * be called. 239 */ 240 public DTDHandler getDTDHandler() { 241 return null; 242 } 243 244 /** 245 * This class is only used internally so this method should never 246 * be called. 247 */ 248 public ErrorHandler getErrorHandler() { 249 return null; 250 } 251 252 /** 253 * This class is only used internally so this method should never 254 * be called. 255 */ 256 public boolean getFeature(String name) throws SAXNotRecognizedException, 257 SAXNotSupportedException 258 { 259 return false; 260 } 261 262 /** 263 * This class is only used internally so this method should never 264 * be called. 265 */ 266 public void setFeature(String name, boolean value) throws 267 SAXNotRecognizedException, SAXNotSupportedException 268 { 269 } 270 271 /** 272 * This class is only used internally so this method should never 273 * be called. 274 */ 275 public void parse(String sysId) throws IOException, SAXException { 276 throw new IOException("This method is not yet implemented."); 277 } 278 279 /** 280 * This class is only used internally so this method should never 281 * be called. 282 */ 283 public void setDTDHandler(DTDHandler handler) throws NullPointerException { 284 } 285 286 /** 287 * This class is only used internally so this method should never 288 * be called. 289 */ 290 public void setEntityResolver(EntityResolver resolver) throws 291 NullPointerException 292 { 293 } 294 295 /** 296 * This class is only used internally so this method should never 297 * be called. 298 */ 299 public EntityResolver getEntityResolver() { 300 return null; 301 } 302 303 /** 304 * This class is only used internally so this method should never 305 * be called. 306 */ 307 public void setErrorHandler(ErrorHandler handler) throws 308 NullPointerException 309 { 310 } 311 312 /** 313 * This class is only used internally so this method should never 314 * be called. 315 */ 316 public void setProperty(String name, Object value) throws 317 SAXNotRecognizedException, SAXNotSupportedException { 318 } 319 320 /** 321 * This class is only used internally so this method should never 322 * be called. 323 */ 324 public Object getProperty(String name) throws SAXNotRecognizedException, 325 SAXNotSupportedException 326 { 327 return null; 328 } 329 330 /** 331 * This class is only used internally so this method should never 332 * be called. 333 */ 334 public int getColumnNumber() { 335 return 0; 336 } 337 338 /** 339 * This class is only used internally so this method should never 340 * be called. 341 */ 342 public int getLineNumber() { 343 return 0; 344 } 345 346 /** 347 * This class is only used internally so this method should never 348 * be called. 349 */ 350 public String getPublicId() { 351 return null; 352 } 353 354 /** 355 * This class is only used internally so this method should never 356 * be called. 357 */ 358 public String getSystemId() { 359 return null; 360 } 361 362 // Debugging 363 private String getNodeTypeFromCode(short code) { 364 String retval = null; 365 switch (code) { 366 case Node.ATTRIBUTE_NODE : 367 retval = "ATTRIBUTE_NODE"; break; 368 case Node.CDATA_SECTION_NODE : 369 retval = "CDATA_SECTION_NODE"; break; 370 case Node.COMMENT_NODE : 371 retval = "COMMENT_NODE"; break; 372 case Node.DOCUMENT_FRAGMENT_NODE : 373 retval = "DOCUMENT_FRAGMENT_NODE"; break; 374 case Node.DOCUMENT_NODE : 375 retval = "DOCUMENT_NODE"; break; 376 case Node.DOCUMENT_TYPE_NODE : 377 retval = "DOCUMENT_TYPE_NODE"; break; 378 case Node.ELEMENT_NODE : 379 retval = "ELEMENT_NODE"; break; 380 case Node.ENTITY_NODE : 381 retval = "ENTITY_NODE"; break; 382 case Node.ENTITY_REFERENCE_NODE : 383 retval = "ENTITY_REFERENCE_NODE"; break; 384 case Node.NOTATION_NODE : 385 retval = "NOTATION_NODE"; break; 386 case Node.PROCESSING_INSTRUCTION_NODE : 387 retval = "PROCESSING_INSTRUCTION_NODE"; break; 388 case Node.TEXT_NODE: 389 retval = "TEXT_NODE"; break; 390 } 391 return retval; 392 } 393 }