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