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