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