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