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