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