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