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