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