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