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