001/* 002 * Copyright (c) 1999-2000 by David Brownell. All Rights Reserved. 003 * 004 * This program is open source software; you may use, copy, modify, and 005 * redistribute it under the terms of the LICENSE with which it was 006 * originally distributed. 007 * 008 * This program is distributed in the hope that it will be useful, 009 * but WITHOUT ANY WARRANTY; without even the implied warranty of 010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 011 * LICENSE for more details. 012 */ 013 014// 015// Copyright (c) 1998 by Microstar Software Ltd. 016// From Microstar's README (the entire original license): 017// 018// AElfred is free for both commercial and non-commercial use and 019// redistribution, provided that Microstar's copyright and disclaimer are 020// retained intact. You are free to modify AElfred for your own use and 021// to redistribute AElfred with your modifications, provided that the 022// modifications are clearly documented. 023// 024// This program is distributed in the hope that it will be useful, but 025// WITHOUT ANY WARRANTY; without even the implied warranty of 026// merchantability or fitness for a particular purpose. Please use it AT 027// YOUR OWN RISK. 028// 029 030 031package org.dom4j.io.aelfred; 032 033import java.io.InputStreamReader; 034import java.io.IOException; 035import java.io.Reader; 036import java.util.ArrayList; 037import java.util.Enumeration; 038import java.util.HashMap; 039import java.util.Iterator; 040import java.util.List; 041import java.util.Locale; 042 043import org.xml.sax.*; 044import org.xml.sax.ext.*; 045import org.xml.sax.helpers.NamespaceSupport; 046 047//import org.brownell.xml.DefaultHandler; 048import org.dom4j.io.aelfred.DefaultHandler; 049 050 051// $Id: SAXDriver.java,v 1.4 2002/02/01 10:55:25 jstrachan Exp $ 052 053/** 054 * An enhanced SAX2 version of Microstar's Ælfred XML parser. 055 * The enhancements primarily relate to significant improvements in 056 * conformance to the XML specification, and SAX2 support. Performance 057 * has been improved. However, the Ælfred proprietary APIs are 058 * no longer public. See the package level documentation for more 059 * information. 060 * 061 * <table border="1" width='100%' cellpadding='3' cellspacing='0'> 062 * <tr bgcolor='#ccccff'> 063 * <th><font size='+1'>Name</font></th> 064 * <th><font size='+1'>Notes</font></th></tr> 065 * 066 * <tr><td colspan=2><center><em>Features ... URL prefix is 067 * <b>http://xml.org/sax/features/</b></em></center></td></tr> 068 * 069 * <tr><td>(URL)/external-general-entities</td> 070 * <td>Value is fixed at <em>true</em></td></tr> 071 * <tr><td>(URL)/external-parameter-entities</td> 072 * <td>Value is fixed at <em>true</em></td></tr> 073 * <tr><td>(URL)/namespace-prefixes</td> 074 * <td>Value defaults to <em>false</em> (but XML 1.0 names are 075 * always reported)</td></tr> 076 * <tr><td>(URL)/namespaces</td> 077 * <td>Value defaults to <em>true</em></td></tr> 078 * <tr><td>(URL)/string-interning</td> 079 * <td>Value is fixed at <em>true</em></td></tr> 080 * <tr><td>(URL)/validation</td> 081 * <td>Value is fixed at <em>false</em></td></tr> 082 * 083 * <tr><td colspan=2><center><em>Handler Properties ... URL prefix is 084 * <b>http://xml.org/sax/properties/</b></em></center></td></tr> 085 * 086 * <tr><td>(URL)/declaration-handler</td> 087 * <td>A declaration handler may be provided. Declaration of general 088 * entities is exposed, but not parameter entities; none of the entity 089 * names reported here will begin with "%". </td></tr> 090 * <tr><td>(URL)/lexical-handler</td> 091 * <td>A lexical handler may be provided. Entity boundaries and 092 * comments are not exposed; only CDATA sections and the start/end of 093 * the DTD (the internal subset is not detectible). </td></tr> 094 * </table> 095 * 096 * <p> Note that the declaration handler doesn't suffice for showing all 097 * the logical structure 098 * of the DTD; it doesn't expose the name of the root element, or the values 099 * that are permitted in a NOTATIONS attribute. (The former is exposed as 100 * lexical data, and SAX2 beta doesn't expose the latter.) 101 * 102 * <p> Although support for several features and properties is "built in" 103 * to this parser, it support all others by storing the assigned values 104 * and returning them. 105 * 106 * <p>This parser currently implements the SAX1 Parser API, but 107 * it may not continue to do so in the future. 108 * 109 * @author Written by David Megginson <dmeggins@microstar.com> 110 * (version 1.2a from Microstar) 111 * @author Updated by David Brownell <david-b@pacbell.net> 112 * @version $Date: 2002/02/01 10:55:25 $ 113 * @see org.xml.sax.Parser 114 */ 115final public class SAXDriver 116 implements Locator, Attributes, XMLReader, Parser, AttributeList 117{ 118 private final DefaultHandler base = new DefaultHandler (); 119 private XmlParser parser; 120 121 private EntityResolver entityResolver = base; 122 private ContentHandler contentHandler = base; 123 private DTDHandler dtdHandler = base; 124 private ErrorHandler errorHandler = base; 125 private DeclHandler declHandler = base; 126 private LexicalHandler lexicalHandler = base; 127 128 private String elementName = null; 129 private ArrayList entityStack = new ArrayList (); 130 131 private ArrayList attributeNames = new ArrayList (); 132 private ArrayList attributeNamespaces = new ArrayList (); 133 private ArrayList attributeLocalNames = new ArrayList (); 134 private ArrayList attributeValues = new ArrayList (); 135 136 private boolean namespaces = true; 137 private boolean xmlNames = false; 138 private boolean nspending = false; // indicates an attribute was read before its 139 // namespace declaration 140 141 private int attributeCount = 0; 142 private String nsTemp [] = new String [3]; 143 private NamespaceSupport prefixStack = new NamespaceSupport (); 144 145 private HashMap features; 146 private HashMap properties; 147 148 149 // 150 // Constructor. 151 // 152 153 /** Constructs a SAX Parser. */ 154 public SAXDriver () {} 155 156 157 // 158 // Implementation of org.xml.sax.Parser. 159 // 160 161 /** 162 * <b>SAX1</b>: Sets the locale used for diagnostics; currently, 163 * only locales using the English language are supported. 164 * @param locale The locale for which diagnostics will be generated 165 */ 166 public void setLocale (Locale locale) 167 throws SAXException 168 { 169 if ("en".equals (locale.getLanguage ())) 170 return ; 171 172 throw new SAXException ("AElfred only supports English locales."); 173 } 174 175 176 /** 177 * <b>SAX2</b>: Returns the object used when resolving external 178 * entities during parsing (both general and parameter entities). 179 */ 180 public EntityResolver getEntityResolver () 181 { 182 return entityResolver; 183 } 184 185 /** 186 * <b>SAX1, SAX2</b>: Set the entity resolver for this parser. 187 * @param handler The object to receive entity events. 188 */ 189 public void setEntityResolver (EntityResolver resolver) 190 { 191 if (resolver == null) 192 resolver = base; 193 this.entityResolver = resolver; 194 } 195 196 197 /** 198 * <b>SAX2</b>: Returns the object used to process declarations related 199 * to notations and unparsed entities. 200 */ 201 public DTDHandler getDTDHandler () 202 { 203 return dtdHandler; 204 } 205 206 /** 207 * <b>SAX1, SAX2</b>: Set the DTD handler for this parser. 208 * @param handler The object to receive DTD events. 209 */ 210 public void setDTDHandler (DTDHandler handler) 211 { 212 if (handler == null) 213 handler = base; 214 this.dtdHandler = handler; 215 } 216 217 218 /** 219 * <b>SAX1</b>: Set the document handler for this parser. If a 220 * content handler was set, this document handler will supplant it. 221 * The parser is set to report all XML 1.0 names rather than to 222 * filter out "xmlns" attributes (the "namespace-prefixes" feature 223 * is set to true). 224 * 225 * @deprecated SAX2 programs should use the XMLReader interface 226 * and a ContentHandler. 227 * 228 * @param handler The object to receive document events. 229 */ 230 public void setDocumentHandler (DocumentHandler handler) 231 { 232 contentHandler = new Adapter (handler); 233 xmlNames = true; 234 } 235 236 /** 237 * <b>SAX2</b>: Returns the object used to report the logical 238 * content of an XML document. 239 */ 240 public ContentHandler getContentHandler () 241 { 242 return contentHandler; 243 } 244 245 /** 246 * <b>SAX2</b>: Assigns the object used to report the logical 247 * content of an XML document. If a document handler was set, 248 * this content handler will supplant it (but XML 1.0 style name 249 * reporting may remain enabled). 250 */ 251 public void setContentHandler (ContentHandler handler) 252 { 253 if (handler == null) 254 handler = base; 255 contentHandler = handler; 256 } 257 258 /** 259 * <b>SAX1, SAX2</b>: Set the error handler for this parser. 260 * @param handler The object to receive error events. 261 */ 262 public void setErrorHandler (ErrorHandler handler) 263 { 264 if (handler == null) 265 handler = base; 266 this.errorHandler = handler; 267 } 268 269 /** 270 * <b>SAX2</b>: Returns the object used to receive callbacks for XML 271 * errors of all levels (fatal, nonfatal, warning); this is never null; 272 */ 273 public ErrorHandler getErrorHandler () 274 { 275 return errorHandler; 276 } 277 278 279 /** 280 * <b>SAX1, SAX2</b>: Auxiliary API to parse an XML document, used mostly 281 * when no URI is available. 282 * If you want anything useful to happen, you should set 283 * at least one type of handler. 284 * @param source The XML input source. Don't set 'encoding' unless 285 * you know for a fact that it's correct. 286 * @see #setEntityResolver 287 * @see #setDTDHandler 288 * @see #setContentHandler 289 * @see #setErrorHandler 290 * @exception SAXException The handlers may throw any SAXException, 291 * and the parser normally throws SAXParseException objects. 292 * @exception IOException IOExceptions are normally through through 293 * the parser if there are problems reading the source document. 294 */ 295 296 public void parse (InputSource source) throws SAXException, IOException 297 { 298 synchronized (base) { 299 parser = new XmlParser (); 300 parser.setHandler (this); 301 302 try { 303 String systemId = source.getSystemId (); 304 305 // duplicate first entry, in case startDocument handler 306 // needs to use Locator.getSystemId(), before entities 307 // start to get reported by the parser 308 309 if (systemId != null) 310 entityStack.add (systemId); 311 else 312 entityStack.add ("illegal:unknown system ID"); 313 314 parser.doParse (systemId, 315 source.getPublicId (), 316 source.getCharacterStream (), 317 source.getByteStream (), 318 source.getEncoding ()); 319 } catch (SAXException e) { 320 throw e; 321 } catch (IOException e) { 322 throw e; 323 } catch (RuntimeException e) { 324 throw e; 325 } catch (Exception e) { 326 throw new SAXException (e.getMessage (), e); 327 } finally { 328 contentHandler.endDocument (); 329 entityStack.clear (); 330 } 331 } 332 } 333 334 335 /** 336 * <b>SAX1, SAX2</b>: Preferred API to parse an XML document, using a 337 * system identifier (URI). 338 */ 339 340 public void parse (String systemId) throws SAXException, IOException 341 { 342 parse (new InputSource (systemId)); 343 } 344 345 // 346 // Implementation of SAX2 "XMLReader" interface 347 // 348 static final String FEATURE = "http://xml.org/sax/features/"; 349 static final String HANDLER = "http://xml.org/sax/properties/"; 350 351 /** 352 * <b>SAX2</b>: Tells the value of the specified feature flag. 353 * 354 * @exception SAXNotRecognizedException thrown if the feature flag 355 * is neither built in, nor yet assigned. 356 */ 357 public boolean getFeature (String featureId) 358 throws SAXNotRecognizedException 359 { 360 if ((FEATURE + "validation").equals (featureId)) 361 return false; 362 363 // external entities (both types) are currently always included 364 if ((FEATURE + "external-general-entities").equals (featureId) 365 || (FEATURE + "external-parameter-entities") 366 .equals (featureId)) 367 return true; 368 369 // element/attribute names are as written in document; no mangling 370 if ((FEATURE + "namespace-prefixes").equals (featureId)) 371 return xmlNames; 372 373 // report element/attribute namespaces? 374 if ((FEATURE + "namespaces").equals (featureId)) 375 return namespaces; 376 377 // XXX always provides a locator ... removed in beta 378 379 // always interns 380 if ((FEATURE + "string-interning").equals (featureId)) 381 return true; 382 383 if (features != null && features.containsKey (featureId)) 384 return ((Boolean)features.get (featureId)).booleanValue (); 385 386 throw new SAXNotRecognizedException (featureId); 387 } 388 389 /** 390 * <b>SAX2</b>: Returns the specified property. 391 * 392 * @exception SAXNotRecognizedException thrown if the property value 393 * is neither built in, nor yet stored. 394 */ 395 public Object getProperty (String propertyId) 396 throws SAXNotRecognizedException 397 { 398 if ((HANDLER + "declaration-handler").equals (propertyId)) 399 return declHandler; 400 401 if ((HANDLER + "lexical-handler").equals (propertyId)) 402 return lexicalHandler; 403 404 if (properties != null && properties.containsKey (propertyId)) 405 return properties.get (propertyId); 406 407 // unknown properties 408 throw new SAXNotRecognizedException (propertyId); 409 } 410 411 /** 412 * <b>SAX2</b>: Sets the state of feature flags in this parser. Some 413 * built-in feature flags are mutable; all flags not built-in are 414 * motable. 415 */ 416 public void setFeature (String featureId, boolean state) 417 throws SAXNotRecognizedException, SAXNotSupportedException 418 { 419 boolean value; 420 421 try { 422 // Features with a defined value, we just change it if we can. 423 value = getFeature (featureId); 424 425 if (state == value) 426 return; 427 428 if ((FEATURE + "namespace-prefixes").equals (featureId)) { 429 // in this implementation, this only affects xmlns reporting 430 xmlNames = state; 431 return; 432 } 433 434 if ((FEATURE + "namespaces").equals (featureId)) { 435 // XXX if not currently parsing ... 436 if (true) { 437 namespaces = state; 438 return; 439 } 440 // if in mid-parse, critical info hasn't been computed/saved 441 } 442 443 // can't change builtins 444 if (features == null || !features.containsKey (featureId)) 445 throw new SAXNotSupportedException (featureId); 446 447 } catch (SAXNotRecognizedException e) { 448 // as-yet unknown features 449 if (features == null) 450 features = new HashMap (5); 451 } 452 453 // record first value, or modify existing one 454 features.put (featureId, 455 state 456 ? Boolean.TRUE 457 : Boolean.FALSE); 458 } 459 460 /** 461 * <b>SAX2</b>: Assigns the specified property. Like SAX1 handlers, 462 * these may be changed at any time. 463 */ 464 public void setProperty (String propertyId, Object property) 465 throws SAXNotRecognizedException, SAXNotSupportedException 466 { 467 Object value; 468 469 try { 470 // Properties with a defined value, we just change it if we can. 471 value = getProperty (propertyId); 472 473 if ((HANDLER + "declaration-handler").equals (propertyId)) { 474 if (property == null) 475 declHandler = base; 476 else if (! (property instanceof DeclHandler)) 477 throw new SAXNotSupportedException (propertyId); 478 else 479 declHandler = (DeclHandler) property; 480 return ; 481 } 482 483 if ((HANDLER + "lexical-handler").equals (propertyId)) { 484 if (property == null) 485 lexicalHandler = base; 486 else if (! (property instanceof LexicalHandler)) 487 throw new SAXNotSupportedException (propertyId); 488 else 489 lexicalHandler = (LexicalHandler) property; 490 return ; 491 } 492 493 // can't change builtins 494 if (properties == null || !properties.containsKey (propertyId)) 495 throw new SAXNotSupportedException (propertyId); 496 497 } catch (SAXNotRecognizedException e) { 498 // as-yet unknown properties 499 if (properties == null) 500 properties = new HashMap (5); 501 } 502 503 // record first value, or modify existing one 504 properties.put (propertyId, property); 505 } 506 507 508 // 509 // This is where the driver receives AElfred callbacks and translates 510 // them into SAX callbacks. Some more callbacks have been added for 511 // SAX2 support. 512 // 513 514 // NOTE: in some cases, local copies of handlers are 515 // created and used, to work around codegen bugs in at 516 // least one snapshot version of GCJ. 517 518 void startDocument () throws SAXException 519 { 520 contentHandler.setDocumentLocator (this); 521 contentHandler.startDocument (); 522 attributeNames.clear (); 523 attributeValues.clear (); 524 } 525 526 void endDocument () throws SAXException 527 { 528 // SAX says endDocument _must_ be called (handy to close 529 // files etc) so it's in a "finally" clause 530 } 531 532 Object resolveEntity (String publicId, String systemId) 533 throws SAXException, IOException 534 { 535 InputSource source = entityResolver.resolveEntity (publicId, 536 systemId); 537 538 if (source == null) { 539 return null; 540 } else if (source.getCharacterStream () != null) { 541 return source.getCharacterStream (); 542 } else if (source.getByteStream () != null) { 543 if (source.getEncoding () == null) 544 return source.getByteStream (); 545 else try { 546 return new InputStreamReader ( 547 source.getByteStream (), 548 source.getEncoding () 549 ); 550 } catch (IOException e) { 551 return source.getByteStream (); 552 } 553 } else { 554 return source.getSystemId (); 555 } 556 // XXX no way to tell AElfred about new public 557 // or system ids ... so relative URL resolution 558 // through that entity could be less than reliable. 559 } 560 561 562 void startExternalEntity (String systemId) 563 throws SAXException 564 { 565 entityStack.add (systemId); 566 } 567 568 void endExternalEntity (String systemId) 569 throws SAXException 570 { 571 entityStack.remove ( entityStack.size() - 1 ); 572 } 573 574 void doctypeDecl (String name, String publicId, String systemId) 575 throws SAXException 576 { 577 lexicalHandler.startDTD (name, publicId, systemId); 578 579 // ... the "name" is a declaration and should be given 580 // to the DeclHandler (but sax2 beta doesn't). 581 582 // the IDs for the external subset are lexical details, 583 // as are the contents of the internal subset; but sax2 584 // beta only provides the external subset "pre-parse" 585 } 586 587 void endDoctype () throws SAXException 588 { 589 // NOTE: some apps may care that comments and PIs, 590 // are stripped from their DTD declaration context, 591 // and that those declarations are themselves quite 592 // thoroughly reordered here. 593 594 deliverDTDEvents (); 595 lexicalHandler.endDTD (); 596 } 597 598 599 void attribute (String aname, String value, boolean isSpecified) 600 throws SAXException 601 { 602 if (attributeCount++ == 0) { 603 if (namespaces) 604 prefixStack.pushContext (); 605 } 606 607 // set nsTemp [0] == namespace URI (or empty) 608 // set nsTemp [1] == local name (or empty) 609 if (value != null) { 610 if (namespaces) { 611 int index = aname.indexOf (':'); 612 613 // prefixed name? 614 if (index > 0) { 615 // prefix declaration? 616 if (index == 5 && aname.startsWith ("xmlns")) { 617 String prefix = aname.substring (index + 1); 618 619 if (value.length () == 0) { 620 errorHandler.error (new SAXParseException ( 621 "missing URI in namespace decl attribute: " 622 + aname, 623 this)); 624 } else { 625 prefixStack.declarePrefix (prefix, value); 626 contentHandler.startPrefixMapping (prefix, value); 627 } 628 if (!xmlNames) 629 return; 630 nsTemp [0] = ""; 631 nsTemp [1] = aname; 632 633 // prefix reference 634 } else { 635 if (prefixStack.processName (aname, nsTemp, true) 636 == null) { 637 // start of MHK code 638 nsTemp[0] = ""; 639 nsTemp[1] = aname; // defer checking till later 640 nspending = true; 641 // end of MHK code 642 // start of previous code 643 // errorHandler.error (new SAXParseException ( 644 // "undeclared name prefix in: " + aname, 645 // this)); 646 // nsTemp [0] = nsTemp [1] = ""; 647 // end of previous code 648 } // else nsTemp [0, 1] received { uri, local } 649 } 650 651 // no prefix 652 } else { 653 // default declaration? 654 if ("xmlns".equals (aname)) { 655 prefixStack.declarePrefix ("", value); 656 contentHandler.startPrefixMapping ("", value); 657 if (!xmlNames) 658 return; 659 } 660 nsTemp [0] = ""; 661 nsTemp [1] = aname; 662 } 663 } else 664 nsTemp [0] = nsTemp [1] = ""; 665 666 attributeNamespaces.add (nsTemp [0]); 667 attributeLocalNames.add (nsTemp [1]); 668 attributeNames.add (aname); 669 // attribute type comes from querying parser's DTD records 670 attributeValues.add (value); 671 } 672 } 673 674 void startElement (String elname) 675 throws SAXException 676 { 677 ContentHandler handler = contentHandler; 678 679 // 680 // NOTE: this implementation of namespace support adds something 681 // like six percent to parsing CPU time, in a large (~50 MB) 682 // document that doesn't use namespaces at all. (Measured by PC 683 // sampling.) 684 // 685 // It ought to become notably faster in such cases. Most 686 // costs are the prefix stack calling Hashtable.get() (2%), 687 // String.hashCode() (1.5%) and about 1.3% each for pushing 688 // the context, and two chunks of name processing. 689 // 690 691 if (attributeCount == 0) 692 prefixStack.pushContext (); 693 694 // save element name so attribute callbacks work 695 elementName = elname; 696 if (namespaces) { 697 // START MHK CODE 698 // Check that namespace prefixes for attributes are valid 699 if (attributeCount > 0 && nspending) { 700 for (int i=0; i<attributeLocalNames.size(); i++) { 701 String aname = (String)attributeLocalNames.get(i); 702 if (aname.indexOf(':')>0) { 703 704 if (prefixStack.processName (aname, nsTemp, true) == null) { 705 errorHandler.error (new SAXParseException ( 706 "undeclared name prefix in: " + aname, 707 this)); 708 } else { 709 attributeNamespaces.set(i, nsTemp[0]); 710 attributeLocalNames.set(i, nsTemp[1]); 711 } 712 } 713 } 714 } 715 // END MHK CODE 716 717 if (prefixStack.processName (elname, nsTemp, false) == null) { 718 errorHandler.error (new SAXParseException ( 719 "undeclared name prefix in: " + elname, 720 this)); 721 nsTemp [0] = nsTemp [1] = ""; 722 } 723 724 handler.startElement (nsTemp [0], nsTemp [1], elname, this); 725 } else 726 handler.startElement ("", "", elname, this); 727 // elementName = null; 728 729 // elements with no attributes are pretty common! 730 if (attributeCount != 0) { 731 attributeNames.clear (); 732 attributeNamespaces.clear (); 733 attributeLocalNames.clear (); 734 attributeValues.clear (); 735 attributeCount = 0; 736 } 737 nspending = false; 738 } 739 740 void endElement (String elname) 741 throws SAXException 742 { 743 ContentHandler handler = contentHandler; 744 745 handler.endElement ("", "", elname); 746 747 if (!namespaces) 748 return; 749 750 Enumeration prefixes = prefixStack.getDeclaredPrefixes (); 751 752 while (prefixes.hasMoreElements ()) 753 handler.endPrefixMapping ((String) prefixes.nextElement ()); 754 prefixStack.popContext (); 755 } 756 757 void startCDATA () 758 throws SAXException 759 { 760 lexicalHandler.startCDATA (); 761 } 762 763 void charData (char ch[], int start, int length) 764 throws SAXException 765 { 766 contentHandler.characters (ch, start, length); 767 } 768 769 void endCDATA () 770 throws SAXException 771 { 772 lexicalHandler.endCDATA (); 773 } 774 775 void ignorableWhitespace (char ch[], int start, int length) 776 throws SAXException 777 { 778 contentHandler.ignorableWhitespace (ch, start, length); 779 } 780 781 void processingInstruction (String target, String data) 782 throws SAXException 783 { 784 // XXX if within DTD, perhaps it's best to discard 785 // PIs since the decls to which they (usually) 786 // apply get significantly rearranged 787 788 contentHandler.processingInstruction (target, data); 789 } 790 791 void comment (char ch[], int start, int length) 792 throws SAXException 793 { 794 // XXX if within DTD, perhaps it's best to discard 795 // comments since the decls to which they (usually) 796 // apply get significantly rearranged 797 798 if (lexicalHandler != base) 799 lexicalHandler.comment (ch, start, length); 800 } 801 802 // AElfred only has fatal errors 803 void error (String message, String url, int line, int column) 804 throws SAXException 805 { 806 SAXParseException fatal; 807 808 fatal = new SAXParseException (message, null, url, line, column); 809 errorHandler.fatalError (fatal); 810 811 // Even if the application can continue ... we can't! 812 throw fatal; 813 } 814 815 816 // 817 // Before the endDtd event, deliver all non-PE declarations. 818 // 819 private void deliverDTDEvents () 820 throws SAXException 821 { 822 String ename; 823 String nname; 824 String publicId; 825 String systemId; 826 827 // First, report all notations. 828 if (dtdHandler != base) { 829 Iterator notationNames = parser.declaredNotations (); 830 831 while (notationNames.hasNext ()) { 832 nname = (String) notationNames.next (); 833 publicId = parser.getNotationPublicId (nname); 834 systemId = parser.getNotationSystemId (nname); 835 dtdHandler.notationDecl (nname, publicId, systemId); 836 } 837 } 838 839 // Next, report all entities. 840 if (dtdHandler != base || declHandler != base) { 841 Iterator entityNames = parser.declaredEntities (); 842 int type; 843 844 while (entityNames.hasNext ()) { 845 ename = (String) entityNames.next (); 846 type = parser.getEntityType (ename); 847 848 if (ename.charAt (0) == '%') 849 continue; 850 851 // unparsed 852 if (type == XmlParser.ENTITY_NDATA) { 853 publicId = parser.getEntityPublicId (ename); 854 systemId = parser.getEntitySystemId (ename); 855 nname = parser.getEntityNotationName (ename); 856 dtdHandler.unparsedEntityDecl (ename, 857 publicId, systemId, nname); 858 859 // external parsed 860 } 861 else if (type == XmlParser.ENTITY_TEXT) { 862 publicId = parser.getEntityPublicId (ename); 863 systemId = parser.getEntitySystemId (ename); 864 declHandler.externalEntityDecl (ename, 865 publicId, systemId); 866 867 // internal parsed 868 } 869 else if (type == XmlParser.ENTITY_INTERNAL) { 870 // filter out the built-ins; even if they were 871 // declared, they didn't need to be. 872 if ("lt".equals (ename) || "gt".equals (ename) 873 || "quot".equals (ename) 874 || "apos".equals (ename) 875 || "amp".equals (ename)) 876 continue; 877 declHandler.internalEntityDecl (ename, 878 parser.getEntityValue (ename)); 879 } 880 } 881 } 882 883 // elements, attributes 884 if (declHandler != base) { 885 Iterator elementNames = parser.declaredElements (); 886 Iterator attNames; 887 888 while (elementNames.hasNext ()) { 889 String model = null; 890 891 ename = (String) elementNames.next (); 892 switch (parser.getElementContentType (ename)) { 893 case XmlParser.CONTENT_ANY: 894 model = "ANY"; 895 break; 896 case XmlParser.CONTENT_EMPTY: 897 model = "EMPTY"; 898 break; 899 case XmlParser.CONTENT_MIXED: 900 case XmlParser.CONTENT_ELEMENTS: 901 model = parser.getElementContentModel (ename); 902 break; 903 case XmlParser.CONTENT_UNDECLARED: 904 default: 905 model = null; 906 break; 907 } 908 if (model != null) 909 declHandler.elementDecl (ename, model); 910 911 attNames = parser.declaredAttributes (ename); 912 while (attNames != null && attNames.hasNext ()) { 913 String aname = (String) attNames.next (); 914 String type; 915 String valueDefault; 916 String value; 917 918 switch (parser.getAttributeType (ename, aname)) { 919 case XmlParser.ATTRIBUTE_CDATA: 920 type = "CDATA"; 921 break; 922 case XmlParser.ATTRIBUTE_ENTITY: 923 type = "ENTITY"; 924 break; 925 case XmlParser.ATTRIBUTE_ENTITIES: 926 type = "ENTITIES"; 927 break; 928 case XmlParser.ATTRIBUTE_ENUMERATED: 929 type = parser.getAttributeIterator (ename, aname); 930 break; 931 case XmlParser.ATTRIBUTE_ID: 932 type = "ID"; 933 break; 934 case XmlParser.ATTRIBUTE_IDREF: 935 type = "IDREF"; 936 break; 937 case XmlParser.ATTRIBUTE_IDREFS: 938 type = "IDREFS"; 939 break; 940 case XmlParser.ATTRIBUTE_NMTOKEN: 941 type = "NMTOKEN"; 942 break; 943 case XmlParser.ATTRIBUTE_NMTOKENS: 944 type = "NMTOKENS"; 945 break; 946 947 // XXX SAX2 beta doesn't have a way to return the 948 // enumerated list of permitted notations ... SAX1 949 // kluged it as NMTOKEN, but that won't work for 950 // the sort of apps that really use the DTD info 951 case XmlParser.ATTRIBUTE_NOTATION: 952 type = "NOTATION"; 953 break; 954 955 default: 956 errorHandler.fatalError (new SAXParseException ( 957 "internal error, att type", this)); 958 type = null; 959 } 960 961 switch (parser.getAttributeDefaultValueType ( 962 ename, aname)) { 963 case XmlParser.ATTRIBUTE_DEFAULT_IMPLIED: 964 valueDefault = "#IMPLIED"; 965 break; 966 case XmlParser.ATTRIBUTE_DEFAULT_REQUIRED: 967 valueDefault = "#REQUIRED"; 968 break; 969 case XmlParser.ATTRIBUTE_DEFAULT_FIXED: 970 valueDefault = "#FIXED"; 971 break; 972 case XmlParser.ATTRIBUTE_DEFAULT_SPECIFIED: 973 valueDefault = null; 974 break; 975 976 default: 977 errorHandler.fatalError (new SAXParseException ( 978 "internal error, att default", this)); 979 valueDefault = null; 980 } 981 982 value = parser.getAttributeDefaultValue (ename, aname); 983 984 declHandler.attributeDecl (ename, aname, 985 type, valueDefault, value); 986 } 987 } 988 } 989 } 990 991 992 // 993 // Implementation of org.xml.sax.Attributes. 994 // 995 996 /** 997 * <b>SAX1 AttributeList, SAX2 Attributes</b> method 998 * (don't invoke on parser); 999 */ 1000 public int getLength () 1001 { 1002 return attributeNames.size (); 1003 } 1004 1005 /** 1006 * <b>SAX2 Attributes</b> method (don't invoke on parser); 1007 */ 1008 public String getURI (int index) 1009 { 1010 return (String) (attributeNamespaces.get (index)); 1011 } 1012 1013 /** 1014 * <b>SAX2 Attributes</b> method (don't invoke on parser); 1015 */ 1016 public String getLocalName (int index) 1017 { 1018 return (String) (attributeLocalNames.get (index)); 1019 } 1020 1021 /** 1022 * <b>SAX2 Attributes</b> method (don't invoke on parser); 1023 */ 1024 public String getQName (int i) 1025 { 1026 return (String) (attributeNames.get (i)); 1027 } 1028 1029 /** 1030 * <b>SAX1 AttributeList</b> method (don't invoke on parser); 1031 */ 1032 public String getName (int i) 1033 { 1034 return (String) (attributeNames.get (i)); 1035 } 1036 1037 /** 1038 * <b>SAX1 AttributeList, SAX2 Attributes</b> method 1039 * (don't invoke on parser); 1040 */ 1041 public String getType (int i) 1042 { 1043 switch (parser.getAttributeType (elementName, getQName (i))) { 1044 1045 case XmlParser.ATTRIBUTE_UNDECLARED: 1046 case XmlParser.ATTRIBUTE_CDATA: 1047 return "CDATA"; 1048 case XmlParser.ATTRIBUTE_ID: 1049 return "ID"; 1050 case XmlParser.ATTRIBUTE_IDREF: 1051 return "IDREF"; 1052 case XmlParser.ATTRIBUTE_IDREFS: 1053 return "IDREFS"; 1054 case XmlParser.ATTRIBUTE_ENTITY: 1055 return "ENTITY"; 1056 case XmlParser.ATTRIBUTE_ENTITIES: 1057 return "ENTITIES"; 1058 case XmlParser.ATTRIBUTE_ENUMERATED: 1059 // XXX doesn't have a way to return permitted enum values, 1060 // though they must each be a NMTOKEN 1061 case XmlParser.ATTRIBUTE_NMTOKEN: 1062 return "NMTOKEN"; 1063 case XmlParser.ATTRIBUTE_NMTOKENS: 1064 return "NMTOKENS"; 1065 case XmlParser.ATTRIBUTE_NOTATION: 1066 // XXX doesn't have a way to return the permitted values, 1067 // each of which must be name a declared notation 1068 return "NOTATION"; 1069 1070 } 1071 1072 return null; 1073 } 1074 1075 1076 /** 1077 * <b>SAX1 AttributeList, SAX2 Attributes</b> method 1078 * (don't invoke on parser); 1079 */ 1080 public String getValue (int i) 1081 { 1082 return (String) (attributeValues.get (i)); 1083 } 1084 1085 1086 /** 1087 * <b>SAX2 Attributes</b> method (don't invoke on parser); 1088 */ 1089 public int getIndex (String uri, String local) 1090 { 1091 int length = getLength (); 1092 1093 for (int i = 0; i < length; i++) { 1094 if (!getURI (i).equals (uri)) 1095 continue; 1096 if (getLocalName (i).equals (local)) 1097 return i; 1098 } 1099 return -1; 1100 } 1101 1102 1103 /** 1104 * <b>SAX2 Attributes</b> method (don't invoke on parser); 1105 */ 1106 public int getIndex (String xmlName) 1107 { 1108 int length = getLength (); 1109 1110 for (int i = 0; i < length; i++) { 1111 if (getQName (i).equals (xmlName)) 1112 return i; 1113 } 1114 return -1; 1115 } 1116 1117 1118 /** 1119 * <b>SAX2 Attributes</b> method (don't invoke on parser); 1120 */ 1121 public String getType (String uri, String local) 1122 { 1123 int index = getIndex (uri, local); 1124 1125 if (index < 0) 1126 return null; 1127 return getType (index); 1128 } 1129 1130 1131 /** 1132 * <b>SAX1 AttributeList, SAX2 Attributes</b> method 1133 * (don't invoke on parser); 1134 */ 1135 public String getType (String xmlName) 1136 { 1137 int index = getIndex (xmlName); 1138 1139 if (index < 0) 1140 return null; 1141 return getType (index); 1142 } 1143 1144 1145 /** 1146 * <b>SAX Attributes</b> method (don't invoke on parser); 1147 */ 1148 public String getValue (String uri, String local) 1149 { 1150 int index = getIndex (uri, local); 1151 1152 if (index < 0) 1153 return null; 1154 return getValue (index); 1155 } 1156 1157 1158 /** 1159 * <b>SAX1 AttributeList, SAX2 Attributes</b> method 1160 * (don't invoke on parser); 1161 */ 1162 public String getValue (String xmlName) 1163 { 1164 int index = getIndex (xmlName); 1165 1166 if (index < 0) 1167 return null; 1168 return getValue (index); 1169 } 1170 1171 1172 // 1173 // Implementation of org.xml.sax.Locator. 1174 // 1175 1176 /** 1177 * <b>SAX Locator</b> method (don't invoke on parser); 1178 */ 1179 public String getPublicId () 1180 { 1181 return null; // XXX track public IDs too 1182 } 1183 1184 /** 1185 * <b>SAX Locator</b> method (don't invoke on parser); 1186 */ 1187 public String getSystemId () 1188 { 1189 return (String) entityStack.get ( entityStack.size() - 1 ); 1190 } 1191 1192 /** 1193 * <b>SAX Locator</b> method (don't invoke on parser); 1194 */ 1195 public int getLineNumber () 1196 { 1197 return parser.getLineNumber (); 1198 } 1199 1200 /** 1201 * <b>SAX Locator</b> method (don't invoke on parser); 1202 */ 1203 public int getColumnNumber () 1204 { 1205 return parser.getColumnNumber (); 1206 } 1207 1208 // adapter between content handler and document handler callbacks 1209 1210 private static class Adapter implements ContentHandler 1211 { 1212 private DocumentHandler docHandler; 1213 1214 Adapter (DocumentHandler dh) 1215 { docHandler = dh; } 1216 1217 1218 public void setDocumentLocator (Locator l) 1219 { docHandler.setDocumentLocator (l); } 1220 1221 public void startDocument () throws SAXException 1222 { docHandler.startDocument (); } 1223 1224 public void processingInstruction (String target, String data) 1225 throws SAXException 1226 { docHandler.processingInstruction (target, data); } 1227 1228 public void startPrefixMapping (String prefix, String uri) 1229 { /* ignored */ } 1230 1231 public void startElement ( 1232 String namespace, 1233 String local, 1234 String name, 1235 Attributes attrs ) throws SAXException 1236 { 1237 docHandler.startElement (name, (AttributeList) attrs); 1238 } 1239 1240 public void characters (char buf [], int offset, int len) 1241 throws SAXException 1242 { 1243 docHandler.characters (buf, offset, len); 1244 } 1245 1246 public void ignorableWhitespace (char buf [], int offset, int len) 1247 throws SAXException 1248 { 1249 docHandler.ignorableWhitespace (buf, offset, len); 1250 } 1251 1252 public void skippedEntity (String name) 1253 { /* ignored */ } 1254 1255 public void endElement (String u, String l, String name) 1256 throws SAXException 1257 { docHandler.endElement (name); } 1258 1259 public void endPrefixMapping (String prefix) 1260 { /* ignored */ } 1261 1262 public void endDocument () throws SAXException 1263 { docHandler.endDocument (); } 1264 } 1265}