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