XIncludeHandler.java revision 988:1c6c21d87aa4
1112158Sdas/*
2112158Sdas * Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved.
3112158Sdas */
4112158Sdas/*
5112158Sdas * Licensed to the Apache Software Foundation (ASF) under one or more
6112158Sdas * contributor license agreements.  See the NOTICE file distributed with
7112158Sdas * this work for additional information regarding copyright ownership.
8112158Sdas * The ASF licenses this file to You under the Apache License, Version 2.0
9112158Sdas * (the "License"); you may not use this file except in compliance with
10112158Sdas * the License.  You may obtain a copy of the License at
11112158Sdas *
12112158Sdas *      http://www.apache.org/licenses/LICENSE-2.0
13112158Sdas *
14112158Sdas * Unless required by applicable law or agreed to in writing, software
15112158Sdas * distributed under the License is distributed on an "AS IS" BASIS,
16112158Sdas * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17112158Sdas * See the License for the specific language governing permissions and
18112158Sdas * limitations under the License.
19112158Sdas */
20112158Sdas
21112158Sdaspackage com.sun.org.apache.xerces.internal.xinclude;
22112158Sdas
23112158Sdasimport java.io.CharConversionException;
24112158Sdasimport java.io.IOException;
25112158Sdasimport java.util.ArrayList;
26112158Sdasimport java.util.Enumeration;
27112158Sdasimport java.util.Locale;
28112158Sdasimport java.util.Stack;
29112158Sdasimport java.util.StringTokenizer;
30112158Sdasimport javax.xml.XMLConstants;
31112158Sdas
32112158Sdasimport com.sun.org.apache.xerces.internal.impl.Constants;
33112158Sdasimport com.sun.org.apache.xerces.internal.impl.XMLEntityManager;
34112158Sdasimport com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
35112158Sdasimport com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException;
36112158Sdasimport com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
37112158Sdasimport com.sun.org.apache.xerces.internal.util.AugmentationsImpl;
38112158Sdasimport com.sun.org.apache.xerces.internal.util.HTTPInputSource;
39112158Sdasimport com.sun.org.apache.xerces.internal.util.IntStack;
40112158Sdasimport com.sun.org.apache.xerces.internal.util.ParserConfigurationSettings;
41112158Sdasimport com.sun.org.apache.xerces.internal.util.SymbolTable;
42112158Sdasimport com.sun.org.apache.xerces.internal.util.URI;
43112158Sdasimport com.sun.org.apache.xerces.internal.util.XMLAttributesImpl;
44112158Sdasimport com.sun.org.apache.xerces.internal.util.XMLResourceIdentifierImpl;
45112158Sdasimport com.sun.org.apache.xerces.internal.util.XMLChar;
46112158Sdasimport com.sun.org.apache.xerces.internal.util.XMLSymbols;
47112158Sdasimport com.sun.org.apache.xerces.internal.util.URI.MalformedURIException;
48112158Sdasimport com.sun.org.apache.xerces.internal.utils.XMLSecurityManager;
49112158Sdasimport com.sun.org.apache.xerces.internal.xni.Augmentations;
50112158Sdasimport com.sun.org.apache.xerces.internal.xni.NamespaceContext;
51112158Sdasimport com.sun.org.apache.xerces.internal.xni.QName;
52112158Sdasimport com.sun.org.apache.xerces.internal.xni.XMLAttributes;
53112158Sdasimport com.sun.org.apache.xerces.internal.xni.XMLDTDHandler;
54112158Sdasimport com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler;
55112158Sdasimport com.sun.org.apache.xerces.internal.xni.XMLLocator;
56112158Sdasimport com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier;
57112158Sdasimport com.sun.org.apache.xerces.internal.xni.XMLString;
58112158Sdasimport com.sun.org.apache.xerces.internal.xni.XNIException;
59112158Sdasimport com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
60112158Sdasimport com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
61112158Sdasimport com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
62112158Sdasimport com.sun.org.apache.xerces.internal.xni.parser.XMLDTDFilter;
63112158Sdasimport com.sun.org.apache.xerces.internal.xni.parser.XMLDTDSource;
64112158Sdasimport com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentFilter;
65112158Sdasimport com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentSource;
66112158Sdasimport com.sun.org.apache.xerces.internal.xni.parser.XMLEntityResolver;
67112158Sdasimport com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource;
68112158Sdasimport com.sun.org.apache.xerces.internal.xni.parser.XMLParserConfiguration;
69112158Sdasimport com.sun.org.apache.xerces.internal.xpointer.XPointerHandler;
70112158Sdasimport com.sun.org.apache.xerces.internal.xpointer.XPointerProcessor;
71112158Sdasimport com.sun.org.apache.xerces.internal.utils.ObjectFactory;
72112158Sdasimport com.sun.org.apache.xerces.internal.utils.XMLSecurityPropertyManager;
73112158Sdasimport java.util.Objects;
74112158Sdasimport javax.xml.catalog.CatalogException;
75112158Sdasimport javax.xml.catalog.CatalogFeatures;
76112158Sdasimport javax.xml.catalog.CatalogManager;
77112158Sdasimport javax.xml.catalog.CatalogResolver;
78112158Sdasimport javax.xml.transform.Source;
79112158Sdasimport jdk.xml.internal.JdkXmlUtils;
80112158Sdasimport org.xml.sax.InputSource;
81112158Sdas
82112158Sdas/**
83112158Sdas * <p>
84112158Sdas * This is a pipeline component which performs XInclude handling, according to the
85112158Sdas * W3C specification for XML Inclusions.
86112158Sdas * </p>
87112158Sdas * <p>
88112158Sdas * This component analyzes each event in the pipeline, looking for &lt;include&gt;
89112158Sdas * elements. An &lt;include&gt; element is one which has a namespace of
90112158Sdas * <code>http://www.w3.org/2001/XInclude</code> and a localname of <code>include</code>.
91112158Sdas * When it finds an &lt;include&gt; element, it attempts to include the file specified
92112158Sdas * in the <code>href</code> attribute of the element.  If inclusion succeeds, all
93112158Sdas * children of the &lt;include&gt; element are ignored (with the exception of
94112158Sdas * checking for invalid children as outlined in the specification).  If the inclusion
95112158Sdas * fails, the &lt;fallback&gt; child of the &lt;include&gt; element is processed.
96112158Sdas * </p>
97112158Sdas * <p>
98112158Sdas * See the <a href="http://www.w3.org/TR/xinclude/">XInclude specification</a> for
99112158Sdas * more information on how XInclude is to be used.
100112158Sdas * </p>
101112158Sdas * <p>
102112158Sdas * This component requires the following features and properties from the
103112158Sdas * component manager that uses it:
104112158Sdas * <ul>
105112158Sdas *  <li>http://xml.org/sax/features/allow-dtd-events-after-endDTD</li>
106112158Sdas *  <li>http://apache.org/xml/properties/internal/error-reporter</li>
107112158Sdas *  <li>http://apache.org/xml/properties/internal/entity-resolver</li>
108112158Sdas * </ul>
109112158Sdas * Optional property:
110112158Sdas * <ul>
111112158Sdas *  <li>http://apache.org/xml/properties/input-buffer-size</li>
112112158Sdas * </ul>
113112158Sdas *
114112158Sdas * Furthermore, the <code>NamespaceContext</code> used in the pipeline is required
115112158Sdas * to be an instance of <code>XIncludeNamespaceSupport</code>.
116112158Sdas * </p>
117112158Sdas * <p>
118112158Sdas * Currently, this implementation has only partial support for the XInclude specification.
119112158Sdas * Specifically, it is missing support for XPointer document fragments.  Thus, only whole
120112158Sdas * documents can be included using this component in the pipeline.
121112158Sdas * </p>
122112158Sdas *
123112158Sdas * @author Peter McCracken, IBM
124112158Sdas * @author Michael Glavassevich, IBM
125112158Sdas *
126112158Sdas *
127112158Sdas * @see XIncludeNamespaceSupport
128112158Sdas */
129112158Sdaspublic class XIncludeHandler
130112158Sdas    implements XMLComponent, XMLDocumentFilter, XMLDTDFilter {
131112158Sdas
132112158Sdas    public final static String XINCLUDE_DEFAULT_CONFIGURATION =
133112158Sdas        "com.sun.org.apache.xerces.internal.parsers.XIncludeParserConfiguration";
134112158Sdas    public final static String HTTP_ACCEPT = "Accept";
135112158Sdas    public final static String HTTP_ACCEPT_LANGUAGE = "Accept-Language";
136112158Sdas    public final static String XPOINTER = "xpointer";
137112158Sdas
138112158Sdas    public final static String XINCLUDE_NS_URI =
139112158Sdas        "http://www.w3.org/2001/XInclude".intern();
140112158Sdas    public final static String XINCLUDE_INCLUDE = "include".intern();
141112158Sdas    public final static String XINCLUDE_FALLBACK = "fallback".intern();
142112158Sdas
143112158Sdas    public final static String XINCLUDE_PARSE_XML = "xml".intern();
144112158Sdas    public final static String XINCLUDE_PARSE_TEXT = "text".intern();
145112158Sdas
146112158Sdas    public final static String XINCLUDE_ATTR_HREF = "href".intern();
147112158Sdas    public final static String XINCLUDE_ATTR_PARSE = "parse".intern();
148112158Sdas    public final static String XINCLUDE_ATTR_ENCODING = "encoding".intern();
149112158Sdas    public final static String XINCLUDE_ATTR_ACCEPT = "accept".intern();
150112415Sdas    public final static String XINCLUDE_ATTR_ACCEPT_LANGUAGE = "accept-language".intern();
151112415Sdas
152112415Sdas    // Top Level Information Items have [included] property in infoset
153112158Sdas    public final static String XINCLUDE_INCLUDED = "[included]".intern();
154112415Sdas
155112158Sdas    /** The identifier for the Augmentation that contains the current base URI */
156112158Sdas    public final static String CURRENT_BASE_URI = "currentBaseURI";
157112158Sdas
158112158Sdas    // used for adding [base URI] attributes
159112158Sdas    public final static String XINCLUDE_BASE = "base".intern();
160112158Sdas    public final static QName XML_BASE_QNAME =
161112158Sdas        new QName(
162112158Sdas            XMLSymbols.PREFIX_XML,
163112158Sdas            XINCLUDE_BASE,
164112158Sdas            (XMLSymbols.PREFIX_XML + ":" + XINCLUDE_BASE).intern(),
165112158Sdas            NamespaceContext.XML_URI);
166112158Sdas
167112158Sdas    // used for adding [language] attributes
168112158Sdas    public final static String XINCLUDE_LANG = "lang".intern();
169112158Sdas    public final static QName XML_LANG_QNAME =
170112158Sdas        new QName(
171112158Sdas            XMLSymbols.PREFIX_XML,
172112158Sdas            XINCLUDE_LANG,
173112158Sdas            (XMLSymbols.PREFIX_XML + ":" + XINCLUDE_LANG).intern(),
174112158Sdas            NamespaceContext.XML_URI);
175112158Sdas
176112158Sdas    public final static QName NEW_NS_ATTR_QNAME =
177112158Sdas        new QName(
178112158Sdas            XMLSymbols.PREFIX_XMLNS,
179112158Sdas            "",
180112158Sdas            XMLSymbols.PREFIX_XMLNS + ":",
181112158Sdas            NamespaceContext.XMLNS_URI);
182112158Sdas
183112158Sdas    // Processing States
184112158Sdas    private final static int STATE_NORMAL_PROCESSING = 1;
185112158Sdas    // we go into this state after a successful include (thus we ignore the children
186112158Sdas    // of the include) or after a fallback
187112158Sdas    private final static int STATE_IGNORE = 2;
188112158Sdas    // we go into this state after a failed include.  If we don't encounter a fallback
189112158Sdas    // before we reach the end include tag, it's a fatal error
190112158Sdas    private final static int STATE_EXPECT_FALLBACK = 3;
191112158Sdas
192112158Sdas    // recognized features and properties
193112158Sdas
194112158Sdas    /** Feature identifier: validation. */
195112158Sdas    protected static final String VALIDATION =
196112158Sdas        Constants.SAX_FEATURE_PREFIX + Constants.VALIDATION_FEATURE;
197112158Sdas
198112158Sdas    /** Feature identifier: schema validation. */
199112158Sdas    protected static final String SCHEMA_VALIDATION =
200112158Sdas        Constants.XERCES_FEATURE_PREFIX + Constants.SCHEMA_VALIDATION_FEATURE;
201112158Sdas
202112158Sdas    /** Feature identifier: dynamic validation. */
203112158Sdas    protected static final String DYNAMIC_VALIDATION =
204112158Sdas        Constants.XERCES_FEATURE_PREFIX + Constants.DYNAMIC_VALIDATION_FEATURE;
205112158Sdas
206112158Sdas    /** Feature identifier: allow notation and unparsed entity events to be sent out of order. */
207112158Sdas    protected static final String ALLOW_UE_AND_NOTATION_EVENTS =
208112158Sdas        Constants.SAX_FEATURE_PREFIX
209112158Sdas            + Constants.ALLOW_DTD_EVENTS_AFTER_ENDDTD_FEATURE;
210112158Sdas
211112158Sdas    /** Feature identifier: fixup base URIs. */
212112158Sdas    protected static final String XINCLUDE_FIXUP_BASE_URIS =
213112158Sdas        Constants.XERCES_FEATURE_PREFIX + Constants.XINCLUDE_FIXUP_BASE_URIS_FEATURE;
214112158Sdas
215112158Sdas    /** Feature identifier: fixup language. */
216112158Sdas    protected static final String XINCLUDE_FIXUP_LANGUAGE =
217112158Sdas        Constants.XERCES_FEATURE_PREFIX + Constants.XINCLUDE_FIXUP_LANGUAGE_FEATURE;
218112158Sdas
219112158Sdas    /** Property identifier: symbol table. */
220112158Sdas    protected static final String SYMBOL_TABLE =
221112158Sdas        Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY;
222112158Sdas
223112158Sdas    /** Property identifier: error reporter. */
224112158Sdas    protected static final String ERROR_REPORTER =
225112158Sdas        Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;
226112158Sdas
227112158Sdas    /** Property identifier: entity resolver. */
228112158Sdas    protected static final String ENTITY_RESOLVER =
229112158Sdas        Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY;
230112158Sdas
231112158Sdas    /** property identifier: security manager. */
232112158Sdas    protected static final String SECURITY_MANAGER =
233112158Sdas        Constants.XERCES_PROPERTY_PREFIX + Constants.SECURITY_MANAGER_PROPERTY;
234112158Sdas
235112158Sdas    /** property identifier: buffer size. */
236112158Sdas    public static final String BUFFER_SIZE =
237112158Sdas        Constants.XERCES_PROPERTY_PREFIX + Constants.BUFFER_SIZE_PROPERTY;
238112158Sdas
239112158Sdas    protected static final String PARSER_SETTINGS =
240112158Sdas        Constants.XERCES_FEATURE_PREFIX + Constants.PARSER_SETTINGS;
241112158Sdas
242112158Sdas    /** property identifier: XML security property manager. */
243112158Sdas    protected static final String XML_SECURITY_PROPERTY_MANAGER =
244112158Sdas            Constants.XML_SECURITY_PROPERTY_MANAGER;
245112158Sdas
246112158Sdas    /** Recognized features. */
247112158Sdas    private static final String[] RECOGNIZED_FEATURES =
248112158Sdas        { ALLOW_UE_AND_NOTATION_EVENTS, XINCLUDE_FIXUP_BASE_URIS, XINCLUDE_FIXUP_LANGUAGE };
249112158Sdas
250112158Sdas    /** Feature defaults. */
251112158Sdas    private static final Boolean[] FEATURE_DEFAULTS = { Boolean.TRUE, Boolean.TRUE, Boolean.TRUE };
252112158Sdas
253112158Sdas    /** Recognized properties. */
254112158Sdas    private static final String[] RECOGNIZED_PROPERTIES =
255112158Sdas        { ERROR_REPORTER, ENTITY_RESOLVER, SECURITY_MANAGER, BUFFER_SIZE };
256112158Sdas
257112158Sdas    /** Property defaults. */
258112158Sdas    private static final Object[] PROPERTY_DEFAULTS = { null, null, null, XMLEntityManager.DEFAULT_BUFFER_SIZE};
259112158Sdas
260112158Sdas    // instance variables
261112158Sdas
262112158Sdas    // for XMLDocumentFilter
263112158Sdas    protected XMLDocumentHandler fDocumentHandler;
264112158Sdas    protected XMLDocumentSource fDocumentSource;
265112158Sdas
266112158Sdas    // for XMLDTDFilter
267112158Sdas    protected XMLDTDHandler fDTDHandler;
268112158Sdas    protected XMLDTDSource fDTDSource;
269112158Sdas
270112158Sdas    // for XIncludeHandler
271112158Sdas    protected XIncludeHandler fParentXIncludeHandler;
272112158Sdas
273112158Sdas    // for buffer size in XIncludeTextReader
274112158Sdas    protected int fBufferSize = XMLEntityManager.DEFAULT_BUFFER_SIZE;
275112158Sdas
276112158Sdas    // It "feels wrong" to store this value here.  However,
277112158Sdas    // calculating it can be time consuming, so we cache it.
278112158Sdas    // It's never going to change in the lifetime of this XIncludeHandler
279112158Sdas    protected String fParentRelativeURI;
280112158Sdas
281112158Sdas    // we cache the child parser configuration, so we don't have to re-create
282112158Sdas    // the objects when the parser is re-used
283112158Sdas    protected XMLParserConfiguration fChildConfig;
284112158Sdas
285112158Sdas    // The cached child parser configuration, may contain a
286112158Sdas    // XInclude or XPointer Handler.  Cache both these
287112158Sdas    protected XMLParserConfiguration fXIncludeChildConfig;
288112158Sdas    protected XMLParserConfiguration fXPointerChildConfig;
289112158Sdas
290112158Sdas    // The XPointerProcessor
291112158Sdas    protected XPointerProcessor fXPtrProcessor = null;
292112158Sdas
293112158Sdas    protected XMLLocator fDocLocation;
294112158Sdas    protected XIncludeMessageFormatter fXIncludeMessageFormatter = new XIncludeMessageFormatter();
295112158Sdas    protected XIncludeNamespaceSupport fNamespaceContext;
296112158Sdas    protected SymbolTable fSymbolTable;
297112158Sdas    protected XMLErrorReporter fErrorReporter;
298112158Sdas    protected XMLEntityResolver fEntityResolver;
299112158Sdas    protected XMLSecurityManager fSecurityManager;
300112158Sdas    protected XMLSecurityPropertyManager fSecurityPropertyMgr;
301112158Sdas
302112158Sdas    // these are needed for text include processing
303112158Sdas    protected XIncludeTextReader fXInclude10TextReader;
304112158Sdas    protected XIncludeTextReader fXInclude11TextReader;
305112158Sdas
306112158Sdas    // these are needed for XML Base processing
307112158Sdas    protected XMLResourceIdentifier fCurrentBaseURI;
308112158Sdas    protected IntStack fBaseURIScope;
309112158Sdas    protected Stack fBaseURI;
310112158Sdas    protected Stack fLiteralSystemID;
311112158Sdas    protected Stack fExpandedSystemID;
312112158Sdas
313112158Sdas    // these are needed for Language Fixup
314112158Sdas    protected IntStack fLanguageScope;
315112158Sdas    protected Stack fLanguageStack;
316112158Sdas    protected String fCurrentLanguage;
317112158Sdas
318112158Sdas    // used for passing features on to child XIncludeHandler objects
319112158Sdas    protected ParserConfigurationSettings fSettings;
320112158Sdas
321112158Sdas    // The current element depth.  We start at depth 0 (before we've reached any elements).
322112158Sdas    // The first element is at depth 1.
323112158Sdas    private int fDepth;
324112158Sdas
325112158Sdas    // The current element depth of the result infoset.
326112158Sdas    private int fResultDepth;
327112158Sdas
328112158Sdas    // this value must be at least 1
329112158Sdas    private static final int INITIAL_SIZE = 8;
330112158Sdas
331112158Sdas    // Used to ensure that fallbacks are always children of include elements,
332112158Sdas    // and that include elements are never children of other include elements.
333112158Sdas    // An index contains true if the ancestor of the current element which resides
334112158Sdas    // at that depth was an include element.
335112158Sdas    private boolean[] fSawInclude = new boolean[INITIAL_SIZE];
336112158Sdas
337112158Sdas    // Ensures that only one fallback element can be at a single depth.
338112158Sdas    // An index contains true if we have seen any fallback elements at that depth,
339112158Sdas    // and it is only reset to false when the end tag of the parent is encountered.
340112158Sdas    private boolean[] fSawFallback = new boolean[INITIAL_SIZE];
341112158Sdas
342112158Sdas    // The state of the processor at each given depth.
343112158Sdas    private int[] fState = new int[INITIAL_SIZE];
344112158Sdas
345112158Sdas    // buffering the necessary DTD events
346112158Sdas    private final ArrayList<Notation> fNotations;
347112158Sdas    private final ArrayList<UnparsedEntity> fUnparsedEntities;
348112158Sdas
349112158Sdas    // flags which control whether base URI or language fixup is performed.
350112158Sdas    private boolean fFixupBaseURIs = true;
351112158Sdas    private boolean fFixupLanguage = true;
352112158Sdas
353112158Sdas    // for SAX compatibility.
354112158Sdas    // Has the value of the ALLOW_UE_AND_NOTATION_EVENTS feature
355112158Sdas    private boolean fSendUEAndNotationEvents;
356112158Sdas
357112158Sdas    // track the version of the document being parsed
358112158Sdas    private boolean fIsXML11;
359112158Sdas
360112158Sdas    // track whether a DTD is being parsed
361112158Sdas    private boolean fInDTD;
362112158Sdas
363112158Sdas    // track whether the root element of the result infoset has been processed
364112158Sdas    private boolean fSeenRootElement;
365112158Sdas
366112158Sdas    // track whether the child config needs its features refreshed
367112158Sdas    private boolean fNeedCopyFeatures = true;
368112158Sdas
369112158Sdas    /** indicate whether Catalog should be used for resolving external resources */
370112158Sdas    private boolean fUseCatalog = true;
371112158Sdas    CatalogFeatures fCatalogFeatures;
372112158Sdas    CatalogResolver fCatalogResolver;
373112158Sdas
374112158Sdas    private String fCatalogFile;
375112158Sdas    private String fDefer;
376112158Sdas    private String fPrefer;
377112158Sdas    private String fResolve;
378112158Sdas
379112158Sdas    // Constructors
380112158Sdas
381112158Sdas    public XIncludeHandler() {
382112158Sdas        fDepth = 0;
383112158Sdas
384112158Sdas        fSawFallback[fDepth] = false;
385112158Sdas        fSawInclude[fDepth] = false;
386112158Sdas        fState[fDepth] = STATE_NORMAL_PROCESSING;
387112158Sdas        fNotations = new ArrayList<>();
388112158Sdas        fUnparsedEntities = new ArrayList<>();
389112158Sdas
390112158Sdas        fBaseURIScope = new IntStack();
391112158Sdas        fBaseURI = new Stack();
392112158Sdas        fLiteralSystemID = new Stack();
393112158Sdas        fExpandedSystemID = new Stack();
394112158Sdas        fCurrentBaseURI = new XMLResourceIdentifierImpl();
395112158Sdas
396112158Sdas        fLanguageScope = new IntStack();
397112158Sdas        fLanguageStack = new Stack();
398112158Sdas        fCurrentLanguage = null;
399112158Sdas    }
400112158Sdas
401112158Sdas    // XMLComponent methods
402112158Sdas
403112158Sdas    @Override
404112158Sdas    public void reset(XMLComponentManager componentManager)
405112158Sdas        throws XNIException {
406112158Sdas        fNamespaceContext = null;
407112158Sdas        fDepth = 0;
408112158Sdas        fResultDepth = isRootDocument() ? 0 : fParentXIncludeHandler.getResultDepth();
409112158Sdas        fNotations.clear();
410112158Sdas        fUnparsedEntities.clear();
411112158Sdas        fParentRelativeURI = null;
412112158Sdas        fIsXML11 = false;
413112158Sdas        fInDTD = false;
414112158Sdas        fSeenRootElement = false;
415112158Sdas
416112158Sdas        fBaseURIScope.clear();
417112158Sdas        fBaseURI.clear();
418112158Sdas        fLiteralSystemID.clear();
419112158Sdas        fExpandedSystemID.clear();
420112158Sdas        fLanguageScope.clear();
421112158Sdas        fLanguageStack.clear();
422112158Sdas
423112158Sdas        // REVISIT: Find a better method for maintaining
424112158Sdas        // the state of the XInclude processor. These arrays
425112158Sdas        // can potentially grow quite large. Cleaning them
426112158Sdas        // out on reset may be very time consuming. -- mrglavas
427112158Sdas        //
428112158Sdas        // clear the previous settings from the arrays
429112158Sdas        for (int i = 0; i < fState.length; ++i) {
430112158Sdas            fState[i] = STATE_NORMAL_PROCESSING;
431112158Sdas        }
432112158Sdas        for (int i = 0; i < fSawFallback.length; ++i) {
433112158Sdas            fSawFallback[i] = false;
434112158Sdas        }
435112158Sdas        for (int i = 0; i < fSawInclude.length; ++i) {
436112158Sdas            fSawInclude[i] = false;
437112158Sdas        }
438112158Sdas
439112158Sdas        try {
440112158Sdas            if (!componentManager.getFeature(PARSER_SETTINGS)) {
441112158Sdas                // if parser settings have not changed return.
442112158Sdas                return;
443112158Sdas            }
444112158Sdas        }
445112158Sdas        catch (XMLConfigurationException e) {}
446112158Sdas
447112158Sdas        // parser settings changed. Need to refresh features on child config.
448112158Sdas        fNeedCopyFeatures = true;
449112158Sdas
450112158Sdas        try {
451112158Sdas            fSendUEAndNotationEvents =
452112158Sdas                componentManager.getFeature(ALLOW_UE_AND_NOTATION_EVENTS);
453112158Sdas            if (fChildConfig != null) {
454112158Sdas                fChildConfig.setFeature(
455112158Sdas                    ALLOW_UE_AND_NOTATION_EVENTS,
456112158Sdas                    fSendUEAndNotationEvents);
457112158Sdas            }
458112158Sdas        }
459112158Sdas        catch (XMLConfigurationException e) {
460112158Sdas        }
461112158Sdas
462112158Sdas        try {
463112158Sdas            fFixupBaseURIs =
464112158Sdas                componentManager.getFeature(XINCLUDE_FIXUP_BASE_URIS);
465112158Sdas            if (fChildConfig != null) {
466112158Sdas                fChildConfig.setFeature(
467112158Sdas                    XINCLUDE_FIXUP_BASE_URIS,
468112158Sdas                    fFixupBaseURIs);
469112158Sdas            }
470112158Sdas        }
471112158Sdas        catch (XMLConfigurationException e) {
472112158Sdas            fFixupBaseURIs = true;
473112158Sdas        }
474112158Sdas
475112158Sdas        try {
476112158Sdas            fFixupLanguage =
477112158Sdas                componentManager.getFeature(XINCLUDE_FIXUP_LANGUAGE);
478112158Sdas            if (fChildConfig != null) {
479112158Sdas                fChildConfig.setFeature(
480112158Sdas                    XINCLUDE_FIXUP_LANGUAGE,
481112158Sdas                    fFixupLanguage);
482112158Sdas            }
483112158Sdas        }
484112158Sdas        catch (XMLConfigurationException e) {
485112158Sdas            fFixupLanguage = true;
486112158Sdas        }
487112158Sdas
488112158Sdas        // Get symbol table.
489112158Sdas        try {
490112158Sdas            SymbolTable value =
491112158Sdas                (SymbolTable)componentManager.getProperty(SYMBOL_TABLE);
492112158Sdas            if (value != null) {
493112158Sdas                fSymbolTable = value;
494112158Sdas                if (fChildConfig != null) {
495112158Sdas                    fChildConfig.setProperty(SYMBOL_TABLE, value);
496112158Sdas                }
497112158Sdas            }
498112158Sdas        }
499112158Sdas        catch (XMLConfigurationException e) {
500112158Sdas            fSymbolTable = null;
501112158Sdas        }
502112158Sdas
503112158Sdas        // Get error reporter.
504112158Sdas        try {
505112158Sdas            XMLErrorReporter value =
506112158Sdas                (XMLErrorReporter)componentManager.getProperty(ERROR_REPORTER);
507112158Sdas            if (value != null) {
508112158Sdas                setErrorReporter(value);
509112158Sdas                if (fChildConfig != null) {
510112158Sdas                    fChildConfig.setProperty(ERROR_REPORTER, value);
511112158Sdas                }
512112158Sdas            }
513112158Sdas        }
514112158Sdas        catch (XMLConfigurationException e) {
515112158Sdas            fErrorReporter = null;
516112158Sdas        }
517112158Sdas
518112158Sdas        // Get entity resolver.
519112158Sdas        try {
520112158Sdas            XMLEntityResolver value =
521112158Sdas                (XMLEntityResolver)componentManager.getProperty(
522112158Sdas                    ENTITY_RESOLVER);
523112158Sdas
524112158Sdas            if (value != null) {
525112158Sdas                fEntityResolver = value;
526112158Sdas                if (fChildConfig != null) {
527112158Sdas                    fChildConfig.setProperty(ENTITY_RESOLVER, value);
528112158Sdas                }
529112158Sdas            }
530112158Sdas        }
531112158Sdas        catch (XMLConfigurationException e) {
532112158Sdas            fEntityResolver = null;
533112158Sdas        }
534112158Sdas
535112158Sdas        // Get security manager.
536112158Sdas        try {
537112158Sdas            XMLSecurityManager value =
538112158Sdas                (XMLSecurityManager)componentManager.getProperty(
539112158Sdas                    SECURITY_MANAGER);
540112158Sdas
541112158Sdas            if (value != null) {
542112158Sdas                fSecurityManager = value;
543112158Sdas                if (fChildConfig != null) {
544112158Sdas                    fChildConfig.setProperty(SECURITY_MANAGER, value);
545112158Sdas                }
546112158Sdas            }
547112158Sdas        }
548112158Sdas        catch (XMLConfigurationException e) {
549112158Sdas            fSecurityManager = null;
550112158Sdas        }
551112158Sdas
552112158Sdas        fSecurityPropertyMgr = (XMLSecurityPropertyManager)
553112158Sdas                componentManager.getProperty(Constants.XML_SECURITY_PROPERTY_MANAGER);
554112158Sdas
555112158Sdas        //Use Catalog
556112158Sdas        fUseCatalog = componentManager.getFeature(XMLConstants.USE_CATALOG);
557112158Sdas        fCatalogFile = (String)componentManager.getProperty(CatalogFeatures.Feature.FILES.getPropertyName());
558112158Sdas        fDefer = (String)componentManager.getProperty(CatalogFeatures.Feature.DEFER.getPropertyName());
559112158Sdas        fPrefer = (String)componentManager.getProperty(CatalogFeatures.Feature.PREFER.getPropertyName());
560112158Sdas        fResolve = (String)componentManager.getProperty(CatalogFeatures.Feature.RESOLVE.getPropertyName());
561112158Sdas
562112158Sdas        // Get buffer size.
563112158Sdas        try {
564112158Sdas            Integer value =
565112158Sdas                (Integer)componentManager.getProperty(
566112158Sdas                    BUFFER_SIZE);
567112158Sdas
568112158Sdas            if (value != null && value > 0) {
569112158Sdas                fBufferSize = value;
570112158Sdas                if (fChildConfig != null) {
571112158Sdas                    fChildConfig.setProperty(BUFFER_SIZE, value);
572112158Sdas                }
573112158Sdas            }
574112158Sdas            else {
575112158Sdas                fBufferSize = ((Integer)getPropertyDefault(BUFFER_SIZE));
576112158Sdas            }
577112158Sdas        }
578112158Sdas        catch (XMLConfigurationException e) {
579112158Sdas            fBufferSize = ((Integer)getPropertyDefault(BUFFER_SIZE));
580112158Sdas        }
581112158Sdas
582112158Sdas        // Reset XML 1.0 text reader.
583112158Sdas        if (fXInclude10TextReader != null) {
584112158Sdas                fXInclude10TextReader.setBufferSize(fBufferSize);
585112158Sdas        }
586112158Sdas        // Reset XML 1.1 text reader.
587112158Sdas        if (fXInclude11TextReader != null) {
588112158Sdas            fXInclude11TextReader.setBufferSize(fBufferSize);
589112158Sdas        }
590112158Sdas
591112158Sdas        fSettings = new ParserConfigurationSettings();
592112158Sdas        copyFeatures(componentManager, fSettings);
593112158Sdas
594112158Sdas        // We don't want a schema validator on the new pipeline,
595112158Sdas        // so if it was enabled, we set the feature to false. If
596112158Sdas        // the validation feature was also enabled we turn on
597112158Sdas        // dynamic validation, so that DTD validation is performed
598112158Sdas        // on the included documents only if they have a DOCTYPE.
599112158Sdas        // This is consistent with the behaviour on the main pipeline.
600112158Sdas        try {
601112158Sdas            if (componentManager.getFeature(SCHEMA_VALIDATION)) {
602112158Sdas                fSettings.setFeature(SCHEMA_VALIDATION, false);
603112158Sdas                if (componentManager.getFeature(VALIDATION)) {
604112158Sdas                    fSettings.setFeature(DYNAMIC_VALIDATION, true);
605112158Sdas                }
606112158Sdas            }
607112158Sdas        }
608112158Sdas        catch (XMLConfigurationException e) {}
609112158Sdas
610112158Sdas        // Don't reset fChildConfig -- we don't want it to share the same components.
611112158Sdas        // It will be reset when it is actually used to parse something.
612112158Sdas    } // reset(XMLComponentManager)
613112158Sdas
614112158Sdas    /**
615112158Sdas     * Returns a list of feature identifiers that are recognized by
616112158Sdas     * this component. This method may return null if no features
617112158Sdas     * are recognized by this component.
618112158Sdas     */
619112158Sdas    @Override
620112158Sdas    public String[] getRecognizedFeatures() {
621112158Sdas        return (String[])(RECOGNIZED_FEATURES.clone());
622112158Sdas    } // getRecognizedFeatures():String[]
623112158Sdas
624112158Sdas    /**
625112158Sdas     * Sets the state of a feature. This method is called by the component
626112158Sdas     * manager any time after reset when a feature changes state.
627112158Sdas     * <p>
628112158Sdas     * <strong>Note:</strong> Components should silently ignore features
629112158Sdas     * that do not affect the operation of the component.
630112158Sdas     *
631112158Sdas     * @param featureId The feature identifier.
632112158Sdas     * @param state     The state of the feature.
633112158Sdas     *
634112158Sdas     * @throws SAXNotRecognizedException The component should not throw
635112158Sdas     *                                   this exception.
636112158Sdas     * @throws SAXNotSupportedException The component should not throw
637112158Sdas     *                                  this exception.
638112158Sdas     */
639112158Sdas    @Override
640112158Sdas    public void setFeature(String featureId, boolean state)
641112158Sdas        throws XMLConfigurationException {
642112158Sdas        if (featureId.equals(ALLOW_UE_AND_NOTATION_EVENTS)) {
643112158Sdas            fSendUEAndNotationEvents = state;
644112158Sdas        }
645112158Sdas        if (fSettings != null) {
646112158Sdas            fNeedCopyFeatures = true;
647112158Sdas            fSettings.setFeature(featureId, state);
648112158Sdas        }
649112158Sdas    } // setFeature(String,boolean)
650112158Sdas
651112158Sdas    /**
652112158Sdas     * Returns a list of property identifiers that are recognized by
653112158Sdas     * this component. This method may return null if no properties
654112158Sdas     * are recognized by this component.
655112158Sdas     */
656112158Sdas    @Override
657112158Sdas    public String[] getRecognizedProperties() {
658112158Sdas        return (String[])(RECOGNIZED_PROPERTIES.clone());
659112158Sdas    } // getRecognizedProperties():String[]
660112158Sdas
661112158Sdas    /**
662112158Sdas     * Sets the value of a property. This method is called by the component
663112158Sdas     * manager any time after reset when a property changes value.
664112158Sdas     * <p>
665112158Sdas     * <strong>Note:</strong> Components should silently ignore properties
666112158Sdas     * that do not affect the operation of the component.
667112158Sdas     *
668112158Sdas     * @param propertyId The property identifier.
669112158Sdas     * @param value      The value of the property.
670112158Sdas     *
671112158Sdas     * @throws SAXNotRecognizedException The component should not throw
672112158Sdas     *                                   this exception.
673112158Sdas     * @throws SAXNotSupportedException The component should not throw
674112158Sdas     *                                  this exception.
675112158Sdas     */
676112158Sdas    @Override
677112158Sdas    public void setProperty(String propertyId, Object value)
678112158Sdas        throws XMLConfigurationException {
679112158Sdas        if (propertyId.equals(SYMBOL_TABLE)) {
680112158Sdas            fSymbolTable = (SymbolTable)value;
681112158Sdas            if (fChildConfig != null) {
682112158Sdas                fChildConfig.setProperty(propertyId, value);
683112158Sdas            }
684112158Sdas            return;
685112158Sdas        }
686112158Sdas        if (propertyId.equals(ERROR_REPORTER)) {
687112158Sdas            setErrorReporter((XMLErrorReporter)value);
688112158Sdas            if (fChildConfig != null) {
689112158Sdas                fChildConfig.setProperty(propertyId, value);
690112158Sdas            }
691112158Sdas            return;
692112158Sdas        }
693112158Sdas        if (propertyId.equals(ENTITY_RESOLVER)) {
694112158Sdas            fEntityResolver = (XMLEntityResolver)value;
695112158Sdas            if (fChildConfig != null) {
696112158Sdas                fChildConfig.setProperty(propertyId, value);
697112158Sdas            }
698112158Sdas            return;
699112158Sdas        }
700112158Sdas        if (propertyId.equals(SECURITY_MANAGER)) {
701112158Sdas            fSecurityManager = (XMLSecurityManager)value;
702112158Sdas            if (fChildConfig != null) {
703112158Sdas                fChildConfig.setProperty(propertyId, value);
704112158Sdas            }
705112158Sdas            return;
706112158Sdas        }
707112158Sdas        if (propertyId.equals(XML_SECURITY_PROPERTY_MANAGER)) {
708112158Sdas            fSecurityPropertyMgr = (XMLSecurityPropertyManager)value;
709112158Sdas
710112158Sdas            if (fChildConfig != null) {
711112158Sdas                fChildConfig.setProperty(XML_SECURITY_PROPERTY_MANAGER, value);
712112158Sdas            }
713112158Sdas
714112158Sdas            return;
715112158Sdas        }
716112158Sdas
717112158Sdas        if (propertyId.equals(BUFFER_SIZE)) {
718112158Sdas            Integer bufferSize = (Integer) value;
719112158Sdas            if (fChildConfig != null) {
720112158Sdas                fChildConfig.setProperty(propertyId, value);
721112158Sdas            }
722112158Sdas            if (bufferSize != null && bufferSize.intValue() > 0) {
723112158Sdas                fBufferSize = bufferSize.intValue();
724112158Sdas                // Reset XML 1.0 text reader.
725112158Sdas                if (fXInclude10TextReader != null) {
726112158Sdas                    fXInclude10TextReader.setBufferSize(fBufferSize);
727112158Sdas                }
728112158Sdas                // Reset XML 1.1 text reader.
729112158Sdas                if (fXInclude11TextReader != null) {
730112158Sdas                    fXInclude11TextReader.setBufferSize(fBufferSize);
731112158Sdas                }
732112158Sdas            }
733112158Sdas            return;
734112158Sdas        }
735112158Sdas
736112158Sdas    } // setProperty(String,Object)
737112158Sdas
738112158Sdas    /**
739112158Sdas     * Returns the default state for a feature, or null if this
740112158Sdas     * component does not want to report a default value for this
741112158Sdas     * feature.
742112158Sdas     *
743112158Sdas     * @param featureId The feature identifier.
744112158Sdas     *
745112158Sdas     * @since Xerces 2.2.0
746112158Sdas     */
747112158Sdas    @Override
748112158Sdas    public Boolean getFeatureDefault(String featureId) {
749112158Sdas        for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
750112158Sdas            if (RECOGNIZED_FEATURES[i].equals(featureId)) {
751112158Sdas                return FEATURE_DEFAULTS[i];
752112158Sdas            }
753112158Sdas        }
754112158Sdas        return null;
755112158Sdas    } // getFeatureDefault(String):Boolean
756112158Sdas
757112158Sdas    /**
758112158Sdas     * Returns the default state for a property, or null if this
759112158Sdas     * component does not want to report a default value for this
760112158Sdas     * property.
761112158Sdas     *
762112158Sdas     * @param propertyId The property identifier.
763112158Sdas     *
764112158Sdas     * @since Xerces 2.2.0
765112158Sdas     */
766112158Sdas    @Override
767112158Sdas    public Object getPropertyDefault(String propertyId) {
768112158Sdas        for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
769112158Sdas            if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
770112158Sdas                return PROPERTY_DEFAULTS[i];
771112158Sdas            }
772112158Sdas        }
773112158Sdas        return null;
774112158Sdas    } // getPropertyDefault(String):Object
775112158Sdas
776112158Sdas    @Override
777112158Sdas    public void setDocumentHandler(XMLDocumentHandler handler) {
778112158Sdas        fDocumentHandler = handler;
779112158Sdas    }
780112158Sdas
781112158Sdas    @Override
782112158Sdas    public XMLDocumentHandler getDocumentHandler() {
783112158Sdas        return fDocumentHandler;
784112158Sdas    }
785112158Sdas
786112158Sdas    // XMLDocumentHandler methods
787112158Sdas
788112158Sdas    /**
789112158Sdas     * Event sent at the start of the document.
790112158Sdas     *
791112158Sdas     * A fatal error will occur here, if it is detected that this document has been processed
792112158Sdas     * before.
793112158Sdas     *
794112158Sdas     * This event is only passed on to the document handler if this is the root document.
795112158Sdas     */
796112158Sdas    @Override
797112158Sdas    public void startDocument(
798112158Sdas        XMLLocator locator,
799112158Sdas        String encoding,
800112158Sdas        NamespaceContext namespaceContext,
801112158Sdas        Augmentations augs)
802112158Sdas        throws XNIException {
803112158Sdas
804112158Sdas        // we do this to ensure that the proper location is reported in errors
805112158Sdas        // otherwise, the locator from the root document would always be used
806112158Sdas        fErrorReporter.setDocumentLocator(locator);
807112158Sdas
808112158Sdas        if (!isRootDocument()
809112158Sdas            && fParentXIncludeHandler.searchForRecursiveIncludes(locator)) {
810112158Sdas            reportFatalError(
811112158Sdas                "RecursiveInclude",
812112158Sdas                new Object[] { locator.getExpandedSystemId()});
813112158Sdas        }
814112158Sdas
815112158Sdas        if (!(namespaceContext instanceof XIncludeNamespaceSupport)) {
816112158Sdas            reportFatalError("IncompatibleNamespaceContext");
817112158Sdas        }
818112158Sdas        fNamespaceContext = (XIncludeNamespaceSupport)namespaceContext;
819112158Sdas        fDocLocation = locator;
820112158Sdas
821112158Sdas        // initialize the current base URI
822112158Sdas        fCurrentBaseURI.setBaseSystemId(locator.getBaseSystemId());
823112158Sdas        fCurrentBaseURI.setExpandedSystemId(locator.getExpandedSystemId());
824112158Sdas        fCurrentBaseURI.setLiteralSystemId(locator.getLiteralSystemId());
825112158Sdas        saveBaseURI();
826112158Sdas        if (augs == null) {
827112158Sdas            augs = new AugmentationsImpl();
828112158Sdas        }
829112158Sdas        augs.putItem(CURRENT_BASE_URI, fCurrentBaseURI);
830112158Sdas
831112158Sdas        // initialize the current language
832112158Sdas        fCurrentLanguage = XMLSymbols.EMPTY_STRING;
833112158Sdas        saveLanguage(fCurrentLanguage);
834112158Sdas
835112158Sdas        if (isRootDocument() && fDocumentHandler != null) {
836112158Sdas            fDocumentHandler.startDocument(
837112158Sdas                locator,
838112158Sdas                encoding,
839112158Sdas                namespaceContext,
840112158Sdas                augs);
841112158Sdas        }
842112158Sdas    }
843112158Sdas
844112158Sdas    @Override
845112158Sdas    public void xmlDecl(
846112158Sdas        String version,
847112158Sdas        String encoding,
848112158Sdas        String standalone,
849112158Sdas        Augmentations augs)
850112158Sdas        throws XNIException {
851112158Sdas        fIsXML11 = "1.1".equals(version);
852112158Sdas        if (isRootDocument() && fDocumentHandler != null) {
853112158Sdas            fDocumentHandler.xmlDecl(version, encoding, standalone, augs);
854112158Sdas        }
855112158Sdas    }
856112158Sdas
857112158Sdas    @Override
858112158Sdas    public void doctypeDecl(
859112158Sdas        String rootElement,
860112158Sdas        String publicId,
861112158Sdas        String systemId,
862112158Sdas        Augmentations augs)
863112158Sdas        throws XNIException {
864112158Sdas        if (isRootDocument() && fDocumentHandler != null) {
865112158Sdas            fDocumentHandler.doctypeDecl(rootElement, publicId, systemId, augs);
866112158Sdas        }
867112158Sdas    }
868112158Sdas
869112158Sdas    @Override
870112158Sdas    public void comment(XMLString text, Augmentations augs)
871112158Sdas        throws XNIException {
872112158Sdas        if (!fInDTD) {
873112158Sdas            if (fDocumentHandler != null
874112158Sdas                && getState() == STATE_NORMAL_PROCESSING) {
875112158Sdas                fDepth++;
876112158Sdas                augs = modifyAugmentations(augs);
877112158Sdas                fDocumentHandler.comment(text, augs);
878112158Sdas                fDepth--;
879112158Sdas            }
880112158Sdas        }
881112158Sdas        else if (fDTDHandler != null) {
882112158Sdas            fDTDHandler.comment(text, augs);
883112158Sdas        }
884112158Sdas    }
885112158Sdas
886112158Sdas    @Override
887112158Sdas    public void processingInstruction(
888112158Sdas        String target,
889112158Sdas        XMLString data,
890112158Sdas        Augmentations augs)
891112158Sdas        throws XNIException {
892112158Sdas        if (!fInDTD) {
893112158Sdas            if (fDocumentHandler != null
894112158Sdas                && getState() == STATE_NORMAL_PROCESSING) {
895112158Sdas                // we need to change the depth like this so that modifyAugmentations() works
896112158Sdas                fDepth++;
897112158Sdas                augs = modifyAugmentations(augs);
898112158Sdas                fDocumentHandler.processingInstruction(target, data, augs);
899112158Sdas                fDepth--;
900112158Sdas            }
901112158Sdas        }
902112158Sdas        else if (fDTDHandler != null) {
903112158Sdas            fDTDHandler.processingInstruction(target, data, augs);
904112158Sdas        }
905112158Sdas    }
906112158Sdas
907112158Sdas    @Override
908112158Sdas    public void startElement(
909112158Sdas        QName element,
910112158Sdas        XMLAttributes attributes,
911112158Sdas        Augmentations augs)
912112158Sdas        throws XNIException {
913112158Sdas        fDepth++;
914112158Sdas        int lastState = getState(fDepth - 1);
915112158Sdas        // If the last two states were fallback then this must be a descendant of an include
916112158Sdas        // child which isn't a fallback. The specification says we should ignore such elements
917112158Sdas        // and their children.
918112158Sdas        if (lastState == STATE_EXPECT_FALLBACK && getState(fDepth - 2) == STATE_EXPECT_FALLBACK) {
919112158Sdas            setState(STATE_IGNORE);
920112158Sdas        }
921112158Sdas        else {
922112158Sdas            setState(lastState);
923112158Sdas        }
924112158Sdas
925112158Sdas        // we process the xml:base and xml:lang attributes regardless
926112158Sdas        // of what type of element it is.
927112158Sdas        processXMLBaseAttributes(attributes);
928112158Sdas        if (fFixupLanguage) {
929112158Sdas            processXMLLangAttributes(attributes);
930112158Sdas        }
931112158Sdas
932112158Sdas        if (isIncludeElement(element)) {
933112158Sdas            boolean success = this.handleIncludeElement(attributes);
934112158Sdas            if (success) {
935112158Sdas                setState(STATE_IGNORE);
936112158Sdas            }
937112158Sdas            else {
938112158Sdas                setState(STATE_EXPECT_FALLBACK);
939112158Sdas            }
940112158Sdas        }
941112158Sdas        else if (isFallbackElement(element)) {
942112158Sdas            this.handleFallbackElement();
943112158Sdas        }
944112158Sdas        else if (hasXIncludeNamespace(element)) {
945112158Sdas            if (getSawInclude(fDepth - 1)) {
946112158Sdas                reportFatalError(
947112158Sdas                    "IncludeChild",
948112158Sdas                    new Object[] { element.rawname });
949112158Sdas            }
950112158Sdas            if (getSawFallback(fDepth - 1)) {
951112158Sdas                reportFatalError(
952112158Sdas                    "FallbackChild",
953112158Sdas                    new Object[] { element.rawname });
954112158Sdas            }
955112158Sdas            if (getState() == STATE_NORMAL_PROCESSING) {
956112158Sdas                if (fResultDepth++ == 0) {
957112158Sdas                    checkMultipleRootElements();
958112158Sdas                }
959112158Sdas                if (fDocumentHandler != null) {
960112158Sdas                    augs = modifyAugmentations(augs);
961112158Sdas                    attributes = processAttributes(attributes);
962112158Sdas                    fDocumentHandler.startElement(element, attributes, augs);
963112158Sdas                }
964112158Sdas            }
965112158Sdas        }
966112158Sdas        else if (getState() == STATE_NORMAL_PROCESSING) {
967112158Sdas            if (fResultDepth++ == 0) {
968112158Sdas                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) {
1628                    if (fCatalogFeatures == null) {
1629                        fCatalogFeatures = JdkXmlUtils.getCatalogFeatures(fDefer, fCatalogFile, fPrefer, fResolve);
1630                    }
1631                    fCatalogFile = fCatalogFeatures.get(CatalogFeatures.Feature.FILES);
1632                    if (fUseCatalog && 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                String parserName = XINCLUDE_DEFAULT_CONFIGURATION;
1694                if (xpointer != null)
1695                        parserName = "com.sun.org.apache.xerces.internal.parsers.XPointerParserConfiguration";
1696
1697                fChildConfig =
1698                    (XMLParserConfiguration)ObjectFactory.newInstance(
1699                        parserName,
1700                        true);
1701
1702                // use the same symbol table, error reporter, entity resolver, security manager and buffer size.
1703                if (fSymbolTable != null) fChildConfig.setProperty(SYMBOL_TABLE, fSymbolTable);
1704                if (fErrorReporter != null) fChildConfig.setProperty(ERROR_REPORTER, fErrorReporter);
1705                if (fEntityResolver != null) fChildConfig.setProperty(ENTITY_RESOLVER, fEntityResolver);
1706                fChildConfig.setProperty(SECURITY_MANAGER, fSecurityManager);
1707                fChildConfig.setProperty(XML_SECURITY_PROPERTY_MANAGER, fSecurityPropertyMgr);
1708                fChildConfig.setProperty(BUFFER_SIZE, new Integer(fBufferSize));
1709
1710                // features must be copied to child configuration
1711                fNeedCopyFeatures = true;
1712
1713                // use the same namespace context
1714                fChildConfig.setProperty(
1715                    Constants.XERCES_PROPERTY_PREFIX
1716                        + Constants.NAMESPACE_CONTEXT_PROPERTY,
1717                    fNamespaceContext);
1718
1719                fChildConfig.setFeature(
1720                            XINCLUDE_FIXUP_BASE_URIS,
1721                            fFixupBaseURIs);
1722
1723                fChildConfig.setFeature(
1724                            XINCLUDE_FIXUP_LANGUAGE,
1725                            fFixupLanguage);
1726
1727
1728                // If the xpointer attribute is present
1729                if (xpointer != null ) {
1730
1731                    XPointerHandler newHandler =
1732                        (XPointerHandler)fChildConfig.getProperty(
1733                            Constants.XERCES_PROPERTY_PREFIX
1734                                + Constants.XPOINTER_HANDLER_PROPERTY);
1735
1736                        fXPtrProcessor = newHandler;
1737
1738                        // ???
1739                        ((XPointerHandler)fXPtrProcessor).setProperty(
1740                            Constants.XERCES_PROPERTY_PREFIX
1741                            + Constants.NAMESPACE_CONTEXT_PROPERTY,
1742                        fNamespaceContext);
1743
1744                    ((XPointerHandler)fXPtrProcessor).setProperty(XINCLUDE_FIXUP_BASE_URIS,
1745                            fFixupBaseURIs);
1746
1747                    ((XPointerHandler)fXPtrProcessor).setProperty(
1748                            XINCLUDE_FIXUP_LANGUAGE, fFixupLanguage);
1749
1750                    if (fErrorReporter != null)
1751                        ((XPointerHandler)fXPtrProcessor).setProperty(ERROR_REPORTER, fErrorReporter);
1752                        // ???
1753
1754                    newHandler.setParent(this);
1755                    newHandler.setDocumentHandler(this.getDocumentHandler());
1756                    fXPointerChildConfig = fChildConfig;
1757                } else {
1758                    XIncludeHandler newHandler =
1759                        (XIncludeHandler)fChildConfig.getProperty(
1760                            Constants.XERCES_PROPERTY_PREFIX
1761                                + Constants.XINCLUDE_HANDLER_PROPERTY);
1762
1763                        newHandler.setParent(this);
1764                    newHandler.setDocumentHandler(this.getDocumentHandler());
1765                    fXIncludeChildConfig = fChildConfig;
1766                }
1767            }
1768
1769            // If an xpointer attribute is present
1770            if (xpointer != null ) {
1771                fChildConfig = fXPointerChildConfig ;
1772
1773                // Parse the XPointer expression
1774                try {
1775                    ((XPointerProcessor)fXPtrProcessor).parseXPointer(xpointer);
1776
1777                } catch (XNIException ex) {
1778                    // report the XPointer error as a resource error
1779                    reportResourceError(
1780                            "XMLResourceError",
1781                            new Object[] { href, ex.getMessage()});
1782                        return false;
1783                }
1784            } else {
1785                fChildConfig = fXIncludeChildConfig;
1786            }
1787
1788            // set all features on parserConfig to match this parser configuration
1789            if (fNeedCopyFeatures) {
1790                copyFeatures(fSettings, fChildConfig);
1791            }
1792            fNeedCopyFeatures = false;
1793
1794            try {
1795                fNamespaceContext.pushScope();
1796
1797                fChildConfig.parse(includedSource);
1798                // necessary to make sure proper location is reported in errors
1799                if (fErrorReporter != null) {
1800                    fErrorReporter.setDocumentLocator(fDocLocation);
1801                }
1802
1803                // If the xpointer attribute is present
1804                if (xpointer != null ) {
1805                        // and it was not resolved
1806                        if (!((XPointerProcessor)fXPtrProcessor).isXPointerResolved()) {
1807                        Locale locale = (fErrorReporter != null) ? fErrorReporter.getLocale() : null;
1808                        String reason = fXIncludeMessageFormatter.formatMessage(locale, "XPointerResolutionUnsuccessful", null);
1809                        reportResourceError("XMLResourceError", new Object[] {href, reason});
1810                                // use the fallback
1811                                return false;
1812                        }
1813                }
1814            }
1815            catch (XNIException e) {
1816                // necessary to make sure proper location is reported in errors
1817                if (fErrorReporter != null) {
1818                    fErrorReporter.setDocumentLocator(fDocLocation);
1819                }
1820                reportFatalError("XMLParseError", new Object[] { href, e.getMessage() });
1821            }
1822            catch (IOException e) {
1823                // necessary to make sure proper location is reported in errors
1824                if (fErrorReporter != null) {
1825                    fErrorReporter.setDocumentLocator(fDocLocation);
1826                }
1827                // An IOException indicates that we had trouble reading the file, not
1828                // that it was an invalid XML file.  So we send a resource error, not a
1829                // fatal error.
1830                reportResourceError(
1831                    "XMLResourceError",
1832                    new Object[] { href, e.getMessage()});
1833                return false;
1834            }
1835            finally {
1836                fNamespaceContext.popScope();
1837            }
1838        }
1839        else if (parse.equals(XINCLUDE_PARSE_TEXT)) {
1840            // we only care about encoding for parse="text"
1841            String encoding = attributes.getValue(XINCLUDE_ATTR_ENCODING);
1842            includedSource.setEncoding(encoding);
1843            XIncludeTextReader textReader = null;
1844
1845            try {
1846                // Setup the appropriate text reader.
1847                if (!fIsXML11) {
1848                    if (fXInclude10TextReader == null) {
1849                        fXInclude10TextReader = new XIncludeTextReader(includedSource, this, fBufferSize);
1850                    }
1851                    else {
1852                        fXInclude10TextReader.setInputSource(includedSource);
1853                    }
1854                    textReader = fXInclude10TextReader;
1855                }
1856                else {
1857                    if (fXInclude11TextReader == null) {
1858                        fXInclude11TextReader = new XInclude11TextReader(includedSource, this, fBufferSize);
1859                    }
1860                    else {
1861                        fXInclude11TextReader.setInputSource(includedSource);
1862                    }
1863                    textReader = fXInclude11TextReader;
1864                }
1865                textReader.setErrorReporter(fErrorReporter);
1866                textReader.parse();
1867            }
1868            // encoding errors
1869            catch (MalformedByteSequenceException ex) {
1870                fErrorReporter.reportError(ex.getDomain(), ex.getKey(),
1871                    ex.getArguments(), XMLErrorReporter.SEVERITY_FATAL_ERROR);
1872            }
1873            catch (CharConversionException e) {
1874                fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1875                    "CharConversionFailure", null, XMLErrorReporter.SEVERITY_FATAL_ERROR);
1876            }
1877            catch (IOException e) {
1878                reportResourceError(
1879                    "TextResourceError",
1880                    new Object[] { href, e.getMessage()});
1881                return false;
1882            }
1883            finally {
1884                if (textReader != null) {
1885                    try {
1886                        textReader.close();
1887                    }
1888                    catch (IOException e) {
1889                        reportResourceError(
1890                            "TextResourceError",
1891                            new Object[] { href, e.getMessage()});
1892                        return false;
1893                    }
1894                }
1895            }
1896        }
1897        else {
1898            reportFatalError("InvalidParseValue", new Object[] { parse });
1899        }
1900        return true;
1901    }
1902
1903    /**
1904     * Returns true if the element has the namespace "http://www.w3.org/2001/XInclude"
1905     * @param element the element to check
1906     * @return true if the element has the namespace "http://www.w3.org/2001/XInclude"
1907     */
1908    protected boolean hasXIncludeNamespace(QName element) {
1909        // REVISIT: The namespace of this element should be bound
1910        // already. Why are we looking it up from the namespace
1911        // context? -- mrglavas
1912        return element.uri == XINCLUDE_NS_URI
1913            || fNamespaceContext.getURI(element.prefix) == XINCLUDE_NS_URI;
1914    }
1915
1916    /**
1917     * Checks if the element is an &lt;include&gt; element.  The element must have
1918     * the XInclude namespace, and a local name of "include".
1919     *
1920     * @param element the element to check
1921     * @return true if the element is an &lt;include&gt; element
1922     * @see #hasXIncludeNamespace(QName)
1923     */
1924    protected boolean isIncludeElement(QName element) {
1925        return element.localpart.equals(XINCLUDE_INCLUDE) &&
1926            hasXIncludeNamespace(element);
1927    }
1928
1929    /**
1930     * Checks if the element is an &lt;fallback&gt; element.  The element must have
1931     * the XInclude namespace, and a local name of "fallback".
1932     *
1933     * @param element the element to check
1934     * @return true if the element is an &lt;fallback; element
1935     * @see #hasXIncludeNamespace(QName)
1936     */
1937    protected boolean isFallbackElement(QName element) {
1938        return element.localpart.equals(XINCLUDE_FALLBACK) &&
1939            hasXIncludeNamespace(element);
1940    }
1941
1942    /**
1943     * Returns true if the current [base URI] is the same as the [base URI] that
1944     * was in effect on the include parent.  This method should <em>only</em> be called
1945     * when the current element is a top level included element, i.e. the direct child
1946     * of a fallback element, or the root elements in an included document.
1947     * The "include parent" is the element which, in the result infoset, will be the
1948     * direct parent of the current element.
1949     * @return true if the [base URIs] are the same string
1950     */
1951    protected boolean sameBaseURIAsIncludeParent() {
1952        String parentBaseURI = getIncludeParentBaseURI();
1953        String baseURI = fCurrentBaseURI.getExpandedSystemId();
1954        // REVISIT: should we use File#sameFile() ?
1955        //          I think the benefit of using it is that it resolves host names
1956        //          instead of just doing a string comparison.
1957        // TODO: [base URI] is still an open issue with the working group.
1958        //       They're deciding if xml:base should be added if the [base URI] is different in terms
1959        //       of resolving relative references, or if it should be added if they are different at all.
1960        //       Revisit this after a final decision has been made.
1961        //       The decision also affects whether we output the file name of the URI, or just the path.
1962        return parentBaseURI != null && parentBaseURI.equals(baseURI);
1963    }
1964
1965    /**
1966     * Returns true if the current [language] is equivalent to the [language] that
1967     * was in effect on the include parent, taking case-insensitivity into account
1968     * as per [RFC 3066].  This method should <em>only</em> be called when the
1969     * current element is a top level included element, i.e. the direct child
1970     * of a fallback element, or the root elements in an included document.
1971     * The "include parent" is the element which, in the result infoset, will be the
1972     * direct parent of the current element.
1973     *
1974     * @return true if the [language] properties have the same value
1975     * taking case-insensitivity into account as per [RFC 3066].
1976     */
1977    protected boolean sameLanguageAsIncludeParent() {
1978        String parentLanguage = getIncludeParentLanguage();
1979        return parentLanguage != null && parentLanguage.equalsIgnoreCase(fCurrentLanguage);
1980    }
1981
1982    /**
1983     * Checks if the file indicated by the given XMLLocator has already been included
1984     * in the current stack.
1985     * @param includedSource the source to check for inclusion
1986     * @return true if the source has already been included
1987     */
1988    protected boolean searchForRecursiveIncludes(XMLLocator includedSource) {
1989        String includedSystemId = includedSource.getExpandedSystemId();
1990
1991        if (includedSystemId == null) {
1992            try {
1993                includedSystemId =
1994                    XMLEntityManager.expandSystemId(
1995                        includedSource.getLiteralSystemId(),
1996                        includedSource.getBaseSystemId(),
1997                        false);
1998            }
1999            catch (MalformedURIException e) {
2000                reportFatalError("ExpandedSystemId");
2001            }
2002        }
2003
2004        if (includedSystemId.equals(fCurrentBaseURI.getExpandedSystemId())) {
2005            return true;
2006        }
2007
2008        if (fParentXIncludeHandler == null) {
2009            return false;
2010        }
2011        return fParentXIncludeHandler.searchForRecursiveIncludes(
2012            includedSource);
2013    }
2014
2015    /**
2016     * Returns true if the current element is a top level included item.  This means
2017     * it's either the child of a fallback element, or the top level item in an
2018     * included document
2019     * @return true if the current element is a top level included item
2020     */
2021    protected boolean isTopLevelIncludedItem() {
2022        return isTopLevelIncludedItemViaInclude()
2023            || isTopLevelIncludedItemViaFallback();
2024    }
2025
2026    protected boolean isTopLevelIncludedItemViaInclude() {
2027        return fDepth == 1 && !isRootDocument();
2028    }
2029
2030    protected boolean isTopLevelIncludedItemViaFallback() {
2031        // Technically, this doesn't check if the parent was a fallback, it also
2032        // would return true if any of the parent's sibling elements were fallbacks.
2033        // However, this doesn't matter, since we will always be ignoring elements
2034        // whose parent's siblings were fallbacks.
2035        return getSawFallback(fDepth - 1);
2036    }
2037
2038    /**
2039     * Processes the XMLAttributes object of startElement() calls.  Performs the following tasks:
2040     * <ul>
2041     * <li> If the element is a top level included item whose [base URI] is different from the
2042     * [base URI] of the include parent, then an xml:base attribute is added to specify the
2043     * true [base URI]
2044     * <li> For all namespace prefixes which are in-scope in an included item, but not in scope
2045     * in the include parent, a xmlns:prefix attribute is added
2046     * <li> For all attributes with a type of ENTITY, ENTITIES or NOTATIONS, the notations and
2047     * unparsed entities are processed as described in the spec, sections 4.5.1 and 4.5.2
2048     * </ul>
2049     * @param attributes
2050     * @return
2051     */
2052    protected XMLAttributes processAttributes(XMLAttributes attributes) {
2053        if (isTopLevelIncludedItem()) {
2054            // Modify attributes to fix the base URI (spec 4.5.5).
2055            // We only do it to top level included elements, which have a different
2056            // base URI than their include parent.
2057            if (fFixupBaseURIs && !sameBaseURIAsIncludeParent()) {
2058                if (attributes == null) {
2059                    attributes = new XMLAttributesImpl();
2060                }
2061
2062                // This causes errors with schema validation, if the schema doesn't
2063                // specify that these elements can have an xml:base attribute
2064                String uri = null;
2065                try {
2066                    uri = this.getRelativeBaseURI();
2067                }
2068                catch (MalformedURIException e) {
2069                    // this shouldn't ever happen, since by definition, we had to traverse
2070                    // the same URIs to even get to this place
2071                    uri = fCurrentBaseURI.getExpandedSystemId();
2072                }
2073                int index =
2074                    attributes.addAttribute(
2075                        XML_BASE_QNAME,
2076                        XMLSymbols.fCDATASymbol,
2077                        uri);
2078                attributes.setSpecified(index, true);
2079            }
2080
2081            // Modify attributes to perform language-fixup (spec 4.5.6).
2082            // We only do it to top level included elements, which have a different
2083            // [language] than their include parent.
2084            if (fFixupLanguage && !sameLanguageAsIncludeParent()) {
2085                if (attributes == null) {
2086                    attributes = new XMLAttributesImpl();
2087                }
2088                int index =
2089                    attributes.addAttribute(
2090                        XML_LANG_QNAME,
2091                        XMLSymbols.fCDATASymbol,
2092                        fCurrentLanguage);
2093                attributes.setSpecified(index, true);
2094            }
2095
2096            // Modify attributes of included items to do namespace-fixup. (spec 4.5.4)
2097            Enumeration inscopeNS = fNamespaceContext.getAllPrefixes();
2098            while (inscopeNS.hasMoreElements()) {
2099                String prefix = (String)inscopeNS.nextElement();
2100                String parentURI =
2101                    fNamespaceContext.getURIFromIncludeParent(prefix);
2102                String uri = fNamespaceContext.getURI(prefix);
2103                if (parentURI != uri && attributes != null) {
2104                    if (prefix == XMLSymbols.EMPTY_STRING) {
2105                        if (attributes
2106                            .getValue(
2107                                NamespaceContext.XMLNS_URI,
2108                                XMLSymbols.PREFIX_XMLNS)
2109                            == null) {
2110                            if (attributes == null) {
2111                                attributes = new XMLAttributesImpl();
2112                            }
2113
2114                            QName ns = (QName)NEW_NS_ATTR_QNAME.clone();
2115                            ns.prefix = null;
2116                            ns.localpart = XMLSymbols.PREFIX_XMLNS;
2117                            ns.rawname = XMLSymbols.PREFIX_XMLNS;
2118                            int index =
2119                                attributes.addAttribute(
2120                                    ns,
2121                                    XMLSymbols.fCDATASymbol,
2122                                    uri != null ? uri : XMLSymbols.EMPTY_STRING);
2123                            attributes.setSpecified(index, true);
2124                            // Need to re-declare this prefix in the current context
2125                            // in order for the SAX parser to report the appropriate
2126                            // start and end prefix mapping events. -- mrglavas
2127                            fNamespaceContext.declarePrefix(prefix, uri);
2128                        }
2129                    }
2130                    else if (
2131                        attributes.getValue(NamespaceContext.XMLNS_URI, prefix)
2132                            == null) {
2133                        if (attributes == null) {
2134                            attributes = new XMLAttributesImpl();
2135                        }
2136
2137                        QName ns = (QName)NEW_NS_ATTR_QNAME.clone();
2138                        ns.localpart = prefix;
2139                        ns.rawname += prefix;
2140                        ns.rawname = (fSymbolTable != null) ?
2141                            fSymbolTable.addSymbol(ns.rawname) :
2142                            ns.rawname.intern();
2143                        int index =
2144                            attributes.addAttribute(
2145                                ns,
2146                                XMLSymbols.fCDATASymbol,
2147                                uri != null ? uri : XMLSymbols.EMPTY_STRING);
2148                        attributes.setSpecified(index, true);
2149                        // Need to re-declare this prefix in the current context
2150                        // in order for the SAX parser to report the appropriate
2151                        // start and end prefix mapping events. -- mrglavas
2152                        fNamespaceContext.declarePrefix(prefix, uri);
2153                    }
2154                }
2155            }
2156        }
2157
2158        if (attributes != null) {
2159            int length = attributes.getLength();
2160            for (int i = 0; i < length; i++) {
2161                String type = attributes.getType(i);
2162                String value = attributes.getValue(i);
2163                if (type == XMLSymbols.fENTITYSymbol) {
2164                    this.checkUnparsedEntity(value);
2165                }
2166                if (type == XMLSymbols.fENTITIESSymbol) {
2167                    // 4.5.1 - Unparsed Entities
2168                    StringTokenizer st = new StringTokenizer(value);
2169                    while (st.hasMoreTokens()) {
2170                        String entName = st.nextToken();
2171                        this.checkUnparsedEntity(entName);
2172                    }
2173                }
2174                else if (type == XMLSymbols.fNOTATIONSymbol) {
2175                    // 4.5.2 - Notations
2176                    this.checkNotation(value);
2177                }
2178                /* We actually don't need to do anything for 4.5.3, because at this stage the
2179                 * value of the attribute is just a string. It will be taken care of later
2180                 * in the pipeline, when the IDREFs are actually resolved against IDs.
2181                 *
2182                 * if (type == XMLSymbols.fIDREFSymbol || type == XMLSymbols.fIDREFSSymbol) { }
2183                 */
2184            }
2185        }
2186
2187        return attributes;
2188    }
2189
2190    /**
2191     * Returns a URI, relative to the include parent's base URI, of the current
2192     * [base URI].  For instance, if the current [base URI] was "dir1/dir2/file.xml"
2193     * and the include parent's [base URI] was "dir/", this would return "dir2/file.xml".
2194     * @return the relative URI
2195     */
2196    protected String getRelativeBaseURI() throws MalformedURIException {
2197        int includeParentDepth = getIncludeParentDepth();
2198        String relativeURI = this.getRelativeURI(includeParentDepth);
2199        if (isRootDocument()) {
2200            return relativeURI;
2201        }
2202        else {
2203            if (relativeURI.equals("")) {
2204                relativeURI = fCurrentBaseURI.getLiteralSystemId();
2205            }
2206
2207            if (includeParentDepth == 0) {
2208                if (fParentRelativeURI == null) {
2209                    fParentRelativeURI =
2210                        fParentXIncludeHandler.getRelativeBaseURI();
2211                }
2212                if (fParentRelativeURI.equals("")) {
2213                    return relativeURI;
2214                }
2215
2216                URI base = new URI(fParentRelativeURI, true);
2217                URI uri = new URI(base, relativeURI);
2218
2219                /** Check whether the scheme components are equal. */
2220                final String baseScheme = base.getScheme();
2221                final String literalScheme = uri.getScheme();
2222                if (!Objects.equals(baseScheme, literalScheme)) {
2223                    return relativeURI;
2224                }
2225
2226                /** Check whether the authority components are equal. */
2227                final String baseAuthority = base.getAuthority();
2228                final String literalAuthority = uri.getAuthority();
2229                if (!Objects.equals(baseAuthority, literalAuthority)) {
2230                    return uri.getSchemeSpecificPart();
2231                }
2232
2233                /**
2234                 * The scheme and authority components are equal,
2235                 * return the path and the possible query and/or
2236                 * fragment which follow.
2237                 */
2238                final String literalPath = uri.getPath();
2239                final String literalQuery = uri.getQueryString();
2240                final String literalFragment = uri.getFragment();
2241                if (literalQuery != null || literalFragment != null) {
2242                    final StringBuilder buffer = new StringBuilder();
2243                    if (literalPath != null) {
2244                        buffer.append(literalPath);
2245                    }
2246                    if (literalQuery != null) {
2247                        buffer.append('?');
2248                        buffer.append(literalQuery);
2249                    }
2250                    if (literalFragment != null) {
2251                        buffer.append('#');
2252                        buffer.append(literalFragment);
2253                    }
2254                    return buffer.toString();
2255                }
2256                return literalPath;
2257            }
2258            else {
2259                return relativeURI;
2260            }
2261        }
2262    }
2263
2264    /**
2265     * Returns the [base URI] of the include parent.
2266     * @return the base URI of the include parent.
2267     */
2268    private String getIncludeParentBaseURI() {
2269        int depth = getIncludeParentDepth();
2270        if (!isRootDocument() && depth == 0) {
2271            return fParentXIncludeHandler.getIncludeParentBaseURI();
2272        }
2273        else {
2274            return this.getBaseURI(depth);
2275        }
2276    }
2277
2278    /**
2279     * Returns the [language] of the include parent.
2280     *
2281     * @return the language property of the include parent.
2282     */
2283    private String getIncludeParentLanguage() {
2284        int depth = getIncludeParentDepth();
2285        if (!isRootDocument() && depth == 0) {
2286            return fParentXIncludeHandler.getIncludeParentLanguage();
2287        }
2288        else {
2289            return getLanguage(depth);
2290        }
2291    }
2292
2293    /**
2294     * Returns the depth of the include parent.  Here, the include parent is
2295     * calculated as the last non-include or non-fallback element. It is assumed
2296     * this method is called when the current element is a top level included item.
2297     * Returning 0 indicates that the top level element in this document
2298     * was an include element.
2299     * @return the depth of the top level include element
2300     */
2301    private int getIncludeParentDepth() {
2302        // We don't start at fDepth, since it is either the top level included item,
2303        // or an include element, when this method is called.
2304        for (int i = fDepth - 1; i >= 0; i--) {
2305            // This technically might not always return the first non-include/fallback
2306            // element that it comes to, since sawFallback() returns true if a fallback
2307            // was ever encountered at that depth.  However, if a fallback was encountered
2308            // at that depth, and it wasn't the direct descendant of the current element
2309            // then we can't be in a situation where we're calling this method (because
2310            // we'll always be in STATE_IGNORE)
2311            if (!getSawInclude(i) && !getSawFallback(i)) {
2312                return i;
2313            }
2314        }
2315        // shouldn't get here, since depth 0 should never have an include element or
2316        // a fallback element
2317        return 0;
2318    }
2319
2320    /**
2321     * Returns the current element depth of the result infoset.
2322     */
2323    private int getResultDepth() {
2324        return fResultDepth;
2325    }
2326
2327    /**
2328     * Modify the augmentations.  Add an [included] infoset item, if the current
2329     * element is a top level included item.
2330     * @param augs the Augmentations to modify.
2331     * @return the modified Augmentations
2332     */
2333    protected Augmentations modifyAugmentations(Augmentations augs) {
2334        return modifyAugmentations(augs, false);
2335    }
2336
2337    /**
2338     * Modify the augmentations.  Add an [included] infoset item, if <code>force</code>
2339     * is true, or if the current element is a top level included item.
2340     * @param augs the Augmentations to modify.
2341     * @param force whether to force modification
2342     * @return the modified Augmentations
2343     */
2344    protected Augmentations modifyAugmentations(
2345        Augmentations augs,
2346        boolean force) {
2347        if (force || isTopLevelIncludedItem()) {
2348            if (augs == null) {
2349                augs = new AugmentationsImpl();
2350            }
2351            augs.putItem(XINCLUDE_INCLUDED, Boolean.TRUE);
2352        }
2353        return augs;
2354    }
2355
2356    protected int getState(int depth) {
2357        return fState[depth];
2358    }
2359
2360    protected int getState() {
2361        return fState[fDepth];
2362    }
2363
2364    protected void setState(int state) {
2365        if (fDepth >= fState.length) {
2366            int[] newarray = new int[fDepth * 2];
2367            System.arraycopy(fState, 0, newarray, 0, fState.length);
2368            fState = newarray;
2369        }
2370        fState[fDepth] = state;
2371    }
2372
2373    /**
2374     * Records that an &lt;fallback&gt; was encountered at the specified depth,
2375     * as an ancestor of the current element, or as a sibling of an ancestor of the
2376     * current element.
2377     *
2378     * @param depth
2379     * @param val
2380     */
2381    protected void setSawFallback(int depth, boolean val) {
2382        if (depth >= fSawFallback.length) {
2383            boolean[] newarray = new boolean[depth * 2];
2384            System.arraycopy(fSawFallback, 0, newarray, 0, fSawFallback.length);
2385            fSawFallback = newarray;
2386        }
2387        fSawFallback[depth] = val;
2388    }
2389
2390    /**
2391     * Returns whether an &lt;fallback&gt; was encountered at the specified depth,
2392     * as an ancestor of the current element, or as a sibling of an ancestor of the
2393     * current element.
2394     *
2395     * @param depth
2396     */
2397    protected boolean getSawFallback(int depth) {
2398        if (depth >= fSawFallback.length) {
2399            return false;
2400        }
2401        return fSawFallback[depth];
2402    }
2403
2404    /**
2405     * Records that an &lt;include&gt; was encountered at the specified depth,
2406     * as an ancestor of the current item.
2407     *
2408     * @param depth
2409     * @param val
2410     */
2411    protected void setSawInclude(int depth, boolean val) {
2412        if (depth >= fSawInclude.length) {
2413            boolean[] newarray = new boolean[depth * 2];
2414            System.arraycopy(fSawInclude, 0, newarray, 0, fSawInclude.length);
2415            fSawInclude = newarray;
2416        }
2417        fSawInclude[depth] = val;
2418    }
2419
2420    /**
2421     * Return whether an &lt;include&gt; was encountered at the specified depth,
2422     * as an ancestor of the current item.
2423     *
2424     * @param depth
2425     * @return
2426     */
2427    protected boolean getSawInclude(int depth) {
2428        if (depth >= fSawInclude.length) {
2429            return false;
2430        }
2431        return fSawInclude[depth];
2432    }
2433
2434    protected void reportResourceError(String key) {
2435        this.reportFatalError(key, null);
2436    }
2437
2438    protected void reportResourceError(String key, Object[] args) {
2439        this.reportError(key, args, XMLErrorReporter.SEVERITY_WARNING);
2440    }
2441
2442    protected void reportFatalError(String key) {
2443        this.reportFatalError(key, null);
2444    }
2445
2446    protected void reportFatalError(String key, Object[] args) {
2447        this.reportError(key, args, XMLErrorReporter.SEVERITY_FATAL_ERROR);
2448    }
2449
2450    private void reportError(String key, Object[] args, short severity) {
2451        if (fErrorReporter != null) {
2452            fErrorReporter.reportError(
2453                XIncludeMessageFormatter.XINCLUDE_DOMAIN,
2454                key,
2455                args,
2456                severity);
2457        }
2458        // we won't worry about when error reporter is null, since there should always be
2459        // at least the default error reporter
2460    }
2461
2462    /**
2463     * Set the parent of this XIncludeHandler in the tree
2464     * @param parent
2465     */
2466    protected void setParent(XIncludeHandler parent) {
2467        fParentXIncludeHandler = parent;
2468    }
2469
2470    // used to know whether to pass declarations to the document handler
2471    protected boolean isRootDocument() {
2472        return fParentXIncludeHandler == null;
2473    }
2474
2475    /**
2476     * Caches an unparsed entity.
2477     * @param name the name of the unparsed entity
2478     * @param identifier the location of the unparsed entity
2479     * @param augmentations any Augmentations that were on the original unparsed entity declaration
2480     */
2481    protected void addUnparsedEntity(
2482        String name,
2483        XMLResourceIdentifier identifier,
2484        String notation,
2485        Augmentations augmentations) {
2486        UnparsedEntity ent = new UnparsedEntity();
2487        ent.name = name;
2488        ent.systemId = identifier.getLiteralSystemId();
2489        ent.publicId = identifier.getPublicId();
2490        ent.baseURI = identifier.getBaseSystemId();
2491        ent.expandedSystemId = identifier.getExpandedSystemId();
2492        ent.notation = notation;
2493        ent.augmentations = augmentations;
2494        fUnparsedEntities.add(ent);
2495    }
2496
2497    /**
2498     * Caches a notation.
2499     * @param name the name of the notation
2500     * @param identifier the location of the notation
2501     * @param augmentations any Augmentations that were on the original notation declaration
2502     */
2503    protected void addNotation(
2504        String name,
2505        XMLResourceIdentifier identifier,
2506        Augmentations augmentations) {
2507        Notation not = new Notation();
2508        not.name = name;
2509        not.systemId = identifier.getLiteralSystemId();
2510        not.publicId = identifier.getPublicId();
2511        not.baseURI = identifier.getBaseSystemId();
2512        not.expandedSystemId = identifier.getExpandedSystemId();
2513        not.augmentations = augmentations;
2514        fNotations.add(not);
2515    }
2516
2517    /**
2518     * Checks if an UnparsedEntity with the given name was declared in the DTD of the document
2519     * for the current pipeline.  If so, then the notation for the UnparsedEntity is checked.
2520     * If that turns out okay, then the UnparsedEntity is passed to the root pipeline to
2521     * be checked for conflicts, and sent to the root DTDHandler.
2522     *
2523     * @param entName the name of the UnparsedEntity to check
2524     */
2525    protected void checkUnparsedEntity(String entName) {
2526        UnparsedEntity ent = new UnparsedEntity();
2527        ent.name = entName;
2528        int index = fUnparsedEntities.indexOf(ent);
2529        if (index != -1) {
2530            ent = (UnparsedEntity)fUnparsedEntities.get(index);
2531            // first check the notation of the unparsed entity
2532            checkNotation(ent.notation);
2533            checkAndSendUnparsedEntity(ent);
2534        }
2535    }
2536
2537    /**
2538     * Checks if a Notation with the given name was declared in the DTD of the document
2539     * for the current pipeline.  If so, that Notation is passed to the root pipeline to
2540     * be checked for conflicts, and sent to the root DTDHandler
2541     *
2542     * @param notName the name of the Notation to check
2543     */
2544    protected void checkNotation(String notName) {
2545        Notation not = new Notation();
2546        not.name = notName;
2547        int index = fNotations.indexOf(not);
2548        if (index != -1) {
2549            not = (Notation)fNotations.get(index);
2550            checkAndSendNotation(not);
2551        }
2552    }
2553
2554    /**
2555     * The purpose of this method is to check if an UnparsedEntity conflicts with a previously
2556     * declared entity in the current pipeline stack.  If there is no conflict, the
2557     * UnparsedEntity is sent by the root pipeline.
2558     *
2559     * @param ent the UnparsedEntity to check for conflicts
2560     */
2561    protected void checkAndSendUnparsedEntity(UnparsedEntity ent) {
2562        if (isRootDocument()) {
2563            int index = fUnparsedEntities.indexOf(ent);
2564            if (index == -1) {
2565                // There is no unparsed entity with the same name that we have sent.
2566                // Calling unparsedEntityDecl() will add the entity to our local store,
2567                // and also send the unparsed entity to the DTDHandler
2568                XMLResourceIdentifier id =
2569                    new XMLResourceIdentifierImpl(
2570                        ent.publicId,
2571                        ent.systemId,
2572                        ent.baseURI,
2573                        ent.expandedSystemId);
2574                addUnparsedEntity(
2575                    ent.name,
2576                    id,
2577                    ent.notation,
2578                    ent.augmentations);
2579                if (fSendUEAndNotationEvents && fDTDHandler != null) {
2580                    fDTDHandler.unparsedEntityDecl(
2581                        ent.name,
2582                        id,
2583                        ent.notation,
2584                        ent.augmentations);
2585                }
2586            }
2587            else {
2588                UnparsedEntity localEntity =
2589                    (UnparsedEntity)fUnparsedEntities.get(index);
2590                if (!ent.isDuplicate(localEntity)) {
2591                    reportFatalError(
2592                        "NonDuplicateUnparsedEntity",
2593                        new Object[] { ent.name });
2594                }
2595            }
2596        }
2597        else {
2598            fParentXIncludeHandler.checkAndSendUnparsedEntity(ent);
2599        }
2600    }
2601
2602    /**
2603     * The purpose of this method is to check if a Notation conflicts with a previously
2604     * declared notation in the current pipeline stack.  If there is no conflict, the
2605     * Notation is sent by the root pipeline.
2606     *
2607     * @param not the Notation to check for conflicts
2608     */
2609    protected void checkAndSendNotation(Notation not) {
2610        if (isRootDocument()) {
2611            int index = fNotations.indexOf(not);
2612            if (index == -1) {
2613                // There is no notation with the same name that we have sent.
2614                XMLResourceIdentifier id =
2615                    new XMLResourceIdentifierImpl(
2616                        not.publicId,
2617                        not.systemId,
2618                        not.baseURI,
2619                        not.expandedSystemId);
2620                addNotation(not.name, id, not.augmentations);
2621                if (fSendUEAndNotationEvents && fDTDHandler != null) {
2622                    fDTDHandler.notationDecl(not.name, id, not.augmentations);
2623                }
2624            }
2625            else {
2626                Notation localNotation = (Notation)fNotations.get(index);
2627                if (!not.isDuplicate(localNotation)) {
2628                    reportFatalError(
2629                        "NonDuplicateNotation",
2630                        new Object[] { not.name });
2631                }
2632            }
2633        }
2634        else {
2635            fParentXIncludeHandler.checkAndSendNotation(not);
2636        }
2637    }
2638
2639    /**
2640     * Checks whether the string only contains white space characters.
2641     *
2642     * @param value the text to check
2643     */
2644    private void checkWhitespace(XMLString value) {
2645        int end = value.offset + value.length;
2646        for (int i = value.offset; i < end; ++i) {
2647            if (!XMLChar.isSpace(value.ch[i])) {
2648                reportFatalError("ContentIllegalAtTopLevel");
2649                return;
2650            }
2651        }
2652    }
2653
2654    /**
2655     * Checks whether the root element has already been processed.
2656     */
2657    private void checkMultipleRootElements() {
2658        if (getRootElementProcessed()) {
2659            reportFatalError("MultipleRootElements");
2660        }
2661        setRootElementProcessed(true);
2662    }
2663
2664    /**
2665     * Sets whether the root element has been processed.
2666     */
2667    private void setRootElementProcessed(boolean seenRoot) {
2668        if (isRootDocument()) {
2669            fSeenRootElement = seenRoot;
2670            return;
2671        }
2672        fParentXIncludeHandler.setRootElementProcessed(seenRoot);
2673    }
2674
2675    /**
2676     * Returns whether the root element has been processed.
2677     */
2678    private boolean getRootElementProcessed() {
2679        return isRootDocument() ? fSeenRootElement : fParentXIncludeHandler.getRootElementProcessed();
2680    }
2681
2682    // It would be nice if we didn't have to repeat code like this, but there's no interface that has
2683    // setFeature() and addRecognizedFeatures() that the objects have in common.
2684    protected void copyFeatures(
2685        XMLComponentManager from,
2686        ParserConfigurationSettings to) {
2687        Enumeration features = Constants.getXercesFeatures();
2688        copyFeatures1(features, Constants.XERCES_FEATURE_PREFIX, from, to);
2689        features = Constants.getSAXFeatures();
2690        copyFeatures1(features, Constants.SAX_FEATURE_PREFIX, from, to);
2691    }
2692
2693    protected void copyFeatures(
2694        XMLComponentManager from,
2695        XMLParserConfiguration to) {
2696        Enumeration features = Constants.getXercesFeatures();
2697        copyFeatures1(features, Constants.XERCES_FEATURE_PREFIX, from, to);
2698        features = Constants.getSAXFeatures();
2699        copyFeatures1(features, Constants.SAX_FEATURE_PREFIX, from, to);
2700    }
2701
2702    private void copyFeatures1(
2703        Enumeration features,
2704        String featurePrefix,
2705        XMLComponentManager from,
2706        ParserConfigurationSettings to) {
2707        while (features.hasMoreElements()) {
2708            String featureId = featurePrefix + (String)features.nextElement();
2709
2710            to.addRecognizedFeatures(new String[] { featureId });
2711
2712            try {
2713                to.setFeature(featureId, from.getFeature(featureId));
2714            }
2715            catch (XMLConfigurationException e) {
2716                // componentManager doesn't support this feature,
2717                // so we won't worry about it
2718            }
2719        }
2720    }
2721
2722    private void copyFeatures1(
2723        Enumeration features,
2724        String featurePrefix,
2725        XMLComponentManager from,
2726        XMLParserConfiguration to) {
2727        while (features.hasMoreElements()) {
2728            String featureId = featurePrefix + (String)features.nextElement();
2729            boolean value = from.getFeature(featureId);
2730
2731            try {
2732                to.setFeature(featureId, value);
2733            }
2734            catch (XMLConfigurationException e) {
2735                // componentManager doesn't support this feature,
2736                // so we won't worry about it
2737            }
2738        }
2739    }
2740
2741    // This is a storage class to hold information about the notations.
2742    // We're not using XMLNotationDecl because we don't want to lose the augmentations.
2743    protected static class Notation {
2744        public String name;
2745        public String systemId;
2746        public String baseURI;
2747        public String publicId;
2748        public String expandedSystemId;
2749        public Augmentations augmentations;
2750
2751        // equals() returns true if two Notations have the same name.
2752        // Useful for searching Vectors for notations with the same name
2753        @Override
2754        public boolean equals(Object obj) {
2755            return obj == this || obj instanceof Notation
2756                    && Objects.equals(name, ((Notation)obj).name);
2757        }
2758
2759        @Override
2760        public int hashCode() {
2761            return Objects.hashCode(name);
2762        }
2763
2764        // from 4.5.2
2765        // Notation items with the same [name], [system identifier],
2766        // [public identifier], and [declaration base URI] are considered
2767        // to be duplicate. An application may also be able to detect that
2768        // notations are duplicate through other means. For instance, the URI
2769        // resulting from combining the system identifier and the declaration
2770        // base URI is the same.
2771        public boolean isDuplicate(Object obj) {
2772            if (obj != null && obj instanceof Notation) {
2773                Notation other = (Notation)obj;
2774                return Objects.equals(name, other.name)
2775                && Objects.equals(publicId, other.publicId)
2776                && Objects.equals(expandedSystemId, other.expandedSystemId);
2777            }
2778            return false;
2779        }
2780    }
2781
2782    // This is a storage class to hold information about the unparsed entities.
2783    // We're not using XMLEntityDecl because we don't want to lose the augmentations.
2784    protected static class UnparsedEntity {
2785        public String name;
2786        public String systemId;
2787        public String baseURI;
2788        public String publicId;
2789        public String expandedSystemId;
2790        public String notation;
2791        public Augmentations augmentations;
2792
2793        // equals() returns true if two UnparsedEntities have the same name.
2794        // Useful for searching Vectors for entities with the same name
2795        @Override
2796        public boolean equals(Object obj) {
2797            return obj == this || obj instanceof UnparsedEntity
2798                    && Objects.equals(name, ((UnparsedEntity)obj).name);
2799        }
2800
2801        @Override
2802        public int hashCode() {
2803            return Objects.hashCode(name);
2804        }
2805
2806        // from 4.5.1:
2807        // Unparsed entity items with the same [name], [system identifier],
2808        // [public identifier], [declaration base URI], [notation name], and
2809        // [notation] are considered to be duplicate. An application may also
2810        // be able to detect that unparsed entities are duplicate through other
2811        // means. For instance, the URI resulting from combining the system
2812        // identifier and the declaration base URI is the same.
2813        public boolean isDuplicate(Object obj) {
2814            if (obj != null && obj instanceof UnparsedEntity) {
2815                UnparsedEntity other = (UnparsedEntity)obj;
2816                return Objects.equals(name, other.name)
2817                && Objects.equals(publicId, other.publicId)
2818                && Objects.equals(expandedSystemId, other.expandedSystemId)
2819                && Objects.equals(notation, other.notation);
2820            }
2821            return false;
2822        }
2823    }
2824
2825    // The following methods are used for XML Base processing
2826
2827    /**
2828     * Saves the current base URI to the top of the stack.
2829     */
2830    protected void saveBaseURI() {
2831        fBaseURIScope.push(fDepth);
2832        fBaseURI.push(fCurrentBaseURI.getBaseSystemId());
2833        fLiteralSystemID.push(fCurrentBaseURI.getLiteralSystemId());
2834        fExpandedSystemID.push(fCurrentBaseURI.getExpandedSystemId());
2835    }
2836
2837    /**
2838     * Discards the URIs at the top of the stack, and restores the ones beneath it.
2839     */
2840    protected void restoreBaseURI() {
2841        fBaseURI.pop();
2842        fLiteralSystemID.pop();
2843        fExpandedSystemID.pop();
2844        fBaseURIScope.pop();
2845        fCurrentBaseURI.setBaseSystemId((String)fBaseURI.peek());
2846        fCurrentBaseURI.setLiteralSystemId((String)fLiteralSystemID.peek());
2847        fCurrentBaseURI.setExpandedSystemId((String)fExpandedSystemID.peek());
2848    }
2849
2850    // The following methods are used for language processing
2851
2852    /**
2853     * Saves the given language on the top of the stack.
2854     *
2855     * @param lanaguage the language to push onto the stack.
2856     */
2857    protected void saveLanguage(String language) {
2858        fLanguageScope.push(fDepth);
2859        fLanguageStack.push(language);
2860    }
2861
2862    /**
2863     * Discards the language at the top of the stack, and returns the one beneath it.
2864     */
2865    public String restoreLanguage() {
2866        fLanguageStack.pop();
2867        fLanguageScope.pop();
2868        return (String) fLanguageStack.peek();
2869    }
2870
2871    /**
2872     * Gets the base URI that was in use at that depth
2873     * @param depth
2874     * @return the base URI
2875     */
2876    public String getBaseURI(int depth) {
2877        int scope = scopeOfBaseURI(depth);
2878        return (String)fExpandedSystemID.elementAt(scope);
2879    }
2880
2881    /**
2882     * Gets the language that was in use at that depth.
2883     * @param depth
2884     * @return the language
2885     */
2886    public String getLanguage(int depth) {
2887        int scope = scopeOfLanguage(depth);
2888        return (String)fLanguageStack.elementAt(scope);
2889    }
2890
2891    /**
2892     * Returns a relative URI, which when resolved against the base URI at the
2893     * specified depth, will create the current base URI.
2894     * This is accomplished by merged the literal system IDs.
2895     * @param depth the depth at which to start creating the relative URI
2896     * @return a relative URI to convert the base URI at the given depth to the current
2897     *         base URI
2898     */
2899    public String getRelativeURI(int depth) throws MalformedURIException {
2900        // The literal system id at the location given by "start" is *in focus* at
2901        // the given depth. So we need to adjust it to the next scope, so that we
2902        // only process out of focus literal system ids
2903        int start = scopeOfBaseURI(depth) + 1;
2904        if (start == fBaseURIScope.size()) {
2905            // If that is the last system id, then we don't need a relative URI
2906            return "";
2907        }
2908        URI uri = new URI("file", (String)fLiteralSystemID.elementAt(start));
2909        for (int i = start + 1; i < fBaseURIScope.size(); i++) {
2910            uri = new URI(uri, (String)fLiteralSystemID.elementAt(i));
2911        }
2912        return uri.getPath();
2913    }
2914
2915    // We need to find two consecutive elements in the scope stack,
2916    // such that the first is lower than 'depth' (or equal), and the
2917    // second is higher.
2918    private int scopeOfBaseURI(int depth) {
2919        for (int i = fBaseURIScope.size() - 1; i >= 0; i--) {
2920            if (fBaseURIScope.elementAt(i) <= depth)
2921                return i;
2922        }
2923        // we should never get here, because 0 was put on the stack in startDocument()
2924        return -1;
2925    }
2926
2927    private int scopeOfLanguage(int depth) {
2928        for (int i = fLanguageScope.size() - 1; i >= 0; i--) {
2929            if (fLanguageScope.elementAt(i) <= depth)
2930                return i;
2931        }
2932        // we should never get here, because 0 was put on the stack in startDocument()
2933        return -1;
2934    }
2935
2936    /**
2937     * Search for a xml:base attribute, and if one is found, put the new base URI into
2938     * effect.
2939     */
2940    protected void processXMLBaseAttributes(XMLAttributes attributes) {
2941        String baseURIValue =
2942            attributes.getValue(NamespaceContext.XML_URI, "base");
2943        if (baseURIValue != null) {
2944            try {
2945                String expandedValue =
2946                    XMLEntityManager.expandSystemId(
2947                        baseURIValue,
2948                        fCurrentBaseURI.getExpandedSystemId(),
2949                        false);
2950                fCurrentBaseURI.setLiteralSystemId(baseURIValue);
2951                fCurrentBaseURI.setBaseSystemId(
2952                    fCurrentBaseURI.getExpandedSystemId());
2953                fCurrentBaseURI.setExpandedSystemId(expandedValue);
2954
2955                // push the new values on the stack
2956                saveBaseURI();
2957            }
2958            catch (MalformedURIException e) {
2959                // REVISIT: throw error here
2960            }
2961        }
2962    }
2963
2964    /**
2965     * Search for a xml:lang attribute, and if one is found, put the new
2966     * [language] into effect.
2967     */
2968    protected void processXMLLangAttributes(XMLAttributes attributes) {
2969        String language = attributes.getValue(NamespaceContext.XML_URI, "lang");
2970        if (language != null) {
2971            fCurrentLanguage = language;
2972            saveLanguage(fCurrentLanguage);
2973        }
2974    }
2975
2976    /**
2977     * Returns <code>true</code> if the given string
2978     * would be valid in an HTTP header.
2979     *
2980     * @param value string to check
2981     * @return <code>true</code> if the given string
2982     * would be valid in an HTTP header
2983     */
2984    private boolean isValidInHTTPHeader (String value) {
2985        char ch;
2986        for (int i = value.length() - 1; i >= 0; --i) {
2987            ch = value.charAt(i);
2988            if (ch < 0x20 || ch > 0x7E) {
2989                return false;
2990            }
2991        }
2992        return true;
2993    }
2994
2995    /**
2996     * Returns a new <code>XMLInputSource</code> from the given parameters.
2997     */
2998    private XMLInputSource createInputSource(String publicId,
2999            String systemId, String baseSystemId,
3000            String accept, String acceptLanguage) {
3001
3002        HTTPInputSource httpSource = new HTTPInputSource(publicId, systemId, baseSystemId);
3003        if (accept != null && accept.length() > 0) {
3004            httpSource.setHTTPRequestProperty(XIncludeHandler.HTTP_ACCEPT, accept);
3005        }
3006        if (acceptLanguage != null && acceptLanguage.length() > 0) {
3007            httpSource.setHTTPRequestProperty(XIncludeHandler.HTTP_ACCEPT_LANGUAGE, acceptLanguage);
3008        }
3009        return httpSource;
3010    }
3011
3012    // which ASCII characters need to be escaped
3013    private static final boolean gNeedEscaping[] = new boolean[128];
3014    // the first hex character if a character needs to be escaped
3015    private static final char gAfterEscaping1[] = new char[128];
3016    // the second hex character if a character needs to be escaped
3017    private static final char gAfterEscaping2[] = new char[128];
3018    private static final char[] gHexChs = {'0', '1', '2', '3', '4', '5', '6', '7',
3019                                     '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
3020    // initialize the above 3 arrays
3021    static {
3022        char[] escChs = {' ', '<', '>', '"', '{', '}', '|', '\\', '^', '`'};
3023        int len = escChs.length;
3024        char ch;
3025        for (int i = 0; i < len; i++) {
3026            ch = escChs[i];
3027            gNeedEscaping[ch] = true;
3028            gAfterEscaping1[ch] = gHexChs[ch >> 4];
3029            gAfterEscaping2[ch] = gHexChs[ch & 0xf];
3030        }
3031    }
3032
3033    //
3034    // Escape an href value according to (4.1.1):
3035    //
3036    // To convert the value of the href attribute to an IRI reference, the following characters must be escaped:
3037    // space #x20
3038    // the delimiters < #x3C, > #x3E and " #x22
3039    // the unwise characters { #x7B, } #x7D, | #x7C, \ #x5C, ^ #x5E and ` #x60
3040    //
3041    // To convert an IRI reference to a URI reference, the following characters must also be escaped:
3042    // the Unicode plane 0 characters #xA0 - #xD7FF, #xF900-#xFDCF, #xFDF0-#xFFEF
3043    // the Unicode plane 1-14 characters #x10000-#x1FFFD ... #xE0000-#xEFFFD
3044    //
3045    private String escapeHref(String href) {
3046        int len = href.length();
3047        int ch;
3048        final StringBuilder buffer = new StringBuilder(len*3);
3049
3050        // for each character in the href
3051        int i = 0;
3052        for (; i < len; i++) {
3053            ch = href.charAt(i);
3054            // if it's not an ASCII character (excluding 0x7F), break here, and use UTF-8 encoding
3055            if (ch > 0x7E) {
3056                break;
3057            }
3058            // abort: href does not allow this character
3059            if (ch < 0x20) {
3060                return href;
3061            }
3062            if (gNeedEscaping[ch]) {
3063                buffer.append('%');
3064                buffer.append(gAfterEscaping1[ch]);
3065                buffer.append(gAfterEscaping2[ch]);
3066            }
3067            else {
3068                buffer.append((char)ch);
3069            }
3070        }
3071
3072        // we saw some non-ascii character
3073        if (i < len) {
3074            // check if remainder of href contains any illegal characters before proceeding
3075            for (int j = i; j < len; ++j) {
3076                ch = href.charAt(j);
3077                if ((ch >= 0x20 && ch <= 0x7E) ||
3078                    (ch >= 0xA0 && ch <= 0xD7FF) ||
3079                    (ch >= 0xF900 && ch <= 0xFDCF) ||
3080                    (ch >= 0xFDF0 && ch <= 0xFFEF)) {
3081                    continue;
3082                }
3083                if (XMLChar.isHighSurrogate(ch) && ++j < len) {
3084                    int ch2 = href.charAt(j);
3085                    if (XMLChar.isLowSurrogate(ch2)) {
3086                        ch2 = XMLChar.supplemental((char)ch, (char)ch2);
3087                        if (ch2 < 0xF0000 && (ch2 & 0xFFFF) <= 0xFFFD) {
3088                            continue;
3089                        }
3090                    }
3091                }
3092                // abort: href does not allow this character
3093                return href;
3094            }
3095
3096            // get UTF-8 bytes for the remaining sub-string
3097            byte[] bytes = null;
3098            byte b;
3099            try {
3100                bytes = href.substring(i).getBytes("UTF-8");
3101            } catch (java.io.UnsupportedEncodingException e) {
3102                // should never happen
3103                return href;
3104            }
3105            len = bytes.length;
3106
3107            // for each byte
3108            for (i = 0; i < len; i++) {
3109                b = bytes[i];
3110                // for non-ascii character: make it positive, then escape
3111                if (b < 0) {
3112                    ch = b + 256;
3113                    buffer.append('%');
3114                    buffer.append(gHexChs[ch >> 4]);
3115                    buffer.append(gHexChs[ch & 0xf]);
3116                }
3117                else if (gNeedEscaping[b]) {
3118                    buffer.append('%');
3119                    buffer.append(gAfterEscaping1[b]);
3120                    buffer.append(gAfterEscaping2[b]);
3121                }
3122                else {
3123                    buffer.append((char)b);
3124                }
3125            }
3126        }
3127
3128        // If escaping happened, create a new string;
3129        // otherwise, return the orginal one.
3130        if (buffer.length() != len) {
3131            return buffer.toString();
3132        }
3133        else {
3134            return href;
3135        }
3136    }
3137}
3138