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