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