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