1 /* SAXEventSink.java -- 2 Copyright (C) 1999,2000,2001 Free Software Foundation, Inc. 3 4 This file is part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301 USA. 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 package gnu.xml.dom.ls; 39 40 import java.util.HashSet; 41 import java.util.Iterator; 42 import java.util.LinkedList; 43 import java.util.List; 44 import javax.xml.XMLConstants; 45 import org.w3c.dom.Attr; 46 import org.w3c.dom.Document; 47 import org.w3c.dom.DocumentType; 48 import org.w3c.dom.Element; 49 import org.w3c.dom.Entity; 50 import org.w3c.dom.EntityReference; 51 import org.w3c.dom.NamedNodeMap; 52 import org.w3c.dom.Node; 53 import org.w3c.dom.Text; 54 import org.xml.sax.Attributes; 55 import org.xml.sax.ContentHandler; 56 import org.xml.sax.DTDHandler; 57 import org.xml.sax.Locator; 58 import org.xml.sax.SAXException; 59 import org.xml.sax.SAXNotRecognizedException; 60 import org.xml.sax.SAXNotSupportedException; 61 import org.xml.sax.XMLReader; 62 import org.xml.sax.ext.Attributes2; 63 import org.xml.sax.ext.DeclHandler; 64 import org.xml.sax.ext.LexicalHandler; 65 import org.xml.sax.ext.Locator2; 66 import gnu.xml.dom.DomAttr; 67 import gnu.xml.dom.DomDocument; 68 import gnu.xml.dom.DomDoctype; 69 import gnu.xml.dom.DomNode; 70 71 /** 72 * A SAX content and lexical handler used to construct a DOM document. 73 * 74 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a> 75 */ 76 public class SAXEventSink 77 implements ContentHandler, LexicalHandler, DTDHandler, DeclHandler 78 { 79 80 private static final String XMLNS_URI = XMLConstants.XMLNS_ATTRIBUTE_NS_URI; 81 private static final String XMLNS_PREFIX = XMLConstants.XMLNS_ATTRIBUTE; 82 private static final HashSet PREDEFINED_ENTITIES = new HashSet(); 83 static 84 { 85 PREDEFINED_ENTITIES.add("amp"); 86 PREDEFINED_ENTITIES.add("lt"); 87 PREDEFINED_ENTITIES.add("gt"); 88 PREDEFINED_ENTITIES.add("quot"); 89 PREDEFINED_ENTITIES.add("apos"); 90 } 91 92 private boolean namespaceAware; 93 boolean ignoreWhitespace; 94 boolean expandEntityReferences; 95 boolean ignoreComments; 96 boolean coalescing; 97 98 XMLReader reader; // reference back to the parser to get features 99 100 DomDocument doc; // document being constructed 101 Node ctx; // current context (parent node) 102 LinkedList entityCtx; // entity context 103 List pending; // namespace nodes waiting for a declaring element 104 Locator locator; 105 boolean inCDATA; 106 boolean inDTD; 107 boolean interrupted; 108 interrupt()109 void interrupt() 110 { 111 interrupted = true; 112 } 113 getDocument()114 public Document getDocument() 115 { 116 return doc; 117 } 118 setReader(XMLReader reader)119 public void setReader(XMLReader reader) 120 { 121 this.reader = reader; 122 } 123 124 // -- ContentHandler2 -- 125 setDocumentLocator(Locator locator)126 public void setDocumentLocator(Locator locator) 127 { 128 this.locator = locator; 129 } 130 setNamespaceAware(boolean namespaceAware)131 public void setNamespaceAware(boolean namespaceAware) 132 { 133 this.namespaceAware = namespaceAware; 134 } 135 startDocument()136 public void startDocument() 137 throws SAXException 138 { 139 if (namespaceAware) 140 { 141 pending = new LinkedList(); 142 } 143 doc = new DomDocument(); 144 doc.setStrictErrorChecking(false); 145 doc.setBuilding(true); 146 doc.setDefaultAttributes(false); 147 ctx = doc; 148 149 final String FEATURES = "http://xml.org/sax/features/"; 150 final String PROPERTIES = "http://xml.org/sax/properties/"; 151 final String GNU_PROPERTIES = "http://gnu.org/sax/properties/"; 152 153 if (reader != null) 154 { 155 boolean standalone = reader.getFeature(FEATURES + "is-standalone"); 156 doc.setXmlStandalone(standalone); 157 try 158 { 159 String version = (String) reader.getProperty(PROPERTIES + 160 "document-xml-version"); 161 doc.setXmlVersion(version); 162 } 163 catch (SAXNotRecognizedException e) 164 { 165 } 166 catch (SAXNotSupportedException e) 167 { 168 } 169 try 170 { 171 String encoding = (String) reader.getProperty(GNU_PROPERTIES + 172 "document-xml-encoding"); 173 doc.setXmlEncoding(encoding); 174 } 175 catch (SAXNotRecognizedException e) 176 { 177 } 178 catch (SAXNotSupportedException e) 179 { 180 } 181 } 182 if (locator != null && locator instanceof Locator2) 183 { 184 String encoding = ((Locator2) locator).getEncoding(); 185 doc.setInputEncoding(encoding); 186 } 187 } 188 endDocument()189 public void endDocument() 190 throws SAXException 191 { 192 doc.setStrictErrorChecking(true); 193 doc.setBuilding(false); 194 doc.setDefaultAttributes(true); 195 DomDoctype doctype = (DomDoctype) doc.getDoctype(); 196 if (doctype != null) 197 { 198 doctype.makeReadonly(); 199 } 200 ctx = null; 201 locator = null; 202 } 203 startPrefixMapping(String prefix, String uri)204 public void startPrefixMapping(String prefix, String uri) 205 throws SAXException 206 { 207 if (namespaceAware) 208 { 209 String nsName = (prefix != null && prefix.length() > 0) ? 210 XMLNS_PREFIX + ":" + prefix : XMLNS_PREFIX; 211 DomAttr ns = (DomAttr) doc.createAttributeNS(XMLNS_URI, nsName); 212 ns.setNodeValue(uri); 213 if (ctx.getNodeType() == Node.ATTRIBUTE_NODE) 214 { 215 // Add to owner element 216 Node target = ((Attr) ctx).getOwnerElement(); 217 target.getAttributes().setNamedItemNS(ns); 218 } 219 else 220 { 221 // Add to pending list; namespace node will be inserted when 222 // element is seen 223 pending.add(ns); 224 } 225 } 226 } 227 endPrefixMapping(String prefix)228 public void endPrefixMapping(String prefix) 229 throws SAXException 230 { 231 } 232 startElement(String uri, String localName, String qName, Attributes atts)233 public void startElement(String uri, String localName, String qName, 234 Attributes atts) 235 throws SAXException 236 { 237 if (interrupted) 238 { 239 return; 240 } 241 Element element = createElement(uri, localName, qName, atts); 242 // add element to context 243 ctx.appendChild(element); 244 ctx = element; 245 } 246 createElement(String uri, String localName, String qName, Attributes atts)247 protected Element createElement(String uri, String localName, String qName, 248 Attributes atts) 249 throws SAXException 250 { 251 // create element node 252 Element element = namespaceAware ? 253 doc.createElementNS(uri, qName) : 254 doc.createElement(qName); 255 NamedNodeMap attrs = element.getAttributes(); 256 if (namespaceAware && !pending.isEmpty()) 257 { 258 // add pending namespace nodes 259 for (Iterator i = pending.iterator(); i.hasNext(); ) 260 { 261 Node ns = (Node) i.next(); 262 attrs.setNamedItemNS(ns); 263 } 264 pending.clear(); 265 } 266 // add attributes 267 int len = atts.getLength(); 268 for (int i = 0; i < len; i++) 269 { 270 // create attribute 271 Attr attr = createAttr(atts, i); 272 if (attr != null) 273 { 274 // add attribute to element 275 if (namespaceAware) 276 { 277 attrs.setNamedItemNS(attr); 278 } 279 else 280 { 281 attrs.setNamedItem(attr); 282 } 283 } 284 } 285 return element; 286 } 287 createAttr(Attributes atts, int index)288 protected Attr createAttr(Attributes atts, int index) 289 { 290 DomAttr attr; 291 if (namespaceAware) 292 { 293 String a_uri = atts.getURI(index); 294 String a_qName = atts.getQName(index); 295 attr = (DomAttr) doc.createAttributeNS(a_uri, a_qName); 296 } 297 else 298 { 299 String a_qName = atts.getQName(index); 300 attr = (DomAttr) doc.createAttribute(a_qName); 301 } 302 attr.setNodeValue(atts.getValue(index)); 303 if (atts instanceof Attributes2) 304 { 305 Attributes2 atts2 = (Attributes2) atts; 306 // TODO attr.setDeclared(atts2.isDeclared(index)); 307 attr.setSpecified(atts2.isSpecified(index)); 308 } 309 return attr; 310 } 311 endElement(String uri, String localName, String qName)312 public void endElement(String uri, String localName, String qName) 313 throws SAXException 314 { 315 if (interrupted) 316 { 317 return; 318 } 319 if (namespaceAware) 320 { 321 pending.clear(); 322 } 323 ctx = ctx.getParentNode(); 324 } 325 characters(char[] c, int off, int len)326 public void characters(char[] c, int off, int len) 327 throws SAXException 328 { 329 if (interrupted || len < 1) 330 { 331 return; 332 } 333 ctx.appendChild(createText(c, off, len)); 334 } 335 createText(char[] c, int off, int len)336 protected Text createText(char[] c, int off, int len) 337 throws SAXException 338 { 339 Text text = (inCDATA && !coalescing) ? 340 doc.createCDATASection(new String(c, off, len)) : 341 doc.createTextNode(new String(c, off, len)); 342 return text; 343 } 344 ignorableWhitespace(char[] c, int off, int len)345 public void ignorableWhitespace(char[] c, int off, int len) 346 throws SAXException 347 { 348 if (interrupted) 349 { 350 return; 351 } 352 if (!ignoreWhitespace) 353 { 354 characters(c, off, len); 355 } 356 } 357 processingInstruction(String target, String data)358 public void processingInstruction(String target, String data) 359 throws SAXException 360 { 361 if (interrupted) 362 { 363 return; 364 } 365 Node pi = createProcessingInstruction(target, data); 366 ctx.appendChild(pi); 367 } 368 createProcessingInstruction(String target, String data)369 protected Node createProcessingInstruction(String target, String data) 370 { 371 return doc.createProcessingInstruction(target, data); 372 } 373 skippedEntity(String name)374 public void skippedEntity(String name) 375 throws SAXException 376 { 377 // This callback is totally pointless 378 } 379 380 // -- LexicalHandler -- 381 startDTD(String name, String publicId, String systemId)382 public void startDTD(String name, String publicId, String systemId) 383 throws SAXException 384 { 385 if (interrupted) 386 { 387 return; 388 } 389 Node doctype = createDocumentType(name, publicId, systemId); 390 doc.appendChild(doctype); 391 ctx = doctype; 392 inDTD = true; 393 } 394 createDocumentType(String name, String publicId, String systemId)395 protected Node createDocumentType(String name, String publicId, 396 String systemId) 397 { 398 return new DomDoctype(doc, name, publicId, systemId); 399 } 400 endDTD()401 public void endDTD() 402 throws SAXException 403 { 404 if (interrupted) 405 { 406 return; 407 } 408 inDTD = false; 409 ctx = ctx.getParentNode(); 410 } 411 startEntity(String name)412 public void startEntity(String name) 413 throws SAXException 414 { 415 if (interrupted) 416 return; 417 DocumentType doctype = doc.getDoctype(); 418 if (doctype == null) 419 { 420 throw new SAXException("SAX parser error: " + 421 "reference to entity in undeclared doctype"); 422 } 423 if ("[dtd]".equals(name) || name.charAt(0) == '%') 424 return; 425 if (PREDEFINED_ENTITIES.contains(name)) 426 return; 427 // Get entity 428 NamedNodeMap entities = doctype.getEntities(); 429 Entity entity = (Entity) entities.getNamedItem(name); 430 if (entity == null) 431 { 432 throw new SAXException("SAX parser error: " + 433 "reference to undeclared entity: " + name); 434 } 435 EntityReference ref = doc.createEntityReference(name); 436 // DomDocument populates with the entity replacement text, remove this 437 Node child = ref.getFirstChild(); 438 while (child != null) 439 { 440 Node nextChild = child.getNextSibling(); 441 ref.removeChild(child); 442 child = nextChild; 443 } 444 ctx.appendChild(ref); 445 ctx = ref; 446 } 447 endEntity(String name)448 public void endEntity(String name) 449 throws SAXException 450 { 451 if (interrupted) 452 return; 453 if ("[dtd]".equals(name) || name.charAt(0) == '%') 454 return; 455 if (PREDEFINED_ENTITIES.contains(name)) 456 return; 457 // Get entity reference 458 EntityReference ref = (EntityReference) ctx; 459 if (!ref.getNodeName().equals(name)) 460 throw new SAXException("expecting end of "+ref.getNodeName()+" entity"); 461 ctx = ctx.getParentNode(); 462 if (ref instanceof DomNode) 463 ((DomNode) ref).makeReadonly(); 464 if (expandEntityReferences) 465 { 466 // Move entity content from reference node onto context 467 Node child = ref.getFirstChild(); 468 while (child != null) 469 { 470 Node nextChild = child.getNextSibling(); 471 ctx.appendChild(child); 472 child = nextChild; 473 } 474 ctx.removeChild(ref); 475 } 476 } 477 startCDATA()478 public void startCDATA() 479 throws SAXException 480 { 481 inCDATA = true; 482 } 483 endCDATA()484 public void endCDATA() 485 throws SAXException 486 { 487 inCDATA = false; 488 } 489 comment(char[] c, int off, int len)490 public void comment(char[] c, int off, int len) 491 throws SAXException 492 { 493 if (interrupted) 494 { 495 return; 496 } 497 Node comment = createComment(c, off, len); 498 ctx.appendChild(comment); 499 } 500 createComment(char[] c, int off, int len)501 protected Node createComment(char[] c, int off, int len) 502 { 503 return doc.createComment(new String(c, off, len)); 504 } 505 506 // -- DTDHandler -- 507 notationDecl(String name, String publicId, String systemId)508 public void notationDecl(String name, String publicId, String systemId) 509 throws SAXException 510 { 511 if (interrupted) 512 { 513 return; 514 } 515 if (!inDTD) 516 throw new SAXException("notation decl outside DTD"); 517 DomDoctype doctype = (DomDoctype) ctx; 518 doctype.declareNotation(name, publicId, systemId); 519 } 520 unparsedEntityDecl(String name, String publicId, String systemId, String notationName)521 public void unparsedEntityDecl(String name, String publicId, String systemId, 522 String notationName) 523 throws SAXException 524 { 525 if (interrupted) 526 { 527 return; 528 } 529 if (!inDTD) 530 throw new SAXException("unparsed entity decl outside DTD"); 531 DomDoctype doctype = (DomDoctype) ctx; 532 Entity entity = doctype.declareEntity(name, publicId, systemId, 533 notationName); 534 } 535 536 // -- DeclHandler -- 537 elementDecl(String name, String model)538 public void elementDecl(String name, String model) 539 throws SAXException 540 { 541 if (interrupted) 542 { 543 return; 544 } 545 if (!inDTD) 546 throw new SAXException("element decl outside DTD"); 547 // Ignore fake element declarations generated by ValidationConsumer. 548 // If an element is not really declared in the DTD it will not be 549 // declared in the document model. 550 if (!(ctx instanceof DomDoctype)) 551 { 552 return; 553 } 554 DomDoctype doctype = (DomDoctype) ctx; 555 doctype.elementDecl(name, model); 556 } 557 attributeDecl(String eName, String aName, String type, String mode, String value)558 public void attributeDecl(String eName, String aName, String type, 559 String mode, String value) 560 throws SAXException 561 { 562 if (interrupted) 563 { 564 return; 565 } 566 if (!inDTD) 567 throw new SAXException("attribute decl outside DTD"); 568 DomDoctype doctype = (DomDoctype) ctx; 569 doctype.attributeDecl(eName, aName, type, mode, value); 570 } 571 internalEntityDecl(String name, String value)572 public void internalEntityDecl(String name, String value) 573 throws SAXException 574 { 575 if (interrupted) 576 { 577 return; 578 } 579 if (!inDTD) 580 throw new SAXException("internal entity decl outside DTD"); 581 DomDoctype doctype = (DomDoctype) ctx; 582 Entity entity = doctype.declareEntity(name, null, null, null); 583 if (entity != null) 584 { 585 Node text = doc.createTextNode(value); 586 entity.appendChild(text); 587 } 588 } 589 externalEntityDecl(String name, String publicId, String systemId)590 public void externalEntityDecl(String name, String publicId, String systemId) 591 throws SAXException 592 { 593 if (interrupted) 594 { 595 return; 596 } 597 if (!inDTD) 598 throw new SAXException("external entity decl outside DTD"); 599 DomDoctype doctype = (DomDoctype) ctx; 600 Entity entity = doctype.declareEntity(name, publicId, systemId, null); 601 } 602 603 } 604