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