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