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