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