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