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