1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Copyright 2003-2005 The Apache Software Foundation.
   7  *
   8  * Licensed under the Apache License, Version 2.0 (the "License");
   9  * you may not use this file except in compliance with the License.
  10  * 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 package com.sun.org.apache.xerces.internal.xinclude;
  21 
  22 import java.io.CharConversionException;
  23 import java.io.IOException;
  24 import java.util.ArrayList;
  25 import java.util.Enumeration;
  26 import java.util.Locale;
  27 import java.util.Stack;
  28 import java.util.StringTokenizer;
  29 
  30 import com.sun.org.apache.xerces.internal.impl.Constants;
  31 import com.sun.org.apache.xerces.internal.impl.XMLEntityManager;
  32 import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
  33 import com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException;
  34 import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
  35 import com.sun.org.apache.xerces.internal.util.AugmentationsImpl;
  36 import com.sun.org.apache.xerces.internal.util.HTTPInputSource;
  37 import com.sun.org.apache.xerces.internal.util.IntStack;
  38 import com.sun.org.apache.xerces.internal.util.ParserConfigurationSettings;
  39 import com.sun.org.apache.xerces.internal.util.SecurityManager;
  40 import com.sun.org.apache.xerces.internal.util.SymbolTable;
  41 import com.sun.org.apache.xerces.internal.util.URI;
  42 import com.sun.org.apache.xerces.internal.util.XMLAttributesImpl;
  43 import com.sun.org.apache.xerces.internal.util.XMLResourceIdentifierImpl;
  44 import com.sun.org.apache.xerces.internal.util.XMLChar;
  45 import com.sun.org.apache.xerces.internal.util.XMLSymbols;
  46 import com.sun.org.apache.xerces.internal.util.URI.MalformedURIException;
  47 import com.sun.org.apache.xerces.internal.xni.Augmentations;
  48 import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
  49 import com.sun.org.apache.xerces.internal.xni.QName;
  50 import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
  51 import com.sun.org.apache.xerces.internal.xni.XMLDTDHandler;
  52 import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler;
  53 import com.sun.org.apache.xerces.internal.xni.XMLLocator;
  54 import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier;
  55 import com.sun.org.apache.xerces.internal.xni.XMLString;
  56 import com.sun.org.apache.xerces.internal.xni.XNIException;
  57 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
  58 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
  59 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
  60 import com.sun.org.apache.xerces.internal.xni.parser.XMLDTDFilter;
  61 import com.sun.org.apache.xerces.internal.xni.parser.XMLDTDSource;
  62 import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentFilter;
  63 import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentSource;
  64 import com.sun.org.apache.xerces.internal.xni.parser.XMLEntityResolver;
  65 import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource;
  66 import com.sun.org.apache.xerces.internal.xni.parser.XMLParserConfiguration;
  67 import com.sun.org.apache.xerces.internal.xpointer.XPointerHandler;
  68 import com.sun.org.apache.xerces.internal.xpointer.XPointerProcessor;
  69 import com.sun.org.apache.xerces.internal.utils.ObjectFactory;
  70 
  71 /**
  72  * <p>
  73  * This is a pipeline component which performs XInclude handling, according to the
  74  * W3C specification for XML Inclusions.
  75  * </p>
  76  * <p>
  77  * This component analyzes each event in the pipeline, looking for &lt;include&gt;
  78  * elements. An &lt;include&gt; element is one which has a namespace of
  79  * <code>http://www.w3.org/2001/XInclude</code> and a localname of <code>include</code>.
  80  * When it finds an &lt;include&gt; element, it attempts to include the file specified
  81  * in the <code>href</code> attribute of the element.  If inclusion succeeds, all
  82  * children of the &lt;include&gt; element are ignored (with the exception of
  83  * checking for invalid children as outlined in the specification).  If the inclusion
  84  * fails, the &lt;fallback&gt; child of the &lt;include&gt; element is processed.
  85  * </p>
  86  * <p>
  87  * See the <a href="http://www.w3.org/TR/xinclude/">XInclude specification</a> for
  88  * more information on how XInclude is to be used.
  89  * </p>
  90  * <p>
  91  * This component requires the following features and properties from the
  92  * component manager that uses it:
  93  * <ul>
  94  *  <li>http://xml.org/sax/features/allow-dtd-events-after-endDTD</li>
  95  *  <li>http://apache.org/xml/properties/internal/error-reporter</li>
  96  *  <li>http://apache.org/xml/properties/internal/entity-resolver</li>
  97  * </ul>
  98  * Optional property:
  99  * <ul>
 100  *  <li>http://apache.org/xml/properties/input-buffer-size</li>
 101  * </ul>
 102  *
 103  * Furthermore, the <code>NamespaceContext</code> used in the pipeline is required
 104  * to be an instance of <code>XIncludeNamespaceSupport</code>.
 105  * </p>
 106  * <p>
 107  * Currently, this implementation has only partial support for the XInclude specification.
 108  * Specifically, it is missing support for XPointer document fragments.  Thus, only whole
 109  * documents can be included using this component in the pipeline.
 110  * </p>
 111  *
 112  * @author Peter McCracken, IBM
 113  * @author Michael Glavassevich, IBM
 114  *
 115  * @version $Id: XIncludeHandler.java,v 1.7 2010-11-01 04:40:18 joehw Exp $
 116  *
 117  * @see XIncludeNamespaceSupport
 118  */
 119 public class XIncludeHandler
 120     implements XMLComponent, XMLDocumentFilter, XMLDTDFilter {
 121 
 122     public final static String XINCLUDE_DEFAULT_CONFIGURATION =
 123         "com.sun.org.apache.xerces.internal.parsers.XIncludeParserConfiguration";
 124     public final static String HTTP_ACCEPT = "Accept";
 125     public final static String HTTP_ACCEPT_LANGUAGE = "Accept-Language";
 126     public final static String XPOINTER = "xpointer";
 127 
 128     public final static String XINCLUDE_NS_URI =
 129         "http://www.w3.org/2001/XInclude".intern();
 130     public final static String XINCLUDE_INCLUDE = "include".intern();
 131     public final static String XINCLUDE_FALLBACK = "fallback".intern();
 132 
 133     public final static String XINCLUDE_PARSE_XML = "xml".intern();
 134     public final static String XINCLUDE_PARSE_TEXT = "text".intern();
 135 
 136     public final static String XINCLUDE_ATTR_HREF = "href".intern();
 137     public final static String XINCLUDE_ATTR_PARSE = "parse".intern();
 138     public final static String XINCLUDE_ATTR_ENCODING = "encoding".intern();
 139     public final static String XINCLUDE_ATTR_ACCEPT = "accept".intern();
 140     public final static String XINCLUDE_ATTR_ACCEPT_LANGUAGE = "accept-language".intern();
 141 
 142     // Top Level Information Items have [included] property in infoset
 143     public final static String XINCLUDE_INCLUDED = "[included]".intern();
 144 
 145     /** The identifier for the Augmentation that contains the current base URI */
 146     public final static String CURRENT_BASE_URI = "currentBaseURI";
 147 
 148     // used for adding [base URI] attributes
 149     public final static String XINCLUDE_BASE = "base".intern();
 150     public final static QName XML_BASE_QNAME =
 151         new QName(
 152             XMLSymbols.PREFIX_XML,
 153             XINCLUDE_BASE,
 154             (XMLSymbols.PREFIX_XML + ":" + XINCLUDE_BASE).intern(),
 155             NamespaceContext.XML_URI);
 156 
 157     // used for adding [language] attributes
 158     public final static String XINCLUDE_LANG = "lang".intern();
 159     public final static QName XML_LANG_QNAME =
 160         new QName(
 161             XMLSymbols.PREFIX_XML,
 162             XINCLUDE_LANG,
 163             (XMLSymbols.PREFIX_XML + ":" + XINCLUDE_LANG).intern(),
 164             NamespaceContext.XML_URI);
 165 
 166     public final static QName NEW_NS_ATTR_QNAME =
 167         new QName(
 168             XMLSymbols.PREFIX_XMLNS,
 169             "",
 170             XMLSymbols.PREFIX_XMLNS + ":",
 171             NamespaceContext.XMLNS_URI);
 172 
 173     // Processing States
 174     private final static int STATE_NORMAL_PROCESSING = 1;
 175     // we go into this state after a successful include (thus we ignore the children
 176     // of the include) or after a fallback
 177     private final static int STATE_IGNORE = 2;
 178     // we go into this state after a failed include.  If we don't encounter a fallback
 179     // before we reach the end include tag, it's a fatal error
 180     private final static int STATE_EXPECT_FALLBACK = 3;
 181 
 182     // recognized features and properties
 183 
 184     /** Feature identifier: validation. */
 185     protected static final String VALIDATION =
 186         Constants.SAX_FEATURE_PREFIX + Constants.VALIDATION_FEATURE;
 187 
 188     /** Feature identifier: schema validation. */
 189     protected static final String SCHEMA_VALIDATION =
 190         Constants.XERCES_FEATURE_PREFIX + Constants.SCHEMA_VALIDATION_FEATURE;
 191 
 192     /** Feature identifier: dynamic validation. */
 193     protected static final String DYNAMIC_VALIDATION =
 194         Constants.XERCES_FEATURE_PREFIX + Constants.DYNAMIC_VALIDATION_FEATURE;
 195 
 196     /** Feature identifier: allow notation and unparsed entity events to be sent out of order. */
 197     protected static final String ALLOW_UE_AND_NOTATION_EVENTS =
 198         Constants.SAX_FEATURE_PREFIX
 199             + Constants.ALLOW_DTD_EVENTS_AFTER_ENDDTD_FEATURE;
 200 
 201     /** Feature identifier: fixup base URIs. */
 202     protected static final String XINCLUDE_FIXUP_BASE_URIS =
 203         Constants.XERCES_FEATURE_PREFIX + Constants.XINCLUDE_FIXUP_BASE_URIS_FEATURE;
 204 
 205     /** Feature identifier: fixup language. */
 206     protected static final String XINCLUDE_FIXUP_LANGUAGE =
 207         Constants.XERCES_FEATURE_PREFIX + Constants.XINCLUDE_FIXUP_LANGUAGE_FEATURE;
 208 
 209     /** Property identifier: symbol table. */
 210     protected static final String SYMBOL_TABLE =
 211         Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY;
 212 
 213     /** Property identifier: error reporter. */
 214     protected static final String ERROR_REPORTER =
 215         Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;
 216 
 217     /** Property identifier: entity resolver. */
 218     protected static final String ENTITY_RESOLVER =
 219         Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY;
 220 
 221     /** property identifier: security manager. */
 222     protected static final String SECURITY_MANAGER =
 223         Constants.XERCES_PROPERTY_PREFIX + Constants.SECURITY_MANAGER_PROPERTY;
 224 
 225     /** property identifier: buffer size. */
 226     public static final String BUFFER_SIZE =
 227         Constants.XERCES_PROPERTY_PREFIX + Constants.BUFFER_SIZE_PROPERTY;
 228 
 229     protected static final String PARSER_SETTINGS =
 230         Constants.XERCES_FEATURE_PREFIX + Constants.PARSER_SETTINGS;
 231 
 232     /** Recognized features. */
 233     private static final String[] RECOGNIZED_FEATURES =
 234         { ALLOW_UE_AND_NOTATION_EVENTS, XINCLUDE_FIXUP_BASE_URIS, XINCLUDE_FIXUP_LANGUAGE };
 235 
 236     /** Feature defaults. */
 237     private static final Boolean[] FEATURE_DEFAULTS = { Boolean.TRUE, Boolean.TRUE, Boolean.TRUE };
 238 
 239     /** Recognized properties. */
 240     private static final String[] RECOGNIZED_PROPERTIES =
 241         { ERROR_REPORTER, ENTITY_RESOLVER, SECURITY_MANAGER, BUFFER_SIZE };
 242 
 243     /** Property defaults. */
 244     private static final Object[] PROPERTY_DEFAULTS = { null, null, null, new Integer(XMLEntityManager.DEFAULT_BUFFER_SIZE) };
 245 
 246     // instance variables
 247 
 248     // for XMLDocumentFilter
 249     protected XMLDocumentHandler fDocumentHandler;
 250     protected XMLDocumentSource fDocumentSource;
 251 
 252     // for XMLDTDFilter
 253     protected XMLDTDHandler fDTDHandler;
 254     protected XMLDTDSource fDTDSource;
 255 
 256     // for XIncludeHandler
 257     protected XIncludeHandler fParentXIncludeHandler;
 258 
 259     // for buffer size in XIncludeTextReader
 260     protected int fBufferSize = XMLEntityManager.DEFAULT_BUFFER_SIZE;
 261 
 262     // It "feels wrong" to store this value here.  However,
 263     // calculating it can be time consuming, so we cache it.
 264     // It's never going to change in the lifetime of this XIncludeHandler
 265     protected String fParentRelativeURI;
 266 
 267     // we cache the child parser configuration, so we don't have to re-create
 268     // the objects when the parser is re-used
 269     protected XMLParserConfiguration fChildConfig;
 270 
 271     // The cached child parser configuration, may contain a
 272     // XInclude or XPointer Handler.  Cache both these
 273     protected XMLParserConfiguration fXIncludeChildConfig;
 274     protected XMLParserConfiguration fXPointerChildConfig;
 275 
 276     // The XPointerProcessor
 277     protected XPointerProcessor fXPtrProcessor = null;
 278 
 279     protected XMLLocator fDocLocation;
 280     protected XIncludeMessageFormatter fXIncludeMessageFormatter = new XIncludeMessageFormatter();
 281     protected XIncludeNamespaceSupport fNamespaceContext;
 282     protected SymbolTable fSymbolTable;
 283     protected XMLErrorReporter fErrorReporter;
 284     protected XMLEntityResolver fEntityResolver;
 285     protected SecurityManager fSecurityManager;
 286 
 287     // these are needed for text include processing
 288     protected XIncludeTextReader fXInclude10TextReader;
 289     protected XIncludeTextReader fXInclude11TextReader;
 290 
 291     // these are needed for XML Base processing
 292     protected XMLResourceIdentifier fCurrentBaseURI;
 293     protected IntStack fBaseURIScope;
 294     protected Stack fBaseURI;
 295     protected Stack fLiteralSystemID;
 296     protected Stack fExpandedSystemID;
 297 
 298     // these are needed for Language Fixup
 299     protected IntStack fLanguageScope;
 300     protected Stack fLanguageStack;
 301     protected String fCurrentLanguage;
 302 
 303     // used for passing features on to child XIncludeHandler objects
 304     protected ParserConfigurationSettings fSettings;
 305 
 306     // The current element depth.  We start at depth 0 (before we've reached any elements).
 307     // The first element is at depth 1.
 308     private int fDepth;
 309 
 310     // The current element depth of the result infoset.
 311     private int fResultDepth;
 312 
 313     // this value must be at least 1
 314     private static final int INITIAL_SIZE = 8;
 315 
 316     // Used to ensure that fallbacks are always children of include elements,
 317     // and that include elements are never children of other include elements.
 318     // An index contains true if the ancestor of the current element which resides
 319     // at that depth was an include element.
 320     private boolean[] fSawInclude = new boolean[INITIAL_SIZE];
 321 
 322     // Ensures that only one fallback element can be at a single depth.
 323     // An index contains true if we have seen any fallback elements at that depth,
 324     // and it is only reset to false when the end tag of the parent is encountered.
 325     private boolean[] fSawFallback = new boolean[INITIAL_SIZE];
 326 
 327     // The state of the processor at each given depth.
 328     private int[] fState = new int[INITIAL_SIZE];
 329 
 330     // buffering the necessary DTD events
 331     private ArrayList fNotations;
 332     private ArrayList fUnparsedEntities;
 333 
 334     // flags which control whether base URI or language fixup is performed.
 335     private boolean fFixupBaseURIs = true;
 336     private boolean fFixupLanguage = true;
 337 
 338     // for SAX compatibility.
 339     // Has the value of the ALLOW_UE_AND_NOTATION_EVENTS feature
 340     private boolean fSendUEAndNotationEvents;
 341 
 342     // track the version of the document being parsed
 343     private boolean fIsXML11;
 344 
 345     // track whether a DTD is being parsed
 346     private boolean fInDTD;
 347 
 348     // track whether the root element of the result infoset has been processed
 349     private boolean fSeenRootElement;
 350 
 351     // track whether the child config needs its features refreshed
 352     private boolean fNeedCopyFeatures = true;
 353 
 354     // Constructors
 355 
 356     public XIncludeHandler() {
 357         fDepth = 0;
 358 
 359         fSawFallback[fDepth] = false;
 360         fSawInclude[fDepth] = false;
 361         fState[fDepth] = STATE_NORMAL_PROCESSING;
 362         fNotations = new ArrayList();
 363         fUnparsedEntities = new ArrayList();
 364 
 365         fBaseURIScope = new IntStack();
 366         fBaseURI = new Stack();
 367         fLiteralSystemID = new Stack();
 368         fExpandedSystemID = new Stack();
 369         fCurrentBaseURI = new XMLResourceIdentifierImpl();
 370 
 371         fLanguageScope = new IntStack();
 372         fLanguageStack = new Stack();
 373         fCurrentLanguage = null;
 374     }
 375 
 376     // XMLComponent methods
 377 
 378     public void reset(XMLComponentManager componentManager)
 379         throws XNIException {
 380         fNamespaceContext = null;
 381         fDepth = 0;
 382         fResultDepth = isRootDocument() ? 0 : fParentXIncludeHandler.getResultDepth();
 383         fNotations.clear();
 384         fUnparsedEntities.clear();
 385         fParentRelativeURI = null;
 386         fIsXML11 = false;
 387         fInDTD = false;
 388         fSeenRootElement = false;
 389 
 390         fBaseURIScope.clear();
 391         fBaseURI.clear();
 392         fLiteralSystemID.clear();
 393         fExpandedSystemID.clear();
 394         fLanguageScope.clear();
 395         fLanguageStack.clear();
 396 
 397         // REVISIT: Find a better method for maintaining
 398         // the state of the XInclude processor. These arrays
 399         // can potentially grow quite large. Cleaning them
 400         // out on reset may be very time consuming. -- mrglavas
 401         //
 402         // clear the previous settings from the arrays
 403         for (int i = 0; i < fState.length; ++i) {
 404             fState[i] = STATE_NORMAL_PROCESSING;
 405         }
 406         for (int i = 0; i < fSawFallback.length; ++i) {
 407             fSawFallback[i] = false;
 408         }
 409         for (int i = 0; i < fSawInclude.length; ++i) {
 410             fSawInclude[i] = false;
 411         }
 412 
 413         try {
 414             if (!componentManager.getFeature(PARSER_SETTINGS)) {
 415                 // if parser settings have not changed return.
 416                 return;
 417             }
 418         }
 419         catch (XMLConfigurationException e) {}
 420 
 421         // parser settings changed. Need to refresh features on child config.
 422         fNeedCopyFeatures = true;
 423 
 424         try {
 425             fSendUEAndNotationEvents =
 426                 componentManager.getFeature(ALLOW_UE_AND_NOTATION_EVENTS);
 427             if (fChildConfig != null) {
 428                 fChildConfig.setFeature(
 429                     ALLOW_UE_AND_NOTATION_EVENTS,
 430                     fSendUEAndNotationEvents);
 431             }
 432         }
 433         catch (XMLConfigurationException e) {
 434         }
 435 
 436         try {
 437             fFixupBaseURIs =
 438                 componentManager.getFeature(XINCLUDE_FIXUP_BASE_URIS);
 439             if (fChildConfig != null) {
 440                 fChildConfig.setFeature(
 441                     XINCLUDE_FIXUP_BASE_URIS,
 442                     fFixupBaseURIs);
 443             }
 444         }
 445         catch (XMLConfigurationException e) {
 446             fFixupBaseURIs = true;
 447         }
 448 
 449         try {
 450             fFixupLanguage =
 451                 componentManager.getFeature(XINCLUDE_FIXUP_LANGUAGE);
 452             if (fChildConfig != null) {
 453                 fChildConfig.setFeature(
 454                     XINCLUDE_FIXUP_LANGUAGE,
 455                     fFixupLanguage);
 456             }
 457         }
 458         catch (XMLConfigurationException e) {
 459             fFixupLanguage = true;
 460         }
 461 
 462         // Get symbol table.
 463         try {
 464             SymbolTable value =
 465                 (SymbolTable)componentManager.getProperty(SYMBOL_TABLE);
 466             if (value != null) {
 467                 fSymbolTable = value;
 468                 if (fChildConfig != null) {
 469                     fChildConfig.setProperty(SYMBOL_TABLE, value);
 470                 }
 471             }
 472         }
 473         catch (XMLConfigurationException e) {
 474             fSymbolTable = null;
 475         }
 476 
 477         // Get error reporter.
 478         try {
 479             XMLErrorReporter value =
 480                 (XMLErrorReporter)componentManager.getProperty(ERROR_REPORTER);
 481             if (value != null) {
 482                 setErrorReporter(value);
 483                 if (fChildConfig != null) {
 484                     fChildConfig.setProperty(ERROR_REPORTER, value);
 485                 }
 486             }
 487         }
 488         catch (XMLConfigurationException e) {
 489             fErrorReporter = null;
 490         }
 491 
 492         // Get entity resolver.
 493         try {
 494             XMLEntityResolver value =
 495                 (XMLEntityResolver)componentManager.getProperty(
 496                     ENTITY_RESOLVER);
 497 
 498             if (value != null) {
 499                 fEntityResolver = value;
 500                 if (fChildConfig != null) {
 501                     fChildConfig.setProperty(ENTITY_RESOLVER, value);
 502                 }
 503             }
 504         }
 505         catch (XMLConfigurationException e) {
 506             fEntityResolver = null;
 507         }
 508 
 509         // Get security manager.
 510         try {
 511             SecurityManager value =
 512                 (SecurityManager)componentManager.getProperty(
 513                     SECURITY_MANAGER);
 514 
 515             if (value != null) {
 516                 fSecurityManager = value;
 517                 if (fChildConfig != null) {
 518                     fChildConfig.setProperty(SECURITY_MANAGER, value);
 519                 }
 520             }
 521         }
 522         catch (XMLConfigurationException e) {
 523             fSecurityManager = null;
 524         }
 525 
 526         // Get buffer size.
 527         try {
 528             Integer value =
 529                 (Integer)componentManager.getProperty(
 530                     BUFFER_SIZE);
 531 
 532             if (value != null && value.intValue() > 0) {
 533                 fBufferSize = value.intValue();
 534                 if (fChildConfig != null) {
 535                     fChildConfig.setProperty(BUFFER_SIZE, value);
 536                 }
 537             }
 538             else {
 539                 fBufferSize = ((Integer)getPropertyDefault(BUFFER_SIZE)).intValue();
 540             }
 541         }
 542         catch (XMLConfigurationException e) {
 543                 fBufferSize = ((Integer)getPropertyDefault(BUFFER_SIZE)).intValue();
 544         }
 545 
 546         // Reset XML 1.0 text reader.
 547         if (fXInclude10TextReader != null) {
 548                 fXInclude10TextReader.setBufferSize(fBufferSize);
 549         }
 550         // Reset XML 1.1 text reader.
 551         if (fXInclude11TextReader != null) {
 552             fXInclude11TextReader.setBufferSize(fBufferSize);
 553         }
 554 
 555         fSettings = new ParserConfigurationSettings();
 556         copyFeatures(componentManager, fSettings);
 557 
 558         // We don't want a schema validator on the new pipeline,
 559         // so if it was enabled, we set the feature to false. If
 560         // the validation feature was also enabled we turn on
 561         // dynamic validation, so that DTD validation is performed
 562         // on the included documents only if they have a DOCTYPE.
 563         // This is consistent with the behaviour on the main pipeline.
 564         try {
 565             if (componentManager.getFeature(SCHEMA_VALIDATION)) {
 566                 fSettings.setFeature(SCHEMA_VALIDATION, false);
 567                 if (componentManager.getFeature(VALIDATION)) {
 568                     fSettings.setFeature(DYNAMIC_VALIDATION, true);
 569                 }
 570             }
 571         }
 572         catch (XMLConfigurationException e) {}
 573 
 574         // Don't reset fChildConfig -- we don't want it to share the same components.
 575         // It will be reset when it is actually used to parse something.
 576     } // reset(XMLComponentManager)
 577 
 578     /**
 579      * Returns a list of feature identifiers that are recognized by
 580      * this component. This method may return null if no features
 581      * are recognized by this component.
 582      */
 583     public String[] getRecognizedFeatures() {
 584         return (String[])(RECOGNIZED_FEATURES.clone());
 585     } // getRecognizedFeatures():String[]
 586 
 587     /**
 588      * Sets the state of a feature. This method is called by the component
 589      * manager any time after reset when a feature changes state.
 590      * <p>
 591      * <strong>Note:</strong> Components should silently ignore features
 592      * that do not affect the operation of the component.
 593      *
 594      * @param featureId The feature identifier.
 595      * @param state     The state of the feature.
 596      *
 597      * @throws SAXNotRecognizedException The component should not throw
 598      *                                   this exception.
 599      * @throws SAXNotSupportedException The component should not throw
 600      *                                  this exception.
 601      */
 602     public void setFeature(String featureId, boolean state)
 603         throws XMLConfigurationException {
 604         if (featureId.equals(ALLOW_UE_AND_NOTATION_EVENTS)) {
 605             fSendUEAndNotationEvents = state;
 606         }
 607         if (fSettings != null) {
 608             fNeedCopyFeatures = true;
 609             fSettings.setFeature(featureId, state);
 610         }
 611     } // setFeature(String,boolean)
 612 
 613     /**
 614      * Returns a list of property identifiers that are recognized by
 615      * this component. This method may return null if no properties
 616      * are recognized by this component.
 617      */
 618     public String[] getRecognizedProperties() {
 619         return (String[])(RECOGNIZED_PROPERTIES.clone());
 620     } // getRecognizedProperties():String[]
 621 
 622     /**
 623      * Sets the value of a property. This method is called by the component
 624      * manager any time after reset when a property changes value.
 625      * <p>
 626      * <strong>Note:</strong> Components should silently ignore properties
 627      * that do not affect the operation of the component.
 628      *
 629      * @param propertyId The property identifier.
 630      * @param value      The value of the property.
 631      *
 632      * @throws SAXNotRecognizedException The component should not throw
 633      *                                   this exception.
 634      * @throws SAXNotSupportedException The component should not throw
 635      *                                  this exception.
 636      */
 637     public void setProperty(String propertyId, Object value)
 638         throws XMLConfigurationException {
 639         if (propertyId.equals(SYMBOL_TABLE)) {
 640             fSymbolTable = (SymbolTable)value;
 641             if (fChildConfig != null) {
 642                 fChildConfig.setProperty(propertyId, value);
 643             }
 644             return;
 645         }
 646         if (propertyId.equals(ERROR_REPORTER)) {
 647             setErrorReporter((XMLErrorReporter)value);
 648             if (fChildConfig != null) {
 649                 fChildConfig.setProperty(propertyId, value);
 650             }
 651             return;
 652         }
 653         if (propertyId.equals(ENTITY_RESOLVER)) {
 654             fEntityResolver = (XMLEntityResolver)value;
 655             if (fChildConfig != null) {
 656                 fChildConfig.setProperty(propertyId, value);
 657             }
 658             return;
 659         }
 660         if (propertyId.equals(SECURITY_MANAGER)) {
 661             fSecurityManager = (SecurityManager)value;
 662             if (fChildConfig != null) {
 663                 fChildConfig.setProperty(propertyId, value);
 664             }
 665             return;
 666         }
 667         if (propertyId.equals(BUFFER_SIZE)) {
 668             Integer bufferSize = (Integer) value;
 669             if (fChildConfig != null) {
 670                 fChildConfig.setProperty(propertyId, value);
 671             }
 672             if (bufferSize != null && bufferSize.intValue() > 0) {
 673                 fBufferSize = bufferSize.intValue();
 674                 // Reset XML 1.0 text reader.
 675                 if (fXInclude10TextReader != null) {
 676                     fXInclude10TextReader.setBufferSize(fBufferSize);
 677                 }
 678                 // Reset XML 1.1 text reader.
 679                 if (fXInclude11TextReader != null) {
 680                     fXInclude11TextReader.setBufferSize(fBufferSize);
 681                 }
 682             }
 683             return;
 684         }
 685 
 686     } // setProperty(String,Object)
 687 
 688     /**
 689      * Returns the default state for a feature, or null if this
 690      * component does not want to report a default value for this
 691      * feature.
 692      *
 693      * @param featureId The feature identifier.
 694      *
 695      * @since Xerces 2.2.0
 696      */
 697     public Boolean getFeatureDefault(String featureId) {
 698         for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
 699             if (RECOGNIZED_FEATURES[i].equals(featureId)) {
 700                 return FEATURE_DEFAULTS[i];
 701             }
 702         }
 703         return null;
 704     } // getFeatureDefault(String):Boolean
 705 
 706     /**
 707      * Returns the default state for a property, or null if this
 708      * component does not want to report a default value for this
 709      * property.
 710      *
 711      * @param propertyId The property identifier.
 712      *
 713      * @since Xerces 2.2.0
 714      */
 715     public Object getPropertyDefault(String propertyId) {
 716         for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
 717             if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
 718                 return PROPERTY_DEFAULTS[i];
 719             }
 720         }
 721         return null;
 722     } // getPropertyDefault(String):Object
 723 
 724     public void setDocumentHandler(XMLDocumentHandler handler) {
 725         fDocumentHandler = handler;
 726     }
 727 
 728     public XMLDocumentHandler getDocumentHandler() {
 729         return fDocumentHandler;
 730     }
 731 
 732     // XMLDocumentHandler methods
 733 
 734     /**
 735      * Event sent at the start of the document.
 736      *
 737      * A fatal error will occur here, if it is detected that this document has been processed
 738      * before.
 739      *
 740      * This event is only passed on to the document handler if this is the root document.
 741      */
 742     public void startDocument(
 743         XMLLocator locator,
 744         String encoding,
 745         NamespaceContext namespaceContext,
 746         Augmentations augs)
 747         throws XNIException {
 748 
 749         // we do this to ensure that the proper location is reported in errors
 750         // otherwise, the locator from the root document would always be used
 751         fErrorReporter.setDocumentLocator(locator);
 752 
 753         if (!isRootDocument()
 754             && fParentXIncludeHandler.searchForRecursiveIncludes(locator)) {
 755             reportFatalError(
 756                 "RecursiveInclude",
 757                 new Object[] { locator.getExpandedSystemId()});
 758         }
 759 
 760         if (!(namespaceContext instanceof XIncludeNamespaceSupport)) {
 761             reportFatalError("IncompatibleNamespaceContext");
 762         }
 763         fNamespaceContext = (XIncludeNamespaceSupport)namespaceContext;
 764         fDocLocation = locator;
 765 
 766         // initialize the current base URI
 767         fCurrentBaseURI.setBaseSystemId(locator.getBaseSystemId());
 768         fCurrentBaseURI.setExpandedSystemId(locator.getExpandedSystemId());
 769         fCurrentBaseURI.setLiteralSystemId(locator.getLiteralSystemId());
 770         saveBaseURI();
 771         if (augs == null) {
 772             augs = new AugmentationsImpl();
 773         }
 774         augs.putItem(CURRENT_BASE_URI, fCurrentBaseURI);
 775 
 776         // initialize the current language
 777         fCurrentLanguage = XMLSymbols.EMPTY_STRING;
 778         saveLanguage(fCurrentLanguage);
 779 
 780         if (isRootDocument() && fDocumentHandler != null) {
 781             fDocumentHandler.startDocument(
 782                 locator,
 783                 encoding,
 784                 namespaceContext,
 785                 augs);
 786         }
 787     }
 788 
 789     public void xmlDecl(
 790         String version,
 791         String encoding,
 792         String standalone,
 793         Augmentations augs)
 794         throws XNIException {
 795         fIsXML11 = "1.1".equals(version);
 796         if (isRootDocument() && fDocumentHandler != null) {
 797             fDocumentHandler.xmlDecl(version, encoding, standalone, augs);
 798         }
 799     }
 800 
 801     public void doctypeDecl(
 802         String rootElement,
 803         String publicId,
 804         String systemId,
 805         Augmentations augs)
 806         throws XNIException {
 807         if (isRootDocument() && fDocumentHandler != null) {
 808             fDocumentHandler.doctypeDecl(rootElement, publicId, systemId, augs);
 809         }
 810     }
 811 
 812     public void comment(XMLString text, Augmentations augs)
 813         throws XNIException {
 814         if (!fInDTD) {
 815             if (fDocumentHandler != null
 816                 && getState() == STATE_NORMAL_PROCESSING) {
 817                 fDepth++;
 818                 augs = modifyAugmentations(augs);
 819                 fDocumentHandler.comment(text, augs);
 820                 fDepth--;
 821             }
 822         }
 823         else if (fDTDHandler != null) {
 824             fDTDHandler.comment(text, augs);
 825         }
 826     }
 827 
 828     public void processingInstruction(
 829         String target,
 830         XMLString data,
 831         Augmentations augs)
 832         throws XNIException {
 833         if (!fInDTD) {
 834             if (fDocumentHandler != null
 835                 && getState() == STATE_NORMAL_PROCESSING) {
 836                 // we need to change the depth like this so that modifyAugmentations() works
 837                 fDepth++;
 838                 augs = modifyAugmentations(augs);
 839                 fDocumentHandler.processingInstruction(target, data, augs);
 840                 fDepth--;
 841             }
 842         }
 843         else if (fDTDHandler != null) {
 844             fDTDHandler.processingInstruction(target, data, augs);
 845         }
 846     }
 847 
 848     public void startElement(
 849         QName element,
 850         XMLAttributes attributes,
 851         Augmentations augs)
 852         throws XNIException {
 853         fDepth++;
 854         int lastState = getState(fDepth - 1);
 855         // If the last two states were fallback then this must be a descendant of an include
 856         // child which isn't a fallback. The specification says we should ignore such elements
 857         // and their children.
 858         if (lastState == STATE_EXPECT_FALLBACK && getState(fDepth - 2) == STATE_EXPECT_FALLBACK) {
 859             setState(STATE_IGNORE);
 860         }
 861         else {
 862             setState(lastState);
 863         }
 864 
 865         // we process the xml:base and xml:lang attributes regardless
 866         // of what type of element it is.
 867         processXMLBaseAttributes(attributes);
 868         if (fFixupLanguage) {
 869             processXMLLangAttributes(attributes);
 870         }
 871 
 872         if (isIncludeElement(element)) {
 873             boolean success = this.handleIncludeElement(attributes);
 874             if (success) {
 875                 setState(STATE_IGNORE);
 876             }
 877             else {
 878                 setState(STATE_EXPECT_FALLBACK);
 879             }
 880         }
 881         else if (isFallbackElement(element)) {
 882             this.handleFallbackElement();
 883         }
 884         else if (hasXIncludeNamespace(element)) {
 885             if (getSawInclude(fDepth - 1)) {
 886                 reportFatalError(
 887                     "IncludeChild",
 888                     new Object[] { element.rawname });
 889             }
 890             if (getSawFallback(fDepth - 1)) {
 891                 reportFatalError(
 892                     "FallbackChild",
 893                     new Object[] { element.rawname });
 894             }
 895             if (getState() == STATE_NORMAL_PROCESSING) {
 896                 if (fResultDepth++ == 0) {
 897                     checkMultipleRootElements();
 898                 }
 899                 if (fDocumentHandler != null) {
 900                     augs = modifyAugmentations(augs);
 901                     attributes = processAttributes(attributes);
 902                     fDocumentHandler.startElement(element, attributes, augs);
 903                 }
 904             }
 905         }
 906         else if (getState() == STATE_NORMAL_PROCESSING) {
 907             if (fResultDepth++ == 0) {
 908                 checkMultipleRootElements();
 909             }
 910             if (fDocumentHandler != null) {
 911                 augs = modifyAugmentations(augs);
 912                 attributes = processAttributes(attributes);
 913                 fDocumentHandler.startElement(element, attributes, augs);
 914             }
 915         }
 916     }
 917 
 918     public void emptyElement(
 919         QName element,
 920         XMLAttributes attributes,
 921         Augmentations augs)
 922         throws XNIException {
 923         fDepth++;
 924         int lastState = getState(fDepth - 1);
 925         // If the last two states were fallback then this must be a descendant of an include
 926         // child which isn't a fallback. The specification says we should ignore such elements
 927         // and their children.
 928         if (lastState == STATE_EXPECT_FALLBACK && getState(fDepth - 2) == STATE_EXPECT_FALLBACK) {
 929             setState(STATE_IGNORE);
 930         }
 931         else {
 932             setState(lastState);
 933         }
 934 
 935         // we process the xml:base and xml:lang attributes regardless
 936         // of what type of element it is.
 937         processXMLBaseAttributes(attributes);
 938         if (fFixupLanguage) {
 939             processXMLLangAttributes(attributes);
 940         }
 941 
 942         if (isIncludeElement(element)) {
 943             boolean success = this.handleIncludeElement(attributes);
 944             if (success) {
 945                 setState(STATE_IGNORE);
 946             }
 947             else {
 948                 reportFatalError("NoFallback",
 949                     new Object[] { attributes.getValue(null, "href") });
 950             }
 951         }
 952         else if (isFallbackElement(element)) {
 953             this.handleFallbackElement();
 954         }
 955         else if (hasXIncludeNamespace(element)) {
 956             if (getSawInclude(fDepth - 1)) {
 957                 reportFatalError(
 958                     "IncludeChild",
 959                     new Object[] { element.rawname });
 960             }
 961             if (getSawFallback(fDepth - 1)) {
 962                 reportFatalError(
 963                     "FallbackChild",
 964                     new Object[] { element.rawname });
 965             }
 966             if (getState() == STATE_NORMAL_PROCESSING) {
 967                 if (fResultDepth == 0) {
 968                     checkMultipleRootElements();
 969                 }
 970                 if (fDocumentHandler != null) {
 971                     augs = modifyAugmentations(augs);
 972                     attributes = processAttributes(attributes);
 973                     fDocumentHandler.emptyElement(element, attributes, augs);
 974                 }
 975             }
 976         }
 977         else if (getState() == STATE_NORMAL_PROCESSING) {
 978             if (fResultDepth == 0) {
 979                 checkMultipleRootElements();
 980             }
 981             if (fDocumentHandler != null) {
 982                 augs = modifyAugmentations(augs);
 983                 attributes = processAttributes(attributes);
 984                 fDocumentHandler.emptyElement(element, attributes, augs);
 985             }
 986         }
 987         // reset the out of scope stack elements
 988         setSawFallback(fDepth + 1, false);
 989         setSawInclude(fDepth, false);
 990 
 991         // check if an xml:base has gone out of scope
 992         if (fBaseURIScope.size() > 0 && fDepth == fBaseURIScope.peek()) {
 993             // pop the values from the stack
 994             restoreBaseURI();
 995         }
 996         fDepth--;
 997     }
 998 
 999     public void endElement(QName element, Augmentations augs)
1000         throws XNIException {
1001 
1002         if (isIncludeElement(element)) {
1003             // if we're ending an include element, and we were expecting a fallback
1004             // we check to see if the children of this include element contained a fallback
1005             if (getState() == STATE_EXPECT_FALLBACK
1006                 && !getSawFallback(fDepth + 1)) {
1007                 reportFatalError("NoFallback",
1008                     new Object[] { "unknown" });
1009             }
1010         }
1011         if (isFallbackElement(element)) {
1012             // the state would have been set to normal processing if we were expecting the fallback element
1013             // now that we're done processing it, we should ignore all the other children of the include element
1014             if (getState() == STATE_NORMAL_PROCESSING) {
1015                 setState(STATE_IGNORE);
1016             }
1017         }
1018         else if (getState() == STATE_NORMAL_PROCESSING) {
1019             --fResultDepth;
1020             if (fDocumentHandler != null) {
1021                 fDocumentHandler.endElement(element, augs);
1022             }
1023         }
1024 
1025         // reset the out of scope stack elements
1026         setSawFallback(fDepth + 1, false);
1027         setSawInclude(fDepth, false);
1028 
1029         // check if an xml:base has gone out of scope
1030         if (fBaseURIScope.size() > 0 && fDepth == fBaseURIScope.peek()) {
1031             // pop the values from the stack
1032             restoreBaseURI();
1033         }
1034 
1035         // check if an xml:lang has gone out of scope
1036         if (fLanguageScope.size() > 0 && fDepth == fLanguageScope.peek()) {
1037             // pop the language from the stack
1038             fCurrentLanguage = restoreLanguage();
1039         }
1040 
1041         fDepth--;
1042     }
1043 
1044     public void startGeneralEntity(
1045         String name,
1046         XMLResourceIdentifier resId,
1047         String encoding,
1048         Augmentations augs)
1049         throws XNIException {
1050         if (getState() == STATE_NORMAL_PROCESSING) {
1051             if (fResultDepth == 0) {
1052                 if (augs != null && Boolean.TRUE.equals(augs.getItem(Constants.ENTITY_SKIPPED))) {
1053                     reportFatalError("UnexpandedEntityReferenceIllegal");
1054                 }
1055             }
1056             else if (fDocumentHandler != null) {
1057                 fDocumentHandler.startGeneralEntity(name, resId, encoding, augs);
1058             }
1059         }
1060     }
1061 
1062     public void textDecl(String version, String encoding, Augmentations augs)
1063         throws XNIException {
1064         if (fDocumentHandler != null
1065             && getState() == STATE_NORMAL_PROCESSING) {
1066             fDocumentHandler.textDecl(version, encoding, augs);
1067         }
1068     }
1069 
1070     public void endGeneralEntity(String name, Augmentations augs)
1071         throws XNIException {
1072         if (fDocumentHandler != null
1073             && getState() == STATE_NORMAL_PROCESSING
1074             && fResultDepth != 0) {
1075             fDocumentHandler.endGeneralEntity(name, augs);
1076         }
1077     }
1078 
1079     public void characters(XMLString text, Augmentations augs)
1080         throws XNIException {
1081         if (getState() == STATE_NORMAL_PROCESSING) {
1082             if (fResultDepth == 0) {
1083                 checkWhitespace(text);
1084             }
1085             else if (fDocumentHandler != null) {
1086                 // we need to change the depth like this so that modifyAugmentations() works
1087                 fDepth++;
1088                 augs = modifyAugmentations(augs);
1089                 fDocumentHandler.characters(text, augs);
1090                 fDepth--;
1091             }
1092         }
1093     }
1094 
1095     public void ignorableWhitespace(XMLString text, Augmentations augs)
1096         throws XNIException {
1097         if (fDocumentHandler != null
1098             && getState() == STATE_NORMAL_PROCESSING
1099             && fResultDepth != 0) {
1100             fDocumentHandler.ignorableWhitespace(text, augs);
1101         }
1102     }
1103 
1104     public void startCDATA(Augmentations augs) throws XNIException {
1105         if (fDocumentHandler != null
1106             && getState() == STATE_NORMAL_PROCESSING
1107             && fResultDepth != 0) {
1108             fDocumentHandler.startCDATA(augs);
1109         }
1110     }
1111 
1112     public void endCDATA(Augmentations augs) throws XNIException {
1113         if (fDocumentHandler != null
1114             && getState() == STATE_NORMAL_PROCESSING
1115             && fResultDepth != 0) {
1116             fDocumentHandler.endCDATA(augs);
1117         }
1118     }
1119 
1120     public void endDocument(Augmentations augs) throws XNIException {
1121         if (isRootDocument()) {
1122             if (!fSeenRootElement) {
1123                 reportFatalError("RootElementRequired");
1124             }
1125             if (fDocumentHandler != null) {
1126                 fDocumentHandler.endDocument(augs);
1127             }
1128         }
1129     }
1130 
1131     public void setDocumentSource(XMLDocumentSource source) {
1132         fDocumentSource = source;
1133     }
1134 
1135     public XMLDocumentSource getDocumentSource() {
1136         return fDocumentSource;
1137     }
1138 
1139     // DTDHandler methods
1140     // We are only interested in the notation and unparsed entity declarations,
1141     // the rest we just pass on
1142 
1143     /* (non-Javadoc)
1144      * @see com.sun.org.apache.xerces.internal.xni.XMLDTDHandler#attributeDecl(java.lang.String, java.lang.String, java.lang.String, java.lang.String[], java.lang.String, com.sun.org.apache.xerces.internal.xni.XMLString, com.sun.org.apache.xerces.internal.xni.XMLString, com.sun.org.apache.xerces.internal.xni.Augmentations)
1145      */
1146     public void attributeDecl(
1147         String elementName,
1148         String attributeName,
1149         String type,
1150         String[] enumeration,
1151         String defaultType,
1152         XMLString defaultValue,
1153         XMLString nonNormalizedDefaultValue,
1154         Augmentations augmentations)
1155         throws XNIException {
1156         if (fDTDHandler != null) {
1157             fDTDHandler.attributeDecl(
1158                 elementName,
1159                 attributeName,
1160                 type,
1161                 enumeration,
1162                 defaultType,
1163                 defaultValue,
1164                 nonNormalizedDefaultValue,
1165                 augmentations);
1166         }
1167     }
1168 
1169     /* (non-Javadoc)
1170      * @see com.sun.org.apache.xerces.internal.xni.XMLDTDHandler#elementDecl(java.lang.String, java.lang.String, com.sun.org.apache.xerces.internal.xni.Augmentations)
1171      */
1172     public void elementDecl(
1173         String name,
1174         String contentModel,
1175         Augmentations augmentations)
1176         throws XNIException {
1177         if (fDTDHandler != null) {
1178             fDTDHandler.elementDecl(name, contentModel, augmentations);
1179         }
1180     }
1181 
1182     /* (non-Javadoc)
1183      * @see com.sun.org.apache.xerces.internal.xni.XMLDTDHandler#endAttlist(com.sun.org.apache.xerces.internal.xni.Augmentations)
1184      */
1185     public void endAttlist(Augmentations augmentations) throws XNIException {
1186         if (fDTDHandler != null) {
1187             fDTDHandler.endAttlist(augmentations);
1188         }
1189     }
1190 
1191     /* (non-Javadoc)
1192      * @see com.sun.org.apache.xerces.internal.xni.XMLDTDHandler#endConditional(com.sun.org.apache.xerces.internal.xni.Augmentations)
1193      */
1194     public void endConditional(Augmentations augmentations)
1195         throws XNIException {
1196         if (fDTDHandler != null) {
1197             fDTDHandler.endConditional(augmentations);
1198         }
1199     }
1200 
1201     /* (non-Javadoc)
1202      * @see com.sun.org.apache.xerces.internal.xni.XMLDTDHandler#endDTD(com.sun.org.apache.xerces.internal.xni.Augmentations)
1203      */
1204     public void endDTD(Augmentations augmentations) throws XNIException {
1205         if (fDTDHandler != null) {
1206             fDTDHandler.endDTD(augmentations);
1207         }
1208         fInDTD = false;
1209     }
1210 
1211     /* (non-Javadoc)
1212      * @see com.sun.org.apache.xerces.internal.xni.XMLDTDHandler#endExternalSubset(com.sun.org.apache.xerces.internal.xni.Augmentations)
1213      */
1214     public void endExternalSubset(Augmentations augmentations)
1215         throws XNIException {
1216         if (fDTDHandler != null) {
1217             fDTDHandler.endExternalSubset(augmentations);
1218         }
1219     }
1220 
1221     /* (non-Javadoc)
1222      * @see com.sun.org.apache.xerces.internal.xni.XMLDTDHandler#endParameterEntity(java.lang.String, com.sun.org.apache.xerces.internal.xni.Augmentations)
1223      */
1224     public void endParameterEntity(String name, Augmentations augmentations)
1225         throws XNIException {
1226         if (fDTDHandler != null) {
1227             fDTDHandler.endParameterEntity(name, augmentations);
1228         }
1229     }
1230 
1231     /* (non-Javadoc)
1232      * @see com.sun.org.apache.xerces.internal.xni.XMLDTDHandler#externalEntityDecl(java.lang.String, com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier, com.sun.org.apache.xerces.internal.xni.Augmentations)
1233      */
1234     public void externalEntityDecl(
1235         String name,
1236         XMLResourceIdentifier identifier,
1237         Augmentations augmentations)
1238         throws XNIException {
1239         if (fDTDHandler != null) {
1240             fDTDHandler.externalEntityDecl(name, identifier, augmentations);
1241         }
1242     }
1243 
1244     /* (non-Javadoc)
1245      * @see com.sun.org.apache.xerces.internal.xni.XMLDTDHandler#getDTDSource()
1246      */
1247     public XMLDTDSource getDTDSource() {
1248         return fDTDSource;
1249     }
1250 
1251     /* (non-Javadoc)
1252      * @see com.sun.org.apache.xerces.internal.xni.XMLDTDHandler#ignoredCharacters(com.sun.org.apache.xerces.internal.xni.XMLString, com.sun.org.apache.xerces.internal.xni.Augmentations)
1253      */
1254     public void ignoredCharacters(XMLString text, Augmentations augmentations)
1255         throws XNIException {
1256         if (fDTDHandler != null) {
1257             fDTDHandler.ignoredCharacters(text, augmentations);
1258         }
1259     }
1260 
1261     /* (non-Javadoc)
1262      * @see com.sun.org.apache.xerces.internal.xni.XMLDTDHandler#internalEntityDecl(java.lang.String, com.sun.org.apache.xerces.internal.xni.XMLString, com.sun.org.apache.xerces.internal.xni.XMLString, com.sun.org.apache.xerces.internal.xni.Augmentations)
1263      */
1264     public void internalEntityDecl(
1265         String name,
1266         XMLString text,
1267         XMLString nonNormalizedText,
1268         Augmentations augmentations)
1269         throws XNIException {
1270         if (fDTDHandler != null) {
1271             fDTDHandler.internalEntityDecl(
1272                 name,
1273                 text,
1274                 nonNormalizedText,
1275                 augmentations);
1276         }
1277     }
1278 
1279     /* (non-Javadoc)
1280      * @see com.sun.org.apache.xerces.internal.xni.XMLDTDHandler#notationDecl(java.lang.String, com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier, com.sun.org.apache.xerces.internal.xni.Augmentations)
1281      */
1282     public void notationDecl(
1283         String name,
1284         XMLResourceIdentifier identifier,
1285         Augmentations augmentations)
1286         throws XNIException {
1287         this.addNotation(name, identifier, augmentations);
1288         if (fDTDHandler != null) {
1289             fDTDHandler.notationDecl(name, identifier, augmentations);
1290         }
1291     }
1292 
1293     /* (non-Javadoc)
1294      * @see com.sun.org.apache.xerces.internal.xni.XMLDTDHandler#setDTDSource(com.sun.org.apache.xerces.internal.xni.parser.XMLDTDSource)
1295      */
1296     public void setDTDSource(XMLDTDSource source) {
1297         fDTDSource = source;
1298     }
1299 
1300     /* (non-Javadoc)
1301      * @see com.sun.org.apache.xerces.internal.xni.XMLDTDHandler#startAttlist(java.lang.String, com.sun.org.apache.xerces.internal.xni.Augmentations)
1302      */
1303     public void startAttlist(String elementName, Augmentations augmentations)
1304         throws XNIException {
1305         if (fDTDHandler != null) {
1306             fDTDHandler.startAttlist(elementName, augmentations);
1307         }
1308     }
1309 
1310     /* (non-Javadoc)
1311      * @see com.sun.org.apache.xerces.internal.xni.XMLDTDHandler#startConditional(short, com.sun.org.apache.xerces.internal.xni.Augmentations)
1312      */
1313     public void startConditional(short type, Augmentations augmentations)
1314         throws XNIException {
1315         if (fDTDHandler != null) {
1316             fDTDHandler.startConditional(type, augmentations);
1317         }
1318     }
1319 
1320     /* (non-Javadoc)
1321      * @see com.sun.org.apache.xerces.internal.xni.XMLDTDHandler#startDTD(com.sun.org.apache.xerces.internal.xni.XMLLocator, com.sun.org.apache.xerces.internal.xni.Augmentations)
1322      */
1323     public void startDTD(XMLLocator locator, Augmentations augmentations)
1324         throws XNIException {
1325         fInDTD = true;
1326         if (fDTDHandler != null) {
1327             fDTDHandler.startDTD(locator, augmentations);
1328         }
1329     }
1330 
1331     /* (non-Javadoc)
1332      * @see com.sun.org.apache.xerces.internal.xni.XMLDTDHandler#startExternalSubset(com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier, com.sun.org.apache.xerces.internal.xni.Augmentations)
1333      */
1334     public void startExternalSubset(
1335         XMLResourceIdentifier identifier,
1336         Augmentations augmentations)
1337         throws XNIException {
1338         if (fDTDHandler != null) {
1339             fDTDHandler.startExternalSubset(identifier, augmentations);
1340         }
1341     }
1342 
1343     /* (non-Javadoc)
1344      * @see com.sun.org.apache.xerces.internal.xni.XMLDTDHandler#startParameterEntity(java.lang.String, com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier, java.lang.String, com.sun.org.apache.xerces.internal.xni.Augmentations)
1345      */
1346     public void startParameterEntity(
1347         String name,
1348         XMLResourceIdentifier identifier,
1349         String encoding,
1350         Augmentations augmentations)
1351         throws XNIException {
1352         if (fDTDHandler != null) {
1353             fDTDHandler.startParameterEntity(
1354                 name,
1355                 identifier,
1356                 encoding,
1357                 augmentations);
1358         }
1359     }
1360 
1361     /* (non-Javadoc)
1362      * @see com.sun.org.apache.xerces.internal.xni.XMLDTDHandler#unparsedEntityDecl(java.lang.String, com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier, java.lang.String, com.sun.org.apache.xerces.internal.xni.Augmentations)
1363      */
1364     public void unparsedEntityDecl(
1365         String name,
1366         XMLResourceIdentifier identifier,
1367         String notation,
1368         Augmentations augmentations)
1369         throws XNIException {
1370         this.addUnparsedEntity(name, identifier, notation, augmentations);
1371         if (fDTDHandler != null) {
1372             fDTDHandler.unparsedEntityDecl(
1373                 name,
1374                 identifier,
1375                 notation,
1376                 augmentations);
1377         }
1378     }
1379 
1380     /* (non-Javadoc)
1381      * @see com.sun.org.apache.xerces.internal.xni.parser.XMLDTDSource#getDTDHandler()
1382      */
1383     public XMLDTDHandler getDTDHandler() {
1384         return fDTDHandler;
1385     }
1386 
1387     /* (non-Javadoc)
1388      * @see com.sun.org.apache.xerces.internal.xni.parser.XMLDTDSource#setDTDHandler(com.sun.org.apache.xerces.internal.xni.XMLDTDHandler)
1389      */
1390     public void setDTDHandler(XMLDTDHandler handler) {
1391         fDTDHandler = handler;
1392     }
1393 
1394     // XIncludeHandler methods
1395 
1396     private void setErrorReporter(XMLErrorReporter reporter) {
1397         fErrorReporter = reporter;
1398         if (fErrorReporter != null) {
1399             fErrorReporter.putMessageFormatter(
1400                 XIncludeMessageFormatter.XINCLUDE_DOMAIN, fXIncludeMessageFormatter);
1401             // this ensures the proper location is displayed in error messages
1402             if (fDocLocation != null) {
1403                 fErrorReporter.setDocumentLocator(fDocLocation);
1404             }
1405         }
1406     }
1407 
1408     protected void handleFallbackElement() {
1409         if (!getSawInclude(fDepth - 1)) {
1410             if (getState() == STATE_IGNORE) {
1411                 return;
1412             }
1413             reportFatalError("FallbackParent");
1414         }
1415 
1416         setSawInclude(fDepth, false);
1417         fNamespaceContext.setContextInvalid();
1418 
1419         if (getSawFallback(fDepth)) {
1420             reportFatalError("MultipleFallbacks");
1421         }
1422         else {
1423             setSawFallback(fDepth, true);
1424         }
1425 
1426         // Either the state is STATE_EXPECT_FALLBACK or it's STATE_IGNORE.
1427         // If we're ignoring, we want to stay ignoring. But if we're expecting this fallback element,
1428         // we want to signal that we should process the children.
1429         if (getState() == STATE_EXPECT_FALLBACK) {
1430             setState(STATE_NORMAL_PROCESSING);
1431         }
1432     }
1433 
1434     protected boolean handleIncludeElement(XMLAttributes attributes)
1435         throws XNIException {
1436         if (getSawInclude(fDepth - 1)) {
1437             reportFatalError("IncludeChild", new Object[] { XINCLUDE_INCLUDE });
1438         }
1439         if (getState() == STATE_IGNORE) {
1440             return true;
1441         }
1442         setSawInclude(fDepth, true);
1443         fNamespaceContext.setContextInvalid();
1444 
1445         // TODO: does Java use IURIs by default?
1446         //       [Definition: An internationalized URI reference, or IURI, is a URI reference that directly uses [Unicode] characters.]
1447         // TODO: figure out what section 4.1.1 of the XInclude spec is talking about
1448         //       has to do with disallowed ASCII character escaping
1449         //       this ties in with the above IURI section, but I suspect Java already does it
1450 
1451         String href = attributes.getValue(XINCLUDE_ATTR_HREF);
1452         String parse = attributes.getValue(XINCLUDE_ATTR_PARSE);
1453         String xpointer =  attributes.getValue(XPOINTER);
1454         String accept = attributes.getValue(XINCLUDE_ATTR_ACCEPT);
1455         String acceptLanguage = attributes.getValue(XINCLUDE_ATTR_ACCEPT_LANGUAGE);
1456 
1457         if (parse == null) {
1458             parse = XINCLUDE_PARSE_XML;
1459         }
1460         if (href == null) {
1461             href = XMLSymbols.EMPTY_STRING;
1462         }
1463         if (href.length() == 0 && XINCLUDE_PARSE_XML.equals(parse)) {
1464             if (xpointer == null) {
1465                 reportFatalError("XpointerMissing");
1466             }
1467             else {
1468                 // When parse="xml" and an xpointer is specified treat
1469                 // all absences of the href attribute as a resource error.
1470                 Locale locale = (fErrorReporter != null) ? fErrorReporter.getLocale() : null;
1471                 String reason = fXIncludeMessageFormatter.formatMessage(locale, "XPointerStreamability", null);
1472                 reportResourceError("XMLResourceError", new Object[] { href, reason });
1473                 return false;
1474             }
1475         }
1476 
1477         URI hrefURI = null;
1478 
1479         // Check whether href is correct and perform escaping as per section 4.1.1 of the XInclude spec.
1480         // Report fatal error if the href value contains a fragment identifier or if the value after
1481         // escaping is a syntactically invalid URI or IRI.
1482         try {
1483             hrefURI = new URI(href, true);
1484             if (hrefURI.getFragment() != null) {
1485                 reportFatalError("HrefFragmentIdentifierIllegal", new Object[] {href});
1486             }
1487         }
1488         catch (URI.MalformedURIException exc) {
1489             String newHref = escapeHref(href);
1490             if (href != newHref) {
1491                 href = newHref;
1492                 try {
1493                     hrefURI = new URI(href, true);
1494                     if (hrefURI.getFragment() != null) {
1495                         reportFatalError("HrefFragmentIdentifierIllegal", new Object[] {href});
1496                     }
1497                 }
1498                 catch (URI.MalformedURIException exc2) {
1499                     reportFatalError("HrefSyntacticallyInvalid", new Object[] {href});
1500                 }
1501             }
1502             else {
1503                 reportFatalError("HrefSyntacticallyInvalid", new Object[] {href});
1504             }
1505         }
1506 
1507         // Verify that if an accept and/or an accept-language attribute exist
1508         // that the value(s) don't contain disallowed characters.
1509         if (accept != null && !isValidInHTTPHeader(accept)) {
1510             reportFatalError("AcceptMalformed", null);
1511             accept = null;
1512         }
1513         if (acceptLanguage != null && !isValidInHTTPHeader(acceptLanguage)) {
1514             reportFatalError("AcceptLanguageMalformed", null);
1515             acceptLanguage = null;
1516         }
1517 
1518         XMLInputSource includedSource = null;
1519         if (fEntityResolver != null) {
1520             try {
1521                 XMLResourceIdentifier resourceIdentifier =
1522                     new XMLResourceIdentifierImpl(
1523                         null,
1524                         href,
1525                         fCurrentBaseURI.getExpandedSystemId(),
1526                         XMLEntityManager.expandSystemId(
1527                             href,
1528                             fCurrentBaseURI.getExpandedSystemId(),
1529                             false));
1530 
1531                 includedSource =
1532                     fEntityResolver.resolveEntity(resourceIdentifier);
1533 
1534                 if (includedSource != null &&
1535                     !(includedSource instanceof HTTPInputSource) &&
1536                     (accept != null || acceptLanguage != null) &&
1537                     includedSource.getCharacterStream() == null &&
1538                     includedSource.getByteStream() == null) {
1539 
1540                     includedSource = createInputSource(includedSource.getPublicId(), includedSource.getSystemId(),
1541                         includedSource.getBaseSystemId(), accept, acceptLanguage);
1542                 }
1543             }
1544             catch (IOException e) {
1545                 reportResourceError(
1546                     "XMLResourceError",
1547                     new Object[] { href, e.getMessage()});
1548                 return false;
1549             }
1550         }
1551 
1552         if (includedSource == null) {
1553             // setup an HTTPInputSource if either of the content negotation attributes were specified.
1554             if (accept != null || acceptLanguage != null) {
1555                 includedSource = createInputSource(null, href, fCurrentBaseURI.getExpandedSystemId(), accept, acceptLanguage);
1556             }
1557             else {
1558                 includedSource = new XMLInputSource(null, href, fCurrentBaseURI.getExpandedSystemId());
1559             }
1560         }
1561 
1562         if (parse.equals(XINCLUDE_PARSE_XML)) {
1563             // Instead of always creating a new configuration, the first one can be reused
1564             if ((xpointer != null && fXPointerChildConfig == null)
1565                         || (xpointer == null && fXIncludeChildConfig == null) ) {
1566 
1567                 String parserName = XINCLUDE_DEFAULT_CONFIGURATION;
1568                 if (xpointer != null)
1569                         parserName = "com.sun.org.apache.xerces.internal.parsers.XPointerParserConfiguration";
1570 
1571                 fChildConfig =
1572                     (XMLParserConfiguration)ObjectFactory.newInstance(
1573                         parserName,
1574                         true);
1575 
1576                 // use the same symbol table, error reporter, entity resolver, security manager and buffer size.
1577                 if (fSymbolTable != null) fChildConfig.setProperty(SYMBOL_TABLE, fSymbolTable);
1578                 if (fErrorReporter != null) fChildConfig.setProperty(ERROR_REPORTER, fErrorReporter);
1579                 if (fEntityResolver != null) fChildConfig.setProperty(ENTITY_RESOLVER, fEntityResolver);
1580                 fChildConfig.setProperty(SECURITY_MANAGER, fSecurityManager);
1581                 fChildConfig.setProperty(BUFFER_SIZE, new Integer(fBufferSize));
1582 
1583                 // features must be copied to child configuration
1584                 fNeedCopyFeatures = true;
1585 
1586                 // use the same namespace context
1587                 fChildConfig.setProperty(
1588                     Constants.XERCES_PROPERTY_PREFIX
1589                         + Constants.NAMESPACE_CONTEXT_PROPERTY,
1590                     fNamespaceContext);
1591 
1592                 fChildConfig.setFeature(
1593                             XINCLUDE_FIXUP_BASE_URIS,
1594                             fFixupBaseURIs);
1595 
1596                 fChildConfig.setFeature(
1597                             XINCLUDE_FIXUP_LANGUAGE,
1598                             fFixupLanguage);
1599 
1600 
1601                 // If the xpointer attribute is present
1602                 if (xpointer != null ) {
1603 
1604                     XPointerHandler newHandler =
1605                         (XPointerHandler)fChildConfig.getProperty(
1606                             Constants.XERCES_PROPERTY_PREFIX
1607                                 + Constants.XPOINTER_HANDLER_PROPERTY);
1608 
1609                         fXPtrProcessor = newHandler;
1610 
1611                         // ???
1612                         ((XPointerHandler)fXPtrProcessor).setProperty(
1613                             Constants.XERCES_PROPERTY_PREFIX
1614                             + Constants.NAMESPACE_CONTEXT_PROPERTY,
1615                         fNamespaceContext);
1616 
1617                     ((XPointerHandler)fXPtrProcessor).setProperty(XINCLUDE_FIXUP_BASE_URIS,
1618                             new Boolean(fFixupBaseURIs));
1619 
1620                     ((XPointerHandler)fXPtrProcessor).setProperty(
1621                             XINCLUDE_FIXUP_LANGUAGE,
1622                             new Boolean (fFixupLanguage));
1623 
1624                     if (fErrorReporter != null)
1625                         ((XPointerHandler)fXPtrProcessor).setProperty(ERROR_REPORTER, fErrorReporter);
1626                         // ???
1627 
1628                     newHandler.setParent(this);
1629                     newHandler.setDocumentHandler(this.getDocumentHandler());
1630                     fXPointerChildConfig = fChildConfig;
1631                 } else {
1632                     XIncludeHandler newHandler =
1633                         (XIncludeHandler)fChildConfig.getProperty(
1634                             Constants.XERCES_PROPERTY_PREFIX
1635                                 + Constants.XINCLUDE_HANDLER_PROPERTY);
1636 
1637                         newHandler.setParent(this);
1638                     newHandler.setDocumentHandler(this.getDocumentHandler());
1639                     fXIncludeChildConfig = fChildConfig;
1640                 }
1641             }
1642 
1643             // If an xpointer attribute is present
1644             if (xpointer != null ) {
1645                 fChildConfig = fXPointerChildConfig ;
1646 
1647                 // Parse the XPointer expression
1648                 try {
1649                     ((XPointerProcessor)fXPtrProcessor).parseXPointer(xpointer);
1650 
1651                 } catch (XNIException ex) {
1652                     // report the XPointer error as a resource error
1653                     reportResourceError(
1654                             "XMLResourceError",
1655                             new Object[] { href, ex.getMessage()});
1656                         return false;
1657                 }
1658             } else {
1659                 fChildConfig = fXIncludeChildConfig;
1660             }
1661 
1662             // set all features on parserConfig to match this parser configuration
1663             if (fNeedCopyFeatures) {
1664                 copyFeatures(fSettings, fChildConfig);
1665             }
1666             fNeedCopyFeatures = false;
1667 
1668             try {
1669                 fNamespaceContext.pushScope();
1670 
1671                 fChildConfig.parse(includedSource);
1672                 // necessary to make sure proper location is reported in errors
1673                 if (fErrorReporter != null) {
1674                     fErrorReporter.setDocumentLocator(fDocLocation);
1675                 }
1676 
1677                 // If the xpointer attribute is present
1678                 if (xpointer != null ) {
1679                         // and it was not resolved
1680                         if (!((XPointerProcessor)fXPtrProcessor).isXPointerResolved()) {
1681                         Locale locale = (fErrorReporter != null) ? fErrorReporter.getLocale() : null;
1682                         String reason = fXIncludeMessageFormatter.formatMessage(locale, "XPointerResolutionUnsuccessful", null);
1683                         reportResourceError("XMLResourceError", new Object[] {href, reason});
1684                                 // use the fallback
1685                                 return false;
1686                         }
1687                 }
1688             }
1689             catch (XNIException e) {
1690                 // necessary to make sure proper location is reported in errors
1691                 if (fErrorReporter != null) {
1692                     fErrorReporter.setDocumentLocator(fDocLocation);
1693                 }
1694                 reportFatalError("XMLParseError", new Object[] { href });
1695             }
1696             catch (IOException e) {
1697                 // necessary to make sure proper location is reported in errors
1698                 if (fErrorReporter != null) {
1699                     fErrorReporter.setDocumentLocator(fDocLocation);
1700                 }
1701                 // An IOException indicates that we had trouble reading the file, not
1702                 // that it was an invalid XML file.  So we send a resource error, not a
1703                 // fatal error.
1704                 reportResourceError(
1705                     "XMLResourceError",
1706                     new Object[] { href, e.getMessage()});
1707                 return false;
1708             }
1709             finally {
1710                 fNamespaceContext.popScope();
1711             }
1712         }
1713         else if (parse.equals(XINCLUDE_PARSE_TEXT)) {
1714             // we only care about encoding for parse="text"
1715             String encoding = attributes.getValue(XINCLUDE_ATTR_ENCODING);
1716             includedSource.setEncoding(encoding);
1717             XIncludeTextReader textReader = null;
1718 
1719             try {
1720                 // Setup the appropriate text reader.
1721                 if (!fIsXML11) {
1722                     if (fXInclude10TextReader == null) {
1723                         fXInclude10TextReader = new XIncludeTextReader(includedSource, this, fBufferSize);
1724                     }
1725                     else {
1726                         fXInclude10TextReader.setInputSource(includedSource);
1727                     }
1728                     textReader = fXInclude10TextReader;
1729                 }
1730                 else {
1731                     if (fXInclude11TextReader == null) {
1732                         fXInclude11TextReader = new XInclude11TextReader(includedSource, this, fBufferSize);
1733                     }
1734                     else {
1735                         fXInclude11TextReader.setInputSource(includedSource);
1736                     }
1737                     textReader = fXInclude11TextReader;
1738                 }
1739                 textReader.setErrorReporter(fErrorReporter);
1740                 textReader.parse();
1741             }
1742             // encoding errors
1743             catch (MalformedByteSequenceException ex) {
1744                 fErrorReporter.reportError(ex.getDomain(), ex.getKey(),
1745                     ex.getArguments(), XMLErrorReporter.SEVERITY_FATAL_ERROR);
1746             }
1747             catch (CharConversionException e) {
1748                 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1749                     "CharConversionFailure", null, XMLErrorReporter.SEVERITY_FATAL_ERROR);
1750             }
1751             catch (IOException e) {
1752                 reportResourceError(
1753                     "TextResourceError",
1754                     new Object[] { href, e.getMessage()});
1755                 return false;
1756             }
1757             finally {
1758                 if (textReader != null) {
1759                     try {
1760                         textReader.close();
1761                     }
1762                     catch (IOException e) {
1763                         reportResourceError(
1764                             "TextResourceError",
1765                             new Object[] { href, e.getMessage()});
1766                         return false;
1767                     }
1768                 }
1769             }
1770         }
1771         else {
1772             reportFatalError("InvalidParseValue", new Object[] { parse });
1773         }
1774         return true;
1775     }
1776 
1777     /**
1778      * Returns true if the element has the namespace "http://www.w3.org/2001/XInclude"
1779      * @param element the element to check
1780      * @return true if the element has the namespace "http://www.w3.org/2001/XInclude"
1781      */
1782     protected boolean hasXIncludeNamespace(QName element) {
1783         // REVISIT: The namespace of this element should be bound
1784         // already. Why are we looking it up from the namespace
1785         // context? -- mrglavas
1786         return element.uri == XINCLUDE_NS_URI
1787             || fNamespaceContext.getURI(element.prefix) == XINCLUDE_NS_URI;
1788     }
1789 
1790     /**
1791      * Checks if the element is an &lt;include&gt; element.  The element must have
1792      * the XInclude namespace, and a local name of "include".
1793      *
1794      * @param element the element to check
1795      * @return true if the element is an &lt;include&gt; element
1796      * @see #hasXIncludeNamespace(QName)
1797      */
1798     protected boolean isIncludeElement(QName element) {
1799         return element.localpart.equals(XINCLUDE_INCLUDE) &&
1800             hasXIncludeNamespace(element);
1801     }
1802 
1803     /**
1804      * Checks if the element is an &lt;fallback&gt; element.  The element must have
1805      * the XInclude namespace, and a local name of "fallback".
1806      *
1807      * @param element the element to check
1808      * @return true if the element is an &lt;fallback; element
1809      * @see #hasXIncludeNamespace(QName)
1810      */
1811     protected boolean isFallbackElement(QName element) {
1812         return element.localpart.equals(XINCLUDE_FALLBACK) &&
1813             hasXIncludeNamespace(element);
1814     }
1815 
1816     /**
1817      * Returns true if the current [base URI] is the same as the [base URI] that
1818      * was in effect on the include parent.  This method should <em>only</em> be called
1819      * when the current element is a top level included element, i.e. the direct child
1820      * of a fallback element, or the root elements in an included document.
1821      * The "include parent" is the element which, in the result infoset, will be the
1822      * direct parent of the current element.
1823      * @return true if the [base URIs] are the same string
1824      */
1825     protected boolean sameBaseURIAsIncludeParent() {
1826         String parentBaseURI = getIncludeParentBaseURI();
1827         String baseURI = fCurrentBaseURI.getExpandedSystemId();
1828         // REVISIT: should we use File#sameFile() ?
1829         //          I think the benefit of using it is that it resolves host names
1830         //          instead of just doing a string comparison.
1831         // TODO: [base URI] is still an open issue with the working group.
1832         //       They're deciding if xml:base should be added if the [base URI] is different in terms
1833         //       of resolving relative references, or if it should be added if they are different at all.
1834         //       Revisit this after a final decision has been made.
1835         //       The decision also affects whether we output the file name of the URI, or just the path.
1836         return parentBaseURI != null && parentBaseURI.equals(baseURI);
1837     }
1838 
1839     /**
1840      * Returns true if the current [language] is equivalent to the [language] that
1841      * was in effect on the include parent, taking case-insensitivity into account
1842      * as per [RFC 3066].  This method should <em>only</em> be called when the
1843      * current element is a top level included element, i.e. the direct child
1844      * of a fallback element, or the root elements in an included document.
1845      * The "include parent" is the element which, in the result infoset, will be the
1846      * direct parent of the current element.
1847      *
1848      * @return true if the [language] properties have the same value
1849      * taking case-insensitivity into account as per [RFC 3066].
1850      */
1851     protected boolean sameLanguageAsIncludeParent() {
1852         String parentLanguage = getIncludeParentLanguage();
1853         return parentLanguage != null && parentLanguage.equalsIgnoreCase(fCurrentLanguage);
1854     }
1855 
1856     /**
1857      * Checks if the file indicated by the given XMLLocator has already been included
1858      * in the current stack.
1859      * @param includedSource the source to check for inclusion
1860      * @return true if the source has already been included
1861      */
1862     protected boolean searchForRecursiveIncludes(XMLLocator includedSource) {
1863         String includedSystemId = includedSource.getExpandedSystemId();
1864 
1865         if (includedSystemId == null) {
1866             try {
1867                 includedSystemId =
1868                     XMLEntityManager.expandSystemId(
1869                         includedSource.getLiteralSystemId(),
1870                         includedSource.getBaseSystemId(),
1871                         false);
1872             }
1873             catch (MalformedURIException e) {
1874                 reportFatalError("ExpandedSystemId");
1875             }
1876         }
1877 
1878         if (includedSystemId.equals(fCurrentBaseURI.getExpandedSystemId())) {
1879             return true;
1880         }
1881 
1882         if (fParentXIncludeHandler == null) {
1883             return false;
1884         }
1885         return fParentXIncludeHandler.searchForRecursiveIncludes(
1886             includedSource);
1887     }
1888 
1889     /**
1890      * Returns true if the current element is a top level included item.  This means
1891      * it's either the child of a fallback element, or the top level item in an
1892      * included document
1893      * @return true if the current element is a top level included item
1894      */
1895     protected boolean isTopLevelIncludedItem() {
1896         return isTopLevelIncludedItemViaInclude()
1897             || isTopLevelIncludedItemViaFallback();
1898     }
1899 
1900     protected boolean isTopLevelIncludedItemViaInclude() {
1901         return fDepth == 1 && !isRootDocument();
1902     }
1903 
1904     protected boolean isTopLevelIncludedItemViaFallback() {
1905         // Technically, this doesn't check if the parent was a fallback, it also
1906         // would return true if any of the parent's sibling elements were fallbacks.
1907         // However, this doesn't matter, since we will always be ignoring elements
1908         // whose parent's siblings were fallbacks.
1909         return getSawFallback(fDepth - 1);
1910     }
1911 
1912     /**
1913      * Processes the XMLAttributes object of startElement() calls.  Performs the following tasks:
1914      * <ul>
1915      * <li> If the element is a top level included item whose [base URI] is different from the
1916      * [base URI] of the include parent, then an xml:base attribute is added to specify the
1917      * true [base URI]
1918      * <li> For all namespace prefixes which are in-scope in an included item, but not in scope
1919      * in the include parent, a xmlns:prefix attribute is added
1920      * <li> For all attributes with a type of ENTITY, ENTITIES or NOTATIONS, the notations and
1921      * unparsed entities are processed as described in the spec, sections 4.5.1 and 4.5.2
1922      * </ul>
1923      * @param attributes
1924      * @return
1925      */
1926     protected XMLAttributes processAttributes(XMLAttributes attributes) {
1927         if (isTopLevelIncludedItem()) {
1928             // Modify attributes to fix the base URI (spec 4.5.5).
1929             // We only do it to top level included elements, which have a different
1930             // base URI than their include parent.
1931             if (fFixupBaseURIs && !sameBaseURIAsIncludeParent()) {
1932                 if (attributes == null) {
1933                     attributes = new XMLAttributesImpl();
1934                 }
1935 
1936                 // This causes errors with schema validation, if the schema doesn't
1937                 // specify that these elements can have an xml:base attribute
1938                 String uri = null;
1939                 try {
1940                     uri = this.getRelativeBaseURI();
1941                 }
1942                 catch (MalformedURIException e) {
1943                     // this shouldn't ever happen, since by definition, we had to traverse
1944                     // the same URIs to even get to this place
1945                     uri = fCurrentBaseURI.getExpandedSystemId();
1946                 }
1947                 int index =
1948                     attributes.addAttribute(
1949                         XML_BASE_QNAME,
1950                         XMLSymbols.fCDATASymbol,
1951                         uri);
1952                 attributes.setSpecified(index, true);
1953             }
1954 
1955             // Modify attributes to perform language-fixup (spec 4.5.6).
1956             // We only do it to top level included elements, which have a different
1957             // [language] than their include parent.
1958             if (fFixupLanguage && !sameLanguageAsIncludeParent()) {
1959                 if (attributes == null) {
1960                     attributes = new XMLAttributesImpl();
1961                 }
1962                 int index =
1963                     attributes.addAttribute(
1964                         XML_LANG_QNAME,
1965                         XMLSymbols.fCDATASymbol,
1966                         fCurrentLanguage);
1967                 attributes.setSpecified(index, true);
1968             }
1969 
1970             // Modify attributes of included items to do namespace-fixup. (spec 4.5.4)
1971             Enumeration inscopeNS = fNamespaceContext.getAllPrefixes();
1972             while (inscopeNS.hasMoreElements()) {
1973                 String prefix = (String)inscopeNS.nextElement();
1974                 String parentURI =
1975                     fNamespaceContext.getURIFromIncludeParent(prefix);
1976                 String uri = fNamespaceContext.getURI(prefix);
1977                 if (parentURI != uri && attributes != null) {
1978                     if (prefix == XMLSymbols.EMPTY_STRING) {
1979                         if (attributes
1980                             .getValue(
1981                                 NamespaceContext.XMLNS_URI,
1982                                 XMLSymbols.PREFIX_XMLNS)
1983                             == null) {
1984                             if (attributes == null) {
1985                                 attributes = new XMLAttributesImpl();
1986                             }
1987 
1988                             QName ns = (QName)NEW_NS_ATTR_QNAME.clone();
1989                             ns.prefix = null;
1990                             ns.localpart = XMLSymbols.PREFIX_XMLNS;
1991                             ns.rawname = XMLSymbols.PREFIX_XMLNS;
1992                             int index =
1993                                 attributes.addAttribute(
1994                                     ns,
1995                                     XMLSymbols.fCDATASymbol,
1996                                     uri != null ? uri : XMLSymbols.EMPTY_STRING);
1997                             attributes.setSpecified(index, true);
1998                             // Need to re-declare this prefix in the current context
1999                             // in order for the SAX parser to report the appropriate
2000                             // start and end prefix mapping events. -- mrglavas
2001                             fNamespaceContext.declarePrefix(prefix, uri);
2002                         }
2003                     }
2004                     else if (
2005                         attributes.getValue(NamespaceContext.XMLNS_URI, prefix)
2006                             == null) {
2007                         if (attributes == null) {
2008                             attributes = new XMLAttributesImpl();
2009                         }
2010 
2011                         QName ns = (QName)NEW_NS_ATTR_QNAME.clone();
2012                         ns.localpart = prefix;
2013                         ns.rawname += prefix;
2014                         ns.rawname = (fSymbolTable != null) ?
2015                             fSymbolTable.addSymbol(ns.rawname) :
2016                             ns.rawname.intern();
2017                         int index =
2018                             attributes.addAttribute(
2019                                 ns,
2020                                 XMLSymbols.fCDATASymbol,
2021                                 uri != null ? uri : XMLSymbols.EMPTY_STRING);
2022                         attributes.setSpecified(index, true);
2023                         // Need to re-declare this prefix in the current context
2024                         // in order for the SAX parser to report the appropriate
2025                         // start and end prefix mapping events. -- mrglavas
2026                         fNamespaceContext.declarePrefix(prefix, uri);
2027                     }
2028                 }
2029             }
2030         }
2031 
2032         if (attributes != null) {
2033             int length = attributes.getLength();
2034             for (int i = 0; i < length; i++) {
2035                 String type = attributes.getType(i);
2036                 String value = attributes.getValue(i);
2037                 if (type == XMLSymbols.fENTITYSymbol) {
2038                     this.checkUnparsedEntity(value);
2039                 }
2040                 if (type == XMLSymbols.fENTITIESSymbol) {
2041                     // 4.5.1 - Unparsed Entities
2042                     StringTokenizer st = new StringTokenizer(value);
2043                     while (st.hasMoreTokens()) {
2044                         String entName = st.nextToken();
2045                         this.checkUnparsedEntity(entName);
2046                     }
2047                 }
2048                 else if (type == XMLSymbols.fNOTATIONSymbol) {
2049                     // 4.5.2 - Notations
2050                     this.checkNotation(value);
2051                 }
2052                 /* We actually don't need to do anything for 4.5.3, because at this stage the
2053                  * value of the attribute is just a string. It will be taken care of later
2054                  * in the pipeline, when the IDREFs are actually resolved against IDs.
2055                  *
2056                  * if (type == XMLSymbols.fIDREFSymbol || type == XMLSymbols.fIDREFSSymbol) { }
2057                  */
2058             }
2059         }
2060 
2061         return attributes;
2062     }
2063 
2064     /**
2065      * Returns a URI, relative to the include parent's base URI, of the current
2066      * [base URI].  For instance, if the current [base URI] was "dir1/dir2/file.xml"
2067      * and the include parent's [base URI] was "dir/", this would return "dir2/file.xml".
2068      * @return the relative URI
2069      */
2070     protected String getRelativeBaseURI() throws MalformedURIException {
2071         int includeParentDepth = getIncludeParentDepth();
2072         String relativeURI = this.getRelativeURI(includeParentDepth);
2073         if (isRootDocument()) {
2074             return relativeURI;
2075         }
2076         else {
2077             if (relativeURI.equals("")) {
2078                 relativeURI = fCurrentBaseURI.getLiteralSystemId();
2079             }
2080 
2081             if (includeParentDepth == 0) {
2082                 if (fParentRelativeURI == null) {
2083                     fParentRelativeURI =
2084                         fParentXIncludeHandler.getRelativeBaseURI();
2085                 }
2086                 if (fParentRelativeURI.equals("")) {
2087                     return relativeURI;
2088                 }
2089 
2090                 URI base = new URI(fParentRelativeURI, true);
2091                 URI uri = new URI(base, relativeURI);
2092 
2093                 /** Check whether the scheme components are equal. */
2094                 final String baseScheme = base.getScheme();
2095                 final String literalScheme = uri.getScheme();
2096                 if (!isEqual(baseScheme, literalScheme)) {
2097                     return relativeURI;
2098                 }
2099 
2100                 /** Check whether the authority components are equal. */
2101                 final String baseAuthority = base.getAuthority();
2102                 final String literalAuthority = uri.getAuthority();
2103                 if (!isEqual(baseAuthority, literalAuthority)) {
2104                     return uri.getSchemeSpecificPart();
2105                 }
2106 
2107                 /**
2108                  * The scheme and authority components are equal,
2109                  * return the path and the possible query and/or
2110                  * fragment which follow.
2111                  */
2112                 final String literalPath = uri.getPath();
2113                 final String literalQuery = uri.getQueryString();
2114                 final String literalFragment = uri.getFragment();
2115                 if (literalQuery != null || literalFragment != null) {
2116                     StringBuffer buffer = new StringBuffer();
2117                     if (literalPath != null) {
2118                         buffer.append(literalPath);
2119                     }
2120                     if (literalQuery != null) {
2121                         buffer.append('?');
2122                         buffer.append(literalQuery);
2123                     }
2124                     if (literalFragment != null) {
2125                         buffer.append('#');
2126                         buffer.append(literalFragment);
2127                     }
2128                     return buffer.toString();
2129                 }
2130                 return literalPath;
2131             }
2132             else {
2133                 return relativeURI;
2134             }
2135         }
2136     }
2137 
2138     /**
2139      * Returns the [base URI] of the include parent.
2140      * @return the base URI of the include parent.
2141      */
2142     private String getIncludeParentBaseURI() {
2143         int depth = getIncludeParentDepth();
2144         if (!isRootDocument() && depth == 0) {
2145             return fParentXIncludeHandler.getIncludeParentBaseURI();
2146         }
2147         else {
2148             return this.getBaseURI(depth);
2149         }
2150     }
2151 
2152     /**
2153      * Returns the [language] of the include parent.
2154      *
2155      * @return the language property of the include parent.
2156      */
2157     private String getIncludeParentLanguage() {
2158         int depth = getIncludeParentDepth();
2159         if (!isRootDocument() && depth == 0) {
2160             return fParentXIncludeHandler.getIncludeParentLanguage();
2161         }
2162         else {
2163             return getLanguage(depth);
2164         }
2165     }
2166 
2167     /**
2168      * Returns the depth of the include parent.  Here, the include parent is
2169      * calculated as the last non-include or non-fallback element. It is assumed
2170      * this method is called when the current element is a top level included item.
2171      * Returning 0 indicates that the top level element in this document
2172      * was an include element.
2173      * @return the depth of the top level include element
2174      */
2175     private int getIncludeParentDepth() {
2176         // We don't start at fDepth, since it is either the top level included item,
2177         // or an include element, when this method is called.
2178         for (int i = fDepth - 1; i >= 0; i--) {
2179             // This technically might not always return the first non-include/fallback
2180             // element that it comes to, since sawFallback() returns true if a fallback
2181             // was ever encountered at that depth.  However, if a fallback was encountered
2182             // at that depth, and it wasn't the direct descendant of the current element
2183             // then we can't be in a situation where we're calling this method (because
2184             // we'll always be in STATE_IGNORE)
2185             if (!getSawInclude(i) && !getSawFallback(i)) {
2186                 return i;
2187             }
2188         }
2189         // shouldn't get here, since depth 0 should never have an include element or
2190         // a fallback element
2191         return 0;
2192     }
2193 
2194     /**
2195      * Returns the current element depth of the result infoset.
2196      */
2197     private int getResultDepth() {
2198         return fResultDepth;
2199     }
2200 
2201     /**
2202      * Modify the augmentations.  Add an [included] infoset item, if the current
2203      * element is a top level included item.
2204      * @param augs the Augmentations to modify.
2205      * @return the modified Augmentations
2206      */
2207     protected Augmentations modifyAugmentations(Augmentations augs) {
2208         return modifyAugmentations(augs, false);
2209     }
2210 
2211     /**
2212      * Modify the augmentations.  Add an [included] infoset item, if <code>force</code>
2213      * is true, or if the current element is a top level included item.
2214      * @param augs the Augmentations to modify.
2215      * @param force whether to force modification
2216      * @return the modified Augmentations
2217      */
2218     protected Augmentations modifyAugmentations(
2219         Augmentations augs,
2220         boolean force) {
2221         if (force || isTopLevelIncludedItem()) {
2222             if (augs == null) {
2223                 augs = new AugmentationsImpl();
2224             }
2225             augs.putItem(XINCLUDE_INCLUDED, Boolean.TRUE);
2226         }
2227         return augs;
2228     }
2229 
2230     protected int getState(int depth) {
2231         return fState[depth];
2232     }
2233 
2234     protected int getState() {
2235         return fState[fDepth];
2236     }
2237 
2238     protected void setState(int state) {
2239         if (fDepth >= fState.length) {
2240             int[] newarray = new int[fDepth * 2];
2241             System.arraycopy(fState, 0, newarray, 0, fState.length);
2242             fState = newarray;
2243         }
2244         fState[fDepth] = state;
2245     }
2246 
2247     /**
2248      * Records that an &lt;fallback&gt; was encountered at the specified depth,
2249      * as an ancestor of the current element, or as a sibling of an ancestor of the
2250      * current element.
2251      *
2252      * @param depth
2253      * @param val
2254      */
2255     protected void setSawFallback(int depth, boolean val) {
2256         if (depth >= fSawFallback.length) {
2257             boolean[] newarray = new boolean[depth * 2];
2258             System.arraycopy(fSawFallback, 0, newarray, 0, fSawFallback.length);
2259             fSawFallback = newarray;
2260         }
2261         fSawFallback[depth] = val;
2262     }
2263 
2264     /**
2265      * Returns whether an &lt;fallback&gt; was encountered at the specified depth,
2266      * as an ancestor of the current element, or as a sibling of an ancestor of the
2267      * current element.
2268      *
2269      * @param depth
2270      */
2271     protected boolean getSawFallback(int depth) {
2272         if (depth >= fSawFallback.length) {
2273             return false;
2274         }
2275         return fSawFallback[depth];
2276     }
2277 
2278     /**
2279      * Records that an &lt;include&gt; was encountered at the specified depth,
2280      * as an ancestor of the current item.
2281      *
2282      * @param depth
2283      * @param val
2284      */
2285     protected void setSawInclude(int depth, boolean val) {
2286         if (depth >= fSawInclude.length) {
2287             boolean[] newarray = new boolean[depth * 2];
2288             System.arraycopy(fSawInclude, 0, newarray, 0, fSawInclude.length);
2289             fSawInclude = newarray;
2290         }
2291         fSawInclude[depth] = val;
2292     }
2293 
2294     /**
2295      * Return whether an &lt;include&gt; was encountered at the specified depth,
2296      * as an ancestor of the current item.
2297      *
2298      * @param depth
2299      * @return
2300      */
2301     protected boolean getSawInclude(int depth) {
2302         if (depth >= fSawInclude.length) {
2303             return false;
2304         }
2305         return fSawInclude[depth];
2306     }
2307 
2308     protected void reportResourceError(String key) {
2309         this.reportFatalError(key, null);
2310     }
2311 
2312     protected void reportResourceError(String key, Object[] args) {
2313         this.reportError(key, args, XMLErrorReporter.SEVERITY_WARNING);
2314     }
2315 
2316     protected void reportFatalError(String key) {
2317         this.reportFatalError(key, null);
2318     }
2319 
2320     protected void reportFatalError(String key, Object[] args) {
2321         this.reportError(key, args, XMLErrorReporter.SEVERITY_FATAL_ERROR);
2322     }
2323 
2324     private void reportError(String key, Object[] args, short severity) {
2325         if (fErrorReporter != null) {
2326             fErrorReporter.reportError(
2327                 XIncludeMessageFormatter.XINCLUDE_DOMAIN,
2328                 key,
2329                 args,
2330                 severity);
2331         }
2332         // we won't worry about when error reporter is null, since there should always be
2333         // at least the default error reporter
2334     }
2335 
2336     /**
2337      * Set the parent of this XIncludeHandler in the tree
2338      * @param parent
2339      */
2340     protected void setParent(XIncludeHandler parent) {
2341         fParentXIncludeHandler = parent;
2342     }
2343 
2344     // used to know whether to pass declarations to the document handler
2345     protected boolean isRootDocument() {
2346         return fParentXIncludeHandler == null;
2347     }
2348 
2349     /**
2350      * Caches an unparsed entity.
2351      * @param name the name of the unparsed entity
2352      * @param identifier the location of the unparsed entity
2353      * @param augmentations any Augmentations that were on the original unparsed entity declaration
2354      */
2355     protected void addUnparsedEntity(
2356         String name,
2357         XMLResourceIdentifier identifier,
2358         String notation,
2359         Augmentations augmentations) {
2360         UnparsedEntity ent = new UnparsedEntity();
2361         ent.name = name;
2362         ent.systemId = identifier.getLiteralSystemId();
2363         ent.publicId = identifier.getPublicId();
2364         ent.baseURI = identifier.getBaseSystemId();
2365         ent.expandedSystemId = identifier.getExpandedSystemId();
2366         ent.notation = notation;
2367         ent.augmentations = augmentations;
2368         fUnparsedEntities.add(ent);
2369     }
2370 
2371     /**
2372      * Caches a notation.
2373      * @param name the name of the notation
2374      * @param identifier the location of the notation
2375      * @param augmentations any Augmentations that were on the original notation declaration
2376      */
2377     protected void addNotation(
2378         String name,
2379         XMLResourceIdentifier identifier,
2380         Augmentations augmentations) {
2381         Notation not = new Notation();
2382         not.name = name;
2383         not.systemId = identifier.getLiteralSystemId();
2384         not.publicId = identifier.getPublicId();
2385         not.baseURI = identifier.getBaseSystemId();
2386         not.expandedSystemId = identifier.getExpandedSystemId();
2387         not.augmentations = augmentations;
2388         fNotations.add(not);
2389     }
2390 
2391     /**
2392      * Checks if an UnparsedEntity with the given name was declared in the DTD of the document
2393      * for the current pipeline.  If so, then the notation for the UnparsedEntity is checked.
2394      * If that turns out okay, then the UnparsedEntity is passed to the root pipeline to
2395      * be checked for conflicts, and sent to the root DTDHandler.
2396      *
2397      * @param entName the name of the UnparsedEntity to check
2398      */
2399     protected void checkUnparsedEntity(String entName) {
2400         UnparsedEntity ent = new UnparsedEntity();
2401         ent.name = entName;
2402         int index = fUnparsedEntities.indexOf(ent);
2403         if (index != -1) {
2404             ent = (UnparsedEntity)fUnparsedEntities.get(index);
2405             // first check the notation of the unparsed entity
2406             checkNotation(ent.notation);
2407             checkAndSendUnparsedEntity(ent);
2408         }
2409     }
2410 
2411     /**
2412      * Checks if a Notation with the given name was declared in the DTD of the document
2413      * for the current pipeline.  If so, that Notation is passed to the root pipeline to
2414      * be checked for conflicts, and sent to the root DTDHandler
2415      *
2416      * @param notName the name of the Notation to check
2417      */
2418     protected void checkNotation(String notName) {
2419         Notation not = new Notation();
2420         not.name = notName;
2421         int index = fNotations.indexOf(not);
2422         if (index != -1) {
2423             not = (Notation)fNotations.get(index);
2424             checkAndSendNotation(not);
2425         }
2426     }
2427 
2428     /**
2429      * The purpose of this method is to check if an UnparsedEntity conflicts with a previously
2430      * declared entity in the current pipeline stack.  If there is no conflict, the
2431      * UnparsedEntity is sent by the root pipeline.
2432      *
2433      * @param ent the UnparsedEntity to check for conflicts
2434      */
2435     protected void checkAndSendUnparsedEntity(UnparsedEntity ent) {
2436         if (isRootDocument()) {
2437             int index = fUnparsedEntities.indexOf(ent);
2438             if (index == -1) {
2439                 // There is no unparsed entity with the same name that we have sent.
2440                 // Calling unparsedEntityDecl() will add the entity to our local store,
2441                 // and also send the unparsed entity to the DTDHandler
2442                 XMLResourceIdentifier id =
2443                     new XMLResourceIdentifierImpl(
2444                         ent.publicId,
2445                         ent.systemId,
2446                         ent.baseURI,
2447                         ent.expandedSystemId);
2448                 addUnparsedEntity(
2449                     ent.name,
2450                     id,
2451                     ent.notation,
2452                     ent.augmentations);
2453                 if (fSendUEAndNotationEvents && fDTDHandler != null) {
2454                     fDTDHandler.unparsedEntityDecl(
2455                         ent.name,
2456                         id,
2457                         ent.notation,
2458                         ent.augmentations);
2459                 }
2460             }
2461             else {
2462                 UnparsedEntity localEntity =
2463                     (UnparsedEntity)fUnparsedEntities.get(index);
2464                 if (!ent.isDuplicate(localEntity)) {
2465                     reportFatalError(
2466                         "NonDuplicateUnparsedEntity",
2467                         new Object[] { ent.name });
2468                 }
2469             }
2470         }
2471         else {
2472             fParentXIncludeHandler.checkAndSendUnparsedEntity(ent);
2473         }
2474     }
2475 
2476     /**
2477      * The purpose of this method is to check if a Notation conflicts with a previously
2478      * declared notation in the current pipeline stack.  If there is no conflict, the
2479      * Notation is sent by the root pipeline.
2480      *
2481      * @param not the Notation to check for conflicts
2482      */
2483     protected void checkAndSendNotation(Notation not) {
2484         if (isRootDocument()) {
2485             int index = fNotations.indexOf(not);
2486             if (index == -1) {
2487                 // There is no notation with the same name that we have sent.
2488                 XMLResourceIdentifier id =
2489                     new XMLResourceIdentifierImpl(
2490                         not.publicId,
2491                         not.systemId,
2492                         not.baseURI,
2493                         not.expandedSystemId);
2494                 addNotation(not.name, id, not.augmentations);
2495                 if (fSendUEAndNotationEvents && fDTDHandler != null) {
2496                     fDTDHandler.notationDecl(not.name, id, not.augmentations);
2497                 }
2498             }
2499             else {
2500                 Notation localNotation = (Notation)fNotations.get(index);
2501                 if (!not.isDuplicate(localNotation)) {
2502                     reportFatalError(
2503                         "NonDuplicateNotation",
2504                         new Object[] { not.name });
2505                 }
2506             }
2507         }
2508         else {
2509             fParentXIncludeHandler.checkAndSendNotation(not);
2510         }
2511     }
2512 
2513     /**
2514      * Checks whether the string only contains white space characters.
2515      *
2516      * @param value the text to check
2517      */
2518     private void checkWhitespace(XMLString value) {
2519         int end = value.offset + value.length;
2520         for (int i = value.offset; i < end; ++i) {
2521             if (!XMLChar.isSpace(value.ch[i])) {
2522                 reportFatalError("ContentIllegalAtTopLevel");
2523                 return;
2524             }
2525         }
2526     }
2527 
2528     /**
2529      * Checks whether the root element has already been processed.
2530      */
2531     private void checkMultipleRootElements() {
2532         if (getRootElementProcessed()) {
2533             reportFatalError("MultipleRootElements");
2534         }
2535         setRootElementProcessed(true);
2536     }
2537 
2538     /**
2539      * Sets whether the root element has been processed.
2540      */
2541     private void setRootElementProcessed(boolean seenRoot) {
2542         if (isRootDocument()) {
2543             fSeenRootElement = seenRoot;
2544             return;
2545         }
2546         fParentXIncludeHandler.setRootElementProcessed(seenRoot);
2547     }
2548 
2549     /**
2550      * Returns whether the root element has been processed.
2551      */
2552     private boolean getRootElementProcessed() {
2553         return isRootDocument() ? fSeenRootElement : fParentXIncludeHandler.getRootElementProcessed();
2554     }
2555 
2556     // It would be nice if we didn't have to repeat code like this, but there's no interface that has
2557     // setFeature() and addRecognizedFeatures() that the objects have in common.
2558     protected void copyFeatures(
2559         XMLComponentManager from,
2560         ParserConfigurationSettings to) {
2561         Enumeration features = Constants.getXercesFeatures();
2562         copyFeatures1(features, Constants.XERCES_FEATURE_PREFIX, from, to);
2563         features = Constants.getSAXFeatures();
2564         copyFeatures1(features, Constants.SAX_FEATURE_PREFIX, from, to);
2565     }
2566 
2567     protected void copyFeatures(
2568         XMLComponentManager from,
2569         XMLParserConfiguration to) {
2570         Enumeration features = Constants.getXercesFeatures();
2571         copyFeatures1(features, Constants.XERCES_FEATURE_PREFIX, from, to);
2572         features = Constants.getSAXFeatures();
2573         copyFeatures1(features, Constants.SAX_FEATURE_PREFIX, from, to);
2574     }
2575 
2576     private void copyFeatures1(
2577         Enumeration features,
2578         String featurePrefix,
2579         XMLComponentManager from,
2580         ParserConfigurationSettings to) {
2581         while (features.hasMoreElements()) {
2582             String featureId = featurePrefix + (String)features.nextElement();
2583 
2584             to.addRecognizedFeatures(new String[] { featureId });
2585 
2586             try {
2587                 to.setFeature(featureId, from.getFeature(featureId));
2588             }
2589             catch (XMLConfigurationException e) {
2590                 // componentManager doesn't support this feature,
2591                 // so we won't worry about it
2592             }
2593         }
2594     }
2595 
2596     private void copyFeatures1(
2597         Enumeration features,
2598         String featurePrefix,
2599         XMLComponentManager from,
2600         XMLParserConfiguration to) {
2601         while (features.hasMoreElements()) {
2602             String featureId = featurePrefix + (String)features.nextElement();
2603             boolean value = from.getFeature(featureId);
2604 
2605             try {
2606                 to.setFeature(featureId, value);
2607             }
2608             catch (XMLConfigurationException e) {
2609                 // componentManager doesn't support this feature,
2610                 // so we won't worry about it
2611             }
2612         }
2613     }
2614 
2615     // This is a storage class to hold information about the notations.
2616     // We're not using XMLNotationDecl because we don't want to lose the augmentations.
2617     protected static class Notation {
2618         public String name;
2619         public String systemId;
2620         public String baseURI;
2621         public String publicId;
2622         public String expandedSystemId;
2623         public Augmentations augmentations;
2624 
2625         // equals() returns true if two Notations have the same name.
2626         // Useful for searching Vectors for notations with the same name
2627         public boolean equals(Object obj) {
2628             if (obj == null) {
2629                 return false;
2630             }
2631             if (obj instanceof Notation) {
2632                 Notation other = (Notation)obj;
2633                 return name.equals(other.name);
2634             }
2635             return false;
2636         }
2637 
2638         // from 4.5.2
2639         // Notation items with the same [name], [system identifier],
2640         // [public identifier], and [declaration base URI] are considered
2641         // to be duplicate. An application may also be able to detect that
2642         // notations are duplicate through other means. For instance, the URI
2643         // resulting from combining the system identifier and the declaration
2644         // base URI is the same.
2645         public boolean isDuplicate(Object obj) {
2646             if (obj != null && obj instanceof Notation) {
2647                 Notation other = (Notation)obj;
2648                 return name.equals(other.name)
2649                 && isEqual(publicId, other.publicId)
2650                 && isEqual(expandedSystemId, other.expandedSystemId);
2651             }
2652             return false;
2653         }
2654 
2655         private boolean isEqual(String one, String two) {
2656             return (one == two || (one != null && one.equals(two)));
2657         }
2658     }
2659 
2660     // This is a storage class to hold information about the unparsed entities.
2661     // We're not using XMLEntityDecl because we don't want to lose the augmentations.
2662     protected static class UnparsedEntity {
2663         public String name;
2664         public String systemId;
2665         public String baseURI;
2666         public String publicId;
2667         public String expandedSystemId;
2668         public String notation;
2669         public Augmentations augmentations;
2670 
2671         // equals() returns true if two UnparsedEntities have the same name.
2672         // Useful for searching Vectors for entities with the same name
2673         public boolean equals(Object obj) {
2674             if (obj == null) {
2675                 return false;
2676             }
2677             if (obj instanceof UnparsedEntity) {
2678                 UnparsedEntity other = (UnparsedEntity)obj;
2679                 return name.equals(other.name);
2680             }
2681             return false;
2682         }
2683 
2684         // from 4.5.1:
2685         // Unparsed entity items with the same [name], [system identifier],
2686         // [public identifier], [declaration base URI], [notation name], and
2687         // [notation] are considered to be duplicate. An application may also
2688         // be able to detect that unparsed entities are duplicate through other
2689         // means. For instance, the URI resulting from combining the system
2690         // identifier and the declaration base URI is the same.
2691         public boolean isDuplicate(Object obj) {
2692             if (obj != null && obj instanceof UnparsedEntity) {
2693                 UnparsedEntity other = (UnparsedEntity)obj;
2694                 return name.equals(other.name)
2695                 && isEqual(publicId, other.publicId)
2696                 && isEqual(expandedSystemId, other.expandedSystemId)
2697                 && isEqual(notation, other.notation);
2698             }
2699             return false;
2700         }
2701 
2702         private boolean isEqual(String one, String two) {
2703             return (one == two || (one != null && one.equals(two)));
2704         }
2705     }
2706 
2707     // The following methods are used for XML Base processing
2708 
2709     /**
2710      * Saves the current base URI to the top of the stack.
2711      */
2712     protected void saveBaseURI() {
2713         fBaseURIScope.push(fDepth);
2714         fBaseURI.push(fCurrentBaseURI.getBaseSystemId());
2715         fLiteralSystemID.push(fCurrentBaseURI.getLiteralSystemId());
2716         fExpandedSystemID.push(fCurrentBaseURI.getExpandedSystemId());
2717     }
2718 
2719     /**
2720      * Discards the URIs at the top of the stack, and restores the ones beneath it.
2721      */
2722     protected void restoreBaseURI() {
2723         fBaseURI.pop();
2724         fLiteralSystemID.pop();
2725         fExpandedSystemID.pop();
2726         fBaseURIScope.pop();
2727         fCurrentBaseURI.setBaseSystemId((String)fBaseURI.peek());
2728         fCurrentBaseURI.setLiteralSystemId((String)fLiteralSystemID.peek());
2729         fCurrentBaseURI.setExpandedSystemId((String)fExpandedSystemID.peek());
2730     }
2731 
2732     // The following methods are used for language processing
2733 
2734     /**
2735      * Saves the given language on the top of the stack.
2736      *
2737      * @param lanaguage the language to push onto the stack.
2738      */
2739     protected void saveLanguage(String language) {
2740         fLanguageScope.push(fDepth);
2741         fLanguageStack.push(language);
2742     }
2743 
2744     /**
2745      * Discards the language at the top of the stack, and returns the one beneath it.
2746      */
2747     public String restoreLanguage() {
2748         fLanguageStack.pop();
2749         fLanguageScope.pop();
2750         return (String) fLanguageStack.peek();
2751     }
2752 
2753     /**
2754      * Gets the base URI that was in use at that depth
2755      * @param depth
2756      * @return the base URI
2757      */
2758     public String getBaseURI(int depth) {
2759         int scope = scopeOfBaseURI(depth);
2760         return (String)fExpandedSystemID.elementAt(scope);
2761     }
2762 
2763     /**
2764      * Gets the language that was in use at that depth.
2765      * @param depth
2766      * @return the language
2767      */
2768     public String getLanguage(int depth) {
2769         int scope = scopeOfLanguage(depth);
2770         return (String)fLanguageStack.elementAt(scope);
2771     }
2772 
2773     /**
2774      * Returns a relative URI, which when resolved against the base URI at the
2775      * specified depth, will create the current base URI.
2776      * This is accomplished by merged the literal system IDs.
2777      * @param depth the depth at which to start creating the relative URI
2778      * @return a relative URI to convert the base URI at the given depth to the current
2779      *         base URI
2780      */
2781     public String getRelativeURI(int depth) throws MalformedURIException {
2782         // The literal system id at the location given by "start" is *in focus* at
2783         // the given depth. So we need to adjust it to the next scope, so that we
2784         // only process out of focus literal system ids
2785         int start = scopeOfBaseURI(depth) + 1;
2786         if (start == fBaseURIScope.size()) {
2787             // If that is the last system id, then we don't need a relative URI
2788             return "";
2789         }
2790         URI uri = new URI("file", (String)fLiteralSystemID.elementAt(start));
2791         for (int i = start + 1; i < fBaseURIScope.size(); i++) {
2792             uri = new URI(uri, (String)fLiteralSystemID.elementAt(i));
2793         }
2794         return uri.getPath();
2795     }
2796 
2797     // We need to find two consecutive elements in the scope stack,
2798     // such that the first is lower than 'depth' (or equal), and the
2799     // second is higher.
2800     private int scopeOfBaseURI(int depth) {
2801         for (int i = fBaseURIScope.size() - 1; i >= 0; i--) {
2802             if (fBaseURIScope.elementAt(i) <= depth)
2803                 return i;
2804         }
2805         // we should never get here, because 0 was put on the stack in startDocument()
2806         return -1;
2807     }
2808 
2809     private int scopeOfLanguage(int depth) {
2810         for (int i = fLanguageScope.size() - 1; i >= 0; i--) {
2811             if (fLanguageScope.elementAt(i) <= depth)
2812                 return i;
2813         }
2814         // we should never get here, because 0 was put on the stack in startDocument()
2815         return -1;
2816     }
2817 
2818     /**
2819      * Search for a xml:base attribute, and if one is found, put the new base URI into
2820      * effect.
2821      */
2822     protected void processXMLBaseAttributes(XMLAttributes attributes) {
2823         String baseURIValue =
2824             attributes.getValue(NamespaceContext.XML_URI, "base");
2825         if (baseURIValue != null) {
2826             try {
2827                 String expandedValue =
2828                     XMLEntityManager.expandSystemId(
2829                         baseURIValue,
2830                         fCurrentBaseURI.getExpandedSystemId(),
2831                         false);
2832                 fCurrentBaseURI.setLiteralSystemId(baseURIValue);
2833                 fCurrentBaseURI.setBaseSystemId(
2834                     fCurrentBaseURI.getExpandedSystemId());
2835                 fCurrentBaseURI.setExpandedSystemId(expandedValue);
2836 
2837                 // push the new values on the stack
2838                 saveBaseURI();
2839             }
2840             catch (MalformedURIException e) {
2841                 // REVISIT: throw error here
2842             }
2843         }
2844     }
2845 
2846     /**
2847      * Search for a xml:lang attribute, and if one is found, put the new
2848      * [language] into effect.
2849      */
2850     protected void processXMLLangAttributes(XMLAttributes attributes) {
2851         String language = attributes.getValue(NamespaceContext.XML_URI, "lang");
2852         if (language != null) {
2853             fCurrentLanguage = language;
2854             saveLanguage(fCurrentLanguage);
2855         }
2856     }
2857 
2858     /**
2859      * Returns <code>true</code> if the given string
2860      * would be valid in an HTTP header.
2861      *
2862      * @param value string to check
2863      * @return <code>true</code> if the given string
2864      * would be valid in an HTTP header
2865      */
2866     private boolean isValidInHTTPHeader (String value) {
2867         char ch;
2868         for (int i = value.length() - 1; i >= 0; --i) {
2869             ch = value.charAt(i);
2870             if (ch < 0x20 || ch > 0x7E) {
2871                 return false;
2872             }
2873         }
2874         return true;
2875     }
2876 
2877     /**
2878      * Returns a new <code>XMLInputSource</code> from the given parameters.
2879      */
2880     private XMLInputSource createInputSource(String publicId,
2881             String systemId, String baseSystemId,
2882             String accept, String acceptLanguage) {
2883 
2884         HTTPInputSource httpSource = new HTTPInputSource(publicId, systemId, baseSystemId);
2885         if (accept != null && accept.length() > 0) {
2886             httpSource.setHTTPRequestProperty(XIncludeHandler.HTTP_ACCEPT, accept);
2887         }
2888         if (acceptLanguage != null && acceptLanguage.length() > 0) {
2889             httpSource.setHTTPRequestProperty(XIncludeHandler.HTTP_ACCEPT_LANGUAGE, acceptLanguage);
2890         }
2891         return httpSource;
2892     }
2893 
2894     private boolean isEqual(String one, String two) {
2895         return (one == two || (one != null && one.equals(two)));
2896     }
2897 
2898     // which ASCII characters need to be escaped
2899     private static boolean gNeedEscaping[] = new boolean[128];
2900     // the first hex character if a character needs to be escaped
2901     private static char gAfterEscaping1[] = new char[128];
2902     // the second hex character if a character needs to be escaped
2903     private static char gAfterEscaping2[] = new char[128];
2904     private static char[] gHexChs = {'0', '1', '2', '3', '4', '5', '6', '7',
2905                                      '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
2906     // initialize the above 3 arrays
2907     static {
2908         char[] escChs = {' ', '<', '>', '"', '{', '}', '|', '\\', '^', '`'};
2909         int len = escChs.length;
2910         char ch;
2911         for (int i = 0; i < len; i++) {
2912             ch = escChs[i];
2913             gNeedEscaping[ch] = true;
2914             gAfterEscaping1[ch] = gHexChs[ch >> 4];
2915             gAfterEscaping2[ch] = gHexChs[ch & 0xf];
2916         }
2917     }
2918 
2919     //
2920     // Escape an href value according to (4.1.1):
2921     //
2922     // To convert the value of the href attribute to an IRI reference, the following characters must be escaped:
2923     // space #x20
2924     // the delimiters < #x3C, > #x3E and " #x22
2925     // the unwise characters { #x7B, } #x7D, | #x7C, \ #x5C, ^ #x5E and ` #x60
2926     //
2927     // To convert an IRI reference to a URI reference, the following characters must also be escaped:
2928     // the Unicode plane 0 characters #xA0 - #xD7FF, #xF900-#xFDCF, #xFDF0-#xFFEF
2929     // the Unicode plane 1-14 characters #x10000-#x1FFFD ... #xE0000-#xEFFFD
2930     //
2931     private String escapeHref(String href) {
2932         int len = href.length();
2933         int ch;
2934         StringBuffer buffer = new StringBuffer(len*3);
2935 
2936         // for each character in the href
2937         int i = 0;
2938         for (; i < len; i++) {
2939             ch = href.charAt(i);
2940             // if it's not an ASCII character (excluding 0x7F), break here, and use UTF-8 encoding
2941             if (ch > 0x7E) {
2942                 break;
2943             }
2944             // abort: href does not allow this character
2945             if (ch < 0x20) {
2946                 return href;
2947             }
2948             if (gNeedEscaping[ch]) {
2949                 buffer.append('%');
2950                 buffer.append(gAfterEscaping1[ch]);
2951                 buffer.append(gAfterEscaping2[ch]);
2952             }
2953             else {
2954                 buffer.append((char)ch);
2955             }
2956         }
2957 
2958         // we saw some non-ascii character
2959         if (i < len) {
2960             // check if remainder of href contains any illegal characters before proceeding
2961             for (int j = i; j < len; ++j) {
2962                 ch = href.charAt(j);
2963                 if ((ch >= 0x20 && ch <= 0x7E) ||
2964                     (ch >= 0xA0 && ch <= 0xD7FF) ||
2965                     (ch >= 0xF900 && ch <= 0xFDCF) ||
2966                     (ch >= 0xFDF0 && ch <= 0xFFEF)) {
2967                     continue;
2968                 }
2969                 if (XMLChar.isHighSurrogate(ch) && ++j < len) {
2970                     int ch2 = href.charAt(j);
2971                     if (XMLChar.isLowSurrogate(ch2)) {
2972                         ch2 = XMLChar.supplemental((char)ch, (char)ch2);
2973                         if (ch2 < 0xF0000 && (ch2 & 0xFFFF) <= 0xFFFD) {
2974                             continue;
2975                         }
2976                     }
2977                 }
2978                 // abort: href does not allow this character
2979                 return href;
2980             }
2981 
2982             // get UTF-8 bytes for the remaining sub-string
2983             byte[] bytes = null;
2984             byte b;
2985             try {
2986                 bytes = href.substring(i).getBytes("UTF-8");
2987             } catch (java.io.UnsupportedEncodingException e) {
2988                 // should never happen
2989                 return href;
2990             }
2991             len = bytes.length;
2992 
2993             // for each byte
2994             for (i = 0; i < len; i++) {
2995                 b = bytes[i];
2996                 // for non-ascii character: make it positive, then escape
2997                 if (b < 0) {
2998                     ch = b + 256;
2999                     buffer.append('%');
3000                     buffer.append(gHexChs[ch >> 4]);
3001                     buffer.append(gHexChs[ch & 0xf]);
3002                 }
3003                 else if (gNeedEscaping[b]) {
3004                     buffer.append('%');
3005                     buffer.append(gAfterEscaping1[b]);
3006                     buffer.append(gAfterEscaping2[b]);
3007                 }
3008                 else {
3009                     buffer.append((char)b);
3010                 }
3011             }
3012         }
3013 
3014         // If escaping happened, create a new string;
3015         // otherwise, return the orginal one.
3016         if (buffer.length() != len) {
3017             return buffer.toString();
3018         }
3019         else {
3020             return href;
3021         }
3022     }
3023 }