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