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