1 /* 2 * Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved. 3 */ 4 /* 5 * Licensed to the Apache Software Foundation (ASF) under one or more 6 * contributor license agreements. See the NOTICE file distributed with 7 * this work for additional information regarding copyright ownership. 8 * The ASF licenses this file to You under the Apache License, Version 2.0 9 * (the "License"); you may not use this file except in compliance with 10 * the License. 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 21 package com.sun.org.apache.xerces.internal.impl ; 22 23 import com.sun.org.apache.xerces.internal.impl.io.ASCIIReader; 24 import com.sun.org.apache.xerces.internal.impl.io.UCSReader; 25 import com.sun.org.apache.xerces.internal.impl.io.UTF16Reader; 26 import com.sun.org.apache.xerces.internal.impl.io.UTF8Reader; 27 import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter; 28 import com.sun.org.apache.xerces.internal.impl.validation.ValidationManager; 29 import com.sun.org.apache.xerces.internal.util.*; 30 import com.sun.org.apache.xerces.internal.util.URI; 31 import com.sun.org.apache.xerces.internal.utils.XMLLimitAnalyzer; 32 import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager; 33 import com.sun.org.apache.xerces.internal.utils.XMLSecurityPropertyManager; 34 import com.sun.org.apache.xerces.internal.xni.Augmentations; 35 import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier; 36 import com.sun.org.apache.xerces.internal.xni.XNIException; 37 import com.sun.org.apache.xerces.internal.xni.parser.*; 38 import com.sun.xml.internal.stream.Entity; 39 import com.sun.xml.internal.stream.StaxEntityResolverWrapper; 40 import com.sun.xml.internal.stream.StaxXMLInputSource; 41 import com.sun.xml.internal.stream.XMLEntityStorage; 42 import java.io.*; 43 import java.net.HttpURLConnection; 44 import java.net.URISyntaxException; 45 import java.net.URL; 46 import java.net.URLConnection; 47 import java.util.HashMap; 48 import java.util.Iterator; 49 import java.util.Locale; 50 import java.util.Map; 51 import java.util.Stack; 52 import java.util.StringTokenizer; 53 import javax.xml.XMLConstants; 54 import javax.xml.catalog.CatalogException; 55 import javax.xml.catalog.CatalogFeatures.Feature; 56 import javax.xml.catalog.CatalogFeatures; 57 import javax.xml.catalog.CatalogManager; 58 import javax.xml.catalog.CatalogResolver; 59 import javax.xml.stream.XMLInputFactory; 60 import javax.xml.transform.Source; 61 import jdk.xml.internal.JdkXmlUtils; 62 import jdk.xml.internal.SecuritySupport; 63 import org.xml.sax.InputSource; 64 65 66 /** 67 * Will keep track of current entity. 68 * 69 * The entity manager handles the registration of general and parameter 70 * entities; resolves entities; and starts entities. The entity manager 71 * is a central component in a standard parser configuration and this 72 * class works directly with the entity scanner to manage the underlying 73 * xni. 74 * <p> 75 * This component requires the following features and properties from the 76 * component manager that uses it: 77 * <ul> 78 * <li>http://xml.org/sax/features/validation</li> 79 * <li>http://xml.org/sax/features/external-general-entities</li> 80 * <li>http://xml.org/sax/features/external-parameter-entities</li> 81 * <li>http://apache.org/xml/features/allow-java-encodings</li> 82 * <li>http://apache.org/xml/properties/internal/symbol-table</li> 83 * <li>http://apache.org/xml/properties/internal/error-reporter</li> 84 * <li>http://apache.org/xml/properties/internal/entity-resolver</li> 85 * </ul> 86 * 87 * 88 * @author Andy Clark, IBM 89 * @author Arnaud Le Hors, IBM 90 * @author K.Venugopal SUN Microsystems 91 * @author Neeraj Bajaj SUN Microsystems 92 * @author Sunitha Reddy SUN Microsystems 93 * @LastModified: Apr 2019 94 */ 95 public class XMLEntityManager implements XMLComponent, XMLEntityResolver { 96 97 // 98 // Constants 99 // 100 101 /** Default buffer size (2048). */ 102 public static final int DEFAULT_BUFFER_SIZE = 8192; 103 104 /** Default buffer size before we've finished with the XMLDecl: */ 105 public static final int DEFAULT_XMLDECL_BUFFER_SIZE = 64; 106 107 /** Default internal entity buffer size (1024). */ 108 public static final int DEFAULT_INTERNAL_BUFFER_SIZE = 1024; 109 110 // feature identifiers 111 112 /** Feature identifier: validation. */ 113 protected static final String VALIDATION = 114 Constants.SAX_FEATURE_PREFIX + Constants.VALIDATION_FEATURE; 115 116 /** 117 * standard uri conformant (strict uri). 118 * http://apache.org/xml/features/standard-uri-conformant 119 */ 120 protected boolean fStrictURI; 121 122 123 /** Feature identifier: external general entities. */ 124 protected static final String EXTERNAL_GENERAL_ENTITIES = 125 Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_GENERAL_ENTITIES_FEATURE; 126 127 /** Feature identifier: external parameter entities. */ 128 protected static final String EXTERNAL_PARAMETER_ENTITIES = 129 Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_PARAMETER_ENTITIES_FEATURE; 130 131 /** Feature identifier: allow Java encodings. */ 132 protected static final String ALLOW_JAVA_ENCODINGS = 133 Constants.XERCES_FEATURE_PREFIX + Constants.ALLOW_JAVA_ENCODINGS_FEATURE; 134 135 /** Feature identifier: warn on duplicate EntityDef */ 136 protected static final String WARN_ON_DUPLICATE_ENTITYDEF = 137 Constants.XERCES_FEATURE_PREFIX +Constants.WARN_ON_DUPLICATE_ENTITYDEF_FEATURE; 138 139 /** Feature identifier: load external DTD. */ 140 protected static final String LOAD_EXTERNAL_DTD = 141 Constants.XERCES_FEATURE_PREFIX + Constants.LOAD_EXTERNAL_DTD_FEATURE; 142 143 // property identifiers 144 145 /** Property identifier: symbol table. */ 146 protected static final String SYMBOL_TABLE = 147 Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY; 148 149 /** Property identifier: error reporter. */ 150 protected static final String ERROR_REPORTER = 151 Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY; 152 153 /** Feature identifier: standard uri conformant */ 154 protected static final String STANDARD_URI_CONFORMANT = 155 Constants.XERCES_FEATURE_PREFIX +Constants.STANDARD_URI_CONFORMANT_FEATURE; 156 157 /** Property identifier: entity resolver. */ 158 protected static final String ENTITY_RESOLVER = 159 Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY; 160 161 protected static final String STAX_ENTITY_RESOLVER = 162 Constants.XERCES_PROPERTY_PREFIX + Constants.STAX_ENTITY_RESOLVER_PROPERTY; 163 164 // property identifier: ValidationManager 165 protected static final String VALIDATION_MANAGER = 166 Constants.XERCES_PROPERTY_PREFIX + Constants.VALIDATION_MANAGER_PROPERTY; 167 168 /** property identifier: buffer size. */ 169 protected static final String BUFFER_SIZE = 170 Constants.XERCES_PROPERTY_PREFIX + Constants.BUFFER_SIZE_PROPERTY; 171 172 /** property identifier: security manager. */ 173 protected static final String SECURITY_MANAGER = 174 Constants.XERCES_PROPERTY_PREFIX + Constants.SECURITY_MANAGER_PROPERTY; 175 176 protected static final String PARSER_SETTINGS = 177 Constants.XERCES_FEATURE_PREFIX + Constants.PARSER_SETTINGS; 178 179 /** Property identifier: Security property manager. */ 180 private static final String XML_SECURITY_PROPERTY_MANAGER = 181 Constants.XML_SECURITY_PROPERTY_MANAGER; 182 183 /** access external dtd: file protocol */ 184 static final String EXTERNAL_ACCESS_DEFAULT = Constants.EXTERNAL_ACCESS_DEFAULT; 185 186 // recognized features and properties 187 188 /** Recognized features. */ 189 private static final String[] RECOGNIZED_FEATURES = { 190 VALIDATION, 191 EXTERNAL_GENERAL_ENTITIES, 192 EXTERNAL_PARAMETER_ENTITIES, 193 ALLOW_JAVA_ENCODINGS, 194 WARN_ON_DUPLICATE_ENTITYDEF, 195 STANDARD_URI_CONFORMANT, 196 XMLConstants.USE_CATALOG 197 }; 198 199 /** Feature defaults. */ 200 private static final Boolean[] FEATURE_DEFAULTS = { 201 null, 202 Boolean.TRUE, 203 Boolean.TRUE, 204 Boolean.TRUE, 205 Boolean.FALSE, 206 Boolean.FALSE, 207 JdkXmlUtils.USE_CATALOG_DEFAULT 208 }; 209 210 /** Recognized properties. */ 211 private static final String[] RECOGNIZED_PROPERTIES = { 212 SYMBOL_TABLE, 213 ERROR_REPORTER, 214 ENTITY_RESOLVER, 215 VALIDATION_MANAGER, 216 BUFFER_SIZE, 217 SECURITY_MANAGER, 218 XML_SECURITY_PROPERTY_MANAGER, 219 JdkXmlUtils.CATALOG_DEFER, 220 JdkXmlUtils.CATALOG_FILES, 221 JdkXmlUtils.CATALOG_PREFER, 222 JdkXmlUtils.CATALOG_RESOLVE, 223 JdkXmlUtils.CDATA_CHUNK_SIZE 224 }; 225 226 /** Property defaults. */ 227 private static final Object[] PROPERTY_DEFAULTS = { 228 null, 229 null, 230 null, 231 null, 232 DEFAULT_BUFFER_SIZE, 233 null, 234 null, 235 null, 236 null, 237 null, 238 null, 239 JdkXmlUtils.CDATA_CHUNK_SIZE_DEFAULT 240 }; 241 242 private static final String XMLEntity = "[xml]".intern(); 243 private static final String DTDEntity = "[dtd]".intern(); 244 245 // debugging 246 247 /** 248 * Debug printing of buffer. This debugging flag works best when you 249 * resize the DEFAULT_BUFFER_SIZE down to something reasonable like 250 * 64 characters. 251 */ 252 private static final boolean DEBUG_BUFFER = false; 253 254 /** warn on duplicate Entity declaration. 255 * http://apache.org/xml/features/warn-on-duplicate-entitydef 256 */ 257 protected boolean fWarnDuplicateEntityDef; 258 259 /** Debug some basic entities. */ 260 private static final boolean DEBUG_ENTITIES = false; 261 262 /** Debug switching readers for encodings. */ 263 private static final boolean DEBUG_ENCODINGS = false; 264 265 // should be diplayed trace resolving messages 266 private static final boolean DEBUG_RESOLVER = false ; 267 268 // 269 // Data 270 // 271 272 // features 273 274 /** 275 * Validation. This feature identifier is: 276 * http://xml.org/sax/features/validation 277 */ 278 protected boolean fValidation; 279 280 /** 281 * External general entities. This feature identifier is: 282 * http://xml.org/sax/features/external-general-entities 283 */ 284 protected boolean fExternalGeneralEntities; 285 286 /** 287 * External parameter entities. This feature identifier is: 288 * http://xml.org/sax/features/external-parameter-entities 289 */ 290 protected boolean fExternalParameterEntities; 291 292 /** 293 * Allow Java encoding names. This feature identifier is: 294 * http://apache.org/xml/features/allow-java-encodings 295 */ 296 protected boolean fAllowJavaEncodings = true ; 297 298 /** Load external DTD. */ 299 protected boolean fLoadExternalDTD = true; 300 301 // properties 302 303 /** 304 * Symbol table. This property identifier is: 305 * http://apache.org/xml/properties/internal/symbol-table 306 */ 307 protected SymbolTable fSymbolTable; 308 309 /** 310 * Error reporter. This property identifier is: 311 * http://apache.org/xml/properties/internal/error-reporter 312 */ 313 protected XMLErrorReporter fErrorReporter; 314 315 /** 316 * Entity resolver. This property identifier is: 317 * http://apache.org/xml/properties/internal/entity-resolver 318 */ 319 protected XMLEntityResolver fEntityResolver; 320 321 /** Stax Entity Resolver. This property identifier is XMLInputFactory.ENTITY_RESOLVER */ 322 323 protected StaxEntityResolverWrapper fStaxEntityResolver; 324 325 /** Property Manager. This is used from Stax */ 326 protected PropertyManager fPropertyManager ; 327 328 /** StAX properties */ 329 boolean fSupportDTD = true; 330 boolean fReplaceEntityReferences = true; 331 boolean fSupportExternalEntities = true; 332 333 /** used to restrict external access */ 334 protected String fAccessExternalDTD = EXTERNAL_ACCESS_DEFAULT; 335 336 // settings 337 338 /** 339 * Validation manager. This property identifier is: 340 * http://apache.org/xml/properties/internal/validation-manager 341 */ 342 protected ValidationManager fValidationManager; 343 344 // settings 345 346 /** 347 * Buffer size. We get this value from a property. The default size 348 * is used if the input buffer size property is not specified. 349 * REVISIT: do we need a property for internal entity buffer size? 350 */ 351 protected int fBufferSize = DEFAULT_BUFFER_SIZE; 352 353 /** Security Manager */ 354 protected XMLSecurityManager fSecurityManager = null; 355 356 protected XMLLimitAnalyzer fLimitAnalyzer = null; 357 358 protected int entityExpansionIndex; 359 360 /** 361 * True if the document entity is standalone. This should really 362 * only be set by the document source (e.g. XMLDocumentScanner). 363 */ 364 protected boolean fStandalone; 365 366 // are the entities being parsed in the external subset? 367 // NOTE: this *is not* the same as whether they're external entities! 368 protected boolean fInExternalSubset = false; 369 370 371 // handlers 372 /** Entity handler. */ 373 protected XMLEntityHandler fEntityHandler; 374 375 /** Current entity scanner */ 376 protected XMLEntityScanner fEntityScanner ; 377 378 /** XML 1.0 entity scanner. */ 379 protected XMLEntityScanner fXML10EntityScanner; 380 381 /** XML 1.1 entity scanner. */ 382 protected XMLEntityScanner fXML11EntityScanner; 383 384 /** count of entities expanded: */ 385 protected int fEntityExpansionCount = 0; 386 387 // entities 388 389 /** Entities. */ 390 protected Map<String, Entity> fEntities = new HashMap<>(); 391 392 /** Entity stack. */ 393 protected Stack<Entity> fEntityStack = new Stack<>(); 394 395 /** Current entity. */ 396 protected Entity.ScannedEntity fCurrentEntity = null; 397 398 /** identify if the InputSource is created by a resolver */ 399 boolean fISCreatedByResolver = false; 400 401 // shared context 402 403 protected XMLEntityStorage fEntityStorage ; 404 405 protected final Object [] defaultEncoding = new Object[]{"UTF-8", null}; 406 407 408 // temp vars 409 410 /** Resource identifer. */ 411 private final XMLResourceIdentifierImpl fResourceIdentifier = new XMLResourceIdentifierImpl(); 412 413 /** Augmentations for entities. */ 414 private final Augmentations fEntityAugs = new AugmentationsImpl(); 415 416 /** indicate whether Catalog should be used for resolving external resources */ 417 private boolean fUseCatalog = true; 418 CatalogFeatures fCatalogFeatures; 419 CatalogResolver fCatalogResolver; 420 421 private String fCatalogFile; 422 private String fDefer; 423 private String fPrefer; 424 private String fResolve; 425 426 // 427 // Constructors 428 // 429 430 /** 431 * If this constructor is used to create the object, reset() should be invoked on this object 432 */ 433 public XMLEntityManager() { 434 //for entity managers not created by parsers 435 fSecurityManager = new XMLSecurityManager(true); 436 fEntityStorage = new XMLEntityStorage(this) ; 437 setScannerVersion(Constants.XML_VERSION_1_0); 438 } // <init>() 439 440 /** Default constructor. */ 441 public XMLEntityManager(PropertyManager propertyManager) { 442 fPropertyManager = propertyManager ; 443 //pass a reference to current entity being scanned 444 //fEntityStorage = new XMLEntityStorage(fCurrentEntity) ; 445 fEntityStorage = new XMLEntityStorage(this) ; 446 fEntityScanner = new XMLEntityScanner(propertyManager, this) ; 447 reset(propertyManager); 448 } // <init>() 449 450 /** 451 * Adds an internal entity declaration. 452 * <p> 453 * <strong>Note:</strong> This method ignores subsequent entity 454 * declarations. 455 * <p> 456 * <strong>Note:</strong> The name should be a unique symbol. The 457 * SymbolTable can be used for this purpose. 458 * 459 * @param name The name of the entity. 460 * @param text The text of the entity. 461 * 462 * @see SymbolTable 463 */ 464 public void addInternalEntity(String name, String text) { 465 if (!fEntities.containsKey(name)) { 466 Entity entity = new Entity.InternalEntity(name, text, fInExternalSubset); 467 fEntities.put(name, entity); 468 } else{ 469 if(fWarnDuplicateEntityDef){ 470 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, 471 "MSG_DUPLICATE_ENTITY_DEFINITION", 472 new Object[]{ name }, 473 XMLErrorReporter.SEVERITY_WARNING ); 474 } 475 } 476 477 } // addInternalEntity(String,String) 478 479 /** 480 * Adds an external entity declaration. 481 * <p> 482 * <strong>Note:</strong> This method ignores subsequent entity 483 * declarations. 484 * <p> 485 * <strong>Note:</strong> The name should be a unique symbol. The 486 * SymbolTable can be used for this purpose. 487 * 488 * @param name The name of the entity. 489 * @param publicId The public identifier of the entity. 490 * @param literalSystemId The system identifier of the entity. 491 * @param baseSystemId The base system identifier of the entity. 492 * This is the system identifier of the entity 493 * where <em>the entity being added</em> and 494 * is used to expand the system identifier when 495 * the system identifier is a relative URI. 496 * When null the system identifier of the first 497 * external entity on the stack is used instead. 498 * 499 * @see SymbolTable 500 */ 501 public void addExternalEntity(String name, 502 String publicId, String literalSystemId, 503 String baseSystemId) throws IOException { 504 if (!fEntities.containsKey(name)) { 505 if (baseSystemId == null) { 506 // search for the first external entity on the stack 507 int size = fEntityStack.size(); 508 if (size == 0 && fCurrentEntity != null && fCurrentEntity.entityLocation != null) { 509 baseSystemId = fCurrentEntity.entityLocation.getExpandedSystemId(); 510 } 511 for (int i = size - 1; i >= 0 ; i--) { 512 Entity.ScannedEntity externalEntity = 513 (Entity.ScannedEntity)fEntityStack.get(i); 514 if (externalEntity.entityLocation != null && externalEntity.entityLocation.getExpandedSystemId() != null) { 515 baseSystemId = externalEntity.entityLocation.getExpandedSystemId(); 516 break; 517 } 518 } 519 } 520 Entity entity = new Entity.ExternalEntity(name, 521 new XMLEntityDescriptionImpl(name, publicId, literalSystemId, baseSystemId, 522 expandSystemId(literalSystemId, baseSystemId, false)), null, fInExternalSubset); 523 fEntities.put(name, entity); 524 } else{ 525 if(fWarnDuplicateEntityDef){ 526 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, 527 "MSG_DUPLICATE_ENTITY_DEFINITION", 528 new Object[]{ name }, 529 XMLErrorReporter.SEVERITY_WARNING ); 530 } 531 } 532 533 } // addExternalEntity(String,String,String,String) 534 535 536 /** 537 * Adds an unparsed entity declaration. 538 * <p> 539 * <strong>Note:</strong> This method ignores subsequent entity 540 * declarations. 541 * <p> 542 * <strong>Note:</strong> The name should be a unique symbol. The 543 * SymbolTable can be used for this purpose. 544 * 545 * @param name The name of the entity. 546 * @param publicId The public identifier of the entity. 547 * @param systemId The system identifier of the entity. 548 * @param notation The name of the notation. 549 * 550 * @see SymbolTable 551 */ 552 public void addUnparsedEntity(String name, 553 String publicId, String systemId, 554 String baseSystemId, String notation) { 555 if (!fEntities.containsKey(name)) { 556 Entity.ExternalEntity entity = new Entity.ExternalEntity(name, 557 new XMLEntityDescriptionImpl(name, publicId, systemId, baseSystemId, null), 558 notation, fInExternalSubset); 559 fEntities.put(name, entity); 560 } else{ 561 if(fWarnDuplicateEntityDef){ 562 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, 563 "MSG_DUPLICATE_ENTITY_DEFINITION", 564 new Object[]{ name }, 565 XMLErrorReporter.SEVERITY_WARNING ); 566 } 567 } 568 } // addUnparsedEntity(String,String,String,String) 569 570 571 /** get the entity storage object from entity manager */ 572 public XMLEntityStorage getEntityStore(){ 573 return fEntityStorage ; 574 } 575 576 /** return the entity responsible for reading the entity */ 577 public XMLEntityScanner getEntityScanner(){ 578 if(fEntityScanner == null) { 579 // default to 1.0 580 if(fXML10EntityScanner == null) { 581 fXML10EntityScanner = new XMLEntityScanner(); 582 } 583 fXML10EntityScanner.reset(fSymbolTable, this, fErrorReporter); 584 fEntityScanner = fXML10EntityScanner; 585 } 586 return fEntityScanner; 587 588 } 589 590 public void setScannerVersion(short version) { 591 592 if(version == Constants.XML_VERSION_1_0) { 593 if(fXML10EntityScanner == null) { 594 fXML10EntityScanner = new XMLEntityScanner(); 595 } 596 fXML10EntityScanner.reset(fSymbolTable, this, fErrorReporter); 597 fEntityScanner = fXML10EntityScanner; 598 fEntityScanner.setCurrentEntity(fCurrentEntity); 599 } else { 600 if(fXML11EntityScanner == null) { 601 fXML11EntityScanner = new XML11EntityScanner(); 602 } 603 fXML11EntityScanner.reset(fSymbolTable, this, fErrorReporter); 604 fEntityScanner = fXML11EntityScanner; 605 fEntityScanner.setCurrentEntity(fCurrentEntity); 606 } 607 608 } 609 610 /** 611 * This method uses the passed-in XMLInputSource to make 612 * fCurrentEntity usable for reading. 613 * 614 * @param reference flag to indicate whether the entity is an Entity Reference. 615 * @param name name of the entity (XML is it's the document entity) 616 * @param xmlInputSource the input source, with sufficient information 617 * to begin scanning characters. 618 * @param literal True if this entity is started within a 619 * literal value. 620 * @param isExternal whether this entity should be treated as an internal or external entity. 621 * @throws IOException if anything can't be read 622 * XNIException If any parser-specific goes wrong. 623 * @return the encoding of the new entity or null if a character stream was employed 624 */ 625 public String setupCurrentEntity(boolean reference, String name, XMLInputSource xmlInputSource, 626 boolean literal, boolean isExternal) 627 throws IOException, XNIException { 628 // get information 629 630 final String publicId = xmlInputSource.getPublicId(); 631 String literalSystemId = xmlInputSource.getSystemId(); 632 String baseSystemId = xmlInputSource.getBaseSystemId(); 633 String encoding = xmlInputSource.getEncoding(); 634 final boolean encodingExternallySpecified = (encoding != null); 635 Boolean isBigEndian = null; 636 637 // create reader 638 InputStream stream = null; 639 Reader reader = xmlInputSource.getCharacterStream(); 640 641 // First chance checking strict URI 642 String expandedSystemId = expandSystemId(literalSystemId, baseSystemId, fStrictURI); 643 if (baseSystemId == null) { 644 baseSystemId = expandedSystemId; 645 } 646 if (reader == null) { 647 stream = xmlInputSource.getByteStream(); 648 if (stream == null) { 649 URL location = new URL(expandedSystemId); 650 URLConnection connect = location.openConnection(); 651 if (!(connect instanceof HttpURLConnection)) { 652 stream = connect.getInputStream(); 653 } 654 else { 655 boolean followRedirects = true; 656 657 // setup URLConnection if we have an HTTPInputSource 658 if (xmlInputSource instanceof HTTPInputSource) { 659 final HttpURLConnection urlConnection = (HttpURLConnection) connect; 660 final HTTPInputSource httpInputSource = (HTTPInputSource) xmlInputSource; 661 662 // set request properties 663 Iterator<Map.Entry<String, String>> propIter = httpInputSource.getHTTPRequestProperties(); 664 while (propIter.hasNext()) { 665 Map.Entry<String, String> entry = propIter.next(); 666 urlConnection.setRequestProperty(entry.getKey(), entry.getValue()); 667 } 668 669 // set preference for redirection 670 followRedirects = httpInputSource.getFollowHTTPRedirects(); 671 if (!followRedirects) { 672 urlConnection.setInstanceFollowRedirects(followRedirects); 673 } 674 } 675 676 stream = connect.getInputStream(); 677 678 // REVISIT: If the URLConnection has external encoding 679 // information, we should be reading it here. It's located 680 // in the charset parameter of Content-Type. -- mrglavas 681 682 if (followRedirects) { 683 String redirect = connect.getURL().toString(); 684 // E43: Check if the URL was redirected, and then 685 // update literal and expanded system IDs if needed. 686 if (!redirect.equals(expandedSystemId)) { 687 literalSystemId = redirect; 688 expandedSystemId = redirect; 689 } 690 } 691 } 692 } 693 694 // wrap this stream in RewindableInputStream 695 RewindableInputStream rewindableStream = new RewindableInputStream(stream); 696 stream = rewindableStream; 697 698 // perform auto-detect of encoding if necessary 699 if (encoding == null) { 700 // read first four bytes and determine encoding 701 final byte[] b4 = new byte[4]; 702 int count = 0; 703 for (; count<4; count++ ) { 704 b4[count] = (byte)rewindableStream.readAndBuffer(); 705 } 706 if (count == 4) { 707 final EncodingInfo info = getEncodingInfo(b4, count); 708 encoding = info.autoDetectedEncoding; 709 final String readerEncoding = info.readerEncoding; 710 isBigEndian = info.isBigEndian; 711 stream.reset(); 712 if (info.hasBOM) { 713 // Special case UTF-8 files with BOM created by Microsoft 714 // tools. It's more efficient to consume the BOM than make 715 // the reader perform extra checks. -Ac 716 if (EncodingInfo.STR_UTF8.equals(readerEncoding)) { 717 // UTF-8 BOM: 0xEF 0xBB 0xBF 718 stream.skip(3); 719 } 720 // It's also more efficient to consume the UTF-16 BOM. 721 else if (EncodingInfo.STR_UTF16.equals(readerEncoding)) { 722 // UTF-16 BE BOM: 0xFE 0xFF 723 // UTF-16 LE BOM: 0xFF 0xFE 724 stream.skip(2); 725 } 726 } 727 reader = createReader(stream, readerEncoding, isBigEndian); 728 } else { 729 reader = createReader(stream, encoding, isBigEndian); 730 } 731 } 732 733 // use specified encoding 734 else { 735 encoding = encoding.toUpperCase(Locale.ENGLISH); 736 737 // If encoding is UTF-8, consume BOM if one is present. 738 if (EncodingInfo.STR_UTF8.equals(encoding)) { 739 final int[] b3 = new int[3]; 740 int count = 0; 741 for (; count < 3; ++count) { 742 b3[count] = rewindableStream.readAndBuffer(); 743 if (b3[count] == -1) 744 break; 745 } 746 if (count == 3) { 747 if (b3[0] != 0xEF || b3[1] != 0xBB || b3[2] != 0xBF) { 748 // First three bytes are not BOM, so reset. 749 stream.reset(); 750 } 751 } else { 752 stream.reset(); 753 } 754 } 755 // If encoding is UTF-16, we still need to read the first 756 // four bytes, in order to discover the byte order. 757 else if (EncodingInfo.STR_UTF16.equals(encoding)) { 758 final int[] b4 = new int[4]; 759 int count = 0; 760 for (; count < 4; ++count) { 761 b4[count] = rewindableStream.readAndBuffer(); 762 if (b4[count] == -1) 763 break; 764 } 765 stream.reset(); 766 if (count >= 2) { 767 final int b0 = b4[0]; 768 final int b1 = b4[1]; 769 if (b0 == 0xFE && b1 == 0xFF) { 770 // UTF-16, big-endian 771 isBigEndian = Boolean.TRUE; 772 stream.skip(2); 773 } 774 else if (b0 == 0xFF && b1 == 0xFE) { 775 // UTF-16, little-endian 776 isBigEndian = Boolean.FALSE; 777 stream.skip(2); 778 } 779 else if (count == 4) { 780 final int b2 = b4[2]; 781 final int b3 = b4[3]; 782 if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x3F) { 783 // UTF-16, big-endian, no BOM 784 isBigEndian = Boolean.TRUE; 785 } 786 if (b0 == 0x3C && b1 == 0x00 && b2 == 0x3F && b3 == 0x00) { 787 // UTF-16, little-endian, no BOM 788 isBigEndian = Boolean.FALSE; 789 } 790 } 791 } 792 } 793 // If encoding is UCS-4, we still need to read the first four bytes 794 // in order to discover the byte order. 795 else if (EncodingInfo.STR_UCS4.equals(encoding)) { 796 final int[] b4 = new int[4]; 797 int count = 0; 798 for (; count < 4; ++count) { 799 b4[count] = rewindableStream.readAndBuffer(); 800 if (b4[count] == -1) 801 break; 802 } 803 stream.reset(); 804 805 // Ignore unusual octet order for now. 806 if (count == 4) { 807 // UCS-4, big endian (1234) 808 if (b4[0] == 0x00 && b4[1] == 0x00 && b4[2] == 0x00 && b4[3] == 0x3C) { 809 isBigEndian = Boolean.TRUE; 810 } 811 // UCS-4, little endian (1234) 812 else if (b4[0] == 0x3C && b4[1] == 0x00 && b4[2] == 0x00 && b4[3] == 0x00) { 813 isBigEndian = Boolean.FALSE; 814 } 815 } 816 } 817 // If encoding is UCS-2, we still need to read the first four bytes 818 // in order to discover the byte order. 819 else if (EncodingInfo.STR_UCS2.equals(encoding)) { 820 final int[] b4 = new int[4]; 821 int count = 0; 822 for (; count < 4; ++count) { 823 b4[count] = rewindableStream.readAndBuffer(); 824 if (b4[count] == -1) 825 break; 826 } 827 stream.reset(); 828 829 if (count == 4) { 830 // UCS-2, big endian 831 if (b4[0] == 0x00 && b4[1] == 0x3C && b4[2] == 0x00 && b4[3] == 0x3F) { 832 isBigEndian = Boolean.TRUE; 833 } 834 // UCS-2, little endian 835 else if (b4[0] == 0x3C && b4[1] == 0x00 && b4[2] == 0x3F && b4[3] == 0x00) { 836 isBigEndian = Boolean.FALSE; 837 } 838 } 839 } 840 841 reader = createReader(stream, encoding, isBigEndian); 842 } 843 844 // read one character at a time so we don't jump too far 845 // ahead, converting characters from the byte stream in 846 // the wrong encoding 847 if (DEBUG_ENCODINGS) { 848 System.out.println("$$$ no longer wrapping reader in OneCharReader"); 849 } 850 //reader = new OneCharReader(reader); 851 } 852 853 // We've seen a new Reader. 854 // Push it on the stack so we can close it later. 855 fReaderStack.push(reader); 856 857 // push entity on stack 858 if (fCurrentEntity != null) { 859 fEntityStack.push(fCurrentEntity); 860 } 861 862 // create entity 863 /* if encoding is specified externally, 'encoding' information present 864 * in the prolog of the XML document is not considered. Hence, prolog can 865 * be read in Chunks of data instead of byte by byte. 866 */ 867 fCurrentEntity = new Entity.ScannedEntity(reference, name, 868 new XMLResourceIdentifierImpl(publicId, literalSystemId, baseSystemId, expandedSystemId), 869 stream, reader, encoding, literal, encodingExternallySpecified, isExternal); 870 fCurrentEntity.setEncodingExternallySpecified(encodingExternallySpecified); 871 fEntityScanner.setCurrentEntity(fCurrentEntity); 872 fResourceIdentifier.setValues(publicId, literalSystemId, baseSystemId, expandedSystemId); 873 if (fLimitAnalyzer != null) { 874 fLimitAnalyzer.startEntity(name); 875 } 876 return encoding; 877 } //setupCurrentEntity(String, XMLInputSource, boolean, boolean): String 878 879 880 /** 881 * Checks whether an entity given by name is external. 882 * 883 * @param entityName The name of the entity to check. 884 * @return True if the entity is external, false otherwise 885 * (including when the entity is not declared). 886 */ 887 public boolean isExternalEntity(String entityName) { 888 889 Entity entity = fEntities.get(entityName); 890 if (entity == null) { 891 return false; 892 } 893 return entity.isExternal(); 894 } 895 896 /** 897 * Checks whether the declaration of an entity given by name is 898 * // in the external subset. 899 * 900 * @param entityName The name of the entity to check. 901 * @return True if the entity was declared in the external subset, false otherwise 902 * (including when the entity is not declared). 903 */ 904 public boolean isEntityDeclInExternalSubset(String entityName) { 905 906 Entity entity = fEntities.get(entityName); 907 if (entity == null) { 908 return false; 909 } 910 return entity.isEntityDeclInExternalSubset(); 911 } 912 913 914 915 // 916 // Public methods 917 // 918 919 /** 920 * Sets whether the document entity is standalone. 921 * 922 * @param standalone True if document entity is standalone. 923 */ 924 public void setStandalone(boolean standalone) { 925 fStandalone = standalone; 926 } 927 // setStandalone(boolean) 928 929 /** Returns true if the document entity is standalone. */ 930 public boolean isStandalone() { 931 return fStandalone; 932 } //isStandalone():boolean 933 934 public boolean isDeclaredEntity(String entityName) { 935 936 Entity entity = fEntities.get(entityName); 937 return entity != null; 938 } 939 940 public boolean isUnparsedEntity(String entityName) { 941 942 Entity entity = fEntities.get(entityName); 943 if (entity == null) { 944 return false; 945 } 946 return entity.isUnparsed(); 947 } 948 949 950 951 // this simply returns the fResourceIdentifier object; 952 // this should only be used with caution by callers that 953 // carefully manage the entity manager's behaviour, so that 954 // this doesn't returning meaningless or misleading data. 955 // @return a reference to the current fResourceIdentifier object 956 public XMLResourceIdentifier getCurrentResourceIdentifier() { 957 return fResourceIdentifier; 958 } 959 960 /** 961 * Sets the entity handler. When an entity starts and ends, the 962 * entity handler is notified of the change. 963 * 964 * @param entityHandler The new entity handler. 965 */ 966 967 public void setEntityHandler(com.sun.org.apache.xerces.internal.impl.XMLEntityHandler entityHandler) { 968 fEntityHandler = entityHandler; 969 } // setEntityHandler(XMLEntityHandler) 970 971 //this function returns StaxXMLInputSource 972 public StaxXMLInputSource resolveEntityAsPerStax(XMLResourceIdentifier resourceIdentifier) throws java.io.IOException{ 973 974 if(resourceIdentifier == null ) return null; 975 976 String publicId = resourceIdentifier.getPublicId(); 977 String literalSystemId = resourceIdentifier.getLiteralSystemId(); 978 String baseSystemId = resourceIdentifier.getBaseSystemId(); 979 String expandedSystemId = resourceIdentifier.getExpandedSystemId(); 980 // if no base systemId given, assume that it's relative 981 // to the systemId of the current scanned entity 982 // Sometimes the system id is not (properly) expanded. 983 // We need to expand the system id if: 984 // a. the expanded one was null; or 985 // b. the base system id was null, but becomes non-null from the current entity. 986 boolean needExpand = (expandedSystemId == null); 987 // REVISIT: why would the baseSystemId ever be null? if we 988 // didn't have to make this check we wouldn't have to reuse the 989 // fXMLResourceIdentifier object... 990 if (baseSystemId == null && fCurrentEntity != null && fCurrentEntity.entityLocation != null) { 991 baseSystemId = fCurrentEntity.entityLocation.getExpandedSystemId(); 992 if (baseSystemId != null) 993 needExpand = true; 994 } 995 if (needExpand) 996 expandedSystemId = expandSystemId(literalSystemId, baseSystemId,false); 997 998 // give the entity resolver a chance 999 StaxXMLInputSource staxInputSource = null; 1000 XMLInputSource xmlInputSource = null; 1001 1002 XMLResourceIdentifierImpl ri = null; 1003 1004 if (resourceIdentifier instanceof XMLResourceIdentifierImpl) { 1005 ri = (XMLResourceIdentifierImpl)resourceIdentifier; 1006 } else { 1007 fResourceIdentifier.clear(); 1008 ri = fResourceIdentifier; 1009 } 1010 ri.setValues(publicId, literalSystemId, baseSystemId, expandedSystemId); 1011 if(DEBUG_RESOLVER){ 1012 System.out.println("BEFORE Calling resolveEntity") ; 1013 } 1014 1015 fISCreatedByResolver = false; 1016 //either of Stax or Xerces would be null 1017 if(fStaxEntityResolver != null){ 1018 staxInputSource = fStaxEntityResolver.resolveEntity(ri); 1019 if(staxInputSource != null) { 1020 fISCreatedByResolver = true; 1021 } 1022 } 1023 1024 if(fEntityResolver != null){ 1025 xmlInputSource = fEntityResolver.resolveEntity(ri); 1026 if(xmlInputSource != null) { 1027 fISCreatedByResolver = true; 1028 } 1029 } 1030 1031 if(xmlInputSource != null){ 1032 //wrap this XMLInputSource to StaxInputSource 1033 staxInputSource = new StaxXMLInputSource(xmlInputSource, fISCreatedByResolver); 1034 } 1035 1036 if (staxInputSource == null && fUseCatalog) { 1037 if (fCatalogFeatures == null) { 1038 fCatalogFeatures = JdkXmlUtils.getCatalogFeatures(fDefer, fCatalogFile, fPrefer, fResolve); 1039 } 1040 fCatalogFile = fCatalogFeatures.get(Feature.FILES); 1041 if (fCatalogFile != null) { 1042 try { 1043 if (fCatalogResolver == null) { 1044 fCatalogResolver = CatalogManager.catalogResolver(fCatalogFeatures); 1045 } 1046 InputSource is = fCatalogResolver.resolveEntity(publicId, literalSystemId); 1047 if (is != null && !is.isEmpty()) { 1048 staxInputSource = new StaxXMLInputSource(new XMLInputSource(is, true), true); 1049 } 1050 } catch (CatalogException e) { 1051 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,"CatalogException", 1052 new Object[]{SecuritySupport.sanitizePath(fCatalogFile)}, 1053 XMLErrorReporter.SEVERITY_FATAL_ERROR, e ); 1054 } 1055 } 1056 } 1057 1058 // do default resolution 1059 //this works for both stax & Xerces, if staxInputSource is null, 1060 //it means parser need to revert to default resolution 1061 if (staxInputSource == null) { 1062 // REVISIT: when systemId is null, I think we should return null. 1063 // is this the right solution? -SG 1064 //if (systemId != null) 1065 staxInputSource = new StaxXMLInputSource( 1066 new XMLInputSource(publicId, literalSystemId, baseSystemId, true), false); 1067 }else if(staxInputSource.hasXMLStreamOrXMLEventReader()){ 1068 //Waiting for the clarification from EG. - nb 1069 } 1070 1071 if (DEBUG_RESOLVER) { 1072 System.err.println("XMLEntityManager.resolveEntity(" + publicId + ")"); 1073 System.err.println(" = " + xmlInputSource); 1074 } 1075 1076 return staxInputSource; 1077 1078 } 1079 1080 /** 1081 * Resolves the specified public and system identifiers. This 1082 * method first attempts to resolve the entity based on the 1083 * EntityResolver registered by the application. If no entity 1084 * resolver is registered or if the registered entity handler 1085 * is unable to resolve the entity, then default entity 1086 * resolution will occur. 1087 * 1088 * @param publicId The public identifier of the entity. 1089 * @param systemId The system identifier of the entity. 1090 * @param baseSystemId The base system identifier of the entity. 1091 * This is the system identifier of the current 1092 * entity and is used to expand the system 1093 * identifier when the system identifier is a 1094 * relative URI. 1095 * 1096 * @return Returns an input source that wraps the resolved entity. 1097 * This method will never return null. 1098 * 1099 * @throws IOException Thrown on i/o error. 1100 * @throws XNIException Thrown by entity resolver to signal an error. 1101 */ 1102 public XMLInputSource resolveEntity(XMLResourceIdentifier resourceIdentifier) throws IOException, XNIException { 1103 if(resourceIdentifier == null ) return null; 1104 String publicId = resourceIdentifier.getPublicId(); 1105 String literalSystemId = resourceIdentifier.getLiteralSystemId(); 1106 String baseSystemId = resourceIdentifier.getBaseSystemId(); 1107 String expandedSystemId = resourceIdentifier.getExpandedSystemId(); 1108 1109 // if no base systemId given, assume that it's relative 1110 // to the systemId of the current scanned entity 1111 // Sometimes the system id is not (properly) expanded. 1112 // We need to expand the system id if: 1113 // a. the expanded one was null; or 1114 // b. the base system id was null, but becomes non-null from the current entity. 1115 boolean needExpand = (expandedSystemId == null); 1116 // REVISIT: why would the baseSystemId ever be null? if we 1117 // didn't have to make this check we wouldn't have to reuse the 1118 // fXMLResourceIdentifier object... 1119 if (baseSystemId == null && fCurrentEntity != null && fCurrentEntity.entityLocation != null) { 1120 baseSystemId = fCurrentEntity.entityLocation.getExpandedSystemId(); 1121 if (baseSystemId != null) 1122 needExpand = true; 1123 } 1124 if (needExpand) 1125 expandedSystemId = expandSystemId(literalSystemId, baseSystemId,false); 1126 1127 // give the entity resolver a chance 1128 XMLInputSource xmlInputSource = null; 1129 1130 if (fEntityResolver != null) { 1131 resourceIdentifier.setBaseSystemId(baseSystemId); 1132 resourceIdentifier.setExpandedSystemId(expandedSystemId); 1133 xmlInputSource = fEntityResolver.resolveEntity(resourceIdentifier); 1134 } 1135 1136 if (xmlInputSource == null && fUseCatalog) { 1137 if (fCatalogFeatures == null) { 1138 fCatalogFeatures = JdkXmlUtils.getCatalogFeatures(fDefer, fCatalogFile, fPrefer, fResolve); 1139 } 1140 fCatalogFile = fCatalogFeatures.get(Feature.FILES); 1141 if (fCatalogFile != null) { 1142 /* 1143 since the method can be called from various processors, both 1144 EntityResolver and URIResolver are used to attempt to find 1145 a match 1146 */ 1147 InputSource is = null; 1148 try { 1149 if (fCatalogResolver == null) { 1150 fCatalogResolver = CatalogManager.catalogResolver(fCatalogFeatures); 1151 } 1152 String pid = (publicId != null? publicId : resourceIdentifier.getNamespace()); 1153 if (pid != null || literalSystemId != null) { 1154 is = fCatalogResolver.resolveEntity(pid, literalSystemId); 1155 } 1156 } catch (CatalogException e) {} 1157 1158 if (is != null && !is.isEmpty()) { 1159 xmlInputSource = new XMLInputSource(is, true); 1160 } else if (literalSystemId != null) { 1161 if (fCatalogResolver == null) { 1162 fCatalogResolver = CatalogManager.catalogResolver(fCatalogFeatures); 1163 } 1164 1165 Source source = null; 1166 try { 1167 source = fCatalogResolver.resolve(literalSystemId, baseSystemId); 1168 } catch (CatalogException e) { 1169 throw new XNIException(e); 1170 } 1171 if (source != null && !source.isEmpty()) { 1172 xmlInputSource = new XMLInputSource(publicId, source.getSystemId(), baseSystemId, true); 1173 } 1174 } 1175 } 1176 } 1177 1178 // do default resolution 1179 // REVISIT: what's the correct behavior if the user provided an entity 1180 // resolver (fEntityResolver != null), but resolveEntity doesn't return 1181 // an input source (xmlInputSource == null)? 1182 // do we do default resolution, or do we just return null? -SG 1183 if (xmlInputSource == null) { 1184 // REVISIT: when systemId is null, I think we should return null. 1185 // is this the right solution? -SG 1186 //if (systemId != null) 1187 xmlInputSource = new XMLInputSource(publicId, literalSystemId, baseSystemId, false); 1188 } 1189 1190 if (DEBUG_RESOLVER) { 1191 System.err.println("XMLEntityManager.resolveEntity(" + publicId + ")"); 1192 System.err.println(" = " + xmlInputSource); 1193 } 1194 1195 return xmlInputSource; 1196 1197 } // resolveEntity(XMLResourceIdentifier):XMLInputSource 1198 1199 /** 1200 * Starts a named entity. 1201 * 1202 * @param isGE flag to indicate whether the entity is a General Entity 1203 * @param entityName The name of the entity to start. 1204 * @param literal True if this entity is started within a literal 1205 * value. 1206 * 1207 * @throws IOException Thrown on i/o error. 1208 * @throws XNIException Thrown by entity handler to signal an error. 1209 */ 1210 public void startEntity(boolean isGE, String entityName, boolean literal) 1211 throws IOException, XNIException { 1212 1213 // was entity declared? 1214 Entity entity = fEntityStorage.getEntity(entityName); 1215 if (entity == null) { 1216 if (fEntityHandler != null) { 1217 String encoding = null; 1218 fResourceIdentifier.clear(); 1219 fEntityAugs.removeAllItems(); 1220 fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE); 1221 fEntityHandler.startEntity(entityName, fResourceIdentifier, encoding, fEntityAugs); 1222 fEntityAugs.removeAllItems(); 1223 fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE); 1224 fEntityHandler.endEntity(entityName, fEntityAugs); 1225 } 1226 return; 1227 } 1228 1229 // should we skip external entities? 1230 boolean external = entity.isExternal(); 1231 Entity.ExternalEntity externalEntity = null; 1232 String extLitSysId = null, extBaseSysId = null, expandedSystemId = null; 1233 if (external) { 1234 externalEntity = (Entity.ExternalEntity)entity; 1235 extLitSysId = (externalEntity.entityLocation != null ? externalEntity.entityLocation.getLiteralSystemId() : null); 1236 extBaseSysId = (externalEntity.entityLocation != null ? externalEntity.entityLocation.getBaseSystemId() : null); 1237 expandedSystemId = expandSystemId(extLitSysId, extBaseSysId); 1238 boolean unparsed = entity.isUnparsed(); 1239 boolean parameter = entityName.startsWith("%"); 1240 boolean general = !parameter; 1241 if (unparsed || (general && !fExternalGeneralEntities) || 1242 (parameter && !fExternalParameterEntities) || 1243 !fSupportDTD || !fSupportExternalEntities) { 1244 1245 if (fEntityHandler != null) { 1246 fResourceIdentifier.clear(); 1247 final String encoding = null; 1248 fResourceIdentifier.setValues( 1249 (externalEntity.entityLocation != null ? externalEntity.entityLocation.getPublicId() : null), 1250 extLitSysId, extBaseSysId, expandedSystemId); 1251 fEntityAugs.removeAllItems(); 1252 fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE); 1253 fEntityHandler.startEntity(entityName, fResourceIdentifier, encoding, fEntityAugs); 1254 fEntityAugs.removeAllItems(); 1255 fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE); 1256 fEntityHandler.endEntity(entityName, fEntityAugs); 1257 } 1258 return; 1259 } 1260 } 1261 1262 // is entity recursive? 1263 int size = fEntityStack.size(); 1264 for (int i = size; i >= 0; i--) { 1265 Entity activeEntity = i == size 1266 ? fCurrentEntity 1267 : fEntityStack.get(i); 1268 if (activeEntity.name == entityName) { 1269 String path = entityName; 1270 for (int j = i + 1; j < size; j++) { 1271 activeEntity = fEntityStack.get(j); 1272 path = path + " -> " + activeEntity.name; 1273 } 1274 path = path + " -> " + fCurrentEntity.name; 1275 path = path + " -> " + entityName; 1276 fErrorReporter.reportError(this.getEntityScanner(),XMLMessageFormatter.XML_DOMAIN, 1277 "RecursiveReference", 1278 new Object[] { entityName, path }, 1279 XMLErrorReporter.SEVERITY_FATAL_ERROR); 1280 1281 if (fEntityHandler != null) { 1282 fResourceIdentifier.clear(); 1283 final String encoding = null; 1284 if (external) { 1285 fResourceIdentifier.setValues( 1286 (externalEntity.entityLocation != null ? externalEntity.entityLocation.getPublicId() : null), 1287 extLitSysId, extBaseSysId, expandedSystemId); 1288 } 1289 fEntityAugs.removeAllItems(); 1290 fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE); 1291 fEntityHandler.startEntity(entityName, fResourceIdentifier, encoding, fEntityAugs); 1292 fEntityAugs.removeAllItems(); 1293 fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE); 1294 fEntityHandler.endEntity(entityName, fEntityAugs); 1295 } 1296 1297 return; 1298 } 1299 } 1300 1301 // resolve external entity 1302 StaxXMLInputSource staxInputSource = null; 1303 XMLInputSource xmlInputSource = null ; 1304 1305 if (external) { 1306 staxInputSource = resolveEntityAsPerStax(externalEntity.entityLocation); 1307 /** xxx: Waiting from the EG 1308 * //simply return if there was entity resolver registered and application 1309 * //returns either XMLStreamReader or XMLEventReader. 1310 * if(staxInputSource.hasXMLStreamOrXMLEventReader()) return ; 1311 */ 1312 xmlInputSource = staxInputSource.getXMLInputSource() ; 1313 if (!fISCreatedByResolver) { 1314 //let the not-LoadExternalDTD or not-SupportDTD process to handle the situation 1315 if (fLoadExternalDTD) { 1316 String accessError = SecuritySupport.checkAccess(expandedSystemId, fAccessExternalDTD, Constants.ACCESS_EXTERNAL_ALL); 1317 if (accessError != null) { 1318 fErrorReporter.reportError(this.getEntityScanner(),XMLMessageFormatter.XML_DOMAIN, 1319 "AccessExternalEntity", 1320 new Object[] { SecuritySupport.sanitizePath(expandedSystemId), accessError }, 1321 XMLErrorReporter.SEVERITY_FATAL_ERROR); 1322 } 1323 } 1324 } 1325 } 1326 // wrap internal entity 1327 else { 1328 Entity.InternalEntity internalEntity = (Entity.InternalEntity)entity; 1329 Reader reader = new StringReader(internalEntity.text); 1330 xmlInputSource = new XMLInputSource(null, null, null, reader, null); 1331 } 1332 1333 // start the entity 1334 startEntity(isGE, entityName, xmlInputSource, literal, external); 1335 1336 } // startEntity(String,boolean) 1337 1338 /** 1339 * Starts the document entity. The document entity has the "[xml]" 1340 * pseudo-name. 1341 * 1342 * @param xmlInputSource The input source of the document entity. 1343 * 1344 * @throws IOException Thrown on i/o error. 1345 * @throws XNIException Thrown by entity handler to signal an error. 1346 */ 1347 public void startDocumentEntity(XMLInputSource xmlInputSource) 1348 throws IOException, XNIException { 1349 startEntity(false, XMLEntity, xmlInputSource, false, true); 1350 } // startDocumentEntity(XMLInputSource) 1351 1352 //xxx these methods are not required. 1353 /** 1354 * Starts the DTD entity. The DTD entity has the "[dtd]" 1355 * pseudo-name. 1356 * 1357 * @param xmlInputSource The input source of the DTD entity. 1358 * 1359 * @throws IOException Thrown on i/o error. 1360 * @throws XNIException Thrown by entity handler to signal an error. 1361 */ 1362 public void startDTDEntity(XMLInputSource xmlInputSource) 1363 throws IOException, XNIException { 1364 startEntity(false, DTDEntity, xmlInputSource, false, true); 1365 } // startDTDEntity(XMLInputSource) 1366 1367 // indicate start of external subset so that 1368 // location of entity decls can be tracked 1369 public void startExternalSubset() { 1370 fInExternalSubset = true; 1371 } 1372 1373 public void endExternalSubset() { 1374 fInExternalSubset = false; 1375 } 1376 1377 /** 1378 * Starts an entity. 1379 * <p> 1380 * This method can be used to insert an application defined XML 1381 * entity stream into the parsing stream. 1382 * 1383 * @param isGE flag to indicate whether the entity is a General Entity 1384 * @param name The name of the entity. 1385 * @param xmlInputSource The input source of the entity. 1386 * @param literal True if this entity is started within a 1387 * literal value. 1388 * @param isExternal whether this entity should be treated as an internal or external entity. 1389 * 1390 * @throws IOException Thrown on i/o error. 1391 * @throws XNIException Thrown by entity handler to signal an error. 1392 */ 1393 public void startEntity(boolean isGE, String name, 1394 XMLInputSource xmlInputSource, 1395 boolean literal, boolean isExternal) 1396 throws IOException, XNIException { 1397 1398 String encoding = setupCurrentEntity(isGE, name, xmlInputSource, literal, isExternal); 1399 1400 //when entity expansion limit is set by the Application, we need to 1401 //check for the entity expansion limit set by the parser, if number of entity 1402 //expansions exceeds the entity expansion limit, parser will throw fatal error. 1403 // Note that this represents the nesting level of open entities. 1404 fEntityExpansionCount++; 1405 if(fLimitAnalyzer != null) { 1406 fLimitAnalyzer.addValue(entityExpansionIndex, name, 1); 1407 } 1408 if( fSecurityManager != null && fSecurityManager.isOverLimit(entityExpansionIndex, fLimitAnalyzer)){ 1409 fSecurityManager.debugPrint(fLimitAnalyzer); 1410 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,"EntityExpansionLimit", 1411 new Object[]{fSecurityManager.getLimitValueByIndex(entityExpansionIndex)}, 1412 XMLErrorReporter.SEVERITY_FATAL_ERROR ); 1413 // is there anything better to do than reset the counter? 1414 // at least one can envision debugging applications where this might 1415 // be useful... 1416 fEntityExpansionCount = 0; 1417 } 1418 1419 // call handler 1420 if (fEntityHandler != null) { 1421 fEntityHandler.startEntity(name, fResourceIdentifier, encoding, null); 1422 } 1423 1424 } // startEntity(String,XMLInputSource) 1425 1426 /** 1427 * Return the current entity being scanned. Current entity is SET using startEntity function. 1428 * @return Entity.ScannedEntity 1429 */ 1430 1431 public Entity.ScannedEntity getCurrentEntity(){ 1432 return fCurrentEntity ; 1433 } 1434 1435 /** 1436 * Return the top level entity handled by this manager, or null 1437 * if no entity was added. 1438 */ 1439 public Entity.ScannedEntity getTopLevelEntity() { 1440 return (Entity.ScannedEntity) 1441 (fEntityStack.empty() ? null : fEntityStack.get(0)); 1442 } 1443 1444 // A stack containing all the open readers 1445 protected Stack<Reader> fReaderStack = new Stack<>(); 1446 1447 /** 1448 * Close all opened InputStreams and Readers opened by this parser. 1449 */ 1450 public void closeReaders() { 1451 // close all readers 1452 while (!fReaderStack.isEmpty()) { 1453 try { 1454 (fReaderStack.pop()).close(); 1455 } catch (IOException e) { 1456 // ignore 1457 } 1458 } 1459 } 1460 1461 public void endEntity() throws IOException, XNIException { 1462 1463 // call handler 1464 if (DEBUG_BUFFER) { 1465 System.out.print("(endEntity: "); 1466 print(); 1467 System.out.println(); 1468 } 1469 //pop the entity from the stack 1470 Entity.ScannedEntity entity = fEntityStack.size() > 0 ? (Entity.ScannedEntity)fEntityStack.pop() : null ; 1471 1472 /** need to close the reader first since the program can end 1473 * prematurely (e.g. fEntityHandler.endEntity may throw exception) 1474 * leaving the reader open 1475 */ 1476 //close the reader 1477 if(fCurrentEntity != null){ 1478 //close the reader 1479 try{ 1480 if (fLimitAnalyzer != null) { 1481 fLimitAnalyzer.endEntity(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT, fCurrentEntity.name); 1482 if (fCurrentEntity.name.equals("[xml]")) { 1483 fSecurityManager.debugPrint(fLimitAnalyzer); 1484 } 1485 } 1486 fCurrentEntity.close(); 1487 }catch(IOException ex){ 1488 throw new XNIException(ex); 1489 } 1490 } 1491 1492 // REVISIT: We should never encounter underflow if the calls 1493 // to startEntity and endEntity are balanced, but guard 1494 // against the EmptyStackException for now. -- mrglavas 1495 if (!fReaderStack.isEmpty()) { 1496 fReaderStack.pop(); 1497 } 1498 1499 if (fEntityHandler != null) { 1500 //so this is the last opened entity, signal it to current fEntityHandler using Augmentation 1501 if(entity == null){ 1502 fEntityAugs.removeAllItems(); 1503 fEntityAugs.putItem(Constants.LAST_ENTITY, Boolean.TRUE); 1504 fEntityHandler.endEntity(fCurrentEntity.name, fEntityAugs); 1505 fEntityAugs.removeAllItems(); 1506 }else{ 1507 fEntityHandler.endEntity(fCurrentEntity.name, null); 1508 } 1509 } 1510 //check if it is a document entity 1511 boolean documentEntity = fCurrentEntity.name == XMLEntity; 1512 1513 //set popped entity as current entity 1514 fCurrentEntity = entity; 1515 fEntityScanner.setCurrentEntity(fCurrentEntity); 1516 1517 //check if there are any entity left in the stack -- if there are 1518 //no entries EOF has been reached. 1519 // throw exception when it is the last entity but it is not a document entity 1520 1521 if(fCurrentEntity == null & !documentEntity){ 1522 throw new EOFException() ; 1523 } 1524 1525 if (DEBUG_BUFFER) { 1526 System.out.print(")endEntity: "); 1527 print(); 1528 System.out.println(); 1529 } 1530 1531 } // endEntity() 1532 1533 1534 // 1535 // XMLComponent methods 1536 // 1537 public void reset(PropertyManager propertyManager){ 1538 // xerces properties 1539 fSymbolTable = (SymbolTable)propertyManager.getProperty(Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY); 1540 fErrorReporter = (XMLErrorReporter)propertyManager.getProperty(Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY); 1541 try { 1542 fStaxEntityResolver = (StaxEntityResolverWrapper)propertyManager.getProperty(STAX_ENTITY_RESOLVER); 1543 } catch (XMLConfigurationException e) { 1544 fStaxEntityResolver = null; 1545 } 1546 1547 fSupportDTD = ((Boolean)propertyManager.getProperty(XMLInputFactory.SUPPORT_DTD)); 1548 fReplaceEntityReferences = ((Boolean)propertyManager.getProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES)); 1549 fSupportExternalEntities = ((Boolean)propertyManager.getProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES)); 1550 1551 // Zephyr feature ignore-external-dtd is the opposite of Xerces' load-external-dtd 1552 fLoadExternalDTD = !((Boolean)propertyManager.getProperty(Constants.ZEPHYR_PROPERTY_PREFIX + Constants.IGNORE_EXTERNAL_DTD)); 1553 1554 //Use Catalog 1555 fUseCatalog = (Boolean)propertyManager.getProperty(XMLConstants.USE_CATALOG); 1556 fCatalogFile = (String)propertyManager.getProperty(JdkXmlUtils.CATALOG_FILES); 1557 fDefer = (String)propertyManager.getProperty(JdkXmlUtils.CATALOG_DEFER); 1558 fPrefer = (String)propertyManager.getProperty(JdkXmlUtils.CATALOG_PREFER); 1559 fResolve = (String)propertyManager.getProperty(JdkXmlUtils.CATALOG_RESOLVE); 1560 1561 // JAXP 1.5 feature 1562 XMLSecurityPropertyManager spm = (XMLSecurityPropertyManager) propertyManager.getProperty(XML_SECURITY_PROPERTY_MANAGER); 1563 fAccessExternalDTD = spm.getValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_DTD); 1564 1565 fSecurityManager = (XMLSecurityManager)propertyManager.getProperty(SECURITY_MANAGER); 1566 1567 fLimitAnalyzer = new XMLLimitAnalyzer(); 1568 //reset fEntityStorage 1569 fEntityStorage.reset(propertyManager); 1570 //reset XMLEntityReaderImpl 1571 fEntityScanner.reset(propertyManager); 1572 1573 // initialize state 1574 //fStandalone = false; 1575 fEntities.clear(); 1576 fEntityStack.removeAllElements(); 1577 fCurrentEntity = null; 1578 fValidation = false; 1579 fExternalGeneralEntities = true; 1580 fExternalParameterEntities = true; 1581 fAllowJavaEncodings = true ; 1582 } 1583 1584 /** 1585 * Resets the component. The component can query the component manager 1586 * about any features and properties that affect the operation of the 1587 * component. 1588 * 1589 * @param componentManager The component manager. 1590 * 1591 * @throws SAXException Thrown by component on initialization error. 1592 * For example, if a feature or property is 1593 * required for the operation of the component, the 1594 * component manager may throw a 1595 * SAXNotRecognizedException or a 1596 * SAXNotSupportedException. 1597 */ 1598 public void reset(XMLComponentManager componentManager) 1599 throws XMLConfigurationException { 1600 1601 boolean parser_settings = componentManager.getFeature(PARSER_SETTINGS, true); 1602 1603 if (!parser_settings) { 1604 // parser settings have not been changed 1605 reset(); 1606 if(fEntityScanner != null){ 1607 fEntityScanner.reset(componentManager); 1608 } 1609 if(fEntityStorage != null){ 1610 fEntityStorage.reset(componentManager); 1611 } 1612 return; 1613 } 1614 1615 // sax features 1616 fValidation = componentManager.getFeature(VALIDATION, false); 1617 fExternalGeneralEntities = componentManager.getFeature(EXTERNAL_GENERAL_ENTITIES, true); 1618 fExternalParameterEntities = componentManager.getFeature(EXTERNAL_PARAMETER_ENTITIES, true); 1619 1620 // xerces features 1621 fAllowJavaEncodings = componentManager.getFeature(ALLOW_JAVA_ENCODINGS, false); 1622 fWarnDuplicateEntityDef = componentManager.getFeature(WARN_ON_DUPLICATE_ENTITYDEF, false); 1623 fStrictURI = componentManager.getFeature(STANDARD_URI_CONFORMANT, false); 1624 fLoadExternalDTD = componentManager.getFeature(LOAD_EXTERNAL_DTD, true); 1625 1626 // xerces properties 1627 fSymbolTable = (SymbolTable)componentManager.getProperty(SYMBOL_TABLE); 1628 fErrorReporter = (XMLErrorReporter)componentManager.getProperty(ERROR_REPORTER); 1629 fEntityResolver = (XMLEntityResolver)componentManager.getProperty(ENTITY_RESOLVER, null); 1630 fStaxEntityResolver = (StaxEntityResolverWrapper)componentManager.getProperty(STAX_ENTITY_RESOLVER, null); 1631 fValidationManager = (ValidationManager)componentManager.getProperty(VALIDATION_MANAGER, null); 1632 fSecurityManager = (XMLSecurityManager)componentManager.getProperty(SECURITY_MANAGER, null); 1633 entityExpansionIndex = fSecurityManager.getIndex(Constants.JDK_ENTITY_EXPANSION_LIMIT); 1634 1635 //StAX Property 1636 fSupportDTD = true; 1637 fReplaceEntityReferences = true; 1638 fSupportExternalEntities = true; 1639 1640 // JAXP 1.5 feature 1641 XMLSecurityPropertyManager spm = (XMLSecurityPropertyManager) componentManager.getProperty(XML_SECURITY_PROPERTY_MANAGER, null); 1642 if (spm == null) { 1643 spm = new XMLSecurityPropertyManager(); 1644 } 1645 fAccessExternalDTD = spm.getValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_DTD); 1646 1647 //Use Catalog 1648 fUseCatalog = componentManager.getFeature(XMLConstants.USE_CATALOG, true); 1649 fCatalogFile = (String)componentManager.getProperty(JdkXmlUtils.CATALOG_FILES); 1650 fDefer = (String)componentManager.getProperty(JdkXmlUtils.CATALOG_DEFER); 1651 fPrefer = (String)componentManager.getProperty(JdkXmlUtils.CATALOG_PREFER); 1652 fResolve = (String)componentManager.getProperty(JdkXmlUtils.CATALOG_RESOLVE); 1653 1654 //reset general state 1655 reset(); 1656 1657 fEntityScanner.reset(componentManager); 1658 fEntityStorage.reset(componentManager); 1659 1660 } // reset(XMLComponentManager) 1661 1662 // reset general state. Should not be called other than by 1663 // a class acting as a component manager but not 1664 // implementing that interface for whatever reason. 1665 public void reset() { 1666 fLimitAnalyzer = new XMLLimitAnalyzer(); 1667 // initialize state 1668 fStandalone = false; 1669 fEntities.clear(); 1670 fEntityStack.removeAllElements(); 1671 fEntityExpansionCount = 0; 1672 1673 fCurrentEntity = null; 1674 // reset scanner 1675 if(fXML10EntityScanner != null){ 1676 fXML10EntityScanner.reset(fSymbolTable, this, fErrorReporter); 1677 } 1678 if(fXML11EntityScanner != null) { 1679 fXML11EntityScanner.reset(fSymbolTable, this, fErrorReporter); 1680 } 1681 1682 // DEBUG 1683 if (DEBUG_ENTITIES) { 1684 addInternalEntity("text", "Hello, World."); 1685 addInternalEntity("empty-element", "<foo/>"); 1686 addInternalEntity("balanced-element", "<foo></foo>"); 1687 addInternalEntity("balanced-element-with-text", "<foo>Hello, World</foo>"); 1688 addInternalEntity("balanced-element-with-entity", "<foo>&text;</foo>"); 1689 addInternalEntity("unbalanced-entity", "<foo>"); 1690 addInternalEntity("recursive-entity", "<foo>&recursive-entity2;</foo>"); 1691 addInternalEntity("recursive-entity2", "<bar>&recursive-entity3;</bar>"); 1692 addInternalEntity("recursive-entity3", "<baz>&recursive-entity;</baz>"); 1693 try { 1694 addExternalEntity("external-text", null, "external-text.ent", "test/external-text.xml"); 1695 addExternalEntity("external-balanced-element", null, "external-balanced-element.ent", "test/external-balanced-element.xml"); 1696 addExternalEntity("one", null, "ent/one.ent", "test/external-entity.xml"); 1697 addExternalEntity("two", null, "ent/two.ent", "test/ent/one.xml"); 1698 } 1699 catch (IOException ex) { 1700 // should never happen 1701 } 1702 } 1703 1704 fEntityHandler = null; 1705 1706 // reset scanner 1707 //if(fEntityScanner!=null) 1708 // fEntityScanner.reset(fSymbolTable, this,fErrorReporter); 1709 1710 } 1711 /** 1712 * Returns a list of feature identifiers that are recognized by 1713 * this component. This method may return null if no features 1714 * are recognized by this component. 1715 */ 1716 public String[] getRecognizedFeatures() { 1717 return RECOGNIZED_FEATURES.clone(); 1718 } // getRecognizedFeatures():String[] 1719 1720 /** 1721 * Sets the state of a feature. This method is called by the component 1722 * manager any time after reset when a feature changes state. 1723 * <p> 1724 * <strong>Note:</strong> Components should silently ignore features 1725 * that do not affect the operation of the component. 1726 * 1727 * @param featureId The feature identifier. 1728 * @param state The state of the feature. 1729 * 1730 * @throws SAXNotRecognizedException The component should not throw 1731 * this exception. 1732 * @throws SAXNotSupportedException The component should not throw 1733 * this exception. 1734 */ 1735 public void setFeature(String featureId, boolean state) 1736 throws XMLConfigurationException { 1737 1738 // xerces features 1739 if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) { 1740 final int suffixLength = featureId.length() - Constants.XERCES_FEATURE_PREFIX.length(); 1741 if (suffixLength == Constants.ALLOW_JAVA_ENCODINGS_FEATURE.length() && 1742 featureId.endsWith(Constants.ALLOW_JAVA_ENCODINGS_FEATURE)) { 1743 fAllowJavaEncodings = state; 1744 } 1745 if (suffixLength == Constants.LOAD_EXTERNAL_DTD_FEATURE.length() && 1746 featureId.endsWith(Constants.LOAD_EXTERNAL_DTD_FEATURE)) { 1747 fLoadExternalDTD = state; 1748 return; 1749 } 1750 } else if (featureId.equals(XMLConstants.USE_CATALOG)) { 1751 fUseCatalog = state; 1752 } 1753 1754 } // setFeature(String,boolean) 1755 1756 /** 1757 * Sets the value of a property. This method is called by the component 1758 * manager any time after reset when a property changes value. 1759 * <p> 1760 * <strong>Note:</strong> Components should silently ignore properties 1761 * that do not affect the operation of the component. 1762 * 1763 * @param propertyId The property identifier. 1764 * @param value The value of the property. 1765 * 1766 * @throws SAXNotRecognizedException The component should not throw 1767 * this exception. 1768 * @throws SAXNotSupportedException The component should not throw 1769 * this exception. 1770 */ 1771 public void setProperty(String propertyId, Object value){ 1772 // Xerces properties 1773 if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) { 1774 final int suffixLength = propertyId.length() - Constants.XERCES_PROPERTY_PREFIX.length(); 1775 1776 if (suffixLength == Constants.SYMBOL_TABLE_PROPERTY.length() && 1777 propertyId.endsWith(Constants.SYMBOL_TABLE_PROPERTY)) { 1778 fSymbolTable = (SymbolTable)value; 1779 return; 1780 } 1781 if (suffixLength == Constants.ERROR_REPORTER_PROPERTY.length() && 1782 propertyId.endsWith(Constants.ERROR_REPORTER_PROPERTY)) { 1783 fErrorReporter = (XMLErrorReporter)value; 1784 return; 1785 } 1786 if (suffixLength == Constants.ENTITY_RESOLVER_PROPERTY.length() && 1787 propertyId.endsWith(Constants.ENTITY_RESOLVER_PROPERTY)) { 1788 fEntityResolver = (XMLEntityResolver)value; 1789 return; 1790 } 1791 if (suffixLength == Constants.BUFFER_SIZE_PROPERTY.length() && 1792 propertyId.endsWith(Constants.BUFFER_SIZE_PROPERTY)) { 1793 Integer bufferSize = (Integer)value; 1794 if (bufferSize != null && 1795 bufferSize.intValue() > DEFAULT_XMLDECL_BUFFER_SIZE) { 1796 fBufferSize = bufferSize.intValue(); 1797 fEntityScanner.setBufferSize(fBufferSize); 1798 } 1799 } 1800 if (suffixLength == Constants.SECURITY_MANAGER_PROPERTY.length() && 1801 propertyId.endsWith(Constants.SECURITY_MANAGER_PROPERTY)) { 1802 fSecurityManager = (XMLSecurityManager)value; 1803 } 1804 } 1805 1806 //JAXP 1.5 properties 1807 if (propertyId.equals(XML_SECURITY_PROPERTY_MANAGER)) 1808 { 1809 XMLSecurityPropertyManager spm = (XMLSecurityPropertyManager)value; 1810 fAccessExternalDTD = spm.getValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_DTD); 1811 return; 1812 } 1813 1814 //Catalog properties 1815 if (propertyId.equals(JdkXmlUtils.CATALOG_FILES)) { 1816 fCatalogFile = (String)value; 1817 } else if (propertyId.equals(JdkXmlUtils.CATALOG_DEFER)) { 1818 fDefer = (String)value; 1819 } else if (propertyId.equals(JdkXmlUtils.CATALOG_PREFER)) { 1820 fPrefer = (String)value; 1821 } else if (propertyId.equals(JdkXmlUtils.CATALOG_RESOLVE)) { 1822 fResolve = (String)value; 1823 } 1824 } 1825 1826 public void setLimitAnalyzer(XMLLimitAnalyzer fLimitAnalyzer) { 1827 this.fLimitAnalyzer = fLimitAnalyzer; 1828 } 1829 1830 /** 1831 * Returns a list of property identifiers that are recognized by 1832 * this component. This method may return null if no properties 1833 * are recognized by this component. 1834 */ 1835 public String[] getRecognizedProperties() { 1836 return RECOGNIZED_PROPERTIES.clone(); 1837 } // getRecognizedProperties():String[] 1838 /** 1839 * Returns the default state for a feature, or null if this 1840 * component does not want to report a default value for this 1841 * feature. 1842 * 1843 * @param featureId The feature identifier. 1844 * 1845 * @since Xerces 2.2.0 1846 */ 1847 public Boolean getFeatureDefault(String featureId) { 1848 for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) { 1849 if (RECOGNIZED_FEATURES[i].equals(featureId)) { 1850 return FEATURE_DEFAULTS[i]; 1851 } 1852 } 1853 return null; 1854 } // getFeatureDefault(String):Boolean 1855 1856 /** 1857 * Returns the default state for a property, or null if this 1858 * component does not want to report a default value for this 1859 * property. 1860 * 1861 * @param propertyId The property identifier. 1862 * 1863 * @since Xerces 2.2.0 1864 */ 1865 public Object getPropertyDefault(String propertyId) { 1866 for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) { 1867 if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) { 1868 return PROPERTY_DEFAULTS[i]; 1869 } 1870 } 1871 return null; 1872 } // getPropertyDefault(String):Object 1873 1874 // 1875 // Public static methods 1876 // 1877 1878 /** 1879 * Expands a system id and returns the system id as a URI, if 1880 * it can be expanded. A return value of null means that the 1881 * identifier is already expanded. An exception thrown 1882 * indicates a failure to expand the id. 1883 * 1884 * @param systemId The systemId to be expanded. 1885 * 1886 * @return Returns the URI string representing the expanded system 1887 * identifier. A null value indicates that the given 1888 * system identifier is already expanded. 1889 * 1890 */ 1891 public static String expandSystemId(String systemId) { 1892 return expandSystemId(systemId, null); 1893 } // expandSystemId(String):String 1894 1895 // 1896 // Public static methods 1897 // 1898 1899 // current value of the "user.dir" property 1900 private static String gUserDir; 1901 // cached URI object for the current value of the escaped "user.dir" property stored as a URI 1902 private static URI gUserDirURI; 1903 // which ASCII characters need to be escaped 1904 private static boolean gNeedEscaping[] = new boolean[128]; 1905 // the first hex character if a character needs to be escaped 1906 private static char gAfterEscaping1[] = new char[128]; 1907 // the second hex character if a character needs to be escaped 1908 private static char gAfterEscaping2[] = new char[128]; 1909 private static char[] gHexChs = {'0', '1', '2', '3', '4', '5', '6', '7', 1910 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; 1911 // initialize the above 3 arrays 1912 static { 1913 for (int i = 0; i <= 0x1f; i++) { 1914 gNeedEscaping[i] = true; 1915 gAfterEscaping1[i] = gHexChs[i >> 4]; 1916 gAfterEscaping2[i] = gHexChs[i & 0xf]; 1917 } 1918 gNeedEscaping[0x7f] = true; 1919 gAfterEscaping1[0x7f] = '7'; 1920 gAfterEscaping2[0x7f] = 'F'; 1921 char[] escChs = {' ', '<', '>', '#', '%', '"', '{', '}', 1922 '|', '\\', '^', '~', '[', ']', '`'}; 1923 int len = escChs.length; 1924 char ch; 1925 for (int i = 0; i < len; i++) { 1926 ch = escChs[i]; 1927 gNeedEscaping[ch] = true; 1928 gAfterEscaping1[ch] = gHexChs[ch >> 4]; 1929 gAfterEscaping2[ch] = gHexChs[ch & 0xf]; 1930 } 1931 } 1932 1933 // To escape the "user.dir" system property, by using %HH to represent 1934 // special ASCII characters: 0x00~0x1F, 0x7F, ' ', '<', '>', '#', '%' 1935 // and '"'. It's a static method, so needs to be synchronized. 1936 // this method looks heavy, but since the system property isn't expected 1937 // to change often, so in most cases, we only need to return the URI 1938 // that was escaped before. 1939 // According to the URI spec, non-ASCII characters (whose value >= 128) 1940 // need to be escaped too. 1941 // REVISIT: don't know how to escape non-ASCII characters, especially 1942 // which encoding to use. Leave them for now. 1943 private static synchronized URI getUserDir() throws URI.MalformedURIException { 1944 // get the user.dir property 1945 String userDir = ""; 1946 try { 1947 userDir = SecuritySupport.getSystemProperty("user.dir"); 1948 } 1949 catch (SecurityException se) { 1950 } 1951 1952 // return empty string if property value is empty string. 1953 if (userDir.length() == 0) 1954 return new URI("file", "", "", null, null); 1955 // compute the new escaped value if the new property value doesn't 1956 // match the previous one 1957 if (gUserDirURI != null && userDir.equals(gUserDir)) { 1958 return gUserDirURI; 1959 } 1960 1961 // record the new value as the global property value 1962 gUserDir = userDir; 1963 1964 char separator = java.io.File.separatorChar; 1965 userDir = userDir.replace(separator, '/'); 1966 1967 int len = userDir.length(), ch; 1968 StringBuilder buffer = new StringBuilder(len*3); 1969 // change C:/blah to /C:/blah 1970 if (len >= 2 && userDir.charAt(1) == ':') { 1971 ch = Character.toUpperCase(userDir.charAt(0)); 1972 if (ch >= 'A' && ch <= 'Z') { 1973 buffer.append('/'); 1974 } 1975 } 1976 1977 // for each character in the path 1978 int i = 0; 1979 for (; i < len; i++) { 1980 ch = userDir.charAt(i); 1981 // if it's not an ASCII character, break here, and use UTF-8 encoding 1982 if (ch >= 128) 1983 break; 1984 if (gNeedEscaping[ch]) { 1985 buffer.append('%'); 1986 buffer.append(gAfterEscaping1[ch]); 1987 buffer.append(gAfterEscaping2[ch]); 1988 // record the fact that it's escaped 1989 } 1990 else { 1991 buffer.append((char)ch); 1992 } 1993 } 1994 1995 // we saw some non-ascii character 1996 if (i < len) { 1997 // get UTF-8 bytes for the remaining sub-string 1998 byte[] bytes = null; 1999 byte b; 2000 try { 2001 bytes = userDir.substring(i).getBytes("UTF-8"); 2002 } catch (java.io.UnsupportedEncodingException e) { 2003 // should never happen 2004 return new URI("file", "", userDir, null, null); 2005 } 2006 len = bytes.length; 2007 2008 // for each byte 2009 for (i = 0; i < len; i++) { 2010 b = bytes[i]; 2011 // for non-ascii character: make it positive, then escape 2012 if (b < 0) { 2013 ch = b + 256; 2014 buffer.append('%'); 2015 buffer.append(gHexChs[ch >> 4]); 2016 buffer.append(gHexChs[ch & 0xf]); 2017 } 2018 else if (gNeedEscaping[b]) { 2019 buffer.append('%'); 2020 buffer.append(gAfterEscaping1[b]); 2021 buffer.append(gAfterEscaping2[b]); 2022 } 2023 else { 2024 buffer.append((char)b); 2025 } 2026 } 2027 } 2028 2029 // change blah/blah to blah/blah/ 2030 if (!userDir.endsWith("/")) 2031 buffer.append('/'); 2032 2033 gUserDirURI = new URI("file", "", buffer.toString(), null, null); 2034 2035 return gUserDirURI; 2036 } 2037 2038 public static OutputStream createOutputStream(String uri) throws IOException { 2039 // URI was specified. Handle relative URIs. 2040 final String expanded = XMLEntityManager.expandSystemId(uri, null, true); 2041 final URL url = new URL(expanded != null ? expanded : uri); 2042 OutputStream out = null; 2043 String protocol = url.getProtocol(); 2044 String host = url.getHost(); 2045 // Use FileOutputStream if this URI is for a local file. 2046 if (protocol.equals("file") 2047 && (host == null || host.length() == 0 || host.equals("localhost"))) { 2048 File file = new File(getPathWithoutEscapes(url.getPath())); 2049 if (!file.exists()) { 2050 File parent = file.getParentFile(); 2051 if (parent != null && !parent.exists()) { 2052 parent.mkdirs(); 2053 } 2054 } 2055 out = new FileOutputStream(file); 2056 } 2057 // Try to write to some other kind of URI. Some protocols 2058 // won't support this, though HTTP should work. 2059 else { 2060 URLConnection urlCon = url.openConnection(); 2061 urlCon.setDoInput(false); 2062 urlCon.setDoOutput(true); 2063 urlCon.setUseCaches(false); // Enable tunneling. 2064 if (urlCon instanceof HttpURLConnection) { 2065 // The DOM L3 REC says if we are writing to an HTTP URI 2066 // it is to be done with an HTTP PUT. 2067 HttpURLConnection httpCon = (HttpURLConnection) urlCon; 2068 httpCon.setRequestMethod("PUT"); 2069 } 2070 out = urlCon.getOutputStream(); 2071 } 2072 return out; 2073 } 2074 2075 private static String getPathWithoutEscapes(String origPath) { 2076 if (origPath != null && origPath.length() != 0 && origPath.indexOf('%') != -1) { 2077 // Locate the escape characters 2078 StringTokenizer tokenizer = new StringTokenizer(origPath, "%"); 2079 StringBuilder result = new StringBuilder(origPath.length()); 2080 int size = tokenizer.countTokens(); 2081 result.append(tokenizer.nextToken()); 2082 for(int i = 1; i < size; ++i) { 2083 String token = tokenizer.nextToken(); 2084 // Decode the 2 digit hexadecimal number following % in '%nn' 2085 result.append((char)Integer.valueOf(token.substring(0, 2), 16).intValue()); 2086 result.append(token.substring(2)); 2087 } 2088 return result.toString(); 2089 } 2090 return origPath; 2091 } 2092 2093 /** 2094 * Absolutizes a URI using the current value 2095 * of the "user.dir" property as the base URI. If 2096 * the URI is already absolute, this is a no-op. 2097 * 2098 * @param uri the URI to absolutize 2099 */ 2100 public static void absolutizeAgainstUserDir(URI uri) 2101 throws URI.MalformedURIException { 2102 uri.absolutize(getUserDir()); 2103 } 2104 2105 /** 2106 * Expands a system id and returns the system id as a URI, if 2107 * it can be expanded. A return value of null means that the 2108 * identifier is already expanded. An exception thrown 2109 * indicates a failure to expand the id. 2110 * 2111 * @param systemId The systemId to be expanded. 2112 * 2113 * @return Returns the URI string representing the expanded system 2114 * identifier. A null value indicates that the given 2115 * system identifier is already expanded. 2116 * 2117 */ 2118 public static String expandSystemId(String systemId, String baseSystemId) { 2119 2120 // check for bad parameters id 2121 if (systemId == null || systemId.length() == 0) { 2122 return systemId; 2123 } 2124 // if id already expanded, return 2125 try { 2126 URI uri = new URI(systemId); 2127 if (uri != null) { 2128 return systemId; 2129 } 2130 } catch (URI.MalformedURIException e) { 2131 // continue on... 2132 } 2133 // normalize id 2134 String id = fixURI(systemId); 2135 2136 // normalize base 2137 URI base = null; 2138 URI uri = null; 2139 try { 2140 if (baseSystemId == null || baseSystemId.length() == 0 || 2141 baseSystemId.equals(systemId)) { 2142 String dir = getUserDir().toString(); 2143 base = new URI("file", "", dir, null, null); 2144 } else { 2145 try { 2146 base = new URI(fixURI(baseSystemId)); 2147 } catch (URI.MalformedURIException e) { 2148 if (baseSystemId.indexOf(':') != -1) { 2149 // for xml schemas we might have baseURI with 2150 // a specified drive 2151 base = new URI("file", "", fixURI(baseSystemId), null, null); 2152 } else { 2153 String dir = getUserDir().toString(); 2154 dir = dir + fixURI(baseSystemId); 2155 base = new URI("file", "", dir, null, null); 2156 } 2157 } 2158 } 2159 // expand id 2160 uri = new URI(base, id); 2161 } catch (Exception e) { 2162 // let it go through 2163 2164 } 2165 2166 if (uri == null) { 2167 return systemId; 2168 } 2169 return uri.toString(); 2170 2171 } // expandSystemId(String,String):String 2172 2173 /** 2174 * Expands a system id and returns the system id as a URI, if 2175 * it can be expanded. A return value of null means that the 2176 * identifier is already expanded. An exception thrown 2177 * indicates a failure to expand the id. 2178 * 2179 * @param systemId The systemId to be expanded. 2180 * 2181 * @return Returns the URI string representing the expanded system 2182 * identifier. A null value indicates that the given 2183 * system identifier is already expanded. 2184 * 2185 */ 2186 public static String expandSystemId(String systemId, String baseSystemId, 2187 boolean strict) 2188 throws URI.MalformedURIException { 2189 2190 // check if there is a system id before 2191 // trying to expand it. 2192 if (systemId == null) { 2193 return null; 2194 } 2195 2196 // system id has to be a valid URI 2197 if (strict) { 2198 try { 2199 // if it's already an absolute one, return it 2200 new URI(systemId); 2201 return systemId; 2202 } 2203 catch (URI.MalformedURIException ex) { 2204 } 2205 URI base = null; 2206 // if there isn't a base uri, use the working directory 2207 if (baseSystemId == null || baseSystemId.length() == 0) { 2208 base = new URI("file", "", getUserDir().toString(), null, null); 2209 } 2210 // otherwise, use the base uri 2211 else { 2212 try { 2213 base = new URI(baseSystemId); 2214 } 2215 catch (URI.MalformedURIException e) { 2216 // assume "base" is also a relative uri 2217 String dir = getUserDir().toString(); 2218 dir = dir + baseSystemId; 2219 base = new URI("file", "", dir, null, null); 2220 } 2221 } 2222 // absolutize the system id using the base 2223 URI uri = new URI(base, systemId); 2224 // return the string rep of the new uri (an absolute one) 2225 return uri.toString(); 2226 2227 // if any exception is thrown, it'll get thrown to the caller. 2228 } 2229 2230 // Assume the URIs are well-formed. If it turns out they're not, try fixing them up. 2231 try { 2232 return expandSystemIdStrictOff(systemId, baseSystemId); 2233 } 2234 catch (URI.MalformedURIException e) { 2235 /** Xerces URI rejects unicode, try java.net.URI 2236 * this is not ideal solution, but it covers known cases which either 2237 * Xerces URI or java.net.URI can handle alone 2238 * will file bug against java.net.URI 2239 */ 2240 try { 2241 return expandSystemIdStrictOff1(systemId, baseSystemId); 2242 } catch (URISyntaxException ex) { 2243 // continue on... 2244 } 2245 } 2246 // check for bad parameters id 2247 if (systemId.length() == 0) { 2248 return systemId; 2249 } 2250 2251 // normalize id 2252 String id = fixURI(systemId); 2253 2254 // normalize base 2255 URI base = null; 2256 URI uri = null; 2257 try { 2258 if (baseSystemId == null || baseSystemId.length() == 0 || 2259 baseSystemId.equals(systemId)) { 2260 base = getUserDir(); 2261 } 2262 else { 2263 try { 2264 base = new URI(fixURI(baseSystemId).trim()); 2265 } 2266 catch (URI.MalformedURIException e) { 2267 if (baseSystemId.indexOf(':') != -1) { 2268 // for xml schemas we might have baseURI with 2269 // a specified drive 2270 base = new URI("file", "", fixURI(baseSystemId).trim(), null, null); 2271 } 2272 else { 2273 base = new URI(getUserDir(), fixURI(baseSystemId)); 2274 } 2275 } 2276 } 2277 // expand id 2278 uri = new URI(base, id.trim()); 2279 } 2280 catch (Exception e) { 2281 // let it go through 2282 2283 } 2284 2285 if (uri == null) { 2286 return systemId; 2287 } 2288 return uri.toString(); 2289 2290 } // expandSystemId(String,String,boolean):String 2291 2292 /** 2293 * Helper method for expandSystemId(String,String,boolean):String 2294 */ 2295 private static String expandSystemIdStrictOn(String systemId, String baseSystemId) 2296 throws URI.MalformedURIException { 2297 2298 URI systemURI = new URI(systemId, true); 2299 // If it's already an absolute one, return it 2300 if (systemURI.isAbsoluteURI()) { 2301 return systemId; 2302 } 2303 2304 // If there isn't a base URI, use the working directory 2305 URI baseURI = null; 2306 if (baseSystemId == null || baseSystemId.length() == 0) { 2307 baseURI = getUserDir(); 2308 } 2309 else { 2310 baseURI = new URI(baseSystemId, true); 2311 if (!baseURI.isAbsoluteURI()) { 2312 // assume "base" is also a relative uri 2313 baseURI.absolutize(getUserDir()); 2314 } 2315 } 2316 2317 // absolutize the system identifier using the base URI 2318 systemURI.absolutize(baseURI); 2319 2320 // return the string rep of the new uri (an absolute one) 2321 return systemURI.toString(); 2322 2323 // if any exception is thrown, it'll get thrown to the caller. 2324 2325 } // expandSystemIdStrictOn(String,String):String 2326 2327 /** 2328 * Helper method for expandSystemId(String,String,boolean):String 2329 */ 2330 private static String expandSystemIdStrictOff(String systemId, String baseSystemId) 2331 throws URI.MalformedURIException { 2332 2333 URI systemURI = new URI(systemId, true); 2334 // If it's already an absolute one, return it 2335 if (systemURI.isAbsoluteURI()) { 2336 if (systemURI.getScheme().length() > 1) { 2337 return systemId; 2338 } 2339 /** 2340 * If the scheme's length is only one character, 2341 * it's likely that this was intended as a file 2342 * path. Fixing this up in expandSystemId to 2343 * maintain backwards compatibility. 2344 */ 2345 throw new URI.MalformedURIException(); 2346 } 2347 2348 // If there isn't a base URI, use the working directory 2349 URI baseURI = null; 2350 if (baseSystemId == null || baseSystemId.length() == 0) { 2351 baseURI = getUserDir(); 2352 } 2353 else { 2354 baseURI = new URI(baseSystemId, true); 2355 if (!baseURI.isAbsoluteURI()) { 2356 // assume "base" is also a relative uri 2357 baseURI.absolutize(getUserDir()); 2358 } 2359 } 2360 2361 // absolutize the system identifier using the base URI 2362 systemURI.absolutize(baseURI); 2363 2364 // return the string rep of the new uri (an absolute one) 2365 return systemURI.toString(); 2366 2367 // if any exception is thrown, it'll get thrown to the caller. 2368 2369 } // expandSystemIdStrictOff(String,String):String 2370 2371 private static String expandSystemIdStrictOff1(String systemId, String baseSystemId) 2372 throws URISyntaxException, URI.MalformedURIException { 2373 2374 java.net.URI systemURI = new java.net.URI(systemId); 2375 // If it's already an absolute one, return it 2376 if (systemURI.isAbsolute()) { 2377 if (systemURI.getScheme().length() > 1) { 2378 return systemId; 2379 } 2380 /** 2381 * If the scheme's length is only one character, 2382 * it's likely that this was intended as a file 2383 * path. Fixing this up in expandSystemId to 2384 * maintain backwards compatibility. 2385 */ 2386 throw new URISyntaxException(systemId, "the scheme's length is only one character"); 2387 } 2388 2389 // If there isn't a base URI, use the working directory 2390 URI baseURI = null; 2391 if (baseSystemId == null || baseSystemId.length() == 0) { 2392 baseURI = getUserDir(); 2393 } 2394 else { 2395 baseURI = new URI(baseSystemId, true); 2396 if (!baseURI.isAbsoluteURI()) { 2397 // assume "base" is also a relative uri 2398 baseURI.absolutize(getUserDir()); 2399 } 2400 } 2401 2402 // absolutize the system identifier using the base URI 2403 // systemURI.absolutize(baseURI); 2404 systemURI = (new java.net.URI(baseURI.toString())).resolve(systemURI); 2405 2406 // return the string rep of the new uri (an absolute one) 2407 return systemURI.toString(); 2408 2409 // if any exception is thrown, it'll get thrown to the caller. 2410 2411 } // expandSystemIdStrictOff(String,String):String 2412 2413 // 2414 // Protected methods 2415 // 2416 2417 2418 /** 2419 * Returns the IANA encoding name that is auto-detected from 2420 * the bytes specified, with the endian-ness of that encoding where appropriate. 2421 * 2422 * @param b4 The first four bytes of the input. 2423 * @param count The number of bytes actually read. 2424 * @return an instance of EncodingInfo which represents the auto-detected encoding. 2425 */ 2426 protected EncodingInfo getEncodingInfo(byte[] b4, int count) { 2427 2428 if (count < 2) { 2429 return EncodingInfo.UTF_8; 2430 } 2431 2432 // UTF-16, with BOM 2433 int b0 = b4[0] & 0xFF; 2434 int b1 = b4[1] & 0xFF; 2435 if (b0 == 0xFE && b1 == 0xFF) { 2436 // UTF-16, big-endian 2437 return EncodingInfo.UTF_16_BIG_ENDIAN_WITH_BOM; 2438 } 2439 if (b0 == 0xFF && b1 == 0xFE) { 2440 // UTF-16, little-endian 2441 return EncodingInfo.UTF_16_LITTLE_ENDIAN_WITH_BOM; 2442 } 2443 2444 // default to UTF-8 if we don't have enough bytes to make a 2445 // good determination of the encoding 2446 if (count < 3) { 2447 return EncodingInfo.UTF_8; 2448 } 2449 2450 // UTF-8 with a BOM 2451 int b2 = b4[2] & 0xFF; 2452 if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF) { 2453 return EncodingInfo.UTF_8_WITH_BOM; 2454 } 2455 2456 // default to UTF-8 if we don't have enough bytes to make a 2457 // good determination of the encoding 2458 if (count < 4) { 2459 return EncodingInfo.UTF_8; 2460 } 2461 2462 // other encodings 2463 int b3 = b4[3] & 0xFF; 2464 if (b0 == 0x00 && b1 == 0x00 && b2 == 0x00 && b3 == 0x3C) { 2465 // UCS-4, big endian (1234) 2466 return EncodingInfo.UCS_4_BIG_ENDIAN; 2467 } 2468 if (b0 == 0x3C && b1 == 0x00 && b2 == 0x00 && b3 == 0x00) { 2469 // UCS-4, little endian (4321) 2470 return EncodingInfo.UCS_4_LITTLE_ENDIAN; 2471 } 2472 if (b0 == 0x00 && b1 == 0x00 && b2 == 0x3C && b3 == 0x00) { 2473 // UCS-4, unusual octet order (2143) 2474 // REVISIT: What should this be? 2475 return EncodingInfo.UCS_4_UNUSUAL_BYTE_ORDER; 2476 } 2477 if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x00) { 2478 // UCS-4, unusual octect order (3412) 2479 // REVISIT: What should this be? 2480 return EncodingInfo.UCS_4_UNUSUAL_BYTE_ORDER; 2481 } 2482 if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x3F) { 2483 // UTF-16, big-endian, no BOM 2484 // (or could turn out to be UCS-2... 2485 // REVISIT: What should this be? 2486 return EncodingInfo.UTF_16_BIG_ENDIAN; 2487 } 2488 if (b0 == 0x3C && b1 == 0x00 && b2 == 0x3F && b3 == 0x00) { 2489 // UTF-16, little-endian, no BOM 2490 // (or could turn out to be UCS-2... 2491 return EncodingInfo.UTF_16_LITTLE_ENDIAN; 2492 } 2493 if (b0 == 0x4C && b1 == 0x6F && b2 == 0xA7 && b3 == 0x94) { 2494 // EBCDIC 2495 // a la xerces1, return CP037 instead of EBCDIC here 2496 return EncodingInfo.EBCDIC; 2497 } 2498 2499 // default encoding 2500 return EncodingInfo.UTF_8; 2501 2502 } // getEncodingName(byte[],int):Object[] 2503 2504 /** 2505 * Creates a reader capable of reading the given input stream in 2506 * the specified encoding. 2507 * 2508 * @param inputStream The input stream. 2509 * @param encoding The encoding name that the input stream is 2510 * encoded using. If the user has specified that 2511 * Java encoding names are allowed, then the 2512 * encoding name may be a Java encoding name; 2513 * otherwise, it is an ianaEncoding name. 2514 * @param isBigEndian For encodings (like uCS-4), whose names cannot 2515 * specify a byte order, this tells whether the order 2516 * is bigEndian. null if unknown or irrelevant. 2517 * 2518 * @return Returns a reader. 2519 */ 2520 protected Reader createReader(InputStream inputStream, String encoding, Boolean isBigEndian) 2521 throws IOException { 2522 2523 String enc = (encoding != null) ? encoding : EncodingInfo.STR_UTF8; 2524 enc = enc.toUpperCase(Locale.ENGLISH); 2525 MessageFormatter f = fErrorReporter.getMessageFormatter(XMLMessageFormatter.XML_DOMAIN); 2526 Locale l = fErrorReporter.getLocale(); 2527 switch (enc) { 2528 case EncodingInfo.STR_UTF8: 2529 return new UTF8Reader(inputStream, fBufferSize, f, l); 2530 case EncodingInfo.STR_UTF16: 2531 if (isBigEndian != null) { 2532 return new UTF16Reader(inputStream, fBufferSize, isBigEndian, f, l); 2533 } 2534 break; 2535 case EncodingInfo.STR_UTF16BE: 2536 return new UTF16Reader(inputStream, fBufferSize, true, f, l); 2537 case EncodingInfo.STR_UTF16LE: 2538 return new UTF16Reader(inputStream, fBufferSize, false, f, l); 2539 case EncodingInfo.STR_UCS4: 2540 if(isBigEndian != null) { 2541 if(isBigEndian) { 2542 return new UCSReader(inputStream, UCSReader.UCS4BE); 2543 } else { 2544 return new UCSReader(inputStream, UCSReader.UCS4LE); 2545 } 2546 } else { 2547 fErrorReporter.reportError(this.getEntityScanner(), 2548 XMLMessageFormatter.XML_DOMAIN, 2549 "EncodingByteOrderUnsupported", 2550 new Object[] { encoding }, 2551 XMLErrorReporter.SEVERITY_FATAL_ERROR); 2552 } 2553 break; 2554 case EncodingInfo.STR_UCS2: 2555 if(isBigEndian != null) { 2556 if(isBigEndian) { 2557 return new UCSReader(inputStream, UCSReader.UCS2BE); 2558 } else { 2559 return new UCSReader(inputStream, UCSReader.UCS2LE); 2560 } 2561 } else { 2562 fErrorReporter.reportError(this.getEntityScanner(), 2563 XMLMessageFormatter.XML_DOMAIN, 2564 "EncodingByteOrderUnsupported", 2565 new Object[] { encoding }, 2566 XMLErrorReporter.SEVERITY_FATAL_ERROR); 2567 } 2568 break; 2569 } 2570 2571 // check for valid name 2572 boolean validIANA = XMLChar.isValidIANAEncoding(encoding); 2573 boolean validJava = XMLChar.isValidJavaEncoding(encoding); 2574 if (!validIANA || (fAllowJavaEncodings && !validJava)) { 2575 fErrorReporter.reportError(this.getEntityScanner(), 2576 XMLMessageFormatter.XML_DOMAIN, 2577 "EncodingDeclInvalid", 2578 new Object[] { encoding }, 2579 XMLErrorReporter.SEVERITY_FATAL_ERROR); 2580 // NOTE: AndyH suggested that, on failure, we use ISO Latin 1 2581 // because every byte is a valid ISO Latin 1 character. 2582 // It may not translate correctly but if we failed on 2583 // the encoding anyway, then we're expecting the content 2584 // of the document to be bad. This will just prevent an 2585 // invalid UTF-8 sequence to be detected. This is only 2586 // important when continue-after-fatal-error is turned 2587 // on. -Ac 2588 encoding = "ISO-8859-1"; 2589 } 2590 2591 // try to use a Java reader 2592 String javaEncoding = EncodingMap.getIANA2JavaMapping(enc); 2593 if (javaEncoding == null) { 2594 if (fAllowJavaEncodings) { 2595 javaEncoding = encoding; 2596 } else { 2597 fErrorReporter.reportError(this.getEntityScanner(), 2598 XMLMessageFormatter.XML_DOMAIN, 2599 "EncodingDeclInvalid", 2600 new Object[] { encoding }, 2601 XMLErrorReporter.SEVERITY_FATAL_ERROR); 2602 // see comment above. 2603 javaEncoding = "ISO8859_1"; 2604 } 2605 } 2606 if (DEBUG_ENCODINGS) { 2607 System.out.print("$$$ creating Java InputStreamReader: encoding="+javaEncoding); 2608 if (javaEncoding == encoding) { 2609 System.out.print(" (IANA encoding)"); 2610 } 2611 System.out.println(); 2612 } 2613 return new BufferedReader( new InputStreamReader(inputStream, javaEncoding)); 2614 2615 } // createReader(InputStream,String, Boolean): Reader 2616 2617 2618 /** 2619 * Return the public identifier for the current document event. 2620 * <p> 2621 * The return value is the public identifier of the document 2622 * entity or of the external parsed entity in which the markup 2623 * triggering the event appears. 2624 * 2625 * @return A string containing the public identifier, or 2626 * null if none is available. 2627 */ 2628 public String getPublicId() { 2629 return (fCurrentEntity != null && fCurrentEntity.entityLocation != null) ? fCurrentEntity.entityLocation.getPublicId() : null; 2630 } // getPublicId():String 2631 2632 /** 2633 * Return the expanded system identifier for the current document event. 2634 * <p> 2635 * The return value is the expanded system identifier of the document 2636 * entity or of the external parsed entity in which the markup 2637 * triggering the event appears. 2638 * <p> 2639 * If the system identifier is a URL, the parser must resolve it 2640 * fully before passing it to the application. 2641 * 2642 * @return A string containing the expanded system identifier, or null 2643 * if none is available. 2644 */ 2645 public String getExpandedSystemId() { 2646 if (fCurrentEntity != null) { 2647 if (fCurrentEntity.entityLocation != null && 2648 fCurrentEntity.entityLocation.getExpandedSystemId() != null ) { 2649 return fCurrentEntity.entityLocation.getExpandedSystemId(); 2650 } else { 2651 // search for the first external entity on the stack 2652 int size = fEntityStack.size(); 2653 for (int i = size - 1; i >= 0 ; i--) { 2654 Entity.ScannedEntity externalEntity = 2655 (Entity.ScannedEntity)fEntityStack.get(i); 2656 2657 if (externalEntity.entityLocation != null && 2658 externalEntity.entityLocation.getExpandedSystemId() != null) { 2659 return externalEntity.entityLocation.getExpandedSystemId(); 2660 } 2661 } 2662 } 2663 } 2664 return null; 2665 } // getExpandedSystemId():String 2666 2667 /** 2668 * Return the literal system identifier for the current document event. 2669 * <p> 2670 * The return value is the literal system identifier of the document 2671 * entity or of the external parsed entity in which the markup 2672 * triggering the event appears. 2673 * <p> 2674 * @return A string containing the literal system identifier, or null 2675 * if none is available. 2676 */ 2677 public String getLiteralSystemId() { 2678 if (fCurrentEntity != null) { 2679 if (fCurrentEntity.entityLocation != null && 2680 fCurrentEntity.entityLocation.getLiteralSystemId() != null ) { 2681 return fCurrentEntity.entityLocation.getLiteralSystemId(); 2682 } else { 2683 // search for the first external entity on the stack 2684 int size = fEntityStack.size(); 2685 for (int i = size - 1; i >= 0 ; i--) { 2686 Entity.ScannedEntity externalEntity = 2687 (Entity.ScannedEntity)fEntityStack.get(i); 2688 2689 if (externalEntity.entityLocation != null && 2690 externalEntity.entityLocation.getLiteralSystemId() != null) { 2691 return externalEntity.entityLocation.getLiteralSystemId(); 2692 } 2693 } 2694 } 2695 } 2696 return null; 2697 } // getLiteralSystemId():String 2698 2699 /** 2700 * Return the line number where the current document event ends. 2701 * <p> 2702 * <strong>Warning:</strong> The return value from the method 2703 * is intended only as an approximation for the sake of error 2704 * reporting; it is not intended to provide sufficient information 2705 * to edit the character content of the original XML document. 2706 * <p> 2707 * The return value is an approximation of the line number 2708 * in the document entity or external parsed entity where the 2709 * markup triggering the event appears. 2710 * <p> 2711 * If possible, the SAX driver should provide the line position 2712 * of the first character after the text associated with the document 2713 * event. The first line in the document is line 1. 2714 * 2715 * @return The line number, or -1 if none is available. 2716 */ 2717 public int getLineNumber() { 2718 if (fCurrentEntity != null) { 2719 if (fCurrentEntity.isExternal()) { 2720 return fCurrentEntity.lineNumber; 2721 } else { 2722 // search for the first external entity on the stack 2723 int size = fEntityStack.size(); 2724 for (int i=size-1; i>0 ; i--) { 2725 Entity.ScannedEntity firstExternalEntity = (Entity.ScannedEntity)fEntityStack.get(i); 2726 if (firstExternalEntity.isExternal()) { 2727 return firstExternalEntity.lineNumber; 2728 } 2729 } 2730 } 2731 } 2732 2733 return -1; 2734 2735 } // getLineNumber():int 2736 2737 /** 2738 * Return the column number where the current document event ends. 2739 * <p> 2740 * <strong>Warning:</strong> The return value from the method 2741 * is intended only as an approximation for the sake of error 2742 * reporting; it is not intended to provide sufficient information 2743 * to edit the character content of the original XML document. 2744 * <p> 2745 * The return value is an approximation of the column number 2746 * in the document entity or external parsed entity where the 2747 * markup triggering the event appears. 2748 * <p> 2749 * If possible, the SAX driver should provide the line position 2750 * of the first character after the text associated with the document 2751 * event. 2752 * <p> 2753 * If possible, the SAX driver should provide the line position 2754 * of the first character after the text associated with the document 2755 * event. The first column in each line is column 1. 2756 * 2757 * @return The column number, or -1 if none is available. 2758 */ 2759 public int getColumnNumber() { 2760 if (fCurrentEntity != null) { 2761 if (fCurrentEntity.isExternal()) { 2762 return fCurrentEntity.columnNumber; 2763 } else { 2764 // search for the first external entity on the stack 2765 int size = fEntityStack.size(); 2766 for (int i=size-1; i>0 ; i--) { 2767 Entity.ScannedEntity firstExternalEntity = (Entity.ScannedEntity)fEntityStack.get(i); 2768 if (firstExternalEntity.isExternal()) { 2769 return firstExternalEntity.columnNumber; 2770 } 2771 } 2772 } 2773 } 2774 2775 return -1; 2776 } // getColumnNumber():int 2777 2778 2779 // 2780 // Protected static methods 2781 // 2782 2783 /** 2784 * Fixes a platform dependent filename to standard URI form. 2785 * 2786 * @param str The string to fix. 2787 * 2788 * @return Returns the fixed URI string. 2789 */ 2790 protected static String fixURI(String str) { 2791 2792 // handle platform dependent strings 2793 str = str.replace(java.io.File.separatorChar, '/'); 2794 2795 // Windows fix 2796 if (str.length() >= 2) { 2797 char ch1 = str.charAt(1); 2798 // change "C:blah" to "/C:blah" 2799 if (ch1 == ':') { 2800 char ch0 = Character.toUpperCase(str.charAt(0)); 2801 if (ch0 >= 'A' && ch0 <= 'Z') { 2802 str = "/" + str; 2803 } 2804 } 2805 // change "//blah" to "file://blah" 2806 else if (ch1 == '/' && str.charAt(0) == '/') { 2807 str = "file:" + str; 2808 } 2809 } 2810 2811 // replace spaces in file names with %20. 2812 // Original comment from JDK5: the following algorithm might not be 2813 // very performant, but people who want to use invalid URI's have to 2814 // pay the price. 2815 int pos = str.indexOf(' '); 2816 if (pos >= 0) { 2817 StringBuilder sb = new StringBuilder(str.length()); 2818 // put characters before ' ' into the string builder 2819 for (int i = 0; i < pos; i++) 2820 sb.append(str.charAt(i)); 2821 // and %20 for the space 2822 sb.append("%20"); 2823 // for the remamining part, also convert ' ' to "%20". 2824 for (int i = pos+1; i < str.length(); i++) { 2825 if (str.charAt(i) == ' ') 2826 sb.append("%20"); 2827 else 2828 sb.append(str.charAt(i)); 2829 } 2830 str = sb.toString(); 2831 } 2832 2833 // done 2834 return str; 2835 2836 } // fixURI(String):String 2837 2838 2839 // 2840 // Package visible methods 2841 // 2842 /** Prints the contents of the buffer. */ 2843 final void print() { 2844 if (DEBUG_BUFFER) { 2845 if (fCurrentEntity != null) { 2846 System.out.print('['); 2847 System.out.print(fCurrentEntity.count); 2848 System.out.print(' '); 2849 System.out.print(fCurrentEntity.position); 2850 if (fCurrentEntity.count > 0) { 2851 System.out.print(" \""); 2852 for (int i = 0; i < fCurrentEntity.count; i++) { 2853 if (i == fCurrentEntity.position) { 2854 System.out.print('^'); 2855 } 2856 char c = fCurrentEntity.ch[i]; 2857 switch (c) { 2858 case '\n': { 2859 System.out.print("\\n"); 2860 break; 2861 } 2862 case '\r': { 2863 System.out.print("\\r"); 2864 break; 2865 } 2866 case '\t': { 2867 System.out.print("\\t"); 2868 break; 2869 } 2870 case '\\': { 2871 System.out.print("\\\\"); 2872 break; 2873 } 2874 default: { 2875 System.out.print(c); 2876 } 2877 } 2878 } 2879 if (fCurrentEntity.position == fCurrentEntity.count) { 2880 System.out.print('^'); 2881 } 2882 System.out.print('"'); 2883 } 2884 System.out.print(']'); 2885 System.out.print(" @ "); 2886 System.out.print(fCurrentEntity.lineNumber); 2887 System.out.print(','); 2888 System.out.print(fCurrentEntity.columnNumber); 2889 } else { 2890 System.out.print("*NO CURRENT ENTITY*"); 2891 } 2892 } 2893 } // print() 2894 2895 /** 2896 * Information about auto-detectable encodings. 2897 * 2898 * @xerces.internal 2899 * 2900 * @author Michael Glavassevich, IBM 2901 */ 2902 private static class EncodingInfo { 2903 public static final String STR_UTF8 = "UTF-8"; 2904 public static final String STR_UTF16 = "UTF-16"; 2905 public static final String STR_UTF16BE = "UTF-16BE"; 2906 public static final String STR_UTF16LE = "UTF-16LE"; 2907 public static final String STR_UCS4 = "ISO-10646-UCS-4"; 2908 public static final String STR_UCS2 = "ISO-10646-UCS-2"; 2909 public static final String STR_CP037 = "CP037"; 2910 2911 /** UTF-8 **/ 2912 public static final EncodingInfo UTF_8 = 2913 new EncodingInfo(STR_UTF8, null, false); 2914 2915 /** UTF-8, with BOM **/ 2916 public static final EncodingInfo UTF_8_WITH_BOM = 2917 new EncodingInfo(STR_UTF8, null, true); 2918 2919 /** UTF-16, big-endian **/ 2920 public static final EncodingInfo UTF_16_BIG_ENDIAN = 2921 new EncodingInfo(STR_UTF16BE, STR_UTF16, Boolean.TRUE, false); 2922 2923 /** UTF-16, big-endian with BOM **/ 2924 public static final EncodingInfo UTF_16_BIG_ENDIAN_WITH_BOM = 2925 new EncodingInfo(STR_UTF16BE, STR_UTF16, Boolean.TRUE, true); 2926 2927 /** UTF-16, little-endian **/ 2928 public static final EncodingInfo UTF_16_LITTLE_ENDIAN = 2929 new EncodingInfo(STR_UTF16LE, STR_UTF16, Boolean.FALSE, false); 2930 2931 /** UTF-16, little-endian with BOM **/ 2932 public static final EncodingInfo UTF_16_LITTLE_ENDIAN_WITH_BOM = 2933 new EncodingInfo(STR_UTF16LE, STR_UTF16, Boolean.FALSE, true); 2934 2935 /** UCS-4, big-endian **/ 2936 public static final EncodingInfo UCS_4_BIG_ENDIAN = 2937 new EncodingInfo(STR_UCS4, Boolean.TRUE, false); 2938 2939 /** UCS-4, little-endian **/ 2940 public static final EncodingInfo UCS_4_LITTLE_ENDIAN = 2941 new EncodingInfo(STR_UCS4, Boolean.FALSE, false); 2942 2943 /** UCS-4, unusual byte-order (2143) or (3412) **/ 2944 public static final EncodingInfo UCS_4_UNUSUAL_BYTE_ORDER = 2945 new EncodingInfo(STR_UCS4, null, false); 2946 2947 /** EBCDIC **/ 2948 public static final EncodingInfo EBCDIC = new EncodingInfo(STR_CP037, null, false); 2949 2950 public final String autoDetectedEncoding; 2951 public final String readerEncoding; 2952 public final Boolean isBigEndian; 2953 public final boolean hasBOM; 2954 2955 private EncodingInfo(String autoDetectedEncoding, Boolean isBigEndian, boolean hasBOM) { 2956 this(autoDetectedEncoding, autoDetectedEncoding, isBigEndian, hasBOM); 2957 } // <init>(String,Boolean,boolean) 2958 2959 private EncodingInfo(String autoDetectedEncoding, String readerEncoding, 2960 Boolean isBigEndian, boolean hasBOM) { 2961 this.autoDetectedEncoding = autoDetectedEncoding; 2962 this.readerEncoding = readerEncoding; 2963 this.isBigEndian = isBigEndian; 2964 this.hasBOM = hasBOM; 2965 } // <init>(String,String,Boolean,boolean) 2966 2967 } // class EncodingInfo 2968 2969 /** 2970 * This class wraps the byte inputstreams we're presented with. 2971 * We need it because java.io.InputStreams don't provide 2972 * functionality to reread processed bytes, and they have a habit 2973 * of reading more than one character when you call their read() 2974 * methods. This means that, once we discover the true (declared) 2975 * encoding of a document, we can neither backtrack to read the 2976 * whole doc again nor start reading where we are with a new 2977 * reader. 2978 * 2979 * This class allows rewinding an inputStream by allowing a mark 2980 * to be set, and the stream reset to that position. <strong>The 2981 * class assumes that it needs to read one character per 2982 * invocation when it's read() method is inovked, but uses the 2983 * underlying InputStream's read(char[], offset length) method--it 2984 * won't buffer data read this way!</strong> 2985 * 2986 * @xerces.internal 2987 * 2988 * @author Neil Graham, IBM 2989 * @author Glenn Marcy, IBM 2990 */ 2991 2992 protected final class RewindableInputStream extends InputStream { 2993 2994 private InputStream fInputStream; 2995 private byte[] fData; 2996 private int fStartOffset; 2997 private int fEndOffset; 2998 private int fOffset; 2999 private int fLength; 3000 private int fMark; 3001 3002 public RewindableInputStream(InputStream is) { 3003 fData = new byte[DEFAULT_XMLDECL_BUFFER_SIZE]; 3004 fInputStream = is; 3005 fStartOffset = 0; 3006 fEndOffset = -1; 3007 fOffset = 0; 3008 fLength = 0; 3009 fMark = 0; 3010 } 3011 3012 public void setStartOffset(int offset) { 3013 fStartOffset = offset; 3014 } 3015 3016 public void rewind() { 3017 fOffset = fStartOffset; 3018 } 3019 3020 public int readAndBuffer() throws IOException { 3021 if (fOffset == fData.length) { 3022 byte[] newData = new byte[fOffset << 1]; 3023 System.arraycopy(fData, 0, newData, 0, fOffset); 3024 fData = newData; 3025 } 3026 final int b = fInputStream.read(); 3027 if (b == -1) { 3028 fEndOffset = fOffset; 3029 return -1; 3030 } 3031 fData[fLength++] = (byte)b; 3032 fOffset++; 3033 return b & 0xff; 3034 } 3035 3036 public int read() throws IOException { 3037 if (fOffset < fLength) { 3038 return fData[fOffset++] & 0xff; 3039 } 3040 if (fOffset == fEndOffset) { 3041 return -1; 3042 } 3043 if (fCurrentEntity.mayReadChunks) { 3044 return fInputStream.read(); 3045 } 3046 return readAndBuffer(); 3047 } 3048 3049 public int read(byte[] b, int off, int len) throws IOException { 3050 final int bytesLeft = fLength - fOffset; 3051 if (bytesLeft == 0) { 3052 if (fOffset == fEndOffset) { 3053 return -1; 3054 } 3055 3056 // read a block of data as requested 3057 if(fCurrentEntity.mayReadChunks || !fCurrentEntity.xmlDeclChunkRead) { 3058 3059 if (!fCurrentEntity.xmlDeclChunkRead) 3060 { 3061 fCurrentEntity.xmlDeclChunkRead = true; 3062 len = Entity.ScannedEntity.DEFAULT_XMLDECL_BUFFER_SIZE; 3063 } 3064 return fInputStream.read(b, off, len); 3065 } 3066 int returnedVal = readAndBuffer(); 3067 if (returnedVal == -1) { 3068 fEndOffset = fOffset; 3069 return -1; 3070 } 3071 b[off] = (byte)returnedVal; 3072 return 1; 3073 } 3074 if (len < bytesLeft) { 3075 if (len <= 0) { 3076 return 0; 3077 } 3078 } else { 3079 len = bytesLeft; 3080 } 3081 if (b != null) { 3082 System.arraycopy(fData, fOffset, b, off, len); 3083 } 3084 fOffset += len; 3085 return len; 3086 } 3087 3088 public long skip(long n) throws IOException { 3089 int bytesLeft; 3090 if (n <= 0) { 3091 return 0; 3092 } 3093 bytesLeft = fLength - fOffset; 3094 if (bytesLeft == 0) { 3095 if (fOffset == fEndOffset) { 3096 return 0; 3097 } 3098 return fInputStream.skip(n); 3099 } 3100 if (n <= bytesLeft) { 3101 fOffset += n; 3102 return n; 3103 } 3104 fOffset += bytesLeft; 3105 if (fOffset == fEndOffset) { 3106 return bytesLeft; 3107 } 3108 n -= bytesLeft; 3109 /* 3110 * In a manner of speaking, when this class isn't permitting more 3111 * than one byte at a time to be read, it is "blocking". The 3112 * available() method should indicate how much can be read without 3113 * blocking, so while we're in this mode, it should only indicate 3114 * that bytes in its buffer are available; otherwise, the result of 3115 * available() on the underlying InputStream is appropriate. 3116 */ 3117 return fInputStream.skip(n) + bytesLeft; 3118 } 3119 3120 public int available() throws IOException { 3121 final int bytesLeft = fLength - fOffset; 3122 if (bytesLeft == 0) { 3123 if (fOffset == fEndOffset) { 3124 return -1; 3125 } 3126 return fCurrentEntity.mayReadChunks ? fInputStream.available() 3127 : 0; 3128 } 3129 return bytesLeft; 3130 } 3131 3132 public void mark(int howMuch) { 3133 fMark = fOffset; 3134 } 3135 3136 public void reset() { 3137 fOffset = fMark; 3138 } 3139 3140 public boolean markSupported() { 3141 return true; 3142 } 3143 3144 public void close() throws IOException { 3145 if (fInputStream != null) { 3146 fInputStream.close(); 3147 fInputStream = null; 3148 } 3149 } 3150 } // end of RewindableInputStream class 3151 3152 public void test(){ 3153 //System.out.println("TESTING: Added familytree to entityManager"); 3154 //Usecase1 3155 fEntityStorage.addExternalEntity("entityUsecase1",null, 3156 "/space/home/stax/sun/6thJan2004/zephyr/data/test.txt", 3157 "/space/home/stax/sun/6thJan2004/zephyr/data/entity.xml"); 3158 3159 //Usecase2 3160 fEntityStorage.addInternalEntity("entityUsecase2","<Test>value</Test>"); 3161 fEntityStorage.addInternalEntity("entityUsecase3","value3"); 3162 fEntityStorage.addInternalEntity("text", "Hello World."); 3163 fEntityStorage.addInternalEntity("empty-element", "<foo/>"); 3164 fEntityStorage.addInternalEntity("balanced-element", "<foo></foo>"); 3165 fEntityStorage.addInternalEntity("balanced-element-with-text", "<foo>Hello, World</foo>"); 3166 fEntityStorage.addInternalEntity("balanced-element-with-entity", "<foo>&text;</foo>"); 3167 fEntityStorage.addInternalEntity("unbalanced-entity", "<foo>"); 3168 fEntityStorage.addInternalEntity("recursive-entity", "<foo>&recursive-entity2;</foo>"); 3169 fEntityStorage.addInternalEntity("recursive-entity2", "<bar>&recursive-entity3;</bar>"); 3170 fEntityStorage.addInternalEntity("recursive-entity3", "<baz>&recursive-entity;</baz>"); 3171 fEntityStorage.addInternalEntity("ch","©"); 3172 fEntityStorage.addInternalEntity("ch1","T"); 3173 fEntityStorage.addInternalEntity("% ch2","param"); 3174 } 3175 3176 } // class XMLEntityManager