001 /* 002 * DomConsumer.java 003 * Copyright (C) 1999,2000,2001 The Free Software Foundation 004 * 005 * This file is part of GNU JAXP, a library. 006 * 007 * GNU JAXP is free software; you can redistribute it and/or modify 008 * it under the terms of the GNU General Public License as published by 009 * the Free Software Foundation; either version 2 of the License, or 010 * (at your option) any later version. 011 * 012 * GNU JAXP is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 015 * GNU General Public License for more details. 016 * 017 * You should have received a copy of the GNU General Public License 018 * along with this program; if not, write to the Free Software 019 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 020 * 021 * Linking this library statically or dynamically with other modules is 022 * making a combined work based on this library. Thus, the terms and 023 * conditions of the GNU General Public License cover the whole 024 * combination. 025 * 026 * As a special exception, the copyright holders of this library give you 027 * permission to link this library with independent modules to produce an 028 * executable, regardless of the license terms of these independent 029 * modules, and to copy and distribute the resulting executable under 030 * terms of your choice, provided that you also meet, for each linked 031 * independent module, the terms and conditions of the license of that 032 * module. An independent module is a module which is not derived from 033 * or based on this library. If you modify this library, you may extend 034 * this exception to your version of the library, but you are not 035 * obliged to do so. If you do not wish to do so, delete this 036 * exception statement from your version. 037 */ 038 039 // moved to fi.karppinen.* -- 2005-09-24 hsivonen 040 package fi.karppinen.gnu.xml.pipeline; 041 042 // organized imports -- 2005-09-24 hsivonen 043 044 import gnu.xml.pipeline.EventConsumer; 045 import gnu.xml.pipeline.EventFilter; 046 import gnu.xml.pipeline.NSFilter; 047 import gnu.xml.pipeline.TeeConsumer; 048 import gnu.xml.util.DomParser; 049 050 import org.w3c.dom.Attr; 051 import org.w3c.dom.CDATASection; 052 import org.w3c.dom.CharacterData; 053 import org.w3c.dom.DOMImplementation; 054 import org.w3c.dom.Document; 055 import org.w3c.dom.Element; 056 import org.w3c.dom.EntityReference; 057 import org.w3c.dom.Node; 058 import org.w3c.dom.ProcessingInstruction; 059 import org.w3c.dom.Text; 060 import org.xml.sax.Attributes; 061 import org.xml.sax.ContentHandler; 062 import org.xml.sax.DTDHandler; 063 import org.xml.sax.ErrorHandler; 064 import org.xml.sax.Locator; 065 import org.xml.sax.SAXException; 066 import org.xml.sax.SAXNotRecognizedException; 067 import org.xml.sax.SAXParseException; 068 import org.xml.sax.ext.DeclHandler; 069 import org.xml.sax.ext.LexicalHandler; 070 import org.xml.sax.helpers.AttributesImpl; 071 072 073 /** 074 * This consumer builds a DOM Document from its input, acting either as a 075 * pipeline terminus or as an intermediate buffer. When a document's worth 076 * of events has been delivered to this consumer, that document is read with 077 * a {@link DomParser} and sent to the next consumer. It is also available 078 * as a read-once property. 079 * 080 * <p>The DOM tree is constructed as faithfully as possible. There are some 081 * complications since a DOM should expose behaviors that can't be implemented 082 * without API backdoors into that DOM, and because some SAX parsers don't 083 * report all the information that DOM permits to be exposed. The general 084 * problem areas involve information from the Document Type Declaration (DTD). 085 * DOM only represents a limited subset, but has some behaviors that depend 086 * on much deeper knowledge of a document's DTD. You shouldn't have much to 087 * worry about unless you change handling of "noise" nodes from its default 088 * setting (which ignores them all); note if you use JAXP to populate your 089 * DOM trees, it wants to save "noise" nodes by default. (Such nodes include 090 * ignorable whitespace, comments, entity references and CDATA boundaries.) 091 * Otherwise, your 092 * main worry will be if you use a SAX parser that doesn't flag ignorable 093 * whitespace unless it's validating (few don't). 094 * 095 * <p> The SAX2 events used as input must contain XML Names for elements 096 * and attributes, with original prefixes. In SAX2, 097 * this is optional unless the "namespace-prefixes" parser feature is set. 098 * Moreover, many application components won't provide completely correct 099 * structures anyway. <em>Before you convert a DOM to an output document, 100 * you should plan to postprocess it to create or repair such namespace 101 * information.</em> The {@link NSFilter} pipeline stage does such work. 102 * 103 * <p> <em>Note: changes late in DOM L2 process made it impractical to 104 * attempt to create the DocumentType node in any implementation-neutral way, 105 * much less to populate it (L1 didn't support even creating such nodes). 106 * To create and populate such a node, subclass the inner 107 * {@link DomConsumer.Handler} class and teach it about the backdoors into 108 * whatever DOM implementation you want. It's possible that some revised 109 * DOM API (L3?) will make this problem solvable again. </em> 110 * 111 * @see DomParser 112 * 113 * @author David Brownell 114 * @author Henri Sivonen 115 */ 116 public class DomConsumer implements EventConsumer 117 { 118 private Class domImpl; 119 120 private boolean hidingCDATA = true; 121 private boolean hidingComments = true; 122 private boolean hidingWhitespace = true; 123 private boolean hidingReferences = true; 124 125 private Handler handler; 126 private ErrorHandler errHandler; 127 128 private EventConsumer next; 129 130 // FIXME: this can't be a generic pipeline stage just now, 131 // since its input became a Class not a String (to be turned 132 // into a class, using the right class loader) 133 134 135 /** 136 * Configures this pipeline terminus to use the specified implementation 137 * of DOM when constructing its result value. 138 * 139 * @param impl class implementing {@link org.w3c.dom.Document Document} 140 * which publicly exposes a default constructor 141 * 142 * @exception SAXException when there is a problem creating an 143 * empty DOM document using the specified implementation 144 */ 145 public DomConsumer (Class impl) 146 throws SAXException 147 { 148 domImpl = impl; 149 handler = new Handler (this); 150 } 151 152 /** 153 * This is the hook through which a subclass provides a handler 154 * which knows how to access DOM extensions, specific to some 155 * implementation, to record additional data in a DOM. 156 * Treat this as part of construction; don't call it except 157 * before (or between) parses. 158 */ 159 protected void setHandler (Handler h) 160 { 161 handler = h; 162 } 163 164 165 private Document emptyDocument () 166 throws SAXException 167 { 168 try { 169 return (Document) domImpl.newInstance (); 170 } catch (IllegalAccessException e) { 171 throw new SAXException ("can't access constructor: " 172 + e.getMessage ()); 173 } catch (InstantiationException e) { 174 throw new SAXException ("can't instantiate Document: " 175 + e.getMessage ()); 176 } 177 } 178 179 180 /** 181 * Configures this consumer as a buffer/filter, using the specified 182 * DOM implementation when constructing its result value. 183 * 184 * <p> This event consumer acts as a buffer and filter, in that it 185 * builds a DOM tree and then writes it out when <em>endDocument</em> 186 * is invoked. Because of the limitations of DOM, much information 187 * will as a rule not be seen in that replay. To get a full fidelity 188 * copy of the input event stream, use a {@link TeeConsumer}. 189 * 190 * @param impl class implementing {@link org.w3c.dom.Document Document} 191 * which publicly exposes a default constructor 192 * @param next receives a "replayed" sequence of parse events when 193 * the <em>endDocument</em> method is invoked. 194 * 195 * @exception SAXException when there is a problem creating an 196 * empty DOM document using the specified DOM implementation 197 */ 198 public DomConsumer (Class impl, EventConsumer n) 199 throws SAXException 200 { 201 this (impl); 202 next = n; 203 } 204 205 206 /** 207 * Returns the document constructed from the preceding 208 * sequence of events. This method should not be 209 * used again until another sequence of events has been 210 * given to this EventConsumer. 211 */ 212 final public Document getDocument () 213 { 214 return handler.clearDocument (); 215 } 216 217 public void setErrorHandler (ErrorHandler handler) 218 { 219 errHandler = handler; 220 } 221 222 223 /** 224 * Returns true if the consumer is hiding entity references nodes 225 * (the default), and false if EntityReference nodes should 226 * instead be created. Such EntityReference nodes will normally be 227 * empty, unless an implementation arranges to populate them and then 228 * turn them back into readonly objects. 229 * 230 * @see #setHidingReferences 231 */ 232 final public boolean isHidingReferences () 233 { return hidingReferences; } 234 235 /** 236 * Controls whether the consumer will hide entity expansions, 237 * or will instead mark them with entity reference nodes. 238 * 239 * @see #isHidingReferences 240 * @param flag False if entity reference nodes will appear 241 */ 242 final public void setHidingReferences (boolean flag) 243 { hidingReferences = flag; } 244 245 246 /** 247 * Returns true if the consumer is hiding comments (the default), 248 * and false if they should be placed into the output document. 249 * 250 * @see #setHidingComments 251 */ 252 public final boolean isHidingComments () 253 { return hidingComments; } 254 255 /** 256 * Controls whether the consumer is hiding comments. 257 * 258 * @see #isHidingComments 259 */ 260 public final void setHidingComments (boolean flag) 261 { hidingComments = flag; } 262 263 264 /** 265 * Returns true if the consumer is hiding ignorable whitespace 266 * (the default), and false if such whitespace should be placed 267 * into the output document as children of element nodes. 268 * 269 * @see #setHidingWhitespace 270 */ 271 public final boolean isHidingWhitespace () 272 { return hidingWhitespace; } 273 274 /** 275 * Controls whether the consumer hides ignorable whitespace 276 * 277 * @see #isHidingComments 278 */ 279 public final void setHidingWhitespace (boolean flag) 280 { hidingWhitespace = flag; } 281 282 283 /** 284 * Returns true if the consumer is saving CDATA boundaries, or 285 * false (the default) otherwise. 286 * 287 * @see #setHidingCDATA 288 */ 289 final public boolean isHidingCDATA () 290 { return hidingCDATA; } 291 292 /** 293 * Controls whether the consumer will save CDATA boundaries. 294 * 295 * @see #isHidingCDATA 296 * @param flag True to treat CDATA text differently from other 297 * text nodes 298 */ 299 final public void setHidingCDATA (boolean flag) 300 { hidingCDATA = flag; } 301 302 303 304 /** Returns the document handler being used. */ 305 final public ContentHandler getContentHandler () 306 { return handler; } 307 308 /** Returns the DTD handler being used. */ 309 final public DTDHandler getDTDHandler () 310 { return handler; } 311 312 /** 313 * Returns the lexical handler being used. 314 * (DOM construction can't really use declaration handlers.) 315 */ 316 final public Object getProperty (String id) 317 throws SAXNotRecognizedException 318 { 319 if ("http://xml.org/sax/properties/lexical-handler".equals (id)) 320 return handler; 321 if ("http://xml.org/sax/properties/declaration-handler".equals (id)) 322 return handler; 323 throw new SAXNotRecognizedException (id); 324 } 325 326 EventConsumer getNext () { return next; } 327 328 ErrorHandler getErrorHandler () { return errHandler; } 329 330 /** 331 * Class used to intercept various parsing events and use them to 332 * populate a DOM document. Subclasses would typically know and use 333 * backdoors into specific DOM implementations, used to implement 334 * DTD-related functionality. 335 * 336 * <p> Note that if this ever throws a DOMException (runtime exception) 337 * that will indicate a bug in the DOM (e.g. doesn't support something 338 * per specification) or the parser (e.g. emitted an illegal name, or 339 * accepted illegal input data). </p> 340 */ 341 public static class Handler 342 implements ContentHandler, LexicalHandler, 343 DTDHandler, DeclHandler 344 { 345 protected DomConsumer consumer; 346 347 private DOMImplementation impl; 348 private Document document; 349 private boolean isL2; 350 351 private Locator locator; 352 private Node top; 353 private boolean inCDATA; 354 private boolean mergeCDATA; 355 private boolean inDTD; 356 private String currentEntity; 357 358 private boolean recreatedAttrs; 359 private AttributesImpl attributes = new AttributesImpl (); 360 361 /** 362 * Subclasses may use SAX2 events to provide additional 363 * behaviors in the resulting DOM. 364 */ 365 protected Handler (DomConsumer consumer) 366 throws SAXException 367 { 368 this.consumer = consumer; 369 document = consumer.emptyDocument (); 370 impl = document.getImplementation (); 371 isL2 = impl.hasFeature ("XML", "2.0"); 372 } 373 374 private void fatal (String message, Exception x) 375 throws SAXException 376 { 377 SAXParseException e; 378 ErrorHandler errHandler = consumer.getErrorHandler (); 379 380 if (locator == null) 381 e = new SAXParseException (message, null, null, -1, -1, x); 382 else 383 e = new SAXParseException (message, locator, x); 384 if (errHandler != null) 385 errHandler.fatalError (e); 386 throw e; 387 } 388 389 /** 390 * Returns and forgets the document produced. If the handler is 391 * reused, a new document may be created. 392 */ 393 Document clearDocument () 394 { 395 Document retval = document; 396 document = null; 397 locator = null; 398 return retval; 399 } 400 401 /** 402 * Returns the document under construction. 403 */ 404 protected Document getDocument () 405 { return document; } 406 407 /** 408 * Returns the current node being populated. This is usually 409 * an Element or Document, but it might be an EntityReference 410 * node if some implementation-specific code knows how to put 411 * those into the result tree and later mark them as readonly. 412 */ 413 protected Node getTop () 414 { return top; } 415 416 417 // SAX1 418 public void setDocumentLocator (Locator locator) 419 { 420 this.locator = locator; 421 } 422 423 // SAX1 424 public void startDocument () 425 throws SAXException 426 { 427 if (document == null) 428 try { 429 if (isL2) { 430 // couple to original implementation 431 document = impl.createDocument (null, "foo", null); 432 document.removeChild (document.getFirstChild ()); 433 } else { 434 document = consumer.emptyDocument (); 435 } 436 } catch (Exception e) { 437 fatal ("DOM create document", e); 438 } 439 top = document; 440 } 441 442 // removed XML decl handling -- 2005-09-24 hsivonen 443 444 445 // SAX1 446 public void endDocument () 447 throws SAXException 448 { 449 try { 450 if (consumer.getNext () != null && document != null) { 451 DomParser parser = new DomParser (document); 452 453 EventFilter.bind (parser, consumer.getNext ()); 454 parser.parse ("ignored"); 455 } 456 } finally { 457 top = null; 458 } 459 } 460 461 // SAX1 462 public void processingInstruction (String target, String data) 463 throws SAXException 464 { 465 // we can't create populated entity ref nodes using 466 // only public DOM APIs (they've got to be readonly) 467 if (currentEntity != null) 468 return; 469 470 ProcessingInstruction pi; 471 472 if (isL2 473 // && consumer.isUsingNamespaces () 474 && target.indexOf (':') != -1) 475 namespaceError ( 476 "PI target name is namespace nonconformant: " 477 + target); 478 if (inDTD) 479 return; 480 pi = document.createProcessingInstruction (target, data); 481 top.appendChild (pi); 482 } 483 484 /** 485 * Subclasses may overrride this method to provide a more efficient 486 * way to construct text nodes. 487 * Typically, copying the text into a single character array will 488 * be more efficient than doing that as well as allocating other 489 * needed for a String, including an internal StringBuilder. 490 * Those additional memory and CPU costs can be incurred later, 491 * if ever needed. 492 * Unfortunately the standard DOM factory APIs encourage those costs 493 * to be incurred early. 494 */ 495 protected Text createText ( 496 boolean isCDATA, 497 char ch [], 498 int start, 499 int length 500 ) { 501 String value = new String (ch, start, length); 502 503 if (isCDATA) 504 return document.createCDATASection (value); 505 else 506 return document.createTextNode (value); 507 } 508 509 // SAX1 510 public void characters (char ch [], int start, int length) 511 throws SAXException 512 { 513 // we can't create populated entity ref nodes using 514 // only public DOM APIs (they've got to be readonly 515 // at creation time) 516 if (currentEntity != null) 517 return; 518 519 Node lastChild = top.getLastChild (); 520 521 // merge consecutive text or CDATA nodes if appropriate. 522 if (lastChild instanceof Text) { 523 if (consumer.isHidingCDATA () 524 // consecutive Text content ... always merge 525 || (!inCDATA 526 && !(lastChild instanceof CDATASection)) 527 // consecutive CDATASection content ... don't 528 // merge between sections, only within them 529 || (inCDATA && mergeCDATA 530 && lastChild instanceof CDATASection) 531 ) { 532 CharacterData last = (CharacterData) lastChild; 533 String value = new String (ch, start, length); 534 535 last.appendData (value); 536 return; 537 } 538 } 539 if (inCDATA && !consumer.isHidingCDATA ()) { 540 top.appendChild (createText (true, ch, start, length)); 541 mergeCDATA = true; 542 } else 543 top.appendChild (createText (false, ch, start, length)); 544 } 545 546 // SAX2 547 public void skippedEntity (String name) 548 throws SAXException 549 { 550 // this callback is useless except to report errors, since 551 // we can't know if the ref was in content, within an 552 // attribute, within a declaration ... only one of those 553 // cases supports more intelligent action than a panic. 554 fatal ("skipped entity: " + name, null); 555 } 556 557 // SAX2 558 public void startPrefixMapping (String prefix, String uri) 559 throws SAXException 560 { 561 // reconstruct "xmlns" attributes deleted by all 562 // SAX2 parsers without "namespace-prefixes" = true 563 if ("".equals (prefix)) 564 attributes.addAttribute ("", "", "xmlns", 565 "CDATA", uri); 566 else 567 attributes.addAttribute ("", "", "xmlns:" + prefix, 568 "CDATA", uri); 569 recreatedAttrs = true; 570 } 571 572 // SAX2 573 public void endPrefixMapping (String prefix) 574 throws SAXException 575 { } 576 577 // SAX2 578 public void startElement ( 579 String uri, 580 String localName, 581 String qName, 582 Attributes atts 583 ) throws SAXException 584 { 585 // we can't create populated entity ref nodes using 586 // only public DOM APIs (they've got to be readonly) 587 if (currentEntity != null) 588 return; 589 590 // parser discarded basic information; DOM tree isn't writable 591 // without massaging to assign prefixes to all nodes. 592 // the "NSFilter" class does that massaging. 593 if (qName.length () == 0) 594 qName = localName; 595 596 597 Element element; 598 int length = atts.getLength (); 599 600 if (!isL2) { 601 element = document.createElement (qName); 602 603 // first the explicit attributes ... 604 length = atts.getLength (); 605 for (int i = 0; i < length; i++) 606 element.setAttribute (atts.getQName (i), 607 atts.getValue (i)); 608 // ... then any recreated ones (DOM deletes duplicates) 609 if (recreatedAttrs) { 610 recreatedAttrs = false; 611 length = attributes.getLength (); 612 for (int i = 0; i < length; i++) 613 element.setAttribute (attributes.getQName (i), 614 attributes.getValue (i)); 615 attributes.clear (); 616 } 617 618 top.appendChild (element); 619 top = element; 620 return; 621 } 622 623 // For an L2 DOM when namespace use is enabled, use 624 // createElementNS/createAttributeNS except when 625 // (a) it's an element in the default namespace, or 626 // (b) it's an attribute with no prefix 627 String namespace; 628 629 if (localName.length () != 0) 630 namespace = (uri.length () == 0) ? null : uri; 631 else 632 namespace = getNamespace (getPrefix (qName), atts); 633 634 // Always use createElementNS -- 2005-09-24 hsivonen 635 element = document.createElementNS (namespace, qName); 636 637 populateAttributes (element, atts); 638 if (recreatedAttrs) { 639 recreatedAttrs = false; 640 // ... DOM deletes any duplicates 641 populateAttributes (element, attributes); 642 attributes.clear (); 643 } 644 645 top.appendChild (element); 646 top = element; 647 } 648 649 final static String xmlnsURI = "http://www.w3.org/2000/xmlns/"; 650 651 private void populateAttributes (Element element, Attributes attrs) 652 throws SAXParseException 653 { 654 int length = attrs.getLength (); 655 656 for (int i = 0; i < length; i++) { 657 String type = attrs.getType (i); 658 String value = attrs.getValue (i); 659 String name = attrs.getQName (i); 660 String local = attrs.getLocalName (i); 661 String uri = attrs.getURI (i); 662 663 // parser discarded basic information, DOM tree isn't writable 664 if (name.length () == 0) 665 name = local; 666 667 // all attribute types other than these three may not 668 // contain scoped names... enumerated attributes get 669 // reported as NMTOKEN, except for NOTATION values 670 if (!("CDATA".equals (type) 671 || "NMTOKEN".equals (type) 672 || "NMTOKENS".equals (type))) { 673 if (value.indexOf (':') != -1) { 674 namespaceError ( 675 "namespace nonconformant attribute value: " 676 + "<" + element.getNodeName () 677 + " " + name + "='" + value + "' ...>"); 678 } 679 } 680 681 // xmlns="" is legal (undoes default NS) 682 // xmlns:foo="" is illegal 683 String prefix = getPrefix (name); 684 String namespace; 685 686 if ("xmlns".equals (prefix)) { 687 if ("".equals (value)) 688 namespaceError ("illegal null namespace decl, " + name); 689 namespace = xmlnsURI; 690 } else if ("xmlns".equals (name)) 691 namespace = xmlnsURI; 692 693 else if (prefix == null) 694 namespace = null; 695 else if (!"".equals(uri) && uri.length () != 0) 696 namespace = uri; 697 else 698 namespace = getNamespace (prefix, attrs); 699 700 if (namespace == null) 701 element.setAttribute (name, value); 702 else 703 element.setAttributeNS (namespace, name, value); 704 } 705 } 706 707 private String getPrefix (String name) 708 { 709 int temp; 710 711 if ((temp = name.indexOf (':')) > 0) 712 return name.substring (0, temp); 713 return null; 714 } 715 716 // used with SAX1-level parser output 717 private String getNamespace (String prefix, Attributes attrs) 718 throws SAXParseException 719 { 720 String namespace; 721 String decl; 722 723 // defaulting 724 if (prefix == null) { 725 decl = "xmlns"; 726 namespace = attrs.getValue (decl); 727 if ("".equals (namespace)) 728 return null; 729 else if (namespace != null) 730 return namespace; 731 732 // "xmlns" is like a keyword 733 // ... according to the Namespace REC, but DOM L2 CR2+ 734 // and Infoset violate that by assigning a namespace. 735 // that conflict is resolved elsewhere. 736 } else if ("xmlns".equals (prefix)) 737 return null; 738 739 // "xml" prefix is fixed 740 else if ("xml".equals (prefix)) 741 return "http://www.w3.org/XML/1998/namespace"; 742 743 // otherwise, expect a declaration 744 else { 745 decl = "xmlns:" + prefix; 746 namespace = attrs.getValue (decl); 747 } 748 749 // if we found a local declaration, great 750 if (namespace != null) 751 return namespace; 752 753 754 // ELSE ... search up the tree we've been building 755 for (Node n = top; 756 n != null && n.getNodeType () != Node.DOCUMENT_NODE; 757 n = n.getParentNode ()) { 758 if (n.getNodeType () == Node.ENTITY_REFERENCE_NODE) 759 continue; 760 Element e = (Element) n; 761 Attr attr = e.getAttributeNode (decl); 762 if (attr != null) 763 return attr.getNodeValue (); 764 } 765 // see above re "xmlns" as keyword 766 if ("xmlns".equals (decl)) 767 return null; 768 769 namespaceError ("Undeclared namespace prefix: " + prefix); 770 return null; 771 } 772 773 // SAX2 774 public void endElement (String uri, String localName, String qName) 775 throws SAXException 776 { 777 // we can't create populated entity ref nodes using 778 // only public DOM APIs (they've got to be readonly) 779 if (currentEntity != null) 780 return; 781 782 top = top.getParentNode (); 783 } 784 785 // SAX1 (mandatory reporting if validating) 786 public void ignorableWhitespace (char ch [], int start, int length) 787 throws SAXException 788 { 789 if (consumer.isHidingWhitespace ()) 790 return; 791 characters (ch, start, length); 792 } 793 794 // SAX2 lexical event 795 public void startCDATA () 796 throws SAXException 797 { 798 inCDATA = true; 799 // true except for the first fragment of a cdata section 800 mergeCDATA = false; 801 } 802 803 // SAX2 lexical event 804 public void endCDATA () 805 throws SAXException 806 { 807 inCDATA = false; 808 } 809 810 // SAX2 lexical event 811 // 812 // this SAX2 callback merges two unrelated things: 813 // - Declaration of the root element type ... belongs with 814 // the other DTD declaration methods, NOT HERE. 815 // - IDs for the optional external subset ... belongs here 816 // with other lexical information. 817 // 818 // ...and it doesn't include the internal DTD subset, desired 819 // both to support DOM L2 and to enable "pass through" processing 820 // 821 public void startDTD (String name, String publicId, String SystemId) 822 throws SAXException 823 { 824 // need to filter out comments and PIs within the DTD 825 inDTD = true; 826 } 827 828 // SAX2 lexical event 829 public void endDTD () 830 throws SAXException 831 { 832 inDTD = false; 833 } 834 835 // SAX2 lexical event 836 public void comment (char ch [], int start, int length) 837 throws SAXException 838 { 839 Node comment; 840 841 // we can't create populated entity ref nodes using 842 // only public DOM APIs (they've got to be readonly) 843 if (consumer.isHidingComments () 844 || inDTD 845 || currentEntity != null) 846 return; 847 comment = document.createComment (new String (ch, start, length)); 848 top.appendChild (comment); 849 } 850 851 /** 852 * May be overridden by subclasses to return true, indicating 853 * that entity reference nodes can be populated and then made 854 * read-only. 855 */ 856 public boolean canPopulateEntityRefs () 857 { return false; } 858 859 // SAX2 lexical event 860 public void startEntity (String name) 861 throws SAXException 862 { 863 // are we ignoring what would be contents of an 864 // entity ref, since we can't populate it? 865 if (currentEntity != null) 866 return; 867 868 // Are we hiding all entity boundaries? 869 if (consumer.isHidingReferences ()) 870 return; 871 872 // SAX2 shows parameter entities; DOM hides them 873 if (name.charAt (0) == '%' || "[dtd]".equals (name)) 874 return; 875 876 // Since we can't create a populated entity ref node in any 877 // standard way, we create an unpopulated one. 878 EntityReference ref = document.createEntityReference (name); 879 top.appendChild (ref); 880 top = ref; 881 882 // ... allowing subclasses to populate them 883 if (!canPopulateEntityRefs ()) 884 currentEntity = name; 885 } 886 887 // SAX2 lexical event 888 public void endEntity (String name) 889 throws SAXException 890 { 891 if (name.charAt (0) == '%' || "[dtd]".equals (name)) 892 return; 893 if (name.equals (currentEntity)) 894 currentEntity = null; 895 if (!consumer.isHidingReferences ()) 896 top = top.getParentNode (); 897 } 898 899 900 // SAX1 DTD event 901 public void notationDecl ( 902 String name, 903 String publicId, String SystemId 904 ) throws SAXException 905 { 906 /* IGNORE -- no public DOM API lets us store these 907 * into the doctype node 908 */ 909 } 910 911 // SAX1 DTD event 912 public void unparsedEntityDecl ( 913 String name, 914 String publicId, String SystemId, 915 String notationName 916 ) throws SAXException 917 { 918 /* IGNORE -- no public DOM API lets us store these 919 * into the doctype node 920 */ 921 } 922 923 // SAX2 declaration event 924 public void elementDecl (String name, String model) 925 throws SAXException 926 { 927 /* IGNORE -- no content model support in DOM L2 */ 928 } 929 930 // SAX2 declaration event 931 public void attributeDecl ( 932 String eName, 933 String aName, 934 String type, 935 String mode, 936 String value 937 ) throws SAXException 938 { 939 /* IGNORE -- no attribute model support in DOM L2 */ 940 } 941 942 // SAX2 declaration event 943 public void internalEntityDecl (String name, String value) 944 throws SAXException 945 { 946 /* IGNORE -- no public DOM API lets us store these 947 * into the doctype node 948 */ 949 } 950 951 // SAX2 declaration event 952 public void externalEntityDecl ( 953 String name, 954 String publicId, 955 String SystemId 956 ) throws SAXException 957 { 958 /* IGNORE -- no public DOM API lets us store these 959 * into the doctype node 960 */ 961 } 962 963 // 964 // These really should offer the option of nonfatal handling, 965 // like other validity errors, though that would cause major 966 // chaos in the DOM data structures. DOM is already spec'd 967 // to treat many of these as fatal, so this is consistent. 968 // 969 private void namespaceError (String description) 970 throws SAXParseException 971 { 972 SAXParseException err; 973 974 err = new SAXParseException (description, locator); 975 throw err; 976 } 977 } 978 }