1 /* SAXDriver.java -- 2 Copyright (C) 1999,2000,2001,2004 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 Portions derived from code which carried the following notice: 39 40 Copyright (c) 1997, 1998 by Microstar Software Ltd. 41 42 AElfred is free for both commercial and non-commercial use and 43 redistribution, provided that Microstar's copyright and disclaimer are 44 retained intact. You are free to modify AElfred for your own use and 45 to redistribute AElfred with your modifications, provided that the 46 modifications are clearly documented. 47 48 This program is distributed in the hope that it will be useful, but 49 WITHOUT ANY WARRANTY; without even the implied warranty of 50 merchantability or fitness for a particular purpose. Please use it AT 51 YOUR OWN RISK. 52 */ 53 54 package gnu.xml.aelfred2; 55 56 import java.io.*; 57 58 import java.net.MalformedURLException; 59 import java.net.URL; 60 import java.util.Locale; 61 import java.util.Stack; 62 63 import java.util.ArrayList; 64 import java.util.Collections; 65 import java.util.Enumeration; 66 import java.util.Iterator; 67 import java.util.List; 68 69 import org.xml.sax.*; 70 import org.xml.sax.ext.*; 71 import org.xml.sax.helpers.NamespaceSupport; 72 73 74 /** 75 * An enhanced SAX2 version of Microstar's Ælfred XML parser. 76 * The enhancements primarily relate to significant improvements in 77 * conformance to the XML specification, and SAX2 support. Performance 78 * has been improved. See the package level documentation for more 79 * information. 80 * 81 * <table border="1" width='100%' cellpadding='3' cellspacing='0'> 82 * <tr bgcolor='#ccccff'> 83 * <th><font size='+1'>Name</font></th> 84 * <th><font size='+1'>Notes</font></th></tr> 85 * 86 * <tr><td colspan=2><center><em>Features ... URL prefix is 87 * <b>http://xml.org/sax/features/</b></em></center></td></tr> 88 * 89 * <tr><td>(URL)/external-general-entities</td> 90 * <td>Value defaults to <em>true</em></td></tr> 91 * <tr><td>(URL)/external-parameter-entities</td> 92 * <td>Value defaults to <em>true</em></td></tr> 93 * <tr><td>(URL)/is-standalone</td> 94 * <td>(PRELIMINARY) Returns true iff the document's parsing 95 * has started (some non-error event after <em>startDocument()</em> 96 * was reported) and the document's standalone flag is set.</td></tr> 97 * <tr><td>(URL)/namespace-prefixes</td> 98 * <td>Value defaults to <em>false</em> (but XML 1.0 names are 99 * always reported)</td></tr> 100 * <tr><td>(URL)/lexical-handler/parameter-entities</td> 101 * <td>Value is fixed at <em>true</em></td></tr> 102 * <tr><td>(URL)/namespaces</td> 103 * <td>Value defaults to <em>true</em></td></tr> 104 * <tr><td>(URL)/resolve-dtd-uris</td> 105 * <td>(PRELIMINARY) Value defaults to <em>true</em></td></tr> 106 * <tr><td>(URL)/string-interning</td> 107 * <td>Value is fixed at <em>true</em></td></tr> 108 * <tr><td>(URL)/use-attributes2</td> 109 * <td>(PRELIMINARY) Value is fixed at <em>true</em></td></tr> 110 * <tr><td>(URL)/use-entity-resolver2</td> 111 * <td>(PRELIMINARY) Value defaults to <em>true</em></td></tr> 112 * <tr><td>(URL)/validation</td> 113 * <td>Value is fixed at <em>false</em></td></tr> 114 * 115 * <tr><td colspan=2><center><em>Handler Properties ... URL prefix is 116 * <b>http://xml.org/sax/properties/</b></em></center></td></tr> 117 * 118 * <tr><td>(URL)/declaration-handler</td> 119 * <td>A declaration handler may be provided. </td></tr> 120 * <tr><td>(URL)/lexical-handler</td> 121 * <td>A lexical handler may be provided. </td></tr> 122 * </table> 123 * 124 * <p>This parser currently implements the SAX1 Parser API, but 125 * it may not continue to do so in the future. 126 * 127 * @author Written by David Megginson (version 1.2a from Microstar) 128 * @author Updated by David Brownell <dbrownell@users.sourceforge.net> 129 * @see org.xml.sax.Parser 130 */ 131 final public class SAXDriver 132 implements Locator, Attributes2, XMLReader, Parser, AttributeList 133 { 134 135 private final DefaultHandler2 base = new DefaultHandler2(); 136 private XmlParser parser; 137 138 private EntityResolver entityResolver = base; 139 private EntityResolver2 resolver2 = null; 140 private ContentHandler contentHandler = base; 141 private DTDHandler dtdHandler = base; 142 private ErrorHandler errorHandler = base; 143 private DeclHandler declHandler = base; 144 private LexicalHandler lexicalHandler = base; 145 146 private String elementName; 147 private Stack entityStack; 148 149 // one vector (of object/struct): faster, smaller 150 private List attributesList; 151 152 private boolean namespaces = true; 153 private boolean xmlNames = false; 154 private boolean extGE = true; 155 private boolean extPE = true; 156 private boolean resolveAll = true; 157 private boolean useResolver2 = true; 158 159 // package private to allow (read-only) access in XmlParser 160 boolean stringInterning = true; 161 162 private int attributeCount; 163 private boolean attributes; 164 private String[] nsTemp; 165 private NamespaceSupport prefixStack; 166 167 // 168 // Constructor. 169 // 170 171 /** 172 * Constructs a SAX Parser. 173 */ SAXDriver()174 public SAXDriver() 175 { 176 reset(); 177 } 178 reset()179 private void reset() 180 { 181 elementName = null; 182 entityStack = new Stack(); 183 attributesList = Collections.synchronizedList(new ArrayList()); 184 attributeCount = 0; 185 attributes = false; 186 nsTemp = new String[3]; 187 prefixStack = null; 188 } 189 190 191 // 192 // Implementation of org.xml.sax.Parser. 193 // 194 195 /** 196 * <b>SAX1</b>: Sets the locale used for diagnostics; currently, 197 * only locales using the English language are supported. 198 * @param locale The locale for which diagnostics will be generated 199 */ setLocale(Locale locale)200 public void setLocale(Locale locale) 201 throws SAXException 202 { 203 if ("en".equals(locale.getLanguage())) 204 { 205 return; 206 } 207 throw new SAXException ("AElfred2 only supports English locales."); 208 } 209 210 /** 211 * <b>SAX2</b>: Returns the object used when resolving external 212 * entities during parsing (both general and parameter entities). 213 */ getEntityResolver()214 public EntityResolver getEntityResolver() 215 { 216 return (entityResolver == base) ? null : entityResolver; 217 } 218 219 /** 220 * <b>SAX1, SAX2</b>: Set the entity resolver for this parser. 221 * @param handler The object to receive entity events. 222 */ setEntityResolver(EntityResolver resolver)223 public void setEntityResolver(EntityResolver resolver) 224 { 225 if (resolver instanceof EntityResolver2) 226 { 227 resolver2 = (EntityResolver2) resolver; 228 } 229 else 230 { 231 resolver2 = null; 232 } 233 if (resolver == null) 234 { 235 resolver = base; 236 } 237 entityResolver = resolver; 238 } 239 240 /** 241 * <b>SAX2</b>: Returns the object used to process declarations related 242 * to notations and unparsed entities. 243 */ getDTDHandler()244 public DTDHandler getDTDHandler() 245 { 246 return (dtdHandler == base) ? null : dtdHandler; 247 } 248 249 /** 250 * <b>SAX1, SAX2</b>: Set the DTD handler for this parser. 251 * @param handler The object to receive DTD events. 252 */ setDTDHandler(DTDHandler handler)253 public void setDTDHandler(DTDHandler handler) 254 { 255 if (handler == null) 256 { 257 handler = base; 258 } 259 this.dtdHandler = handler; 260 } 261 262 263 /** 264 * <b>SAX1</b>: Set the document handler for this parser. If a 265 * content handler was set, this document handler will supplant it. 266 * The parser is set to report all XML 1.0 names rather than to 267 * filter out "xmlns" attributes (the "namespace-prefixes" feature 268 * is set to true). 269 * 270 * @deprecated SAX2 programs should use the XMLReader interface 271 * and a ContentHandler. 272 * 273 * @param handler The object to receive document events. 274 */ setDocumentHandler(DocumentHandler handler)275 public void setDocumentHandler(DocumentHandler handler) 276 { 277 contentHandler = new Adapter(handler); 278 xmlNames = true; 279 } 280 281 /** 282 * <b>SAX2</b>: Returns the object used to report the logical 283 * content of an XML document. 284 */ getContentHandler()285 public ContentHandler getContentHandler() 286 { 287 return (contentHandler == base) ? null : contentHandler; 288 } 289 290 /** 291 * <b>SAX2</b>: Assigns the object used to report the logical 292 * content of an XML document. If a document handler was set, 293 * this content handler will supplant it (but XML 1.0 style name 294 * reporting may remain enabled). 295 */ setContentHandler(ContentHandler handler)296 public void setContentHandler(ContentHandler handler) 297 { 298 if (handler == null) 299 { 300 handler = base; 301 } 302 contentHandler = handler; 303 } 304 305 /** 306 * <b>SAX1, SAX2</b>: Set the error handler for this parser. 307 * @param handler The object to receive error events. 308 */ setErrorHandler(ErrorHandler handler)309 public void setErrorHandler(ErrorHandler handler) 310 { 311 if (handler == null) 312 { 313 handler = base; 314 } 315 this.errorHandler = handler; 316 } 317 318 /** 319 * <b>SAX2</b>: Returns the object used to receive callbacks for XML 320 * errors of all levels (fatal, nonfatal, warning); this is never null; 321 */ getErrorHandler()322 public ErrorHandler getErrorHandler() 323 { 324 return (errorHandler == base) ? null : errorHandler; 325 } 326 327 /** 328 * <b>SAX1, SAX2</b>: Auxiliary API to parse an XML document, used mostly 329 * when no URI is available. 330 * If you want anything useful to happen, you should set 331 * at least one type of handler. 332 * @param source The XML input source. Don't set 'encoding' unless 333 * you know for a fact that it's correct. 334 * @see #setEntityResolver 335 * @see #setDTDHandler 336 * @see #setContentHandler 337 * @see #setErrorHandler 338 * @exception SAXException The handlers may throw any SAXException, 339 * and the parser normally throws SAXParseException objects. 340 * @exception IOException IOExceptions are normally through through 341 * the parser if there are problems reading the source document. 342 */ parse(InputSource source)343 public void parse(InputSource source) 344 throws SAXException, IOException 345 { 346 synchronized (base) 347 { 348 parser = new XmlParser(); 349 if (namespaces) 350 { 351 prefixStack = new NamespaceSupport(); 352 } 353 else if (!xmlNames) 354 { 355 throw new IllegalStateException(); 356 } 357 parser.setHandler(this); 358 359 try 360 { 361 Reader r = source.getCharacterStream(); 362 InputStream in = source.getByteStream(); 363 364 parser.doParse(source.getSystemId(), 365 source.getPublicId(), 366 r, 367 in, 368 source.getEncoding()); 369 } 370 catch (SAXException e) 371 { 372 throw e; 373 } 374 catch (IOException e) 375 { 376 throw e; 377 } 378 catch (RuntimeException e) 379 { 380 throw e; 381 } 382 catch (Exception e) 383 { 384 throw new SAXParseException(e.getMessage(), this, e); 385 } 386 finally 387 { 388 contentHandler.endDocument(); 389 reset(); 390 } 391 } 392 } 393 394 /** 395 * <b>SAX1, SAX2</b>: Preferred API to parse an XML document, using a 396 * system identifier (URI). 397 */ parse(String systemId)398 public void parse(String systemId) 399 throws SAXException, IOException 400 { 401 parse(new InputSource(systemId)); 402 } 403 404 // 405 // Implementation of SAX2 "XMLReader" interface 406 // 407 static final String FEATURE = "http://xml.org/sax/features/"; 408 static final String PROPERTY = "http://xml.org/sax/properties/"; 409 410 /** 411 * <b>SAX2</b>: Tells the value of the specified feature flag. 412 * 413 * @exception SAXNotRecognizedException thrown if the feature flag 414 * is neither built in, nor yet assigned. 415 */ getFeature(String featureId)416 public boolean getFeature(String featureId) 417 throws SAXNotRecognizedException, SAXNotSupportedException 418 { 419 if ((FEATURE + "validation").equals(featureId)) 420 { 421 return false; 422 } 423 424 // external entities (both types) are optionally included 425 if ((FEATURE + "external-general-entities").equals(featureId)) 426 { 427 return extGE; 428 } 429 if ((FEATURE + "external-parameter-entities").equals(featureId)) 430 { 431 return extPE; 432 } 433 434 // element/attribute names are as written in document; no mangling 435 if ((FEATURE + "namespace-prefixes").equals(featureId)) 436 { 437 return xmlNames; 438 } 439 440 // report element/attribute namespaces? 441 if ((FEATURE + "namespaces").equals(featureId)) 442 { 443 return namespaces; 444 } 445 446 // all PEs and GEs are reported 447 if ((FEATURE + "lexical-handler/parameter-entities").equals(featureId)) 448 { 449 return true; 450 } 451 452 // default is true 453 if ((FEATURE + "string-interning").equals(featureId)) 454 { 455 return stringInterning; 456 } 457 458 // EXTENSIONS 1.1 459 460 // always returns isSpecified info 461 if ((FEATURE + "use-attributes2").equals(featureId)) 462 { 463 return true; 464 } 465 466 // meaningful between startDocument/endDocument 467 if ((FEATURE + "is-standalone").equals(featureId)) 468 { 469 if (parser == null) 470 { 471 throw new SAXNotSupportedException(featureId); 472 } 473 return parser.isStandalone(); 474 } 475 476 // optionally don't absolutize URIs in declarations 477 if ((FEATURE + "resolve-dtd-uris").equals(featureId)) 478 { 479 return resolveAll; 480 } 481 482 // optionally use resolver2 interface methods, if possible 483 if ((FEATURE + "use-entity-resolver2").equals(featureId)) 484 { 485 return useResolver2; 486 } 487 488 throw new SAXNotRecognizedException(featureId); 489 } 490 491 // package private getDeclHandler()492 DeclHandler getDeclHandler() 493 { 494 return declHandler; 495 } 496 497 // package private resolveURIs()498 boolean resolveURIs() 499 { 500 return resolveAll; 501 } 502 503 /** 504 * <b>SAX2</b>: Returns the specified property. 505 * 506 * @exception SAXNotRecognizedException thrown if the property value 507 * is neither built in, nor yet stored. 508 */ getProperty(String propertyId)509 public Object getProperty(String propertyId) 510 throws SAXNotRecognizedException 511 { 512 if ((PROPERTY + "declaration-handler").equals(propertyId)) 513 { 514 return (declHandler == base) ? null : declHandler; 515 } 516 517 if ((PROPERTY + "lexical-handler").equals(propertyId)) 518 { 519 return (lexicalHandler == base) ? null : lexicalHandler; 520 } 521 522 // unknown properties 523 throw new SAXNotRecognizedException(propertyId); 524 } 525 526 /** 527 * <b>SAX2</b>: Sets the state of feature flags in this parser. Some 528 * built-in feature flags are mutable. 529 */ setFeature(String featureId, boolean value)530 public void setFeature(String featureId, boolean value) 531 throws SAXNotRecognizedException, SAXNotSupportedException 532 { 533 boolean state; 534 535 // Features with a defined value, we just change it if we can. 536 state = getFeature (featureId); 537 538 if (state == value) 539 { 540 return; 541 } 542 if (parser != null) 543 { 544 throw new SAXNotSupportedException("not while parsing"); 545 } 546 547 if ((FEATURE + "namespace-prefixes").equals(featureId)) 548 { 549 // in this implementation, this only affects xmlns reporting 550 xmlNames = value; 551 // forcibly prevent illegal parser state 552 if (!xmlNames) 553 { 554 namespaces = true; 555 } 556 return; 557 } 558 559 if ((FEATURE + "namespaces").equals(featureId)) 560 { 561 namespaces = value; 562 // forcibly prevent illegal parser state 563 if (!namespaces) 564 { 565 xmlNames = true; 566 } 567 return; 568 } 569 570 if ((FEATURE + "external-general-entities").equals(featureId)) 571 { 572 extGE = value; 573 return; 574 } 575 if ((FEATURE + "external-parameter-entities").equals(featureId)) 576 { 577 extPE = value; 578 return; 579 } 580 if ((FEATURE + "resolve-dtd-uris").equals(featureId)) 581 { 582 resolveAll = value; 583 return; 584 } 585 586 if ((FEATURE + "use-entity-resolver2").equals(featureId)) 587 { 588 useResolver2 = value; 589 return; 590 } 591 592 throw new SAXNotRecognizedException(featureId); 593 } 594 595 /** 596 * <b>SAX2</b>: Assigns the specified property. Like SAX1 handlers, 597 * these may be changed at any time. 598 */ setProperty(String propertyId, Object value)599 public void setProperty(String propertyId, Object value) 600 throws SAXNotRecognizedException, SAXNotSupportedException 601 { 602 // see if the property is recognized 603 getProperty(propertyId); 604 605 // Properties with a defined value, we just change it if we can. 606 607 if ((PROPERTY + "declaration-handler").equals(propertyId)) 608 { 609 if (value == null) 610 { 611 declHandler = base; 612 } 613 else if (!(value instanceof DeclHandler)) 614 { 615 throw new SAXNotSupportedException(propertyId); 616 } 617 else 618 { 619 declHandler = (DeclHandler) value; 620 } 621 return ; 622 } 623 624 if ((PROPERTY + "lexical-handler").equals(propertyId)) 625 { 626 if (value == null) 627 { 628 lexicalHandler = base; 629 } 630 else if (!(value instanceof LexicalHandler)) 631 { 632 throw new SAXNotSupportedException(propertyId); 633 } 634 else 635 { 636 lexicalHandler = (LexicalHandler) value; 637 } 638 return; 639 } 640 641 throw new SAXNotSupportedException(propertyId); 642 } 643 644 // 645 // This is where the driver receives XmlParser callbacks and translates 646 // them into SAX callbacks. Some more callbacks have been added for 647 // SAX2 support. 648 // 649 startDocument()650 void startDocument() 651 throws SAXException 652 { 653 contentHandler.setDocumentLocator(this); 654 contentHandler.startDocument(); 655 attributesList.clear(); 656 } 657 skippedEntity(String name)658 void skippedEntity(String name) 659 throws SAXException 660 { 661 contentHandler.skippedEntity(name); 662 } 663 getExternalSubset(String name, String baseURI)664 InputSource getExternalSubset(String name, String baseURI) 665 throws SAXException, IOException 666 { 667 if (resolver2 == null || !useResolver2 || !extPE) 668 { 669 return null; 670 } 671 return resolver2.getExternalSubset(name, baseURI); 672 } 673 resolveEntity(boolean isPE, String name, InputSource in, String baseURI)674 InputSource resolveEntity(boolean isPE, String name, 675 InputSource in, String baseURI) 676 throws SAXException, IOException 677 { 678 InputSource source; 679 680 // external entities might be skipped 681 if (isPE && !extPE) 682 { 683 return null; 684 } 685 if (!isPE && !extGE) 686 { 687 return null; 688 } 689 690 // ... or not 691 lexicalHandler.startEntity(name); 692 if (resolver2 != null && useResolver2) 693 { 694 source = resolver2.resolveEntity(name, in.getPublicId(), 695 baseURI, in.getSystemId()); 696 if (source == null) 697 { 698 in.setSystemId(absolutize(baseURI, 699 in.getSystemId(), false)); 700 source = in; 701 } 702 } 703 else 704 { 705 in.setSystemId(absolutize(baseURI, 706 in.getSystemId(), 707 entityResolver != base)); 708 source = entityResolver.resolveEntity(in.getPublicId(), 709 in.getSystemId()); 710 if (source == null) 711 { 712 source = in; 713 } 714 } 715 startExternalEntity(name, source.getSystemId(), true); 716 return source; 717 } 718 719 // absolutize a system ID relative to the specified base URI 720 // (temporarily) package-visible for external entity decls absolutize(String baseURI, String systemId, boolean nice)721 String absolutize(String baseURI, String systemId, boolean nice) 722 throws MalformedURLException, SAXException 723 { 724 // FIXME normalize system IDs -- when? 725 // - Convert to UTF-8 726 // - Map reserved and non-ASCII characters to %HH 727 728 try 729 { 730 if (baseURI == null) 731 { 732 if (XmlParser.uriWarnings) 733 { 734 warn ("No base URI; hope this SYSTEM id is absolute: " 735 + systemId); 736 } 737 return new URL(systemId).toString(); 738 } 739 else 740 { 741 return new URL(new URL(baseURI), systemId).toString(); 742 } 743 } 744 catch (MalformedURLException e) 745 { 746 // Let unknown URI schemes pass through unless we need 747 // the JVM to map them to i/o streams for us... 748 if (!nice) 749 { 750 throw e; 751 } 752 753 // sometimes sysids for notations or unparsed entities 754 // aren't really URIs... 755 warn("Can't absolutize SYSTEM id: " + e.getMessage()); 756 return systemId; 757 } 758 } 759 startExternalEntity(String name, String systemId, boolean stackOnly)760 void startExternalEntity(String name, String systemId, boolean stackOnly) 761 throws SAXException 762 { 763 // The following warning was deleted because the application has the 764 // option of not setting systemId. Sun's JAXP or Xerces seems to 765 // ignore this case. 766 /* 767 if (systemId == null) 768 warn ("URI was not reported to parser for entity " + name); 769 */ 770 if (!stackOnly) // spliced [dtd] needs startEntity 771 { 772 lexicalHandler.startEntity(name); 773 } 774 entityStack.push(systemId); 775 } 776 endExternalEntity(String name)777 void endExternalEntity(String name) 778 throws SAXException 779 { 780 if (!"[document]".equals(name)) 781 { 782 lexicalHandler.endEntity(name); 783 } 784 entityStack.pop(); 785 } 786 startInternalEntity(String name)787 void startInternalEntity(String name) 788 throws SAXException 789 { 790 lexicalHandler.startEntity(name); 791 } 792 endInternalEntity(String name)793 void endInternalEntity(String name) 794 throws SAXException 795 { 796 lexicalHandler.endEntity(name); 797 } 798 doctypeDecl(String name, String publicId, String systemId)799 void doctypeDecl(String name, String publicId, String systemId) 800 throws SAXException 801 { 802 lexicalHandler.startDTD(name, publicId, systemId); 803 804 // ... the "name" is a declaration and should be given 805 // to the DeclHandler (but sax2 doesn't). 806 807 // the IDs for the external subset are lexical details, 808 // as are the contents of the internal subset; but sax2 809 // doesn't provide the internal subset "pre-parse" 810 } 811 notationDecl(String name, String publicId, String systemId, String baseUri)812 void notationDecl(String name, String publicId, String systemId, 813 String baseUri) 814 throws SAXException 815 { 816 try 817 { 818 dtdHandler.notationDecl(name, publicId, 819 (resolveAll && systemId != null) 820 ? absolutize(baseUri, systemId, true) 821 : systemId); 822 } 823 catch (IOException e) 824 { 825 // "can't happen" 826 throw new SAXParseException(e.getMessage(), this, e); 827 } 828 } 829 unparsedEntityDecl(String name, String publicId, String systemId, String baseUri, String notation)830 void unparsedEntityDecl(String name, String publicId, String systemId, 831 String baseUri, String notation) 832 throws SAXException 833 { 834 try 835 { 836 dtdHandler.unparsedEntityDecl(name, publicId, 837 resolveAll 838 ? absolutize(baseUri, systemId, true) 839 : systemId, 840 notation); 841 } 842 catch (IOException e) 843 { 844 // "can't happen" 845 throw new SAXParseException(e.getMessage(), this, e); 846 } 847 } 848 endDoctype()849 void endDoctype() 850 throws SAXException 851 { 852 lexicalHandler.endDTD(); 853 } 854 declarePrefix(String prefix, String uri)855 private void declarePrefix(String prefix, String uri) 856 throws SAXException 857 { 858 int index = uri.indexOf(':'); 859 860 // many versions of nwalsh docbook stylesheets 861 // have bogus URLs; so this can't be an error... 862 if (index < 1 && uri.length() != 0) 863 { 864 warn("relative URI for namespace: " + uri); 865 } 866 867 // FIXME: char [0] must be ascii alpha; chars [1..index] 868 // must be ascii alphanumeric or in "+-." [RFC 2396] 869 870 //Namespace Constraints 871 //name for xml prefix must be http://www.w3.org/XML/1998/namespace 872 boolean prefixEquality = prefix.equals("xml"); 873 boolean uriEquality = uri.equals("http://www.w3.org/XML/1998/namespace"); 874 if ((prefixEquality || uriEquality) && !(prefixEquality && uriEquality)) 875 { 876 fatal("xml is by definition bound to the namespace name " + 877 "http://www.w3.org/XML/1998/namespace"); 878 } 879 880 //xmlns prefix declaration is illegal but xml prefix declaration is llegal... 881 if (prefixEquality && uriEquality) 882 { 883 return; 884 } 885 886 //name for xmlns prefix must be http://www.w3.org/2000/xmlns/ 887 prefixEquality = prefix.equals("xmlns"); 888 uriEquality = uri.equals("http://www.w3.org/2000/xmlns/"); 889 if ((prefixEquality || uriEquality) && !(prefixEquality && uriEquality)) 890 { 891 fatal("http://www.w3.org/2000/xmlns/ is by definition bound" + 892 " to prefix xmlns"); 893 } 894 895 //even if the uri is http://www.w3.org/2000/xmlns/ 896 // it is illegal to declare it 897 if (prefixEquality && uriEquality) 898 { 899 fatal ("declaring the xmlns prefix is illegal"); 900 } 901 902 uri = uri.intern(); 903 prefixStack.declarePrefix(prefix, uri); 904 contentHandler.startPrefixMapping(prefix, uri); 905 } 906 attribute(String qname, String value, boolean isSpecified)907 void attribute(String qname, String value, boolean isSpecified) 908 throws SAXException 909 { 910 if (!attributes) 911 { 912 attributes = true; 913 if (namespaces) 914 { 915 prefixStack.pushContext(); 916 } 917 } 918 919 // process namespace decls immediately; 920 // then maybe forget this as an attribute 921 if (namespaces) 922 { 923 int index; 924 925 // default NS declaration? 926 if (stringInterning) 927 { 928 if ("xmlns" == qname) 929 { 930 declarePrefix("", value); 931 if (!xmlNames) 932 { 933 return; 934 } 935 } 936 // NS prefix declaration? 937 else if ((index = qname.indexOf(':')) == 5 938 && qname.startsWith("xmlns")) 939 { 940 String prefix = qname.substring(6); 941 942 if (prefix.equals("")) 943 { 944 fatal("missing prefix " + 945 "in namespace declaration attribute"); 946 } 947 if (value.length() == 0) 948 { 949 verror("missing URI in namespace declaration attribute: " 950 + qname); 951 } 952 else 953 { 954 declarePrefix(prefix, value); 955 } 956 if (!xmlNames) 957 { 958 return; 959 } 960 } 961 } 962 else 963 { 964 if ("xmlns".equals(qname)) 965 { 966 declarePrefix("", value); 967 if (!xmlNames) 968 { 969 return; 970 } 971 } 972 // NS prefix declaration? 973 else if ((index = qname.indexOf(':')) == 5 974 && qname.startsWith("xmlns")) 975 { 976 String prefix = qname.substring(6); 977 978 if (value.length() == 0) 979 { 980 verror("missing URI in namespace decl attribute: " 981 + qname); 982 } 983 else 984 { 985 declarePrefix(prefix, value); 986 } 987 if (!xmlNames) 988 { 989 return; 990 } 991 } 992 } 993 } 994 // remember this attribute ... 995 attributeCount++; 996 997 // attribute type comes from querying parser's DTD records 998 attributesList.add(new Attribute(qname, value, isSpecified)); 999 1000 } 1001 startElement(String elname)1002 void startElement(String elname) 1003 throws SAXException 1004 { 1005 ContentHandler handler = contentHandler; 1006 1007 // 1008 // NOTE: this implementation of namespace support adds something 1009 // like six percent to parsing CPU time, in a large (~50 MB) 1010 // document that doesn't use namespaces at all. (Measured by PC 1011 // sampling, with a bug where endElement processing was omitted.) 1012 // [Measurement referred to older implementation, older JVM ...] 1013 // 1014 // It ought to become notably faster in such cases. Most 1015 // costs are the prefix stack calling Hashtable.get() (2%), 1016 // String.hashCode() (1.5%) and about 1.3% each for pushing 1017 // the context, and two chunks of name processing. 1018 // 1019 1020 if (!attributes) 1021 { 1022 if (namespaces) 1023 { 1024 prefixStack.pushContext(); 1025 } 1026 } 1027 else if (namespaces) 1028 { 1029 1030 // now we can patch up namespace refs; we saw all the 1031 // declarations, so now we'll do the Right Thing 1032 Iterator itt = attributesList.iterator(); 1033 while (itt.hasNext()) 1034 { 1035 Attribute attribute = (Attribute) itt.next(); 1036 String qname = attribute.name; 1037 int index; 1038 1039 // default NS declaration? 1040 if (stringInterning) 1041 { 1042 if ("xmlns" == qname) 1043 { 1044 continue; 1045 } 1046 } 1047 else 1048 { 1049 if ("xmlns".equals(qname)) 1050 { 1051 continue; 1052 } 1053 } 1054 //Illegal in the new Namespaces Draft 1055 //should it be only in 1.1 docs?? 1056 if (qname.equals (":")) 1057 { 1058 fatal("namespace names consisting of a single colon " + 1059 "character are invalid"); 1060 } 1061 index = qname.indexOf(':'); 1062 1063 // NS prefix declaration? 1064 if (index == 5 && qname.startsWith("xmlns")) 1065 { 1066 continue; 1067 } 1068 1069 // it's not a NS decl; patch namespace info items 1070 if (prefixStack.processName(qname, nsTemp, true) == null) 1071 { 1072 fatal("undeclared attribute prefix in: " + qname); 1073 } 1074 else 1075 { 1076 attribute.nameSpace = nsTemp[0]; 1077 attribute.localName = nsTemp[1]; 1078 } 1079 } 1080 } 1081 1082 // save element name so attribute callbacks work 1083 elementName = elname; 1084 if (namespaces) 1085 { 1086 if (prefixStack.processName(elname, nsTemp, false) == null) 1087 { 1088 fatal("undeclared element prefix in: " + elname); 1089 nsTemp[0] = nsTemp[1] = ""; 1090 } 1091 handler.startElement(nsTemp[0], nsTemp[1], elname, this); 1092 } 1093 else 1094 { 1095 handler.startElement("", "", elname, this); 1096 } 1097 // elementName = null; 1098 1099 // elements with no attributes are pretty common! 1100 if (attributes) 1101 { 1102 attributesList.clear(); 1103 attributeCount = 0; 1104 attributes = false; 1105 } 1106 } 1107 endElement(String elname)1108 void endElement(String elname) 1109 throws SAXException 1110 { 1111 ContentHandler handler = contentHandler; 1112 1113 if (!namespaces) 1114 { 1115 handler.endElement("", "", elname); 1116 return; 1117 } 1118 prefixStack.processName(elname, nsTemp, false); 1119 handler.endElement(nsTemp[0], nsTemp[1], elname); 1120 1121 Enumeration prefixes = prefixStack.getDeclaredPrefixes(); 1122 1123 while (prefixes.hasMoreElements()) 1124 { 1125 handler.endPrefixMapping((String) prefixes.nextElement()); 1126 } 1127 prefixStack.popContext(); 1128 } 1129 startCDATA()1130 void startCDATA() 1131 throws SAXException 1132 { 1133 lexicalHandler.startCDATA(); 1134 } 1135 charData(char[] ch, int start, int length)1136 void charData(char[] ch, int start, int length) 1137 throws SAXException 1138 { 1139 contentHandler.characters(ch, start, length); 1140 } 1141 endCDATA()1142 void endCDATA() 1143 throws SAXException 1144 { 1145 lexicalHandler.endCDATA(); 1146 } 1147 ignorableWhitespace(char[] ch, int start, int length)1148 void ignorableWhitespace(char[] ch, int start, int length) 1149 throws SAXException 1150 { 1151 contentHandler.ignorableWhitespace(ch, start, length); 1152 } 1153 processingInstruction(String target, String data)1154 void processingInstruction(String target, String data) 1155 throws SAXException 1156 { 1157 contentHandler.processingInstruction(target, data); 1158 } 1159 comment(char[] ch, int start, int length)1160 void comment(char[] ch, int start, int length) 1161 throws SAXException 1162 { 1163 if (lexicalHandler != base) 1164 { 1165 lexicalHandler.comment(ch, start, length); 1166 } 1167 } 1168 fatal(String message)1169 void fatal(String message) 1170 throws SAXException 1171 { 1172 SAXParseException fatal; 1173 1174 fatal = new SAXParseException(message, this); 1175 errorHandler.fatalError(fatal); 1176 1177 // Even if the application can continue ... we can't! 1178 throw fatal; 1179 } 1180 1181 // We can safely report a few validity errors that 1182 // make layered SAX2 DTD validation more conformant verror(String message)1183 void verror(String message) 1184 throws SAXException 1185 { 1186 SAXParseException err; 1187 1188 err = new SAXParseException(message, this); 1189 errorHandler.error(err); 1190 } 1191 warn(String message)1192 void warn(String message) 1193 throws SAXException 1194 { 1195 SAXParseException err; 1196 1197 err = new SAXParseException(message, this); 1198 errorHandler.warning(err); 1199 } 1200 1201 // 1202 // Implementation of org.xml.sax.Attributes. 1203 // 1204 1205 /** 1206 * <b>SAX1 AttributeList, SAX2 Attributes</b> method 1207 * (don't invoke on parser); 1208 */ getLength()1209 public int getLength() 1210 { 1211 return attributesList.size(); 1212 } 1213 1214 /** 1215 * <b>SAX2 Attributes</b> method (don't invoke on parser); 1216 */ getURI(int index)1217 public String getURI(int index) 1218 { 1219 if (index < 0 || index >= attributesList.size()) 1220 { 1221 return null; 1222 } 1223 return ((Attribute) attributesList.get(index)).nameSpace; 1224 } 1225 1226 /** 1227 * <b>SAX2 Attributes</b> method (don't invoke on parser); 1228 */ getLocalName(int index)1229 public String getLocalName(int index) 1230 { 1231 if (index < 0 || index >= attributesList.size()) 1232 { 1233 return null; 1234 } 1235 Attribute attr = (Attribute) attributesList.get(index); 1236 // FIXME attr.localName is sometimes null, why? 1237 if (namespaces && attr.localName == null) 1238 { 1239 // XXX fix this here for now 1240 int ci = attr.name.indexOf(':'); 1241 attr.localName = (ci == -1) ? attr.name : 1242 attr.name.substring(ci + 1); 1243 } 1244 return (attr.localName == null) ? "" : attr.localName; 1245 } 1246 1247 /** 1248 * <b>SAX2 Attributes</b> method (don't invoke on parser); 1249 */ getQName(int index)1250 public String getQName(int index) 1251 { 1252 if (index < 0 || index >= attributesList.size()) 1253 { 1254 return null; 1255 } 1256 Attribute attr = (Attribute) attributesList.get(index); 1257 return (attr.name == null) ? "" : attr.name; 1258 } 1259 1260 /** 1261 * <b>SAX1 AttributeList</b> method (don't invoke on parser); 1262 */ getName(int index)1263 public String getName(int index) 1264 { 1265 return getQName(index); 1266 } 1267 1268 /** 1269 * <b>SAX1 AttributeList, SAX2 Attributes</b> method 1270 * (don't invoke on parser); 1271 */ getType(int index)1272 public String getType(int index) 1273 { 1274 if (index < 0 || index >= attributesList.size()) 1275 { 1276 return null; 1277 } 1278 String type = parser.getAttributeType(elementName, getQName(index)); 1279 if (type == null) 1280 { 1281 return "CDATA"; 1282 } 1283 // ... use DeclHandler.attributeDecl to see enumerations 1284 if (type == "ENUMERATION") 1285 { 1286 return "NMTOKEN"; 1287 } 1288 return type; 1289 } 1290 1291 /** 1292 * <b>SAX1 AttributeList, SAX2 Attributes</b> method 1293 * (don't invoke on parser); 1294 */ getValue(int index)1295 public String getValue(int index) 1296 { 1297 if (index < 0 || index >= attributesList.size()) 1298 { 1299 return null; 1300 } 1301 return ((Attribute) attributesList.get(index)).value; 1302 } 1303 1304 /** 1305 * <b>SAX2 Attributes</b> method (don't invoke on parser); 1306 */ getIndex(String uri, String local)1307 public int getIndex(String uri, String local) 1308 { 1309 int length = getLength(); 1310 1311 for (int i = 0; i < length; i++) 1312 { 1313 if (!getURI(i).equals(uri)) 1314 { 1315 continue; 1316 } 1317 if (getLocalName(i).equals(local)) 1318 { 1319 return i; 1320 } 1321 } 1322 return -1; 1323 } 1324 1325 /** 1326 * <b>SAX2 Attributes</b> method (don't invoke on parser); 1327 */ getIndex(String xmlName)1328 public int getIndex(String xmlName) 1329 { 1330 int length = getLength(); 1331 1332 for (int i = 0; i < length; i++) 1333 { 1334 if (getQName(i).equals(xmlName)) 1335 { 1336 return i; 1337 } 1338 } 1339 return -1; 1340 } 1341 1342 /** 1343 * <b>SAX2 Attributes</b> method (don't invoke on parser); 1344 */ getType(String uri, String local)1345 public String getType(String uri, String local) 1346 { 1347 int index = getIndex(uri, local); 1348 1349 if (index < 0) 1350 { 1351 return null; 1352 } 1353 return getType(index); 1354 } 1355 1356 /** 1357 * <b>SAX1 AttributeList, SAX2 Attributes</b> method 1358 * (don't invoke on parser); 1359 */ getType(String xmlName)1360 public String getType(String xmlName) 1361 { 1362 int index = getIndex(xmlName); 1363 1364 if (index < 0) 1365 { 1366 return null; 1367 } 1368 return getType(index); 1369 } 1370 1371 /** 1372 * <b>SAX Attributes</b> method (don't invoke on parser); 1373 */ getValue(String uri, String local)1374 public String getValue(String uri, String local) 1375 { 1376 int index = getIndex(uri, local); 1377 1378 if (index < 0) 1379 { 1380 return null; 1381 } 1382 return getValue(index); 1383 } 1384 1385 /** 1386 * <b>SAX1 AttributeList, SAX2 Attributes</b> method 1387 * (don't invoke on parser); 1388 */ getValue(String xmlName)1389 public String getValue(String xmlName) 1390 { 1391 int index = getIndex(xmlName); 1392 1393 if (index < 0) 1394 { 1395 return null; 1396 } 1397 return getValue(index); 1398 } 1399 1400 // 1401 // Implementation of org.xml.sax.ext.Attributes2 1402 // 1403 1404 /** @return false unless the attribute was declared in the DTD. 1405 * @throws java.lang.ArrayIndexOutOfBoundsException 1406 * When the supplied index does not identify an attribute. 1407 */ isDeclared(int index)1408 public boolean isDeclared(int index) 1409 { 1410 if (index < 0 || index >= attributeCount) 1411 { 1412 throw new ArrayIndexOutOfBoundsException(); 1413 } 1414 String type = parser.getAttributeType(elementName, getQName(index)); 1415 return (type != null); 1416 } 1417 1418 /** @return false unless the attribute was declared in the DTD. 1419 * @throws java.lang.IllegalArgumentException 1420 * When the supplied names do not identify an attribute. 1421 */ isDeclared(String qName)1422 public boolean isDeclared(String qName) 1423 { 1424 int index = getIndex(qName); 1425 if (index < 0) 1426 { 1427 throw new IllegalArgumentException(); 1428 } 1429 String type = parser.getAttributeType(elementName, qName); 1430 return (type != null); 1431 } 1432 1433 /** @return false unless the attribute was declared in the DTD. 1434 * @throws java.lang.IllegalArgumentException 1435 * When the supplied names do not identify an attribute. 1436 */ isDeclared(String uri, String localName)1437 public boolean isDeclared(String uri, String localName) 1438 { 1439 int index = getIndex(uri, localName); 1440 return isDeclared(index); 1441 } 1442 1443 /** 1444 * <b>SAX-ext Attributes2</b> method (don't invoke on parser); 1445 */ isSpecified(int index)1446 public boolean isSpecified(int index) 1447 { 1448 return ((Attribute) attributesList.get(index)).specified; 1449 } 1450 1451 /** 1452 * <b>SAX-ext Attributes2</b> method (don't invoke on parser); 1453 */ isSpecified(String uri, String local)1454 public boolean isSpecified(String uri, String local) 1455 { 1456 int index = getIndex (uri, local); 1457 return isSpecified(index); 1458 } 1459 1460 /** 1461 * <b>SAX-ext Attributes2</b> method (don't invoke on parser); 1462 */ isSpecified(String xmlName)1463 public boolean isSpecified(String xmlName) 1464 { 1465 int index = getIndex (xmlName); 1466 return isSpecified(index); 1467 } 1468 1469 // 1470 // Implementation of org.xml.sax.Locator. 1471 // 1472 1473 /** 1474 * <b>SAX Locator</b> method (don't invoke on parser); 1475 */ getPublicId()1476 public String getPublicId() 1477 { 1478 return null; // FIXME track public IDs too 1479 } 1480 1481 /** 1482 * <b>SAX Locator</b> method (don't invoke on parser); 1483 */ getSystemId()1484 public String getSystemId() 1485 { 1486 if (entityStack.empty()) 1487 { 1488 return null; 1489 } 1490 else 1491 { 1492 return (String) entityStack.peek(); 1493 } 1494 } 1495 1496 /** 1497 * <b>SAX Locator</b> method (don't invoke on parser); 1498 */ getLineNumber()1499 public int getLineNumber() 1500 { 1501 return parser.getLineNumber(); 1502 } 1503 1504 /** 1505 * <b>SAX Locator</b> method (don't invoke on parser); 1506 */ getColumnNumber()1507 public int getColumnNumber() 1508 { 1509 return parser.getColumnNumber(); 1510 } 1511 1512 // adapter between SAX2 content handler and SAX1 document handler callbacks 1513 private static class Adapter 1514 implements ContentHandler 1515 { 1516 1517 private DocumentHandler docHandler; 1518 Adapter(DocumentHandler dh)1519 Adapter(DocumentHandler dh) 1520 { 1521 docHandler = dh; 1522 } 1523 setDocumentLocator(Locator l)1524 public void setDocumentLocator(Locator l) 1525 { 1526 docHandler.setDocumentLocator(l); 1527 } 1528 startDocument()1529 public void startDocument() 1530 throws SAXException 1531 { 1532 docHandler.startDocument(); 1533 } 1534 processingInstruction(String target, String data)1535 public void processingInstruction(String target, String data) 1536 throws SAXException 1537 { 1538 docHandler.processingInstruction(target, data); 1539 } 1540 startPrefixMapping(String prefix, String uri)1541 public void startPrefixMapping(String prefix, String uri) 1542 { 1543 /* ignored */ 1544 } 1545 startElement(String namespace, String local, String name, Attributes attrs)1546 public void startElement(String namespace, 1547 String local, 1548 String name, 1549 Attributes attrs) 1550 throws SAXException 1551 { 1552 docHandler.startElement(name, (AttributeList) attrs); 1553 } 1554 characters(char[] buf, int offset, int len)1555 public void characters(char[] buf, int offset, int len) 1556 throws SAXException 1557 { 1558 docHandler.characters(buf, offset, len); 1559 } 1560 ignorableWhitespace(char[] buf, int offset, int len)1561 public void ignorableWhitespace(char[] buf, int offset, int len) 1562 throws SAXException 1563 { 1564 docHandler.ignorableWhitespace(buf, offset, len); 1565 } 1566 skippedEntity(String name)1567 public void skippedEntity(String name) 1568 { 1569 /* ignored */ 1570 } 1571 endElement(String u, String l, String name)1572 public void endElement(String u, String l, String name) 1573 throws SAXException 1574 { 1575 docHandler.endElement(name); 1576 } 1577 endPrefixMapping(String prefix)1578 public void endPrefixMapping(String prefix) 1579 { 1580 /* ignored */ 1581 } 1582 endDocument()1583 public void endDocument() 1584 throws SAXException 1585 { 1586 docHandler.endDocument(); 1587 } 1588 } 1589 1590 private static class Attribute 1591 { 1592 1593 String name; 1594 String value; 1595 String nameSpace; 1596 String localName; 1597 boolean specified; 1598 Attribute(String name, String value, boolean specified)1599 Attribute(String name, String value, boolean specified) 1600 { 1601 this.name = name; 1602 this.value = value; 1603 this.nameSpace = ""; 1604 this.specified = specified; 1605 } 1606 1607 } 1608 1609 } 1610