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