1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 /*
   5  * Licensed to the Apache Software Foundation (ASF) under one or more
   6  * contributor license agreements.  See the NOTICE file distributed with
   7  * this work for additional information regarding copyright ownership.
   8  * The ASF licenses this file to You under the Apache License, Version 2.0
   9  * (the "License"); you may not use this file except in compliance with
  10  * the License.  You may obtain a copy of the License at
  11  *
  12  *     http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 
  21 package com.sun.org.apache.xerces.internal.parsers;
  22 
  23 import com.sun.org.apache.xerces.internal.impl.Constants;
  24 import com.sun.org.apache.xerces.internal.util.EntityResolver2Wrapper;
  25 import com.sun.org.apache.xerces.internal.util.EntityResolverWrapper;
  26 import com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper;
  27 import com.sun.org.apache.xerces.internal.util.SAXMessageFormatter;
  28 import com.sun.org.apache.xerces.internal.util.Status;
  29 import com.sun.org.apache.xerces.internal.util.SymbolHash;
  30 import com.sun.org.apache.xerces.internal.util.XMLSymbols;
  31 import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager;
  32 import com.sun.org.apache.xerces.internal.xni.Augmentations;
  33 import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
  34 import com.sun.org.apache.xerces.internal.xni.QName;
  35 import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
  36 import com.sun.org.apache.xerces.internal.xni.XMLLocator;
  37 import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier;
  38 import com.sun.org.apache.xerces.internal.xni.XMLString;
  39 import com.sun.org.apache.xerces.internal.xni.XNIException;
  40 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
  41 import com.sun.org.apache.xerces.internal.xni.parser.XMLEntityResolver;
  42 import com.sun.org.apache.xerces.internal.xni.parser.XMLErrorHandler;
  43 import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource;
  44 import com.sun.org.apache.xerces.internal.xni.parser.XMLParseException;
  45 import com.sun.org.apache.xerces.internal.xni.parser.XMLParserConfiguration;
  46 import com.sun.org.apache.xerces.internal.xs.AttributePSVI;
  47 import com.sun.org.apache.xerces.internal.xs.ElementPSVI;
  48 import com.sun.org.apache.xerces.internal.xs.PSVIProvider;
  49 import java.io.IOException;
  50 import java.util.Locale;
  51 import javax.xml.XMLConstants;
  52 import org.xml.sax.AttributeList;
  53 import org.xml.sax.ContentHandler;
  54 import org.xml.sax.DTDHandler;
  55 import org.xml.sax.DocumentHandler;
  56 import org.xml.sax.EntityResolver;
  57 import org.xml.sax.ErrorHandler;
  58 import org.xml.sax.InputSource;
  59 import org.xml.sax.Parser;
  60 import org.xml.sax.SAXException;
  61 import org.xml.sax.SAXNotRecognizedException;
  62 import org.xml.sax.SAXNotSupportedException;
  63 import org.xml.sax.SAXParseException;
  64 import org.xml.sax.XMLReader;
  65 import org.xml.sax.ext.Attributes2;
  66 import org.xml.sax.ext.DeclHandler;
  67 import org.xml.sax.ext.EntityResolver2;
  68 import org.xml.sax.ext.LexicalHandler;
  69 import org.xml.sax.ext.Locator2;
  70 import org.xml.sax.helpers.LocatorImpl;
  71 
  72 /**
  73  * This is the base class of all SAX parsers. It implements both the
  74  * SAX1 and SAX2 parser functionality, while the actual pipeline is
  75  * defined in the parser configuration.
  76  *
  77  * @author Arnaud Le Hors, IBM
  78  * @author Andy Clark, IBM
  79  *
  80  */
  81 public abstract class AbstractSAXParser
  82     extends AbstractXMLDocumentParser
  83     implements PSVIProvider, // PSVI
  84               Parser, XMLReader // SAX1, SAX2
  85 {
  86 
  87     //
  88     // Constants
  89     //
  90 
  91     // features
  92 
  93     /** Feature identifier: namespaces. */
  94     protected static final String NAMESPACES =
  95         Constants.SAX_FEATURE_PREFIX + Constants.NAMESPACES_FEATURE;
  96 
  97     /** Feature identifier: namespace prefixes. */
  98     protected static final String NAMESPACE_PREFIXES =
  99         Constants.SAX_FEATURE_PREFIX + Constants.NAMESPACE_PREFIXES_FEATURE;
 100 
 101     /** Feature id: string interning. */
 102     protected static final String STRING_INTERNING =
 103         Constants.SAX_FEATURE_PREFIX + Constants.STRING_INTERNING_FEATURE;
 104 
 105     /** Feature identifier: allow notation and unparsed entity events to be sent out of order. */
 106     // this is not meant to be a recognized feature, but we need it here to use
 107     // if it is already a recognized feature for the pipeline
 108     protected static final String ALLOW_UE_AND_NOTATION_EVENTS =
 109         Constants.SAX_FEATURE_PREFIX + Constants.ALLOW_DTD_EVENTS_AFTER_ENDDTD_FEATURE;
 110 
 111     /** Recognized features. */
 112     private static final String[] RECOGNIZED_FEATURES = {
 113         NAMESPACES,
 114         NAMESPACE_PREFIXES,
 115         STRING_INTERNING,
 116     };
 117 
 118     // properties
 119 
 120     /** Property id: lexical handler. */
 121     protected static final String LEXICAL_HANDLER =
 122         Constants.SAX_PROPERTY_PREFIX + Constants.LEXICAL_HANDLER_PROPERTY;
 123 
 124     /** Property id: declaration handler. */
 125     protected static final String DECLARATION_HANDLER =
 126         Constants.SAX_PROPERTY_PREFIX + Constants.DECLARATION_HANDLER_PROPERTY;
 127 
 128     /** Property id: DOM node. */
 129     protected static final String DOM_NODE =
 130         Constants.SAX_PROPERTY_PREFIX + Constants.DOM_NODE_PROPERTY;
 131 
 132     /** Property id: security manager. */
 133     private static final String SECURITY_MANAGER =
 134         Constants.XERCES_PROPERTY_PREFIX + Constants.SECURITY_MANAGER_PROPERTY;
 135 
 136     /** Recognized properties. */
 137     private static final String[] RECOGNIZED_PROPERTIES = {
 138         LEXICAL_HANDLER,
 139         DECLARATION_HANDLER,
 140         DOM_NODE,
 141     };
 142 
 143     //
 144     // Data
 145     //
 146 
 147     // features
 148 
 149     /** Namespaces. */
 150     protected boolean fNamespaces;
 151 
 152     /** Namespace prefixes. */
 153     protected boolean fNamespacePrefixes = false;
 154 
 155     /** Lexical handler parameter entities. */
 156     protected boolean fLexicalHandlerParameterEntities = true;
 157 
 158     /** Standalone document declaration. */
 159     protected boolean fStandalone;
 160 
 161     /** Resolve DTD URIs. */
 162     protected boolean fResolveDTDURIs = true;
 163 
 164     /** Use EntityResolver2. */
 165     protected boolean fUseEntityResolver2 = true;
 166 
 167     /**
 168      * XMLNS URIs: Namespace declarations in the
 169      * http://www.w3.org/2000/xmlns/ namespace.
 170      */
 171     protected boolean fXMLNSURIs = false;
 172 
 173     // parser handlers
 174 
 175     /** Content handler. */
 176     protected ContentHandler fContentHandler;
 177 
 178     /** Document handler. */
 179     protected DocumentHandler fDocumentHandler;
 180 
 181     /** Namespace context */
 182     protected NamespaceContext fNamespaceContext;
 183 
 184     /** DTD handler. */
 185     protected org.xml.sax.DTDHandler fDTDHandler;
 186 
 187     /** Decl handler. */
 188     protected DeclHandler fDeclHandler;
 189 
 190     /** Lexical handler. */
 191     protected LexicalHandler fLexicalHandler;
 192 
 193     protected QName fQName = new QName();
 194 
 195     // state
 196 
 197     /**
 198      * True if a parse is in progress. This state is needed because
 199      * some features/properties cannot be set while parsing (e.g.
 200      * validation and namespaces).
 201      */
 202     protected boolean fParseInProgress = false;
 203 
 204     // track the version of the document being parsed
 205     protected String fVersion;
 206 
 207     // temp vars
 208     private final AttributesProxy fAttributesProxy = new AttributesProxy();
 209     private Augmentations fAugmentations = null;
 210 
 211 
 212     // temporary buffer for sending normalized values
 213     // REVISIT: what should be the size of the buffer?
 214     private static final int BUFFER_SIZE = 20;
 215     private char[] fCharBuffer =  new char[BUFFER_SIZE];
 216 
 217     // allows us to keep track of whether an attribute has
 218     // been declared twice, so that we can avoid exposing the
 219     // second declaration to any registered DeclHandler
 220     protected SymbolHash fDeclaredAttrs = null;
 221 
 222     //
 223     // Constructors
 224     //
 225 
 226     /** Default constructor. */
 227     protected AbstractSAXParser(XMLParserConfiguration config) {
 228         super(config);
 229 
 230         config.addRecognizedFeatures(RECOGNIZED_FEATURES);
 231         config.addRecognizedProperties(RECOGNIZED_PROPERTIES);
 232 
 233         try {
 234             config.setFeature(ALLOW_UE_AND_NOTATION_EVENTS, false);
 235         }
 236         catch (XMLConfigurationException e) {
 237             // it wasn't a recognized feature, so we don't worry about it
 238         }
 239     } // <init>(XMLParserConfiguration)
 240 
 241     //
 242     // XMLDocumentHandler methods
 243     //
 244 
 245     /**
 246      * The start of the document.
 247      *
 248      * @param locator The document locator, or null if the document
 249      *                 location cannot be reported during the parsing
 250      *                 of this document. However, it is <em>strongly</em>
 251      *                 recommended that a locator be supplied that can
 252      *                 at least report the system identifier of the
 253      *                 document.
 254      * @param encoding The auto-detected IANA encoding name of the entity
 255      *                 stream. This value will be null in those situations
 256      *                 where the entity encoding is not auto-detected (e.g.
 257      *                 internal entities or a document entity that is
 258      *                 parsed from a java.io.Reader).
 259      * @param namespaceContext
 260      *                 The namespace context in effect at the
 261      *                 start of this document.
 262      *                 This object represents the current context.
 263      *                 Implementors of this class are responsible
 264      *                 for copying the namespace bindings from the
 265      *                 the current context (and its parent contexts)
 266      *                 if that information is important.
 267      * @param augs     Additional information that may include infoset augmentations
 268      *
 269      * @throws XNIException Thrown by handler to signal an error.
 270      */
 271     public void startDocument(XMLLocator locator, String encoding,
 272                               NamespaceContext namespaceContext, Augmentations augs)
 273         throws XNIException {
 274 
 275         fNamespaceContext = namespaceContext;
 276 
 277         try {
 278             // SAX1
 279             if (fDocumentHandler != null) {
 280                 if (locator != null) {
 281                     fDocumentHandler.setDocumentLocator(new LocatorProxy(locator));
 282                 }
 283                 fDocumentHandler.startDocument();
 284             }
 285 
 286             // SAX2
 287             if (fContentHandler != null) {
 288                 if (locator != null) {
 289                     fContentHandler.setDocumentLocator(new LocatorProxy(locator));
 290                 }
 291                 fContentHandler.startDocument();
 292             }
 293         }
 294         catch (SAXException e) {
 295             throw new XNIException(e);
 296         }
 297 
 298     } // startDocument(locator,encoding,augs)
 299 
 300     /**
 301      * Notifies of the presence of an XMLDecl line in the document. If
 302      * present, this method will be called immediately following the
 303      * startDocument call.
 304      *
 305      * @param version    The XML version.
 306      * @param encoding   The IANA encoding name of the document, or null if
 307      *                   not specified.
 308      * @param standalone The standalone value, or null if not specified.
 309      * @param augs   Additional information that may include infoset augmentations
 310      *
 311      * @throws XNIException Thrown by handler to signal an error.
 312      */
 313     public void xmlDecl(String version, String encoding, String standalone, Augmentations augs)
 314         throws XNIException {
 315         // the version need only be set once; if
 316         // document's XML 1.0|1.1, that's how it'll stay
 317         fVersion = version;
 318         fStandalone = "yes".equals(standalone);
 319     } // xmlDecl(String,String,String)
 320 
 321     /**
 322      * Notifies of the presence of the DOCTYPE line in the document.
 323      *
 324      * @param rootElement The name of the root element.
 325      * @param publicId    The public identifier if an external DTD or null
 326      *                    if the external DTD is specified using SYSTEM.
 327      * @param systemId    The system identifier if an external DTD, null
 328      *                    otherwise.
 329      * @param augs     Additional information that may include infoset augmentations
 330      *
 331      * @throws XNIException Thrown by handler to signal an error.
 332      */
 333     public void doctypeDecl(String rootElement,
 334                             String publicId, String systemId, Augmentations augs)
 335         throws XNIException {
 336         fInDTD = true;
 337 
 338         try {
 339             // SAX2 extension
 340             if (fLexicalHandler != null) {
 341                 fLexicalHandler.startDTD(rootElement, publicId, systemId);
 342             }
 343         }
 344         catch (SAXException e) {
 345             throw new XNIException(e);
 346         }
 347 
 348         // is there a DeclHandler?
 349         if(fDeclHandler != null) {
 350             fDeclaredAttrs = new SymbolHash();
 351         }
 352 
 353     } // doctypeDecl(String,String,String)
 354 
 355         /**
 356      * This method notifies of the start of an entity. The DTD has the
 357      * pseudo-name of "[dtd]" parameter entity names start with '%'; and
 358      * general entity names are just the entity name.
 359      * <p>
 360      * <strong>Note:</strong> Since the document is an entity, the handler
 361      * will be notified of the start of the document entity by calling the
 362      * startEntity method with the entity name "[xml]" <em>before</em> calling
 363      * the startDocument method. When exposing entity boundaries through the
 364      * SAX API, the document entity is never reported, however.
 365      * <p>
 366      * <strong>Note:</strong> This method is not called for entity references
 367      * appearing as part of attribute values.
 368      *
 369      * @param name     The name of the entity.
 370      * @param identifier The resource identifier.
 371      * @param encoding The auto-detected IANA encoding name of the entity
 372      *                 stream. This value will be null in those situations
 373      *                 where the entity encoding is not auto-detected (e.g.
 374      *                 internal parameter entities).
 375      * @param augs     Additional information that may include infoset augmentations
 376      *
 377      * @throws XNIException Thrown by handler to signal an error.
 378      */
 379     public void startGeneralEntity(String name, XMLResourceIdentifier identifier,
 380                                    String encoding, Augmentations augs)
 381         throws XNIException {
 382 
 383         try {
 384             // Only report startEntity if this entity was actually read.
 385             if (augs != null && Boolean.TRUE.equals(augs.getItem(Constants.ENTITY_SKIPPED))) {
 386                 // report skipped entity to content handler
 387                 if (fContentHandler != null) {
 388                     fContentHandler.skippedEntity(name);
 389                 }
 390             }
 391             else {
 392                 // SAX2 extension
 393                 if (fLexicalHandler != null) {
 394                     fLexicalHandler.startEntity(name);
 395                 }
 396             }
 397         }
 398         catch (SAXException e) {
 399             throw new XNIException(e);
 400         }
 401 
 402     } // startGeneralEntity(String,String,String,String,String)
 403 
 404     /**
 405      * This method notifies the end of an entity. The DTD has the pseudo-name
 406      * of "[dtd]" parameter entity names start with '%'; and general entity
 407      * names are just the entity name.
 408      * <p>
 409      * <strong>Note:</strong> Since the document is an entity, the handler
 410      * will be notified of the end of the document entity by calling the
 411      * endEntity method with the entity name "[xml]" <em>after</em> calling
 412      * the endDocument method. When exposing entity boundaries through the
 413      * SAX API, the document entity is never reported, however.
 414      * <p>
 415      * <strong>Note:</strong> This method is not called for entity references
 416      * appearing as part of attribute values.
 417      *
 418      * @param name The name of the entity.
 419      * @param augs     Additional information that may include infoset augmentations
 420      *
 421      * @throws XNIException Thrown by handler to signal an error.
 422      */
 423     public void endGeneralEntity(String name, Augmentations augs) throws XNIException {
 424 
 425         try {
 426             // Only report endEntity if this entity was actually read.
 427             if (augs == null || !Boolean.TRUE.equals(augs.getItem(Constants.ENTITY_SKIPPED))) {
 428                 // SAX2 extension
 429                 if (fLexicalHandler != null) {
 430                     fLexicalHandler.endEntity(name);
 431                 }
 432             }
 433         }
 434         catch (SAXException e) {
 435             throw new XNIException(e);
 436         }
 437 
 438     } // endEntity(String)
 439 
 440      /**
 441      * The start of an element. If the document specifies the start element
 442      * by using an empty tag, then the startElement method will immediately
 443      * be followed by the endElement method, with no intervening methods.
 444      *
 445      * @param element    The name of the element.
 446      * @param attributes The element attributes.
 447      * @param augs     Additional information that may include infoset augmentations
 448      *
 449      * @throws XNIException Thrown by handler to signal an error.
 450      */
 451     public void startElement(QName element, XMLAttributes attributes, Augmentations augs)
 452         throws XNIException {
 453 
 454         try {
 455             // SAX1
 456             if (fDocumentHandler != null) {
 457                 // REVISIT: should we support schema-normalized-value for SAX1 events
 458                 //
 459                 fAttributesProxy.setAttributes(attributes);
 460                 fDocumentHandler.startElement(element.rawname, fAttributesProxy);
 461             }
 462 
 463             // SAX2
 464             if (fContentHandler != null) {
 465 
 466                 if (fNamespaces) {
 467                     // send prefix mapping events
 468                     startNamespaceMapping();
 469 
 470                     // REVISIT: It should not be necessary to iterate over the attribute
 471                     // list when the set of [namespace attributes] is empty for this
 472                     // element. This should be computable from the NamespaceContext, but
 473                     // since we currently don't report the mappings for the xml prefix
 474                     // we cannot use the declared prefix count for the current context
 475                     // to skip this section. -- mrglavas
 476                     int len = attributes.getLength();
 477                     if (!fNamespacePrefixes) {
 478                         for (int i = len - 1; i >= 0; --i) {
 479                             attributes.getName(i, fQName);
 480                             if ((fQName.prefix == XMLSymbols.PREFIX_XMLNS) ||
 481                                (fQName.rawname == XMLSymbols.PREFIX_XMLNS)) {
 482                                 // remove namespace declaration attributes
 483                                 attributes.removeAttributeAt(i);
 484                             }
 485                         }
 486                     }
 487                     else if (!fXMLNSURIs) {
 488                         for (int i = len - 1; i >= 0; --i) {
 489                             attributes.getName(i, fQName);
 490                             if ((fQName.prefix == XMLSymbols.PREFIX_XMLNS) ||
 491                                (fQName.rawname == XMLSymbols.PREFIX_XMLNS)) {
 492                                 // localpart should be empty string as per SAX documentation:
 493                                 // http://www.saxproject.org/?selected=namespaces
 494                                 fQName.prefix = "";
 495                                 fQName.uri = "";
 496                                 fQName.localpart = "";
 497                                 attributes.setName(i, fQName);
 498                             }
 499                         }
 500                     }
 501                 }
 502 
 503                 fAugmentations = augs;
 504 
 505                 String uri = element.uri != null ? element.uri : "";
 506                 String localpart = fNamespaces ? element.localpart : "";
 507                 fAttributesProxy.setAttributes(attributes);
 508                 fContentHandler.startElement(uri, localpart, element.rawname,
 509                                              fAttributesProxy);
 510             }
 511         }
 512         catch (SAXException e) {
 513             throw new XNIException(e);
 514         }
 515 
 516     } // startElement(QName,XMLAttributes)
 517 
 518     /**
 519      * Character content.
 520      *
 521      * @param text The content.
 522      * @param augs     Additional information that may include infoset augmentations
 523      *
 524      * @throws XNIException Thrown by handler to signal an error.
 525      */
 526     public void characters(XMLString text, Augmentations augs) throws XNIException {
 527 
 528         // if type is union (XML Schema) it is possible that we receive
 529         // character call with empty data
 530         if (text.length == 0) {
 531             return;
 532         }
 533 
 534 
 535         try {
 536             // SAX1
 537             if (fDocumentHandler != null) {
 538                 // REVISIT: should we support schema-normalized-value for SAX1 events
 539                 //
 540                 fDocumentHandler.characters(text.ch, text.offset, text.length);
 541             }
 542 
 543             // SAX2
 544             if (fContentHandler != null) {
 545                 fContentHandler.characters(text.ch, text.offset, text.length);
 546             }
 547         }
 548         catch (SAXException e) {
 549             throw new XNIException(e);
 550         }
 551 
 552     } // characters(XMLString)
 553 
 554     /**
 555      * Ignorable whitespace. For this method to be called, the document
 556      * source must have some way of determining that the text containing
 557      * only whitespace characters should be considered ignorable. For
 558      * example, the validator can determine if a length of whitespace
 559      * characters in the document are ignorable based on the element
 560      * content model.
 561      *
 562      * @param text The ignorable whitespace.
 563      * @param augs     Additional information that may include infoset augmentations
 564      *
 565      * @throws XNIException Thrown by handler to signal an error.
 566      */
 567     public void ignorableWhitespace(XMLString text, Augmentations augs) throws XNIException {
 568 
 569         try {
 570             // SAX1
 571             if (fDocumentHandler != null) {
 572                 fDocumentHandler.ignorableWhitespace(text.ch, text.offset, text.length);
 573             }
 574 
 575             // SAX2
 576             if (fContentHandler != null) {
 577                 fContentHandler.ignorableWhitespace(text.ch, text.offset, text.length);
 578             }
 579         }
 580         catch (SAXException e) {
 581             throw new XNIException(e);
 582         }
 583 
 584     } // ignorableWhitespace(XMLString)
 585 
 586     /**
 587      * The end of an element.
 588      *
 589      * @param element The name of the element.
 590      * @param augs     Additional information that may include infoset augmentations
 591      *
 592      * @throws XNIException Thrown by handler to signal an error.
 593      */
 594     public void endElement(QName element, Augmentations augs) throws XNIException {
 595 
 596 
 597         try {
 598             // SAX1
 599             if (fDocumentHandler != null) {
 600                 fDocumentHandler.endElement(element.rawname);
 601             }
 602 
 603             // SAX2
 604             if (fContentHandler != null) {
 605                 fAugmentations = augs;
 606                 String uri = element.uri != null ? element.uri : "";
 607                 String localpart = fNamespaces ? element.localpart : "";
 608                 fContentHandler.endElement(uri, localpart,
 609                                            element.rawname);
 610                 if (fNamespaces) {
 611                     endNamespaceMapping();
 612                 }
 613             }
 614         }
 615         catch (SAXException e) {
 616             throw new XNIException(e);
 617         }
 618 
 619     } // endElement(QName)
 620 
 621         /**
 622      * The start of a CDATA section.
 623      * @param augs     Additional information that may include infoset augmentations
 624      *
 625      * @throws XNIException Thrown by handler to signal an error.
 626      */
 627     public void startCDATA(Augmentations augs) throws XNIException {
 628 
 629         try {
 630             // SAX2 extension
 631             if (fLexicalHandler != null) {
 632                 fLexicalHandler.startCDATA();
 633             }
 634         }
 635         catch (SAXException e) {
 636             throw new XNIException(e);
 637         }
 638 
 639     } // startCDATA()
 640 
 641     /**
 642      * The end of a CDATA section.
 643      * @param augs     Additional information that may include infoset augmentations
 644      *
 645      * @throws XNIException Thrown by handler to signal an error.
 646      */
 647     public void endCDATA(Augmentations augs) throws XNIException {
 648 
 649         try {
 650             // SAX2 extension
 651             if (fLexicalHandler != null) {
 652                 fLexicalHandler.endCDATA();
 653             }
 654         }
 655         catch (SAXException e) {
 656             throw new XNIException(e);
 657         }
 658 
 659     } // endCDATA()
 660 
 661     /**
 662      * A comment.
 663      *
 664      * @param text The text in the comment.
 665      * @param augs     Additional information that may include infoset augmentations
 666      *
 667      * @throws XNIException Thrown by application to signal an error.
 668      */
 669     public void comment(XMLString text, Augmentations augs) throws XNIException {
 670 
 671         try {
 672             // SAX2 extension
 673             if (fLexicalHandler != null) {
 674                 fLexicalHandler.comment(text.ch, 0, text.length);
 675             }
 676         }
 677         catch (SAXException e) {
 678             throw new XNIException(e);
 679         }
 680 
 681     } // comment(XMLString)
 682 
 683     /**
 684      * A processing instruction. Processing instructions consist of a
 685      * target name and, optionally, text data. The data is only meaningful
 686      * to the application.
 687      * <p>
 688      * Typically, a processing instruction's data will contain a series
 689      * of pseudo-attributes. These pseudo-attributes follow the form of
 690      * element attributes but are <strong>not</strong> parsed or presented
 691      * to the application as anything other than text. The application is
 692      * responsible for parsing the data.
 693      *
 694      * @param target The target.
 695      * @param data   The data or null if none specified.
 696      * @param augs     Additional information that may include infoset augmentations
 697      *
 698      * @throws XNIException Thrown by handler to signal an error.
 699      */
 700     public void processingInstruction(String target, XMLString data, Augmentations augs)
 701         throws XNIException {
 702 
 703         //
 704         // REVISIT - I keep running into SAX apps that expect
 705         //   null data to be an empty string, which is contrary
 706         //   to the comment for this method in the SAX API.
 707         //
 708 
 709         try {
 710             // SAX1
 711             if (fDocumentHandler != null) {
 712                 fDocumentHandler.processingInstruction(target,
 713                                                        data.toString());
 714             }
 715 
 716             // SAX2
 717             if (fContentHandler != null) {
 718                 fContentHandler.processingInstruction(target, data.toString());
 719             }
 720         }
 721         catch (SAXException e) {
 722             throw new XNIException(e);
 723         }
 724 
 725     } // processingInstruction(String,XMLString)
 726 
 727 
 728     /**
 729      * The end of the document.
 730      * @param augs     Additional information that may include infoset augmentations
 731      *
 732      * @throws XNIException Thrown by handler to signal an error.
 733      */
 734     public void endDocument(Augmentations augs) throws XNIException {
 735 
 736         try {
 737             // SAX1
 738             if (fDocumentHandler != null) {
 739                 fDocumentHandler.endDocument();
 740             }
 741 
 742             // SAX2
 743             if (fContentHandler != null) {
 744                 fContentHandler.endDocument();
 745             }
 746         }
 747         catch (SAXException e) {
 748             throw new XNIException(e);
 749         }
 750 
 751     } // endDocument()
 752 
 753     //
 754     // XMLDTDHandler methods
 755     //
 756 
 757     /**
 758      * The start of the DTD external subset.
 759      *
 760      * @param augs Additional information that may include infoset
 761      *                      augmentations.
 762      *
 763      * @throws XNIException Thrown by handler to signal an error.
 764      */
 765     public void startExternalSubset(XMLResourceIdentifier identifier,
 766                                     Augmentations augs) throws XNIException {
 767         startParameterEntity("[dtd]", null, null, augs);
 768     }
 769 
 770     /**
 771      * The end of the DTD external subset.
 772      *
 773      * @param augs Additional information that may include infoset
 774      *                      augmentations.
 775      *
 776      * @throws XNIException Thrown by handler to signal an error.
 777      */
 778     public void endExternalSubset(Augmentations augs) throws XNIException {
 779         endParameterEntity("[dtd]", augs);
 780     }
 781 
 782     /**
 783      * This method notifies of the start of parameter entity. The DTD has the
 784      * pseudo-name of "[dtd]" parameter entity names start with '%'; and
 785      * general entity names are just the entity name.
 786      * <p>
 787      * <strong>Note:</strong> Since the document is an entity, the handler
 788      * will be notified of the start of the document entity by calling the
 789      * startEntity method with the entity name "[xml]" <em>before</em> calling
 790      * the startDocument method. When exposing entity boundaries through the
 791      * SAX API, the document entity is never reported, however.
 792      * <p>
 793      * <strong>Note:</strong> This method is not called for entity references
 794      * appearing as part of attribute values.
 795      *
 796      * @param name     The name of the parameter entity.
 797      * @param identifier The resource identifier.
 798      * @param encoding The auto-detected IANA encoding name of the entity
 799      *                 stream. This value will be null in those situations
 800      *                 where the entity encoding is not auto-detected (e.g.
 801      *                 internal parameter entities).
 802      * @param augs Additional information that may include infoset
 803      *                      augmentations.
 804      *
 805      * @throws XNIException Thrown by handler to signal an error.
 806      */
 807     public void startParameterEntity(String name,
 808                                      XMLResourceIdentifier identifier,
 809                                      String encoding, Augmentations augs)
 810         throws XNIException {
 811 
 812         try {
 813             // Only report startEntity if this entity was actually read.
 814             if (augs != null && Boolean.TRUE.equals(augs.getItem(Constants.ENTITY_SKIPPED))) {
 815                 // report skipped entity to content handler
 816                 if (fContentHandler != null) {
 817                     fContentHandler.skippedEntity(name);
 818                 }
 819             }
 820             else {
 821                 // SAX2 extension
 822                 if (fLexicalHandler != null && fLexicalHandlerParameterEntities) {
 823                     fLexicalHandler.startEntity(name);
 824                 }
 825             }
 826         }
 827         catch (SAXException e) {
 828             throw new XNIException(e);
 829         }
 830 
 831     } // startParameterEntity(String,identifier,String,Augmentation)
 832 
 833     /**
 834      * This method notifies the end of an entity. The DTD has the pseudo-name
 835      * of "[dtd]" parameter entity names start with '%'; and general entity
 836      * names are just the entity name.
 837      * <p>
 838      * <strong>Note:</strong> Since the document is an entity, the handler
 839      * will be notified of the end of the document entity by calling the
 840      * endEntity method with the entity name "[xml]" <em>after</em> calling
 841      * the endDocument method. When exposing entity boundaries through the
 842      * SAX API, the document entity is never reported, however.
 843      * <p>
 844      * <strong>Note:</strong> This method is not called for entity references
 845      * appearing as part of attribute values.
 846      *
 847      * @param name The name of the parameter entity.
 848      * @param augs Additional information that may include infoset
 849      *                      augmentations.
 850      *
 851      * @throws XNIException Thrown by handler to signal an error.
 852      */
 853     public void endParameterEntity(String name, Augmentations augs) throws XNIException {
 854 
 855         try {
 856             // Only report endEntity if this entity was actually read.
 857             if (augs == null || !Boolean.TRUE.equals(augs.getItem(Constants.ENTITY_SKIPPED))) {
 858                 // SAX2 extension
 859                 if (fLexicalHandler != null && fLexicalHandlerParameterEntities) {
 860                     fLexicalHandler.endEntity(name);
 861                 }
 862             }
 863         }
 864         catch (SAXException e) {
 865             throw new XNIException(e);
 866         }
 867 
 868     } // endEntity(String)
 869 
 870     /**
 871      * An element declaration.
 872      *
 873      * @param name         The name of the element.
 874      * @param contentModel The element content model.
 875      *
 876      * @param augs Additional information that may include infoset
 877      *                      augmentations.
 878      *
 879      * @throws XNIException Thrown by handler to signal an error.
 880      */
 881     public void elementDecl(String name, String contentModel, Augmentations augs)
 882         throws XNIException {
 883 
 884         try {
 885             // SAX2 extension
 886             if (fDeclHandler != null) {
 887                 fDeclHandler.elementDecl(name, contentModel);
 888             }
 889         }
 890         catch (SAXException e) {
 891             throw new XNIException(e);
 892         }
 893 
 894     } // elementDecl(String,String, Augmentations)
 895 
 896     /**
 897      * An attribute declaration.
 898      *
 899      * @param elementName   The name of the element that this attribute
 900      *                      is associated with.
 901      * @param attributeName The name of the attribute.
 902      * @param type          The attribute type. This value will be one of
 903      *                      the following: "CDATA", "ENTITY", "ENTITIES",
 904      *                      "ENUMERATION", "ID", "IDREF", "IDREFS",
 905      *                      "NMTOKEN", "NMTOKENS", or "NOTATION".
 906      * @param enumeration   If the type has the value "ENUMERATION" or
 907      *                      "NOTATION", this array holds the allowed attribute
 908      *                      values; otherwise, this array is null.
 909      * @param defaultType   The attribute default type. This value will be
 910      *                      one of the following: "#FIXED", "#IMPLIED",
 911      *                      "#REQUIRED", or null.
 912      * @param defaultValue  The attribute default value, or null if no
 913      *                      default value is specified.
 914      *
 915      * @param nonNormalizedDefaultValue  The attribute default value with no normalization
 916      *                      performed, or null if no default value is specified.
 917      * @param augs Additional information that may include infoset
 918      *                      augmentations.
 919      *
 920      * @throws XNIException Thrown by handler to signal an error.
 921      */
 922     public void attributeDecl(String elementName, String attributeName,
 923                               String type, String[] enumeration,
 924                               String defaultType, XMLString defaultValue,
 925                               XMLString nonNormalizedDefaultValue, Augmentations augs) throws XNIException {
 926 
 927         try {
 928             // SAX2 extension
 929             if (fDeclHandler != null) {
 930                 // used as a key to detect duplicate attribute definitions.
 931                 String elemAttr = new StringBuffer(elementName).append("<").append(attributeName).toString();
 932                 if(fDeclaredAttrs.get(elemAttr) != null) {
 933                     // we aren't permitted to return duplicate attribute definitions
 934                     return;
 935                 }
 936                 fDeclaredAttrs.put(elemAttr, Boolean.TRUE);
 937                 if (type.equals("NOTATION") ||
 938                     type.equals("ENUMERATION")) {
 939 
 940                     StringBuffer str = new StringBuffer();
 941                     if (type.equals("NOTATION")) {
 942                       str.append(type);
 943                       str.append(" (");
 944                     }
 945                     else {
 946                       str.append("(");
 947                     }
 948                     for (int i = 0; i < enumeration.length; i++) {
 949                         str.append(enumeration[i]);
 950                         if (i < enumeration.length - 1) {
 951                             str.append('|');
 952                         }
 953                     }
 954                     str.append(')');
 955                     type = str.toString();
 956                 }
 957                 String value = (defaultValue==null) ? null : defaultValue.toString();
 958                 fDeclHandler.attributeDecl(elementName, attributeName,
 959                                            type, defaultType, value);
 960             }
 961         }
 962         catch (SAXException e) {
 963             throw new XNIException(e);
 964         }
 965 
 966     } // attributeDecl(String,String,String,String[],String,XMLString, XMLString, Augmentations)
 967 
 968     /**
 969      * An internal entity declaration.
 970      *
 971      * @param name The name of the entity. Parameter entity names start with
 972      *             '%', whereas the name of a general entity is just the
 973      *             entity name.
 974      * @param text The value of the entity.
 975      * @param nonNormalizedText The non-normalized value of the entity. This
 976      *             value contains the same sequence of characters that was in
 977      *             the internal entity declaration, without any entity
 978      *             references expanded.
 979      *
 980      * @param augs Additional information that may include infoset
 981      *                      augmentations.
 982      *
 983      * @throws XNIException Thrown by handler to signal an error.
 984      */
 985     public void internalEntityDecl(String name, XMLString text,
 986                                    XMLString nonNormalizedText,
 987                                    Augmentations augs) throws XNIException {
 988 
 989         try {
 990             // SAX2 extensions
 991             if (fDeclHandler != null) {
 992                 fDeclHandler.internalEntityDecl(name, text.toString());
 993             }
 994         }
 995         catch (SAXException e) {
 996             throw new XNIException(e);
 997         }
 998 
 999     } // internalEntityDecl(String,XMLString,XMLString)
1000 
1001     /**
1002      * An external entity declaration.
1003      *
1004      * @param name     The name of the entity. Parameter entity names start
1005      *                 with '%', whereas the name of a general entity is just
1006      *                 the entity name.
1007      * @param identifier    An object containing all location information
1008      *                      pertinent to this entity.
1009      * @param augs Additional information that may include infoset
1010      *                      augmentations.
1011      *
1012      * @throws XNIException Thrown by handler to signal an error.
1013      */
1014     public void externalEntityDecl(String name, XMLResourceIdentifier identifier,
1015                                    Augmentations augs) throws XNIException {
1016         try {
1017             // SAX2 extension
1018             if (fDeclHandler != null) {
1019                 String publicId = identifier.getPublicId();
1020                 String systemId = fResolveDTDURIs ?
1021                     identifier.getExpandedSystemId() : identifier.getLiteralSystemId();
1022                 fDeclHandler.externalEntityDecl(name, publicId, systemId);
1023             }
1024         }
1025         catch (SAXException e) {
1026             throw new XNIException(e);
1027         }
1028 
1029     } // externalEntityDecl(String,,XMLResourceIdentifier, Augmentations)
1030 
1031     /**
1032      * An unparsed entity declaration.
1033      *
1034      * @param name     The name of the entity.
1035      * @param identifier    An object containing all location information
1036      *                      pertinent to this entity.
1037      * @param notation The name of the notation.
1038      *
1039      * @param augs Additional information that may include infoset
1040      *                      augmentations.
1041      *
1042      * @throws XNIException Thrown by handler to signal an error.
1043      */
1044     public void unparsedEntityDecl(String name, XMLResourceIdentifier identifier,
1045                                    String notation,
1046                                    Augmentations augs) throws XNIException {
1047         try {
1048             // SAX2 extension
1049             if (fDTDHandler != null) {
1050                 String publicId = identifier.getPublicId();
1051                 String systemId = fResolveDTDURIs ?
1052                     identifier.getExpandedSystemId() : identifier.getLiteralSystemId();
1053                 fDTDHandler.unparsedEntityDecl(name, publicId, systemId, notation);
1054             }
1055         }
1056         catch (SAXException e) {
1057             throw new XNIException(e);
1058         }
1059 
1060     } // unparsedEntityDecl(String,XMLResourceIdentifier, String, Augmentations)
1061 
1062     /**
1063      * A notation declaration
1064      *
1065      * @param name     The name of the notation.
1066      * @param identifier    An object containing all location information
1067      *                      pertinent to this notation.
1068      * @param augs Additional information that may include infoset
1069      *                      augmentations.
1070      *
1071      * @throws XNIException Thrown by handler to signal an error.
1072      */
1073     public void notationDecl(String name, XMLResourceIdentifier identifier,
1074                              Augmentations augs) throws XNIException {
1075         try {
1076             // SAX1 and SAX2
1077             if (fDTDHandler != null) {
1078                 String publicId = identifier.getPublicId();
1079                 String systemId = fResolveDTDURIs ?
1080                     identifier.getExpandedSystemId() : identifier.getLiteralSystemId();
1081                 fDTDHandler.notationDecl(name, publicId, systemId);
1082             }
1083         }
1084         catch (SAXException e) {
1085             throw new XNIException(e);
1086         }
1087 
1088     } // notationDecl(String,XMLResourceIdentifier, Augmentations)
1089 
1090     /**
1091      * The end of the DTD.
1092      *
1093      * @param augs Additional information that may include infoset
1094      *                      augmentations.
1095      *
1096      * @throws XNIException Thrown by handler to signal an error.
1097      */
1098     public void endDTD(Augmentations augs) throws XNIException {
1099         fInDTD = false;
1100 
1101         try {
1102             // SAX2 extension
1103             if (fLexicalHandler != null) {
1104                 fLexicalHandler.endDTD();
1105             }
1106         }
1107         catch (SAXException e) {
1108             throw new XNIException(e);
1109         }
1110         if(fDeclaredAttrs != null) {
1111             // help out the GC
1112             fDeclaredAttrs.clear();
1113         }
1114 
1115     } // endDTD()
1116 
1117     //
1118     // Parser and XMLReader methods
1119     //
1120 
1121     /**
1122      * Parses the input source specified by the given system identifier.
1123      * <p>
1124      * This method is equivalent to the following:
1125      * <pre>
1126      *     parse(new InputSource(systemId));
1127      * </pre>
1128      *
1129      * @param systemId The system identifier (URI).
1130      *
1131      * @exception org.xml.sax.SAXException Throws exception on SAX error.
1132      * @exception java.io.IOException Throws exception on i/o error.
1133      */
1134     public void parse(String systemId) throws SAXException, IOException {
1135 
1136         // parse document
1137         XMLInputSource source = new XMLInputSource(null, systemId, null, false);
1138         try {
1139             parse(source);
1140         }
1141 
1142         // wrap XNI exceptions as SAX exceptions
1143         catch (XMLParseException e) {
1144             Exception ex = e.getException();
1145             if (ex == null) {
1146                 // must be a parser exception; mine it for locator info and throw
1147                 // a SAXParseException
1148                 LocatorImpl locatorImpl = new LocatorImpl(){
1149                     public String getXMLVersion() {
1150                         return fVersion;
1151                     }
1152                     // since XMLParseExceptions know nothing about encoding,
1153                     // we cannot return anything meaningful in this context.
1154                     // We *could* consult the LocatorProxy, but the
1155                     // application can do this itself if it wishes to possibly
1156                     // be mislead.
1157                     public String getEncoding() {
1158                         return null;
1159                     }
1160                 };
1161                 locatorImpl.setPublicId(e.getPublicId());
1162                 locatorImpl.setSystemId(e.getExpandedSystemId());
1163                 locatorImpl.setLineNumber(e.getLineNumber());
1164                 locatorImpl.setColumnNumber(e.getColumnNumber());
1165                 throw new SAXParseException(e.getMessage(), locatorImpl);
1166             }
1167             if (ex instanceof SAXException) {
1168                 // why did we create an XMLParseException?
1169                 throw (SAXException)ex;
1170             }
1171             if (ex instanceof IOException) {
1172                 throw (IOException)ex;
1173             }
1174             throw new SAXException(ex);
1175         }
1176         catch (XNIException e) {
1177             Exception ex = e.getException();
1178             if (ex == null) {
1179                 throw new SAXException(e.getMessage());
1180             }
1181             if (ex instanceof SAXException) {
1182                 throw (SAXException)ex;
1183             }
1184             if (ex instanceof IOException) {
1185                 throw (IOException)ex;
1186             }
1187             throw new SAXException(ex);
1188         }
1189 
1190     } // parse(String)
1191 
1192     /**
1193      * parse
1194      *
1195      * @param inputSource
1196      *
1197      * @exception org.xml.sax.SAXException
1198      * @exception java.io.IOException
1199      */
1200     public void parse(InputSource inputSource)
1201         throws SAXException, IOException {
1202 
1203         // parse document
1204         try {
1205             XMLInputSource xmlInputSource =
1206                 new XMLInputSource(inputSource.getPublicId(),
1207                                    inputSource.getSystemId(),
1208                                    null, false);
1209             xmlInputSource.setByteStream(inputSource.getByteStream());
1210             xmlInputSource.setCharacterStream(inputSource.getCharacterStream());
1211             xmlInputSource.setEncoding(inputSource.getEncoding());
1212             parse(xmlInputSource);
1213         }
1214 
1215         // wrap XNI exceptions as SAX exceptions
1216         catch (XMLParseException e) {
1217             Exception ex = e.getException();
1218             if (ex == null) {
1219                 // must be a parser exception; mine it for locator info and throw
1220                 // a SAXParseException
1221                 LocatorImpl locatorImpl = new LocatorImpl() {
1222                     public String getXMLVersion() {
1223                         return fVersion;
1224                     }
1225                     // since XMLParseExceptions know nothing about encoding,
1226                     // we cannot return anything meaningful in this context.
1227                     // We *could* consult the LocatorProxy, but the
1228                     // application can do this itself if it wishes to possibly
1229                     // be mislead.
1230                     public String getEncoding() {
1231                         return null;
1232                     }
1233                 };
1234                 locatorImpl.setPublicId(e.getPublicId());
1235                 locatorImpl.setSystemId(e.getExpandedSystemId());
1236                 locatorImpl.setLineNumber(e.getLineNumber());
1237                 locatorImpl.setColumnNumber(e.getColumnNumber());
1238                 throw new SAXParseException(e.getMessage(), locatorImpl);
1239             }
1240             if (ex instanceof SAXException) {
1241                 // why did we create an XMLParseException?
1242                 throw (SAXException)ex;
1243             }
1244             if (ex instanceof IOException) {
1245                 throw (IOException)ex;
1246             }
1247             throw new SAXException(ex);
1248         }
1249         catch (XNIException e) {
1250             Exception ex = e.getException();
1251             if (ex == null) {
1252                 throw new SAXException(e.getMessage());
1253             }
1254             if (ex instanceof SAXException) {
1255                 throw (SAXException)ex;
1256             }
1257             if (ex instanceof IOException) {
1258                 throw (IOException)ex;
1259             }
1260             throw new SAXException(ex);
1261         }
1262 
1263     } // parse(InputSource)
1264 
1265     /**
1266      * Sets the resolver used to resolve external entities. The EntityResolver
1267      * interface supports resolution of public and system identifiers.
1268      *
1269      * @param resolver The new entity resolver. Passing a null value will
1270      *                 uninstall the currently installed resolver.
1271      */
1272     public void setEntityResolver(EntityResolver resolver) {
1273 
1274         try {
1275             XMLEntityResolver xer = (XMLEntityResolver) fConfiguration.getProperty(ENTITY_RESOLVER);
1276             if (fUseEntityResolver2 && resolver instanceof EntityResolver2) {
1277                 if (xer instanceof EntityResolver2Wrapper) {
1278                     EntityResolver2Wrapper er2w = (EntityResolver2Wrapper) xer;
1279                     er2w.setEntityResolver((EntityResolver2) resolver);
1280                 }
1281                 else {
1282                     fConfiguration.setProperty(ENTITY_RESOLVER,
1283                             new EntityResolver2Wrapper((EntityResolver2) resolver));
1284                 }
1285             }
1286             else {
1287                 if (xer instanceof EntityResolverWrapper) {
1288                     EntityResolverWrapper erw = (EntityResolverWrapper) xer;
1289                     erw.setEntityResolver(resolver);
1290                 }
1291                 else {
1292                     fConfiguration.setProperty(ENTITY_RESOLVER,
1293                             new EntityResolverWrapper(resolver));
1294                 }
1295             }
1296         }
1297         catch (XMLConfigurationException e) {
1298             // do nothing
1299         }
1300 
1301     } // setEntityResolver(EntityResolver)
1302 
1303     /**
1304      * Return the current entity resolver.
1305      *
1306      * @return The current entity resolver, or null if none
1307      *         has been registered.
1308      * @see #setEntityResolver
1309      */
1310     public EntityResolver getEntityResolver() {
1311 
1312         EntityResolver entityResolver = null;
1313         try {
1314             XMLEntityResolver xmlEntityResolver =
1315                 (XMLEntityResolver)fConfiguration.getProperty(ENTITY_RESOLVER);
1316             if (xmlEntityResolver != null) {
1317                 if (xmlEntityResolver instanceof EntityResolverWrapper) {
1318                     entityResolver =
1319                         ((EntityResolverWrapper) xmlEntityResolver).getEntityResolver();
1320                 }
1321                 else if (xmlEntityResolver instanceof EntityResolver2Wrapper) {
1322                     entityResolver =
1323                         ((EntityResolver2Wrapper) xmlEntityResolver).getEntityResolver();
1324                 }
1325             }
1326         }
1327         catch (XMLConfigurationException e) {
1328             // do nothing
1329         }
1330         return entityResolver;
1331 
1332     } // getEntityResolver():EntityResolver
1333 
1334     /**
1335      * Allow an application to register an error event handler.
1336      *
1337      * <p>If the application does not register an error handler, all
1338      * error events reported by the SAX parser will be silently
1339      * ignored; however, normal processing may not continue.  It is
1340      * highly recommended that all SAX applications implement an
1341      * error handler to avoid unexpected bugs.</p>
1342      *
1343      * <p>Applications may register a new or different handler in the
1344      * middle of a parse, and the SAX parser must begin using the new
1345      * handler immediately.</p>
1346      *
1347      * @param errorHandler The error handler.
1348      * @see #getErrorHandler
1349      */
1350     public void setErrorHandler(ErrorHandler errorHandler) {
1351 
1352         try {
1353             XMLErrorHandler xeh = (XMLErrorHandler) fConfiguration.getProperty(ERROR_HANDLER);
1354             if (xeh instanceof ErrorHandlerWrapper) {
1355                 ErrorHandlerWrapper ehw = (ErrorHandlerWrapper) xeh;
1356                 ehw.setErrorHandler(errorHandler);
1357             }
1358             else {
1359                 fConfiguration.setProperty(ERROR_HANDLER,
1360                         new ErrorHandlerWrapper(errorHandler));
1361             }
1362         }
1363         catch (XMLConfigurationException e) {
1364             // do nothing
1365         }
1366 
1367     } // setErrorHandler(ErrorHandler)
1368 
1369     /**
1370      * Return the current error handler.
1371      *
1372      * @return The current error handler, or null if none
1373      *         has been registered.
1374      * @see #setErrorHandler
1375      */
1376     public ErrorHandler getErrorHandler() {
1377 
1378         ErrorHandler errorHandler = null;
1379         try {
1380             XMLErrorHandler xmlErrorHandler =
1381                 (XMLErrorHandler)fConfiguration.getProperty(ERROR_HANDLER);
1382             if (xmlErrorHandler != null &&
1383                 xmlErrorHandler instanceof ErrorHandlerWrapper) {
1384                 errorHandler = ((ErrorHandlerWrapper)xmlErrorHandler).getErrorHandler();
1385             }
1386         }
1387         catch (XMLConfigurationException e) {
1388             // do nothing
1389         }
1390         return errorHandler;
1391 
1392     } // getErrorHandler():ErrorHandler
1393 
1394     /**
1395      * Set the locale to use for messages.
1396      *
1397      * @param locale The locale object to use for localization of messages.
1398      *
1399      * @exception SAXException An exception thrown if the parser does not
1400      *                         support the specified locale.
1401      *
1402      * @see org.xml.sax.Parser
1403      */
1404     public void setLocale(Locale locale) throws SAXException {
1405         //REVISIT:this methods is not part of SAX2 interfaces, we should throw exception
1406         //if any application uses SAX2 and sets locale also. -nb
1407         fConfiguration.setLocale(locale);
1408 
1409     } // setLocale(Locale)
1410 
1411     /**
1412      * Allow an application to register a DTD event handler.
1413      * <p>
1414      * If the application does not register a DTD handler, all DTD
1415      * events reported by the SAX parser will be silently ignored.
1416      * <p>
1417      * Applications may register a new or different handler in the
1418      * middle of a parse, and the SAX parser must begin using the new
1419      * handler immediately.
1420      *
1421      * @param dtdHandler The DTD handler.
1422      *
1423 
1424      * @see #getDTDHandler
1425      */
1426     public void setDTDHandler(DTDHandler dtdHandler) {
1427         fDTDHandler = dtdHandler;
1428     } // setDTDHandler(DTDHandler)
1429 
1430     //
1431     // Parser methods
1432     //
1433 
1434     /**
1435      * Allow an application to register a document event handler.
1436      * <p>
1437      * If the application does not register a document handler, all
1438      * document events reported by the SAX parser will be silently
1439      * ignored (this is the default behaviour implemented by
1440      * HandlerBase).
1441      * <p>
1442      * Applications may register a new or different handler in the
1443      * middle of a parse, and the SAX parser must begin using the new
1444      * handler immediately.
1445      *
1446      * @param documentHandler The document handler.
1447      */
1448     public void setDocumentHandler(DocumentHandler documentHandler) {
1449         fDocumentHandler = documentHandler;
1450     } // setDocumentHandler(DocumentHandler)
1451 
1452     //
1453     // XMLReader methods
1454     //
1455 
1456     /**
1457      * Allow an application to register a content event handler.
1458      * <p>
1459      * If the application does not register a content handler, all
1460      * content events reported by the SAX parser will be silently
1461      * ignored.
1462      * <p>
1463      * Applications may register a new or different handler in the
1464      * middle of a parse, and the SAX parser must begin using the new
1465      * handler immediately.
1466      *
1467      * @param contentHandler The content handler.
1468      *
1469      * @see #getContentHandler
1470      */
1471     public void setContentHandler(ContentHandler contentHandler) {
1472         fContentHandler = contentHandler;
1473     } // setContentHandler(ContentHandler)
1474 
1475     /**
1476      * Return the current content handler.
1477      *
1478      * @return The current content handler, or null if none
1479      *         has been registered.
1480      *
1481      * @see #setContentHandler
1482      */
1483     public ContentHandler getContentHandler() {
1484         return fContentHandler;
1485     } // getContentHandler():ContentHandler
1486 
1487     /**
1488      * Return the current DTD handler.
1489      *
1490      * @return The current DTD handler, or null if none
1491      *         has been registered.
1492      * @see #setDTDHandler
1493      */
1494     public DTDHandler getDTDHandler() {
1495         return fDTDHandler;
1496     } // getDTDHandler():DTDHandler
1497 
1498     /**
1499      * Set the state of any feature in a SAX2 parser.  The parser
1500      * might not recognize the feature, and if it does recognize
1501      * it, it might not be able to fulfill the request.
1502      *
1503      * @param featureId The unique identifier (URI) of the feature.
1504      * @param state The requested state of the feature (true or false).
1505      *
1506      * @exception SAXNotRecognizedException If the
1507      *            requested feature is not known.
1508      * @exception SAXNotSupportedException If the
1509      *            requested feature is known, but the requested
1510      *            state is not supported.
1511      */
1512     public void setFeature(String featureId, boolean state)
1513         throws SAXNotRecognizedException, SAXNotSupportedException {
1514 
1515         try {
1516             //
1517             // SAX2 Features
1518             //
1519 
1520             if (featureId.startsWith(Constants.SAX_FEATURE_PREFIX)) {
1521                 final int suffixLength = featureId.length() - Constants.SAX_FEATURE_PREFIX.length();
1522 
1523                 // http://xml.org/sax/features/namespaces
1524                 if (suffixLength == Constants.NAMESPACES_FEATURE.length() &&
1525                     featureId.endsWith(Constants.NAMESPACES_FEATURE)) {
1526                     fConfiguration.setFeature(featureId, state);
1527                     fNamespaces = state;
1528                     return;
1529                 }
1530 
1531                 // http://xml.org/sax/features/namespace-prefixes
1532                 //   controls the reporting of raw prefixed names and Namespace
1533                 //   declarations (xmlns* attributes): when this feature is false
1534                 //   (the default), raw prefixed names may optionally be reported,
1535                 //   and xmlns* attributes must not be reported.
1536                 //
1537                 if (suffixLength == Constants.NAMESPACE_PREFIXES_FEATURE.length() &&
1538                     featureId.endsWith(Constants.NAMESPACE_PREFIXES_FEATURE)) {
1539                     fConfiguration.setFeature(featureId, state);
1540                     fNamespacePrefixes = state;
1541                     return;
1542                 }
1543 
1544                 // http://xml.org/sax/features/string-interning
1545                 //   controls the use of java.lang.String#intern() for strings
1546                 //   passed to SAX handlers.
1547                 //
1548                 if (suffixLength == Constants.STRING_INTERNING_FEATURE.length() &&
1549                     featureId.endsWith(Constants.STRING_INTERNING_FEATURE)) {
1550                     if (!state) {
1551                         throw new SAXNotSupportedException(
1552                             SAXMessageFormatter.formatMessage(fConfiguration.getLocale(),
1553                             "false-not-supported", new Object [] {featureId}));
1554                     }
1555                     return;
1556                 }
1557 
1558                 // http://xml.org/sax/features/lexical-handler/parameter-entities
1559                 //   controls whether the beginning and end of parameter entities
1560                 //   will be reported to the LexicalHandler.
1561                 //
1562                 if (suffixLength == Constants.LEXICAL_HANDLER_PARAMETER_ENTITIES_FEATURE.length() &&
1563                     featureId.endsWith(Constants.LEXICAL_HANDLER_PARAMETER_ENTITIES_FEATURE)) {
1564                     fLexicalHandlerParameterEntities = state;
1565                     return;
1566                 }
1567 
1568                 // http://xml.org/sax/features/resolve-dtd-uris
1569                 //   controls whether system identifiers will be absolutized relative to
1570                 //   their base URIs before reporting.
1571                 //
1572                 if (suffixLength == Constants.RESOLVE_DTD_URIS_FEATURE.length() &&
1573                     featureId.endsWith(Constants.RESOLVE_DTD_URIS_FEATURE)) {
1574                     fResolveDTDURIs = state;
1575                     return;
1576                 }
1577 
1578                 // http://xml.org/sax/features/unicode-normalization-checking
1579                 //   controls whether Unicode normalization checking is performed
1580                 //   as per Appendix B of the XML 1.1 specification
1581                 //
1582                 if (suffixLength == Constants.UNICODE_NORMALIZATION_CHECKING_FEATURE.length() &&
1583                     featureId.endsWith(Constants.UNICODE_NORMALIZATION_CHECKING_FEATURE)) {
1584                     // REVISIT: Allow this feature to be set once Unicode normalization
1585                     // checking is supported -- mrglavas.
1586                     if (state) {
1587                         throw new SAXNotSupportedException(
1588                             SAXMessageFormatter.formatMessage(fConfiguration.getLocale(),
1589                             "true-not-supported", new Object [] {featureId}));
1590                     }
1591                     return;
1592                 }
1593 
1594                 // http://xml.org/sax/features/xmlns-uris
1595                 //   controls whether the parser reports that namespace declaration
1596                 //   attributes as being in the namespace: http://www.w3.org/2000/xmlns/
1597                 //
1598                 if (suffixLength == Constants.XMLNS_URIS_FEATURE.length() &&
1599                     featureId.endsWith(Constants.XMLNS_URIS_FEATURE)) {
1600                     fXMLNSURIs = state;
1601                     return;
1602                 }
1603 
1604                 // http://xml.org/sax/features/use-entity-resolver2
1605                 //   controls whether the methods of an object implementing
1606                 //   org.xml.sax.ext.EntityResolver2 will be used by the parser.
1607                 //
1608                 if (suffixLength == Constants.USE_ENTITY_RESOLVER2_FEATURE.length() &&
1609                     featureId.endsWith(Constants.USE_ENTITY_RESOLVER2_FEATURE)) {
1610                     if (state != fUseEntityResolver2) {
1611                         fUseEntityResolver2 = state;
1612                         // Refresh EntityResolver wrapper.
1613                         setEntityResolver(getEntityResolver());
1614                     }
1615                     return;
1616                 }
1617 
1618                 //
1619                 // Read only features.
1620                 //
1621 
1622                 // http://xml.org/sax/features/is-standalone
1623                 //   reports whether the document specified a standalone document declaration.
1624                 // http://xml.org/sax/features/use-attributes2
1625                 //   reports whether Attributes objects passed to startElement also implement
1626                 //   the org.xml.sax.ext.Attributes2 interface.
1627                 // http://xml.org/sax/features/use-locator2
1628                 //   reports whether Locator objects passed to setDocumentLocator also implement
1629                 //   the org.xml.sax.ext.Locator2 interface.
1630                 // http://xml.org/sax/features/xml-1.1
1631                 //   reports whether the parser supports both XML 1.1 and XML 1.0.
1632                 if ((suffixLength == Constants.IS_STANDALONE_FEATURE.length() &&
1633                     featureId.endsWith(Constants.IS_STANDALONE_FEATURE)) ||
1634                     (suffixLength == Constants.USE_ATTRIBUTES2_FEATURE.length() &&
1635                     featureId.endsWith(Constants.USE_ATTRIBUTES2_FEATURE)) ||
1636                     (suffixLength == Constants.USE_LOCATOR2_FEATURE.length() &&
1637                     featureId.endsWith(Constants.USE_LOCATOR2_FEATURE)) ||
1638                     (suffixLength == Constants.XML_11_FEATURE.length() &&
1639                     featureId.endsWith(Constants.XML_11_FEATURE))) {
1640                     throw new SAXNotSupportedException(
1641                         SAXMessageFormatter.formatMessage(fConfiguration.getLocale(),
1642                         "feature-read-only", new Object [] {featureId}));
1643                 }
1644 
1645 
1646                 //
1647                 // Drop through and perform default processing
1648                 //
1649             }
1650             else if (featureId.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) {
1651                 if (state) {
1652                     if (fConfiguration.getProperty(SECURITY_MANAGER )==null) {
1653                         fConfiguration.setProperty(SECURITY_MANAGER, new XMLSecurityManager());
1654                     }
1655                 }
1656             }
1657 
1658             //
1659             // Default handling
1660             //
1661 
1662             fConfiguration.setFeature(featureId, state);
1663         }
1664         catch (XMLConfigurationException e) {
1665             String identifier = e.getIdentifier();
1666             if (e.getType() == Status.NOT_RECOGNIZED) {
1667                 throw new SAXNotRecognizedException(
1668                     SAXMessageFormatter.formatMessage(fConfiguration.getLocale(),
1669                     "feature-not-recognized", new Object [] {identifier}));
1670             }
1671             else {
1672                 throw new SAXNotSupportedException(
1673                     SAXMessageFormatter.formatMessage(fConfiguration.getLocale(),
1674                     "feature-not-supported", new Object [] {identifier}));
1675             }
1676         }
1677 
1678     } // setFeature(String,boolean)
1679 
1680     /**
1681      * Query the state of a feature.
1682      *
1683      * Query the current state of any feature in a SAX2 parser.  The
1684      * parser might not recognize the feature.
1685      *
1686      * @param featureId The unique identifier (URI) of the feature
1687      *                  being set.
1688      * @return The current state of the feature.
1689      * @exception org.xml.sax.SAXNotRecognizedException If the
1690      *            requested feature is not known.
1691      * @exception SAXNotSupportedException If the
1692      *            requested feature is known but not supported.
1693      */
1694     public boolean getFeature(String featureId)
1695         throws SAXNotRecognizedException, SAXNotSupportedException {
1696 
1697         try {
1698             //
1699             // SAX2 Features
1700             //
1701 
1702             if (featureId.startsWith(Constants.SAX_FEATURE_PREFIX)) {
1703                 final int suffixLength = featureId.length() - Constants.SAX_FEATURE_PREFIX.length();
1704 
1705                 // http://xml.org/sax/features/namespace-prefixes
1706                 //   controls the reporting of raw prefixed names and Namespace
1707                 //   declarations (xmlns* attributes): when this feature is false
1708                 //   (the default), raw prefixed names may optionally be reported,
1709                 //   and xmlns* attributes must not be reported.
1710                 //
1711                 if (suffixLength == Constants.NAMESPACE_PREFIXES_FEATURE.length() &&
1712                     featureId.endsWith(Constants.NAMESPACE_PREFIXES_FEATURE)) {
1713                     boolean state = fConfiguration.getFeature(featureId);
1714                     return state;
1715                 }
1716                 // http://xml.org/sax/features/string-interning
1717                 //   controls the use of java.lang.String#intern() for strings
1718                 //   passed to SAX handlers.
1719                 //
1720                 if (suffixLength == Constants.STRING_INTERNING_FEATURE.length() &&
1721                     featureId.endsWith(Constants.STRING_INTERNING_FEATURE)) {
1722                     return true;
1723                 }
1724 
1725                 // http://xml.org/sax/features/is-standalone
1726                 //   reports whether the document specified a standalone document declaration.
1727                 //
1728                 if (suffixLength == Constants.IS_STANDALONE_FEATURE.length() &&
1729                     featureId.endsWith(Constants.IS_STANDALONE_FEATURE)) {
1730                     return fStandalone;
1731                 }
1732 
1733                 // http://xml.org/sax/features/xml-1.1
1734                 //   reports whether the parser supports both XML 1.1 and XML 1.0.
1735                 //
1736                 if (suffixLength == Constants.XML_11_FEATURE.length() &&
1737                     featureId.endsWith(Constants.XML_11_FEATURE)) {
1738                     return (fConfiguration instanceof XML11Configurable);
1739                 }
1740 
1741                 // http://xml.org/sax/features/lexical-handler/parameter-entities
1742                 //   controls whether the beginning and end of parameter entities
1743                 //   will be reported to the LexicalHandler.
1744                 //
1745                 if (suffixLength == Constants.LEXICAL_HANDLER_PARAMETER_ENTITIES_FEATURE.length() &&
1746                     featureId.endsWith(Constants.LEXICAL_HANDLER_PARAMETER_ENTITIES_FEATURE)) {
1747                     return fLexicalHandlerParameterEntities;
1748                 }
1749 
1750                 // http://xml.org/sax/features/resolve-dtd-uris
1751                 //   controls whether system identifiers will be absolutized relative to
1752                 //   their base URIs before reporting.
1753                 if (suffixLength == Constants.RESOLVE_DTD_URIS_FEATURE.length() &&
1754                     featureId.endsWith(Constants.RESOLVE_DTD_URIS_FEATURE)) {
1755                     return fResolveDTDURIs;
1756                 }
1757 
1758                 // http://xml.org/sax/features/xmlns-uris
1759                 //   controls whether the parser reports that namespace declaration
1760                 //   attributes as being in the namespace: http://www.w3.org/2000/xmlns/
1761                 //
1762                 if (suffixLength == Constants.XMLNS_URIS_FEATURE.length() &&
1763                     featureId.endsWith(Constants.XMLNS_URIS_FEATURE)) {
1764                     return fXMLNSURIs;
1765                 }
1766 
1767                 // http://xml.org/sax/features/unicode-normalization-checking
1768                 //   controls whether Unicode normalization checking is performed
1769                 //   as per Appendix B of the XML 1.1 specification
1770                 //
1771                 if (suffixLength == Constants.UNICODE_NORMALIZATION_CHECKING_FEATURE.length() &&
1772                     featureId.endsWith(Constants.UNICODE_NORMALIZATION_CHECKING_FEATURE)) {
1773                     // REVISIT: Allow this feature to be set once Unicode normalization
1774                     // checking is supported -- mrglavas.
1775                     return false;
1776                 }
1777 
1778                 // http://xml.org/sax/features/use-entity-resolver2
1779                 //   controls whether the methods of an object implementing
1780                 //   org.xml.sax.ext.EntityResolver2 will be used by the parser.
1781                 //
1782                 if (suffixLength == Constants.USE_ENTITY_RESOLVER2_FEATURE.length() &&
1783                     featureId.endsWith(Constants.USE_ENTITY_RESOLVER2_FEATURE)) {
1784                     return fUseEntityResolver2;
1785                 }
1786 
1787                 // http://xml.org/sax/features/use-attributes2
1788                 //   reports whether Attributes objects passed to startElement also implement
1789                 //   the org.xml.sax.ext.Attributes2 interface.
1790                 // http://xml.org/sax/features/use-locator2
1791                 //   reports whether Locator objects passed to setDocumentLocator also implement
1792                 //   the org.xml.sax.ext.Locator2 interface.
1793                 //
1794                 if ((suffixLength == Constants.USE_ATTRIBUTES2_FEATURE.length() &&
1795                     featureId.endsWith(Constants.USE_ATTRIBUTES2_FEATURE)) ||
1796                     (suffixLength == Constants.USE_LOCATOR2_FEATURE.length() &&
1797                     featureId.endsWith(Constants.USE_LOCATOR2_FEATURE))) {
1798                     return true;
1799                 }
1800 
1801 
1802                 //
1803                 // Drop through and perform default processing
1804                 //
1805             }
1806 
1807             //
1808             // Xerces Features
1809             //
1810 
1811             /*
1812             else if (featureId.startsWith(XERCES_FEATURES_PREFIX)) {
1813                 //
1814                 // Drop through and perform default processing
1815                 //
1816             }
1817             */
1818 
1819             return fConfiguration.getFeature(featureId);
1820         }
1821         catch (XMLConfigurationException e) {
1822             String identifier = e.getIdentifier();
1823             if (e.getType() == Status.NOT_RECOGNIZED) {
1824                 throw new SAXNotRecognizedException(
1825                     SAXMessageFormatter.formatMessage(fConfiguration.getLocale(),
1826                     "feature-not-recognized", new Object [] {identifier}));
1827             }
1828             else {
1829                 throw new SAXNotSupportedException(
1830                     SAXMessageFormatter.formatMessage(fConfiguration.getLocale(),
1831                     "feature-not-supported", new Object [] {identifier}));
1832             }
1833         }
1834 
1835     } // getFeature(String):boolean
1836 
1837     /**
1838      * Set the value of any property in a SAX2 parser.  The parser
1839      * might not recognize the property, and if it does recognize
1840      * it, it might not support the requested value.
1841      *
1842      * @param propertyId The unique identifier (URI) of the property
1843      *                   being set.
1844      * @param value The value to which the property is being set.
1845      *
1846      * @exception SAXNotRecognizedException If the
1847      *            requested property is not known.
1848      * @exception SAXNotSupportedException If the
1849      *            requested property is known, but the requested
1850      *            value is not supported.
1851      */
1852     public void setProperty(String propertyId, Object value)
1853         throws SAXNotRecognizedException, SAXNotSupportedException {
1854 
1855         try {
1856             //
1857             // SAX2 core properties
1858             //
1859 
1860             if (propertyId.startsWith(Constants.SAX_PROPERTY_PREFIX)) {
1861                 final int suffixLength = propertyId.length() - Constants.SAX_PROPERTY_PREFIX.length();
1862 
1863                 //
1864                 // http://xml.org/sax/properties/lexical-handler
1865                 // Value type: org.xml.sax.ext.LexicalHandler
1866                 // Access: read/write, pre-parse only
1867                 //   Set the lexical event handler.
1868                 //
1869                 if (suffixLength == Constants.LEXICAL_HANDLER_PROPERTY.length() &&
1870                     propertyId.endsWith(Constants.LEXICAL_HANDLER_PROPERTY)) {
1871                     try {
1872                         setLexicalHandler((LexicalHandler)value);
1873                     }
1874                     catch (ClassCastException e) {
1875                         throw new SAXNotSupportedException(
1876                             SAXMessageFormatter.formatMessage(fConfiguration.getLocale(),
1877                             "incompatible-class", new Object [] {propertyId, "org.xml.sax.ext.LexicalHandler"}));
1878                     }
1879                     return;
1880                 }
1881                 //
1882                 // http://xml.org/sax/properties/declaration-handler
1883                 // Value type: org.xml.sax.ext.DeclHandler
1884                 // Access: read/write, pre-parse only
1885                 //   Set the DTD declaration event handler.
1886                 //
1887                 if (suffixLength == Constants.DECLARATION_HANDLER_PROPERTY.length() &&
1888                     propertyId.endsWith(Constants.DECLARATION_HANDLER_PROPERTY)) {
1889                     try {
1890                         setDeclHandler((DeclHandler)value);
1891                     }
1892                     catch (ClassCastException e) {
1893                         throw new SAXNotSupportedException(
1894                             SAXMessageFormatter.formatMessage(fConfiguration.getLocale(),
1895                             "incompatible-class", new Object [] {propertyId, "org.xml.sax.ext.DeclHandler"}));
1896                     }
1897                     return;
1898                 }
1899                 //
1900                 // http://xml.org/sax/properties/dom-node
1901                 // Value type: DOM Node
1902                 // Access: read-only
1903                 //   Get the DOM node currently being visited, if the SAX parser is
1904                 //   iterating over a DOM tree.  If the parser recognises and
1905                 //   supports this property but is not currently visiting a DOM
1906                 //   node, it should return null (this is a good way to check for
1907                 //   availability before the parse begins).
1908                 // http://xml.org/sax/properties/document-xml-version
1909                 // Value type: java.lang.String
1910                 // Access: read-only
1911                 //   The literal string describing the actual XML version of the document.
1912                 //
1913                 if ((suffixLength == Constants.DOM_NODE_PROPERTY.length() &&
1914                     propertyId.endsWith(Constants.DOM_NODE_PROPERTY)) ||
1915                     (suffixLength == Constants.DOCUMENT_XML_VERSION_PROPERTY.length() &&
1916                     propertyId.endsWith(Constants.DOCUMENT_XML_VERSION_PROPERTY))) {
1917                     throw new SAXNotSupportedException(
1918                         SAXMessageFormatter.formatMessage(fConfiguration.getLocale(),
1919                         "property-read-only", new Object [] {propertyId}));
1920                 }
1921                 //
1922                 // Drop through and perform default processing
1923                 //
1924             }
1925 
1926             //
1927             // Xerces Properties
1928             //
1929 
1930             /*
1931             else if (propertyId.startsWith(XERCES_PROPERTIES_PREFIX)) {
1932                 //
1933                 // Drop through and perform default processing
1934                 //
1935             }
1936             */
1937 
1938             //
1939             // Perform default processing
1940             //
1941 
1942             fConfiguration.setProperty(propertyId, value);
1943         }
1944         catch (XMLConfigurationException e) {
1945             String identifier = e.getIdentifier();
1946             if (e.getType() == Status.NOT_RECOGNIZED) {
1947                 throw new SAXNotRecognizedException(
1948                     SAXMessageFormatter.formatMessage(fConfiguration.getLocale(),
1949                     "property-not-recognized", new Object [] {identifier}));
1950             }
1951             else {
1952                 throw new SAXNotSupportedException(
1953                     SAXMessageFormatter.formatMessage(fConfiguration.getLocale(),
1954                     "property-not-supported", new Object [] {identifier}));
1955             }
1956         }
1957 
1958     } // setProperty(String,Object)
1959 
1960     /**
1961      * Query the value of a property.
1962      *
1963      * Return the current value of a property in a SAX2 parser.
1964      * The parser might not recognize the property.
1965      *
1966      * @param propertyId The unique identifier (URI) of the property
1967      *                   being set.
1968      * @return The current value of the property.
1969      * @exception org.xml.sax.SAXNotRecognizedException If the
1970      *            requested property is not known.
1971      * @exception SAXNotSupportedException If the
1972      *            requested property is known but not supported.
1973      */
1974     public Object getProperty(String propertyId)
1975         throws SAXNotRecognizedException, SAXNotSupportedException {
1976 
1977         try {
1978             //
1979             // SAX2 core properties
1980             //
1981 
1982             if (propertyId.startsWith(Constants.SAX_PROPERTY_PREFIX)) {
1983                 final int suffixLength = propertyId.length() - Constants.SAX_PROPERTY_PREFIX.length();
1984 
1985                 //
1986                 // http://xml.org/sax/properties/document-xml-version
1987                 // Value type: java.lang.String
1988                 // Access: read-only
1989                 //   The literal string describing the actual XML version of the document.
1990                 //
1991                 if (suffixLength == Constants.DOCUMENT_XML_VERSION_PROPERTY.length() &&
1992                     propertyId.endsWith(Constants.DOCUMENT_XML_VERSION_PROPERTY)) {
1993                     return fVersion;
1994                 }
1995 
1996                 //
1997                 // http://xml.org/sax/properties/lexical-handler
1998                 // Value type: org.xml.sax.ext.LexicalHandler
1999                 // Access: read/write, pre-parse only
2000                 //   Set the lexical event handler.
2001                 //
2002                 if (suffixLength == Constants.LEXICAL_HANDLER_PROPERTY.length() &&
2003                     propertyId.endsWith(Constants.LEXICAL_HANDLER_PROPERTY)) {
2004                     return getLexicalHandler();
2005                 }
2006                 //
2007                 // http://xml.org/sax/properties/declaration-handler
2008                 // Value type: org.xml.sax.ext.DeclHandler
2009                 // Access: read/write, pre-parse only
2010                 //   Set the DTD declaration event handler.
2011                 //
2012                 if (suffixLength == Constants.DECLARATION_HANDLER_PROPERTY.length() &&
2013                     propertyId.endsWith(Constants.DECLARATION_HANDLER_PROPERTY)) {
2014                     return getDeclHandler();
2015                 }
2016 
2017                 //
2018                 // http://xml.org/sax/properties/dom-node
2019                 // Value type: DOM Node
2020                 // Access: read-only
2021                 //   Get the DOM node currently being visited, if the SAX parser is
2022                 //   iterating over a DOM tree.  If the parser recognises and
2023                 //   supports this property but is not currently visiting a DOM
2024                 //   node, it should return null (this is a good way to check for
2025                 //   availability before the parse begins).
2026                 //
2027                 if (suffixLength == Constants.DOM_NODE_PROPERTY.length() &&
2028                     propertyId.endsWith(Constants.DOM_NODE_PROPERTY)) {
2029                     // we are not iterating a DOM tree
2030                     throw new SAXNotSupportedException(
2031                         SAXMessageFormatter.formatMessage(fConfiguration.getLocale(),
2032                         "dom-node-read-not-supported", null));
2033                 }
2034 
2035                 //
2036                 // Drop through and perform default processing
2037                 //
2038             }
2039 
2040             //
2041             // Xerces properties
2042             //
2043 
2044             /*
2045             else if (propertyId.startsWith(XERCES_PROPERTIES_PREFIX)) {
2046                 //
2047                 // Drop through and perform default processing
2048                 //
2049             }
2050             */
2051 
2052             //
2053             // Perform default processing
2054             //
2055 
2056             return fConfiguration.getProperty(propertyId);
2057         }
2058         catch (XMLConfigurationException e) {
2059             String identifier = e.getIdentifier();
2060             if (e.getType() == Status.NOT_RECOGNIZED) {
2061                 throw new SAXNotRecognizedException(
2062                     SAXMessageFormatter.formatMessage(fConfiguration.getLocale(),
2063                     "property-not-recognized", new Object [] {identifier}));
2064             }
2065             else {
2066                 throw new SAXNotSupportedException(
2067                     SAXMessageFormatter.formatMessage(fConfiguration.getLocale(),
2068                     "property-not-supported", new Object [] {identifier}));
2069             }
2070         }
2071 
2072     } // getProperty(String):Object
2073 
2074     //
2075     // Protected methods
2076     //
2077 
2078     // SAX2 core properties
2079 
2080     /**
2081      * Set the DTD declaration event handler.
2082      * <p>
2083      * This method is the equivalent to the property:
2084      * <pre>
2085      * http://xml.org/sax/properties/declaration-handler
2086      * </pre>
2087      *
2088      * @param handler The new handler.
2089      *
2090      * @see #getDeclHandler
2091      * @see #setProperty
2092      */
2093     protected void setDeclHandler(DeclHandler handler)
2094         throws SAXNotRecognizedException, SAXNotSupportedException {
2095 
2096         if (fParseInProgress) {
2097             throw new SAXNotSupportedException(
2098                 SAXMessageFormatter.formatMessage(fConfiguration.getLocale(),
2099                 "property-not-parsing-supported",
2100                 new Object [] {"http://xml.org/sax/properties/declaration-handler"}));
2101         }
2102         fDeclHandler = handler;
2103 
2104     } // setDeclHandler(DeclHandler)
2105 
2106     /**
2107      * Returns the DTD declaration event handler.
2108      *
2109      * @see #setDeclHandler
2110      */
2111     protected DeclHandler getDeclHandler()
2112         throws SAXNotRecognizedException, SAXNotSupportedException {
2113         return fDeclHandler;
2114     } // getDeclHandler():DeclHandler
2115 
2116     /**
2117      * Set the lexical event handler.
2118      * <p>
2119      * This method is the equivalent to the property:
2120      * <pre>
2121      * http://xml.org/sax/properties/lexical-handler
2122      * </pre>
2123      *
2124      * @param handler lexical event handler
2125      *
2126      * @see #getLexicalHandler
2127      * @see #setProperty
2128      */
2129     protected void setLexicalHandler(LexicalHandler handler)
2130         throws SAXNotRecognizedException, SAXNotSupportedException {
2131 
2132         if (fParseInProgress) {
2133             throw new SAXNotSupportedException(
2134                 SAXMessageFormatter.formatMessage(fConfiguration.getLocale(),
2135                 "property-not-parsing-supported",
2136                 new Object [] {"http://xml.org/sax/properties/lexical-handler"}));
2137         }
2138         fLexicalHandler = handler;
2139 
2140     } // setLexicalHandler(LexicalHandler)
2141 
2142     /**
2143      * Returns the lexical handler.
2144      *
2145      * @see #setLexicalHandler
2146      */
2147     protected LexicalHandler getLexicalHandler()
2148         throws SAXNotRecognizedException, SAXNotSupportedException {
2149         return fLexicalHandler;
2150     } // getLexicalHandler():LexicalHandler
2151 
2152     /**
2153      * Send startPrefixMapping events
2154      */
2155     protected final void startNamespaceMapping() throws SAXException{
2156         int count = fNamespaceContext.getDeclaredPrefixCount();
2157         if (count > 0) {
2158             String prefix = null;
2159             String uri = null;
2160             for (int i = 0; i < count; i++) {
2161                 prefix = fNamespaceContext.getDeclaredPrefixAt(i);
2162                 uri = fNamespaceContext.getURI(prefix);
2163                 fContentHandler.startPrefixMapping(prefix,
2164                     (uri == null) ? "" : uri);
2165             }
2166         }
2167     }
2168 
2169     /**
2170      * Send endPrefixMapping events
2171      */
2172     protected final void endNamespaceMapping() throws SAXException {
2173         int count = fNamespaceContext.getDeclaredPrefixCount();
2174         if (count > 0) {
2175             for (int i = 0; i < count; i++) {
2176                 fContentHandler.endPrefixMapping(fNamespaceContext.getDeclaredPrefixAt(i));
2177             }
2178         }
2179     }
2180 
2181     //
2182     // XMLDocumentParser methods
2183     //
2184 
2185     /**
2186      * Reset all components before parsing.
2187      *
2188      * @throws XNIException Thrown if an error occurs during initialization.
2189      */
2190     public void reset() throws XNIException {
2191         super.reset();
2192 
2193         // reset state
2194         fInDTD = false;
2195         fVersion = "1.0";
2196         fStandalone = false;
2197 
2198         // features
2199         fNamespaces = fConfiguration.getFeature(NAMESPACES);
2200         fNamespacePrefixes = fConfiguration.getFeature(NAMESPACE_PREFIXES);
2201         fAugmentations = null;
2202         fDeclaredAttrs = null;
2203 
2204     } // reset()
2205 
2206     //
2207     // Classes
2208     //
2209 
2210     protected class LocatorProxy
2211         implements Locator2 {
2212 
2213         //
2214         // Data
2215         //
2216 
2217         /** XML locator. */
2218         protected XMLLocator fLocator;
2219 
2220         //
2221         // Constructors
2222         //
2223 
2224         /** Constructs an XML locator proxy. */
2225         public LocatorProxy(XMLLocator locator) {
2226             fLocator = locator;
2227         }
2228 
2229         //
2230         // Locator methods
2231         //
2232 
2233         /** Public identifier. */
2234         public String getPublicId() {
2235             return fLocator.getPublicId();
2236         }
2237 
2238         /** System identifier. */
2239         public String getSystemId() {
2240             return fLocator.getExpandedSystemId();
2241         }
2242         /** Line number. */
2243         public int getLineNumber() {
2244             return fLocator.getLineNumber();
2245         }
2246 
2247         /** Column number. */
2248         public int getColumnNumber() {
2249             return fLocator.getColumnNumber();
2250         }
2251 
2252         // Locator2 methods
2253         public String getXMLVersion() {
2254             return fLocator.getXMLVersion();
2255         }
2256 
2257         public String getEncoding() {
2258             return fLocator.getEncoding();
2259         }
2260 
2261     } // class LocatorProxy
2262 
2263     protected static final class AttributesProxy
2264         implements AttributeList, Attributes2 {
2265 
2266         //
2267         // Data
2268         //
2269 
2270         /** XML attributes. */
2271         protected XMLAttributes fAttributes;
2272 
2273         //
2274         // Public methods
2275         //
2276 
2277         /** Sets the XML attributes. */
2278         public void setAttributes(XMLAttributes attributes) {
2279             fAttributes = attributes;
2280         } // setAttributes(XMLAttributes)
2281 
2282         public int getLength() {
2283             return fAttributes.getLength();
2284         }
2285 
2286         public String getName(int i) {
2287             return fAttributes.getQName(i);
2288         }
2289 
2290         public String getQName(int index) {
2291             return fAttributes.getQName(index);
2292         }
2293 
2294         public String getURI(int index) {
2295             // REVISIT: this hides the fact that internally we use
2296             //          null instead of empty string
2297             //          SAX requires URI to be a string or an empty string
2298             String uri= fAttributes.getURI(index);
2299             return uri != null ? uri : "";
2300         }
2301 
2302         public String getLocalName(int index) {
2303             return fAttributes.getLocalName(index);
2304         }
2305 
2306         public String getType(int i) {
2307             return fAttributes.getType(i);
2308         }
2309 
2310         public String getType(String name) {
2311             return fAttributes.getType(name);
2312         }
2313 
2314         public String getType(String uri, String localName) {
2315             return uri.equals("") ? fAttributes.getType(null, localName) :
2316                                     fAttributes.getType(uri, localName);
2317         }
2318 
2319         public String getValue(int i) {
2320             return fAttributes.getValue(i);
2321         }
2322 
2323         public String getValue(String name) {
2324             return fAttributes.getValue(name);
2325         }
2326 
2327         public String getValue(String uri, String localName) {
2328             return uri.equals("") ? fAttributes.getValue(null, localName) :
2329                                     fAttributes.getValue(uri, localName);
2330         }
2331 
2332         public int getIndex(String qName) {
2333             return fAttributes.getIndex(qName);
2334         }
2335 
2336         public int getIndex(String uri, String localPart) {
2337             return uri.equals("") ? fAttributes.getIndex(null, localPart) :
2338                                     fAttributes.getIndex(uri, localPart);
2339         }
2340 
2341         // Attributes2 methods
2342         // REVISIT: Localize exception messages. -- mrglavas
2343         public boolean isDeclared(int index) {
2344             if (index < 0 || index >= fAttributes.getLength()) {
2345                 throw new ArrayIndexOutOfBoundsException(index);
2346             }
2347             return Boolean.TRUE.equals(
2348                 fAttributes.getAugmentations(index).getItem(
2349                 Constants.ATTRIBUTE_DECLARED));
2350         }
2351 
2352         public boolean isDeclared(String qName) {
2353             int index = getIndex(qName);
2354             if (index == -1) {
2355                 throw new IllegalArgumentException(qName);
2356             }
2357             return Boolean.TRUE.equals(
2358                 fAttributes.getAugmentations(index).getItem(
2359                 Constants.ATTRIBUTE_DECLARED));
2360         }
2361 
2362         public boolean isDeclared(String uri, String localName) {
2363             int index = getIndex(uri, localName);
2364             if (index == -1) {
2365                 throw new IllegalArgumentException(localName);
2366             }
2367             return Boolean.TRUE.equals(
2368                 fAttributes.getAugmentations(index).getItem(
2369                 Constants.ATTRIBUTE_DECLARED));
2370         }
2371 
2372         public boolean isSpecified(int index) {
2373             if (index < 0 || index >= fAttributes.getLength()) {
2374                 throw new ArrayIndexOutOfBoundsException(index);
2375             }
2376             return fAttributes.isSpecified(index);
2377         }
2378 
2379         public boolean isSpecified(String qName) {
2380             int index = getIndex(qName);
2381             if (index == -1) {
2382                 throw new IllegalArgumentException(qName);
2383             }
2384             return fAttributes.isSpecified(index);
2385         }
2386 
2387         public boolean isSpecified(String uri, String localName) {
2388             int index = getIndex(uri, localName);
2389             if (index == -1) {
2390                 throw new IllegalArgumentException(localName);
2391             }
2392             return fAttributes.isSpecified(index);
2393         }
2394 
2395     } // class AttributesProxy
2396 
2397 
2398     // PSVIProvider methods
2399 
2400     public ElementPSVI getElementPSVI(){
2401         return (fAugmentations != null)?(ElementPSVI)fAugmentations.getItem(Constants.ELEMENT_PSVI):null;
2402     }
2403 
2404 
2405     public AttributePSVI getAttributePSVI(int index){
2406 
2407         return (AttributePSVI)fAttributesProxy.fAttributes.getAugmentations(index).getItem(Constants.ATTRIBUTE_PSVI);
2408     }
2409 
2410 
2411     public AttributePSVI getAttributePSVIByName(String uri,
2412                                                 String localname){
2413         return (AttributePSVI)fAttributesProxy.fAttributes.getAugmentations(uri, localname).getItem(Constants.ATTRIBUTE_PSVI);
2414     }
2415 
2416 } // class AbstractSAXParser