XMLEntityManager.java revision 1052:3654ce193315
1212420Sken/* 2212420Sken * Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved. 3212420Sken */ 4212420Sken/* 5212420Sken * Licensed to the Apache Software Foundation (ASF) under one or more 6212420Sken * contributor license agreements. See the NOTICE file distributed with 7212420Sken * this work for additional information regarding copyright ownership. 8212420Sken * The ASF licenses this file to You under the Apache License, Version 2.0 9212420Sken * (the "License"); you may not use this file except in compliance with 10212420Sken * the License. You may obtain a copy of the License at 11212420Sken * 12212420Sken * http://www.apache.org/licenses/LICENSE-2.0 13212420Sken * 14212420Sken * Unless required by applicable law or agreed to in writing, software 15212420Sken * distributed under the License is distributed on an "AS IS" BASIS, 16212420Sken * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17212420Sken * See the License for the specific language governing permissions and 18212420Sken * limitations under the License. 19212420Sken */ 20212420Sken 21212420Skenpackage com.sun.org.apache.xerces.internal.impl ; 22212420Sken 23212420Skenimport com.sun.org.apache.xerces.internal.impl.io.ASCIIReader; 24212420Skenimport com.sun.org.apache.xerces.internal.impl.io.UCSReader; 25212420Skenimport com.sun.org.apache.xerces.internal.impl.io.UTF8Reader; 26212420Skenimport com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter; 27212420Skenimport com.sun.org.apache.xerces.internal.impl.validation.ValidationManager; 28212420Skenimport com.sun.org.apache.xerces.internal.util.*; 29212420Skenimport com.sun.org.apache.xerces.internal.util.URI; 30230592Skenimport com.sun.org.apache.xerces.internal.utils.SecuritySupport; 31212420Skenimport com.sun.org.apache.xerces.internal.utils.XMLLimitAnalyzer; 32230592Skenimport com.sun.org.apache.xerces.internal.utils.XMLSecurityManager; 33230592Skenimport com.sun.org.apache.xerces.internal.utils.XMLSecurityPropertyManager; 34230592Skenimport com.sun.org.apache.xerces.internal.xni.Augmentations; 35230592Skenimport com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier; 36230592Skenimport com.sun.org.apache.xerces.internal.xni.XNIException; 37230592Skenimport com.sun.org.apache.xerces.internal.xni.parser.*; 38230592Skenimport com.sun.xml.internal.stream.Entity; 39230592Skenimport com.sun.xml.internal.stream.StaxEntityResolverWrapper; 40230592Skenimport com.sun.xml.internal.stream.StaxXMLInputSource; 41230592Skenimport com.sun.xml.internal.stream.XMLEntityStorage; 42230592Skenimport java.io.*; 43230592Skenimport java.net.HttpURLConnection; 44230592Skenimport java.net.URISyntaxException; 45230592Skenimport java.net.URL; 46230592Skenimport java.net.URLConnection; 47230592Skenimport java.util.HashMap; 48230592Skenimport java.util.Iterator; 49230592Skenimport java.util.Locale; 50230592Skenimport java.util.Map; 51230592Skenimport java.util.Stack; 52230592Skenimport java.util.StringTokenizer; 53230592Skenimport javax.xml.XMLConstants; 54230592Skenimport javax.xml.catalog.CatalogException; 55230592Skenimport javax.xml.catalog.CatalogFeatures; 56230592Skenimport javax.xml.catalog.CatalogFeatures.Feature; 57230592Skenimport javax.xml.catalog.CatalogManager; 58230592Skenimport javax.xml.catalog.CatalogResolver; 59230592Skenimport javax.xml.stream.XMLInputFactory; 60230592Skenimport javax.xml.transform.Source; 61212420Skenimport jdk.xml.internal.JdkXmlUtils; 62212420Skenimport org.xml.sax.InputSource; 63212420Sken 64212420Sken 65213702Smdf/** 66213702Smdf * Will keep track of current entity. 67230592Sken * 68212420Sken * The entity manager handles the registration of general and parameter 69212420Sken * entities; resolves entities; and starts entities. The entity manager 70212420Sken * is a central component in a standard parser configuration and this 71212420Sken * class works directly with the entity scanner to manage the underlying 72212420Sken * xni. 73212420Sken * <p> 74212420Sken * This component requires the following features and properties from the 75212420Sken * component manager that uses it: 76212420Sken * <ul> 77212420Sken * <li>http://xml.org/sax/features/validation</li> 78212420Sken * <li>http://xml.org/sax/features/external-general-entities</li> 79212420Sken * <li>http://xml.org/sax/features/external-parameter-entities</li> 80212420Sken * <li>http://apache.org/xml/features/allow-java-encodings</li> 81212420Sken * <li>http://apache.org/xml/properties/internal/symbol-table</li> 82230592Sken * <li>http://apache.org/xml/properties/internal/error-reporter</li> 83230592Sken * <li>http://apache.org/xml/properties/internal/entity-resolver</li> 84230592Sken * </ul> 85213702Smdf * 86213702Smdf * 87212420Sken * @author Andy Clark, IBM 88212420Sken * @author Arnaud Le Hors, IBM 89212420Sken * @author K.Venugopal SUN Microsystems 90212420Sken * @author Neeraj Bajaj SUN Microsystems 91212420Sken * @author Sunitha Reddy SUN Microsystems 92230592Sken */ 93212420Skenpublic class XMLEntityManager implements XMLComponent, XMLEntityResolver { 94212420Sken 95212420Sken // 96212420Sken // Constants 97212420Sken // 98212420Sken 99230592Sken /** Default buffer size (2048). */ 100230592Sken public static final int DEFAULT_BUFFER_SIZE = 8192; 101230592Sken 102212420Sken /** Default buffer size before we've finished with the XMLDecl: */ 103212420Sken public static final int DEFAULT_XMLDECL_BUFFER_SIZE = 64; 104230592Sken 105230592Sken /** Default internal entity buffer size (1024). */ 106230592Sken public static final int DEFAULT_INTERNAL_BUFFER_SIZE = 1024; 107212420Sken 108212420Sken // feature identifiers 109212420Sken 110213702Smdf /** Feature identifier: validation. */ 111212420Sken protected static final String VALIDATION = 112212420Sken Constants.SAX_FEATURE_PREFIX + Constants.VALIDATION_FEATURE; 113212420Sken 114212420Sken /** 115212420Sken * standard uri conformant (strict uri). 116212420Sken * http://apache.org/xml/features/standard-uri-conformant 117213702Smdf */ 118212420Sken protected boolean fStrictURI; 119212420Sken 120212420Sken 121213708Smdf /** Feature identifier: external general entities. */ 122213708Smdf protected static final String EXTERNAL_GENERAL_ENTITIES = 123213708Smdf Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_GENERAL_ENTITIES_FEATURE; 124213708Smdf 125213708Smdf /** Feature identifier: external parameter entities. */ 126213708Smdf protected static final String EXTERNAL_PARAMETER_ENTITIES = 127213708Smdf Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_PARAMETER_ENTITIES_FEATURE; 128213708Smdf 129213708Smdf /** Feature identifier: allow Java encodings. */ 130213708Smdf protected static final String ALLOW_JAVA_ENCODINGS = 131213707Smdf Constants.XERCES_FEATURE_PREFIX + Constants.ALLOW_JAVA_ENCODINGS_FEATURE; 132213707Smdf 133213707Smdf /** Feature identifier: warn on duplicate EntityDef */ 134213707Smdf protected static final String WARN_ON_DUPLICATE_ENTITYDEF = 135213707Smdf Constants.XERCES_FEATURE_PREFIX +Constants.WARN_ON_DUPLICATE_ENTITYDEF_FEATURE; 136213707Smdf 137213707Smdf /** Feature identifier: load external DTD. */ 138213707Smdf protected static final String LOAD_EXTERNAL_DTD = 139213707Smdf Constants.XERCES_FEATURE_PREFIX + Constants.LOAD_EXTERNAL_DTD_FEATURE; 140213707Smdf 141213708Smdf // property identifiers 142213708Smdf 143213707Smdf /** Property identifier: symbol table. */ 144213707Smdf protected static final String SYMBOL_TABLE = 145230592Sken Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY; 146230592Sken 147230592Sken /** Property identifier: error reporter. */ 148230592Sken protected static final String ERROR_REPORTER = 149230592Sken Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY; 150230592Sken 151230592Sken /** Feature identifier: standard uri conformant */ 152230592Sken protected static final String STANDARD_URI_CONFORMANT = 153230592Sken Constants.XERCES_FEATURE_PREFIX +Constants.STANDARD_URI_CONFORMANT_FEATURE; 154230592Sken 155230592Sken /** Property identifier: entity resolver. */ 156230592Sken protected static final String ENTITY_RESOLVER = 157230592Sken Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY; 158230592Sken 159230592Sken protected static final String STAX_ENTITY_RESOLVER = 160230592Sken Constants.XERCES_PROPERTY_PREFIX + Constants.STAX_ENTITY_RESOLVER_PROPERTY; 161230592Sken 162230592Sken // property identifier: ValidationManager 163230592Sken protected static final String VALIDATION_MANAGER = 164230592Sken Constants.XERCES_PROPERTY_PREFIX + Constants.VALIDATION_MANAGER_PROPERTY; 165230592Sken 166230592Sken /** property identifier: buffer size. */ 167230592Sken protected static final String BUFFER_SIZE = 168230592Sken Constants.XERCES_PROPERTY_PREFIX + Constants.BUFFER_SIZE_PROPERTY; 169230592Sken 170230592Sken /** property identifier: security manager. */ 171230592Sken protected static final String SECURITY_MANAGER = 172230592Sken Constants.XERCES_PROPERTY_PREFIX + Constants.SECURITY_MANAGER_PROPERTY; 173230592Sken 174230592Sken protected static final String PARSER_SETTINGS = 175230592Sken Constants.XERCES_FEATURE_PREFIX + Constants.PARSER_SETTINGS; 176230592Sken 177230592Sken /** Property identifier: Security property manager. */ 178230592Sken private static final String XML_SECURITY_PROPERTY_MANAGER = 179212420Sken Constants.XML_SECURITY_PROPERTY_MANAGER; 180212420Sken 181230592Sken /** access external dtd: file protocol */ 182230592Sken static final String EXTERNAL_ACCESS_DEFAULT = Constants.EXTERNAL_ACCESS_DEFAULT; 183230592Sken 184230592Sken // recognized features and properties 185230592Sken 186230592Sken /** Recognized features. */ 187230592Sken private static final String[] RECOGNIZED_FEATURES = { 188230592Sken VALIDATION, 189230592Sken EXTERNAL_GENERAL_ENTITIES, 190230592Sken EXTERNAL_PARAMETER_ENTITIES, 191212420Sken ALLOW_JAVA_ENCODINGS, 192212420Sken WARN_ON_DUPLICATE_ENTITYDEF, 193212420Sken STANDARD_URI_CONFORMANT, 194212420Sken XMLConstants.USE_CATALOG 195212420Sken }; 196212420Sken 197212420Sken /** Feature defaults. */ 198212420Sken private static final Boolean[] FEATURE_DEFAULTS = { 199212420Sken null, 200212420Sken Boolean.TRUE, 201212420Sken Boolean.TRUE, 202212420Sken Boolean.TRUE, 203212420Sken Boolean.FALSE, 204212420Sken Boolean.FALSE, 205212420Sken JdkXmlUtils.USE_CATALOG_DEFAULT 206212420Sken }; 207212420Sken 208212420Sken /** Recognized properties. */ 209212420Sken private static final String[] RECOGNIZED_PROPERTIES = { 210212420Sken SYMBOL_TABLE, 211212420Sken ERROR_REPORTER, 212212420Sken ENTITY_RESOLVER, 213212420Sken VALIDATION_MANAGER, 214212420Sken BUFFER_SIZE, 215212420Sken SECURITY_MANAGER, 216212420Sken XML_SECURITY_PROPERTY_MANAGER, 217212420Sken JdkXmlUtils.CATALOG_DEFER, 218212420Sken JdkXmlUtils.CATALOG_FILES, 219212420Sken JdkXmlUtils.CATALOG_PREFER, 220212420Sken JdkXmlUtils.CATALOG_RESOLVE, 221212420Sken JdkXmlUtils.CDATA_CHUNK_SIZE 222212420Sken }; 223212420Sken 224212420Sken /** Property defaults. */ 225212420Sken private static final Object[] PROPERTY_DEFAULTS = { 226212420Sken null, 227212420Sken null, 228212420Sken null, 229212420Sken null, 230212420Sken DEFAULT_BUFFER_SIZE, 231212420Sken null, 232212420Sken null, 233212420Sken null, 234212420Sken null, 235212420Sken null, 236212420Sken null, 237212420Sken JdkXmlUtils.CDATA_CHUNK_SIZE_DEFAULT 238212420Sken }; 239212420Sken 240212420Sken private static final String XMLEntity = "[xml]".intern(); 241212420Sken private static final String DTDEntity = "[dtd]".intern(); 242212420Sken 243212420Sken // debugging 244212420Sken 245212420Sken /** 246212420Sken * Debug printing of buffer. This debugging flag works best when you 247212420Sken * resize the DEFAULT_BUFFER_SIZE down to something reasonable like 248212420Sken * 64 characters. 249212420Sken */ 250212420Sken private static final boolean DEBUG_BUFFER = false; 251212420Sken 252212420Sken /** warn on duplicate Entity declaration. 253212420Sken * http://apache.org/xml/features/warn-on-duplicate-entitydef 254212420Sken */ 255212420Sken protected boolean fWarnDuplicateEntityDef; 256212420Sken 257212420Sken /** Debug some basic entities. */ 258212420Sken private static final boolean DEBUG_ENTITIES = false; 259212420Sken 260212420Sken /** Debug switching readers for encodings. */ 261212420Sken private static final boolean DEBUG_ENCODINGS = false; 262212420Sken 263212420Sken // should be diplayed trace resolving messages 264212420Sken private static final boolean DEBUG_RESOLVER = false ; 265212420Sken 266212420Sken // 267212420Sken // Data 268212420Sken // 269212420Sken 270212420Sken // features 271212420Sken 272212420Sken /** 273212420Sken * Validation. This feature identifier is: 274212420Sken * http://xml.org/sax/features/validation 275212420Sken */ 276212420Sken protected boolean fValidation; 277212420Sken 278212420Sken /** 279212420Sken * External general entities. This feature identifier is: 280212420Sken * http://xml.org/sax/features/external-general-entities 281212420Sken */ 282212420Sken protected boolean fExternalGeneralEntities; 283212420Sken 284212420Sken /** 285212420Sken * External parameter entities. This feature identifier is: 286212420Sken * http://xml.org/sax/features/external-parameter-entities 287212420Sken */ 288212420Sken protected boolean fExternalParameterEntities; 289212420Sken 290212420Sken /** 291212420Sken * Allow Java encoding names. This feature identifier is: 292212420Sken * http://apache.org/xml/features/allow-java-encodings 293212420Sken */ 294212420Sken protected boolean fAllowJavaEncodings = true ; 295212420Sken 296212420Sken /** Load external DTD. */ 297212420Sken protected boolean fLoadExternalDTD = true; 298212420Sken 299212420Sken // properties 300212420Sken 301212420Sken /** 302212420Sken * Symbol table. This property identifier is: 303212420Sken * http://apache.org/xml/properties/internal/symbol-table 304212420Sken */ 305212420Sken protected SymbolTable fSymbolTable; 306212420Sken 307212420Sken /** 308212420Sken * Error reporter. This property identifier is: 309212420Sken * http://apache.org/xml/properties/internal/error-reporter 310212420Sken */ 311212420Sken protected XMLErrorReporter fErrorReporter; 312212420Sken 313212420Sken /** 314212420Sken * Entity resolver. This property identifier is: 315212420Sken * http://apache.org/xml/properties/internal/entity-resolver 316212420Sken */ 317212420Sken protected XMLEntityResolver fEntityResolver; 318212420Sken 319212420Sken /** Stax Entity Resolver. This property identifier is XMLInputFactory.ENTITY_RESOLVER */ 320212420Sken 321212420Sken protected StaxEntityResolverWrapper fStaxEntityResolver; 322212420Sken 323212420Sken /** Property Manager. This is used from Stax */ 324212420Sken protected PropertyManager fPropertyManager ; 325212420Sken 326212420Sken /** StAX properties */ 327212420Sken boolean fSupportDTD = true; 328212420Sken boolean fReplaceEntityReferences = true; 329212420Sken boolean fSupportExternalEntities = true; 330212420Sken 331212420Sken /** used to restrict external access */ 332212420Sken protected String fAccessExternalDTD = EXTERNAL_ACCESS_DEFAULT; 333212420Sken 334212420Sken // settings 335212420Sken 336212420Sken /** 337212420Sken * Validation manager. This property identifier is: 338212420Sken * http://apache.org/xml/properties/internal/validation-manager 339212420Sken */ 340212420Sken protected ValidationManager fValidationManager; 341212420Sken 342212420Sken // settings 343212420Sken 344212420Sken /** 345212420Sken * Buffer size. We get this value from a property. The default size 346212420Sken * is used if the input buffer size property is not specified. 347212420Sken * REVISIT: do we need a property for internal entity buffer size? 348212420Sken */ 349212420Sken protected int fBufferSize = DEFAULT_BUFFER_SIZE; 350212420Sken 351212420Sken /** Security Manager */ 352212420Sken protected XMLSecurityManager fSecurityManager = null; 353212420Sken 354212420Sken protected XMLLimitAnalyzer fLimitAnalyzer = null; 355212420Sken 356212420Sken protected int entityExpansionIndex; 357212420Sken 358212420Sken /** 359212420Sken * True if the document entity is standalone. This should really 360212420Sken * only be set by the document source (e.g. XMLDocumentScanner). 361212420Sken */ 362212420Sken protected boolean fStandalone; 363212420Sken 364212420Sken // are the entities being parsed in the external subset? 365212420Sken // NOTE: this *is not* the same as whether they're external entities! 366212420Sken protected boolean fInExternalSubset = false; 367212420Sken 368212420Sken 369212420Sken // handlers 370212420Sken /** Entity handler. */ 371212420Sken protected XMLEntityHandler fEntityHandler; 372212420Sken 373212420Sken /** Current entity scanner */ 374212420Sken protected XMLEntityScanner fEntityScanner ; 375212420Sken 376212420Sken /** XML 1.0 entity scanner. */ 377212420Sken protected XMLEntityScanner fXML10EntityScanner; 378212420Sken 379212420Sken /** XML 1.1 entity scanner. */ 380212420Sken protected XMLEntityScanner fXML11EntityScanner; 381212420Sken 382212420Sken /** count of entities expanded: */ 383212420Sken protected int fEntityExpansionCount = 0; 384212420Sken 385212420Sken // entities 386212420Sken 387212420Sken /** Entities. */ 388212420Sken protected Map<String, Entity> fEntities = new HashMap<>(); 389212420Sken 390212420Sken /** Entity stack. */ 391212420Sken protected Stack<Entity> fEntityStack = new Stack<>(); 392212420Sken 393212420Sken /** Current entity. */ 394212420Sken protected Entity.ScannedEntity fCurrentEntity = null; 395212420Sken 396212420Sken /** identify if the InputSource is created by a resolver */ 397212420Sken boolean fISCreatedByResolver = false; 398212420Sken 399212420Sken // shared context 400212420Sken 401212420Sken protected XMLEntityStorage fEntityStorage ; 402212420Sken 403212420Sken protected final Object [] defaultEncoding = new Object[]{"UTF-8", null}; 404212420Sken 405212420Sken 406212420Sken // temp vars 407212420Sken 408216088Sken /** Resource identifer. */ 409213839Smdf private final XMLResourceIdentifierImpl fResourceIdentifier = new XMLResourceIdentifierImpl(); 410213839Smdf 411213839Smdf /** Augmentations for entities. */ 412213839Smdf private final Augmentations fEntityAugs = new AugmentationsImpl(); 413213839Smdf 414213839Smdf /** Pool of character buffers. */ 415213839Smdf private CharacterBufferPool fBufferPool = new CharacterBufferPool(fBufferSize, DEFAULT_INTERNAL_BUFFER_SIZE); 416213839Smdf 417213839Smdf /** indicate whether Catalog should be used for resolving external resources */ 418213839Smdf private boolean fUseCatalog = true; 419213839Smdf CatalogFeatures fCatalogFeatures; 420213839Smdf CatalogResolver fCatalogResolver; 421213839Smdf 422213839Smdf private String fCatalogFile; 423213708Smdf private String fDefer; 424213708Smdf private String fPrefer; 425213708Smdf private String fResolve; 426213708Smdf 427213708Smdf // 428213708Smdf // Constructors 429213708Smdf // 430213708Smdf 431213708Smdf /** 432213708Smdf * If this constructor is used to create the object, reset() should be invoked on this object 433213708Smdf */ 434213708Smdf public XMLEntityManager() { 435213708Smdf //for entity managers not created by parsers 436213708Smdf fSecurityManager = new XMLSecurityManager(true); 437213708Smdf fEntityStorage = new XMLEntityStorage(this) ; 438213708Smdf setScannerVersion(Constants.XML_VERSION_1_0); 439213708Smdf } // <init>() 440213708Smdf 441213708Smdf /** Default constructor. */ 442213708Smdf public XMLEntityManager(PropertyManager propertyManager) { 443213708Smdf fPropertyManager = propertyManager ; 444213708Smdf //pass a reference to current entity being scanned 445213708Smdf //fEntityStorage = new XMLEntityStorage(fCurrentEntity) ; 446213708Smdf fEntityStorage = new XMLEntityStorage(this) ; 447213708Smdf fEntityScanner = new XMLEntityScanner(propertyManager, this) ; 448213708Smdf reset(propertyManager); 449213708Smdf } // <init>() 450213708Smdf 451213708Smdf /** 452213708Smdf * Adds an internal entity declaration. 453213708Smdf * <p> 454213708Smdf * <strong>Note:</strong> This method ignores subsequent entity 455213708Smdf * declarations. 456213708Smdf * <p> 457213708Smdf * <strong>Note:</strong> The name should be a unique symbol. The 458213708Smdf * SymbolTable can be used for this purpose. 459213708Smdf * 460213708Smdf * @param name The name of the entity. 461213708Smdf * @param text The text of the entity. 462213708Smdf * 463213708Smdf * @see SymbolTable 464213708Smdf */ 465213708Smdf public void addInternalEntity(String name, String text) { 466213708Smdf if (!fEntities.containsKey(name)) { 467213708Smdf Entity entity = new Entity.InternalEntity(name, text, fInExternalSubset); 468213708Smdf fEntities.put(name, entity); 469213840Smdf } else{ 470213840Smdf if(fWarnDuplicateEntityDef){ 471213708Smdf fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, 472213840Smdf "MSG_DUPLICATE_ENTITY_DEFINITION", 473213840Smdf new Object[]{ name }, 474213840Smdf XMLErrorReporter.SEVERITY_WARNING ); 475213840Smdf } 476213840Smdf } 477213840Smdf 478213708Smdf } // addInternalEntity(String,String) 479213708Smdf 480213708Smdf /** 481213708Smdf * Adds an external entity declaration. 482213708Smdf * <p> 483213840Smdf * <strong>Note:</strong> This method ignores subsequent entity 484213840Smdf * declarations. 485213840Smdf * <p> 486213840Smdf * <strong>Note:</strong> The name should be a unique symbol. The 487213840Smdf * SymbolTable can be used for this purpose. 488213840Smdf * 489213840Smdf * @param name The name of the entity. 490213839Smdf * @param publicId The public identifier of the entity. 491213840Smdf * @param literalSystemId The system identifier of the entity. 492213840Smdf * @param baseSystemId The base system identifier of the entity. 493213840Smdf * This is the system identifier of the entity 494213840Smdf * where <em>the entity being added</em> and 495213840Smdf * is used to expand the system identifier when 496213840Smdf * the system identifier is a relative URI. 497213840Smdf * When null the system identifier of the first 498213840Smdf * external entity on the stack is used instead. 499213840Smdf * 500213840Smdf * @see SymbolTable 501213840Smdf */ 502213840Smdf public void addExternalEntity(String name, 503213840Smdf String publicId, String literalSystemId, 504213840Smdf String baseSystemId) throws IOException { 505213840Smdf if (!fEntities.containsKey(name)) { 506213840Smdf if (baseSystemId == null) { 507213840Smdf // search for the first external entity on the stack 508213840Smdf int size = fEntityStack.size(); 509213840Smdf if (size == 0 && fCurrentEntity != null && fCurrentEntity.entityLocation != null) { 510213840Smdf baseSystemId = fCurrentEntity.entityLocation.getExpandedSystemId(); 511213840Smdf } 512213840Smdf for (int i = size - 1; i >= 0 ; i--) { 513213708Smdf Entity.ScannedEntity externalEntity = 514213708Smdf (Entity.ScannedEntity)fEntityStack.elementAt(i); 515213708Smdf if (externalEntity.entityLocation != null && externalEntity.entityLocation.getExpandedSystemId() != null) { 516213708Smdf baseSystemId = externalEntity.entityLocation.getExpandedSystemId(); 517213708Smdf break; 518213708Smdf } 519213708Smdf } 520213708Smdf } 521213708Smdf Entity entity = new Entity.ExternalEntity(name, 522213708Smdf new XMLEntityDescriptionImpl(name, publicId, literalSystemId, baseSystemId, 523213839Smdf expandSystemId(literalSystemId, baseSystemId, false)), null, fInExternalSubset); 524213708Smdf fEntities.put(name, entity); 525213708Smdf } else{ 526213708Smdf if(fWarnDuplicateEntityDef){ 527213708Smdf fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, 528213708Smdf "MSG_DUPLICATE_ENTITY_DEFINITION", 529213839Smdf new Object[]{ name }, 530213708Smdf XMLErrorReporter.SEVERITY_WARNING ); 531213708Smdf } 532213708Smdf } 533213708Smdf 534213708Smdf } // addExternalEntity(String,String,String,String) 535213708Smdf 536213839Smdf 537213708Smdf /** 538213708Smdf * Adds an unparsed entity declaration. 539213708Smdf * <p> 540213708Smdf * <strong>Note:</strong> This method ignores subsequent entity 541213708Smdf * declarations. 542213839Smdf * <p> 543213708Smdf * <strong>Note:</strong> The name should be a unique symbol. The 544213708Smdf * SymbolTable can be used for this purpose. 545213708Smdf * 546213708Smdf * @param name The name of the entity. 547213708Smdf * @param publicId The public identifier of the entity. 548213839Smdf * @param systemId The system identifier of the entity. 549213839Smdf * @param notation The name of the notation. 550213708Smdf * 551213708Smdf * @see SymbolTable 552213708Smdf */ 553213708Smdf public void addUnparsedEntity(String name, 554213839Smdf String publicId, String systemId, 555213839Smdf String baseSystemId, String notation) { 556213708Smdf if (!fEntities.containsKey(name)) { 557213839Smdf Entity.ExternalEntity entity = new Entity.ExternalEntity(name, 558213708Smdf new XMLEntityDescriptionImpl(name, publicId, systemId, baseSystemId, null), 559213708Smdf notation, fInExternalSubset); 560213708Smdf fEntities.put(name, entity); 561213708Smdf } else{ 562213708Smdf if(fWarnDuplicateEntityDef){ 563213708Smdf fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, 564213708Smdf "MSG_DUPLICATE_ENTITY_DEFINITION", 565213708Smdf new Object[]{ name }, 566213708Smdf XMLErrorReporter.SEVERITY_WARNING ); 567213708Smdf } 568213708Smdf } 569213708Smdf } // addUnparsedEntity(String,String,String,String) 570213708Smdf 571213708Smdf 572213708Smdf /** get the entity storage object from entity manager */ 573213708Smdf public XMLEntityStorage getEntityStore(){ 574213839Smdf return fEntityStorage ; 575213708Smdf } 576213708Smdf 577213708Smdf /** return the entity responsible for reading the entity */ 578213708Smdf public XMLEntityScanner getEntityScanner(){ 579213708Smdf if(fEntityScanner == null) { 580213708Smdf // default to 1.0 581213708Smdf if(fXML10EntityScanner == null) { 582213708Smdf fXML10EntityScanner = new XMLEntityScanner(); 583213708Smdf } 584213708Smdf fXML10EntityScanner.reset(fSymbolTable, this, fErrorReporter); 585213708Smdf fEntityScanner = fXML10EntityScanner; 586213708Smdf } 587213708Smdf return fEntityScanner; 588213708Smdf 589213708Smdf } 590213708Smdf 591213708Smdf public void setScannerVersion(short version) { 592213839Smdf 593213708Smdf if(version == Constants.XML_VERSION_1_0) { 594213708Smdf if(fXML10EntityScanner == null) { 595213708Smdf fXML10EntityScanner = new XMLEntityScanner(); 596213708Smdf } 597213708Smdf fXML10EntityScanner.reset(fSymbolTable, this, fErrorReporter); 598213708Smdf fEntityScanner = fXML10EntityScanner; 599213708Smdf fEntityScanner.setCurrentEntity(fCurrentEntity); 600213708Smdf } else { 601213708Smdf if(fXML11EntityScanner == null) { 602213708Smdf fXML11EntityScanner = new XML11EntityScanner(); 603213708Smdf } 604213708Smdf fXML11EntityScanner.reset(fSymbolTable, this, fErrorReporter); 605213708Smdf fEntityScanner = fXML11EntityScanner; 606213708Smdf fEntityScanner.setCurrentEntity(fCurrentEntity); 607213708Smdf } 608213708Smdf 609213708Smdf } 610213839Smdf 611213708Smdf /** 612213708Smdf * This method uses the passed-in XMLInputSource to make 613213708Smdf * fCurrentEntity usable for reading. 614213708Smdf * 615213708Smdf * @param reference flag to indicate whether the entity is an Entity Reference. 616213708Smdf * @param name name of the entity (XML is it's the document entity) 617213708Smdf * @param xmlInputSource the input source, with sufficient information 618213708Smdf * to begin scanning characters. 619213708Smdf * @param literal True if this entity is started within a 620213708Smdf * literal value. 621213708Smdf * @param isExternal whether this entity should be treated as an internal or external entity. 622213708Smdf * @throws IOException if anything can't be read 623213708Smdf * XNIException If any parser-specific goes wrong. 624213708Smdf * @return the encoding of the new entity or null if a character stream was employed 625213708Smdf */ 626213708Smdf public String setupCurrentEntity(boolean reference, String name, XMLInputSource xmlInputSource, 627213708Smdf boolean literal, boolean isExternal) 628213708Smdf throws IOException, XNIException { 629213708Smdf // get information 630213708Smdf 631212420Sken final String publicId = xmlInputSource.getPublicId(); 632213708Smdf String literalSystemId = xmlInputSource.getSystemId(); 633213708Smdf String baseSystemId = xmlInputSource.getBaseSystemId(); 634212420Sken String encoding = xmlInputSource.getEncoding(); 635213708Smdf final boolean encodingExternallySpecified = (encoding != null); 636213708Smdf Boolean isBigEndian = null; 637213708Smdf 638213708Smdf // create reader 639213708Smdf InputStream stream = null; 640213708Smdf Reader reader = xmlInputSource.getCharacterStream(); 641213708Smdf 642213708Smdf // First chance checking strict URI 643213708Smdf String expandedSystemId = expandSystemId(literalSystemId, baseSystemId, fStrictURI); 644213708Smdf if (baseSystemId == null) { 645212420Sken baseSystemId = expandedSystemId; 646212420Sken } 647213708Smdf if (reader == null) { 648212420Sken stream = xmlInputSource.getByteStream(); 649213708Smdf if (stream == null) { 650213708Smdf URL location = new URL(expandedSystemId); 651212420Sken URLConnection connect = location.openConnection(); 652213708Smdf if (!(connect instanceof HttpURLConnection)) { 653213708Smdf stream = connect.getInputStream(); 654213708Smdf } 655213708Smdf else { 656213708Smdf boolean followRedirects = true; 657212420Sken 658212420Sken // setup URLConnection if we have an HTTPInputSource 659212420Sken if (xmlInputSource instanceof HTTPInputSource) { 660212420Sken final HttpURLConnection urlConnection = (HttpURLConnection) connect; 661212420Sken final HTTPInputSource httpInputSource = (HTTPInputSource) xmlInputSource; 662212420Sken 663212420Sken // set request properties 664213704Smdf Iterator<Map.Entry<String, String>> propIter = httpInputSource.getHTTPRequestProperties(); 665213882Smdf while (propIter.hasNext()) { 666212420Sken Map.Entry<String, String> entry = propIter.next(); 667212420Sken urlConnection.setRequestProperty(entry.getKey(), entry.getValue()); 668212420Sken } 669212420Sken 670212420Sken // set preference for redirection 671212420Sken followRedirects = httpInputSource.getFollowHTTPRedirects(); 672212420Sken if (!followRedirects) { 673212420Sken urlConnection.setInstanceFollowRedirects(followRedirects); 674212420Sken } 675212420Sken } 676212420Sken 677212420Sken stream = connect.getInputStream(); 678212420Sken 679212420Sken // REVISIT: If the URLConnection has external encoding 680212420Sken // information, we should be reading it here. It's located 681212420Sken // in the charset parameter of Content-Type. -- mrglavas 682212420Sken 683212420Sken if (followRedirects) { 684213704Smdf String redirect = connect.getURL().toString(); 685213704Smdf // E43: Check if the URL was redirected, and then 686213704Smdf // update literal and expanded system IDs if needed. 687213704Smdf if (!redirect.equals(expandedSystemId)) { 688213704Smdf literalSystemId = redirect; 689213704Smdf expandedSystemId = redirect; 690213704Smdf } 691212420Sken } 692212420Sken } 693212420Sken } 694212420Sken 695213708Smdf // wrap this stream in RewindableInputStream 696212420Sken stream = new RewindableInputStream(stream); 697212420Sken 698212420Sken // perform auto-detect of encoding if necessary 699213704Smdf if (encoding == null) { 700212420Sken // read first four bytes and determine encoding 701212420Sken final byte[] b4 = new byte[4]; 702212420Sken int count = 0; 703212420Sken for (; count<4; count++ ) { 704212420Sken b4[count] = (byte)stream.read(); 705212420Sken } 706212420Sken if (count == 4) { 707212420Sken Object [] encodingDesc = getEncodingName(b4, count); 708212420Sken encoding = (String)(encodingDesc[0]); 709212420Sken isBigEndian = (Boolean)(encodingDesc[1]); 710212420Sken 711230592Sken stream.reset(); 712212420Sken // Special case UTF-8 files with BOM created by Microsoft 713212420Sken // tools. It's more efficient to consume the BOM than make 714212420Sken // the reader perform extra checks. -Ac 715230592Sken if (count > 2 && encoding.equals("UTF-8")) { 716212420Sken int b0 = b4[0] & 0xFF; 717230592Sken int b1 = b4[1] & 0xFF; 718213882Smdf int b2 = b4[2] & 0xFF; 719213882Smdf if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF) { 720212420Sken // ignore first three bytes... 721212420Sken stream.skip(3); 722212420Sken } 723212420Sken } 724212420Sken reader = createReader(stream, encoding, isBigEndian); 725212420Sken } else { 726212420Sken reader = createReader(stream, encoding, isBigEndian); 727212420Sken } 728212420Sken } 729212420Sken 730212420Sken // use specified encoding 731212420Sken else { 732212420Sken encoding = encoding.toUpperCase(Locale.ENGLISH); 733212420Sken 734212420Sken // If encoding is UTF-8, consume BOM if one is present. 735212420Sken if (encoding.equals("UTF-8")) { 736213704Smdf final int[] b3 = new int[3]; 737212420Sken int count = 0; 738212420Sken for (; count < 3; ++count) { 739212420Sken b3[count] = stream.read(); 740213704Smdf if (b3[count] == -1) 741213704Smdf break; 742213882Smdf } 743213882Smdf if (count == 3) { 744213882Smdf if (b3[0] != 0xEF || b3[1] != 0xBB || b3[2] != 0xBF) { 745213704Smdf // First three bytes are not BOM, so reset. 746213704Smdf stream.reset(); 747213704Smdf } 748213704Smdf } else { 749230592Sken stream.reset(); 750212420Sken } 751212420Sken } 752230592Sken // If encoding is UTF-16, we still need to read the first four bytes 753230592Sken // in order to discover the byte order. 754230592Sken else if (encoding.equals("UTF-16")) { 755230592Sken final int[] b4 = new int[4]; 756230592Sken int count = 0; 757230592Sken for (; count < 4; ++count) { 758230592Sken b4[count] = stream.read(); 759230592Sken if (b4[count] == -1) 760230592Sken break; 761230592Sken } 762230592Sken stream.reset(); 763230592Sken 764230592Sken String utf16Encoding = "UTF-16"; 765230592Sken if (count >= 2) { 766230592Sken final int b0 = b4[0]; 767230592Sken final int b1 = b4[1]; 768230592Sken if (b0 == 0xFE && b1 == 0xFF) { 769230592Sken // UTF-16, big-endian 770230592Sken utf16Encoding = "UTF-16BE"; 771230592Sken isBigEndian = Boolean.TRUE; 772230592Sken } 773230592Sken else if (b0 == 0xFF && b1 == 0xFE) { 774230592Sken // UTF-16, little-endian 775230592Sken utf16Encoding = "UTF-16LE"; 776230592Sken isBigEndian = Boolean.FALSE; 777230592Sken } 778230592Sken else if (count == 4) { 779230592Sken final int b2 = b4[2]; 780230592Sken final int b3 = b4[3]; 781230592Sken if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x3F) { 782230592Sken // UTF-16, big-endian, no BOM 783230592Sken utf16Encoding = "UTF-16BE"; 784230592Sken isBigEndian = Boolean.TRUE; 785230592Sken } 786230592Sken if (b0 == 0x3C && b1 == 0x00 && b2 == 0x3F && b3 == 0x00) { 787230592Sken // UTF-16, little-endian, no BOM 788230592Sken utf16Encoding = "UTF-16LE"; 789230592Sken isBigEndian = Boolean.FALSE; 790230592Sken } 791230592Sken } 792230592Sken } 793230592Sken reader = createReader(stream, utf16Encoding, isBigEndian); 794230592Sken } 795230592Sken // If encoding is UCS-4, we still need to read the first four bytes 796230592Sken // in order to discover the byte order. 797230592Sken else if (encoding.equals("ISO-10646-UCS-4")) { 798230592Sken final int[] b4 = new int[4]; 799230592Sken int count = 0; 800230592Sken for (; count < 4; ++count) { 801230592Sken b4[count] = stream.read(); 802230592Sken if (b4[count] == -1) 803230592Sken break; 804230592Sken } 805230592Sken stream.reset(); 806230592Sken 807230592Sken // Ignore unusual octet order for now. 808230592Sken if (count == 4) { 809230592Sken // UCS-4, big endian (1234) 810230592Sken if (b4[0] == 0x00 && b4[1] == 0x00 && b4[2] == 0x00 && b4[3] == 0x3C) { 811230592Sken isBigEndian = Boolean.TRUE; 812230592Sken } 813230592Sken // UCS-4, little endian (1234) 814230592Sken else if (b4[0] == 0x3C && b4[1] == 0x00 && b4[2] == 0x00 && b4[3] == 0x00) { 815230592Sken isBigEndian = Boolean.FALSE; 816230592Sken } 817230592Sken } 818230592Sken } 819230592Sken // If encoding is UCS-2, we still need to read the first four bytes 820230592Sken // in order to discover the byte order. 821230592Sken else if (encoding.equals("ISO-10646-UCS-2")) { 822230592Sken final int[] b4 = new int[4]; 823230592Sken int count = 0; 824230592Sken for (; count < 4; ++count) { 825230592Sken b4[count] = stream.read(); 826230592Sken if (b4[count] == -1) 827230592Sken break; 828230592Sken } 829230592Sken stream.reset(); 830230592Sken 831230592Sken if (count == 4) { 832230592Sken // UCS-2, big endian 833230592Sken if (b4[0] == 0x00 && b4[1] == 0x3C && b4[2] == 0x00 && b4[3] == 0x3F) { 834230592Sken isBigEndian = Boolean.TRUE; 835230592Sken } 836230592Sken // UCS-2, little endian 837230592Sken else if (b4[0] == 0x3C && b4[1] == 0x00 && b4[2] == 0x3F && b4[3] == 0x00) { 838230592Sken isBigEndian = Boolean.FALSE; 839230592Sken } 840230592Sken } 841230592Sken } 842230592Sken 843230592Sken reader = createReader(stream, encoding, isBigEndian); 844230592Sken } 845230592Sken 846230592Sken // read one character at a time so we don't jump too far 847230592Sken // ahead, converting characters from the byte stream in 848230592Sken // the wrong encoding 849230592Sken if (DEBUG_ENCODINGS) { 850230592Sken System.out.println("$$$ no longer wrapping reader in OneCharReader"); 851230592Sken } 852230592Sken //reader = new OneCharReader(reader); 853230592Sken } 854230592Sken 855230592Sken // We've seen a new Reader. 856230592Sken // Push it on the stack so we can close it later. 857230592Sken //fOwnReaders.add(reader); 858230592Sken 859230592Sken // push entity on stack 860230592Sken if (fCurrentEntity != null) { 861230592Sken fEntityStack.push(fCurrentEntity); 862230592Sken } 863230592Sken 864230592Sken // create entity 865230592Sken /* if encoding is specified externally, 'encoding' information present 866230592Sken * in the prolog of the XML document is not considered. Hence, prolog can 867230592Sken * be read in Chunks of data instead of byte by byte. 868230592Sken */ 869230592Sken fCurrentEntity = new Entity.ScannedEntity(reference, name, 870230592Sken new XMLResourceIdentifierImpl(publicId, literalSystemId, baseSystemId, expandedSystemId), 871230592Sken stream, reader, encoding, literal, encodingExternallySpecified, isExternal); 872230592Sken fCurrentEntity.setEncodingExternallySpecified(encodingExternallySpecified); 873230592Sken fEntityScanner.setCurrentEntity(fCurrentEntity); 874230592Sken fResourceIdentifier.setValues(publicId, literalSystemId, baseSystemId, expandedSystemId); 875230592Sken if (fLimitAnalyzer != null) { 876230592Sken fLimitAnalyzer.startEntity(name); 877230592Sken } 878230592Sken return encoding; 879230592Sken } //setupCurrentEntity(String, XMLInputSource, boolean, boolean): String 880230592Sken 881230592Sken 882230592Sken /** 883230592Sken * Checks whether an entity given by name is external. 884230592Sken * 885230592Sken * @param entityName The name of the entity to check. 886230592Sken * @return True if the entity is external, false otherwise 887230592Sken * (including when the entity is not declared). 888230592Sken */ 889230592Sken public boolean isExternalEntity(String entityName) { 890230592Sken 891230592Sken Entity entity = fEntities.get(entityName); 892230592Sken if (entity == null) { 893230592Sken return false; 894230592Sken } 895230592Sken return entity.isExternal(); 896230592Sken } 897230592Sken 898230592Sken /** 899230592Sken * Checks whether the declaration of an entity given by name is 900230592Sken * // in the external subset. 901230592Sken * 902230592Sken * @param entityName The name of the entity to check. 903230592Sken * @return True if the entity was declared in the external subset, false otherwise 904230592Sken * (including when the entity is not declared). 905230592Sken */ 906230592Sken public boolean isEntityDeclInExternalSubset(String entityName) { 907230592Sken 908230592Sken Entity entity = fEntities.get(entityName); 909230592Sken if (entity == null) { 910230592Sken return false; 911230592Sken } 912230592Sken return entity.isEntityDeclInExternalSubset(); 913230592Sken } 914230592Sken 915230592Sken 916230592Sken 917230592Sken // 918230592Sken // Public methods 919230592Sken // 920230592Sken 921230592Sken /** 922230592Sken * Sets whether the document entity is standalone. 923230592Sken * 924230592Sken * @param standalone True if document entity is standalone. 925230592Sken */ 926230592Sken public void setStandalone(boolean standalone) { 927230592Sken fStandalone = standalone; 928230592Sken } 929230592Sken // setStandalone(boolean) 930230592Sken 931230592Sken /** Returns true if the document entity is standalone. */ 932230592Sken public boolean isStandalone() { 933230592Sken return fStandalone; 934230592Sken } //isStandalone():boolean 935230592Sken 936230592Sken public boolean isDeclaredEntity(String entityName) { 937230592Sken 938230592Sken Entity entity = fEntities.get(entityName); 939230592Sken return entity != null; 940230592Sken } 941230592Sken 942230592Sken public boolean isUnparsedEntity(String entityName) { 943230592Sken 944230592Sken Entity entity = fEntities.get(entityName); 945230592Sken if (entity == null) { 946230592Sken return false; 947230592Sken } 948230592Sken return entity.isUnparsed(); 949230592Sken } 950230592Sken 951230592Sken 952230592Sken 953230592Sken // this simply returns the fResourceIdentifier object; 954230592Sken // this should only be used with caution by callers that 955230592Sken // carefully manage the entity manager's behaviour, so that 956230592Sken // this doesn't returning meaningless or misleading data. 957230592Sken // @return a reference to the current fResourceIdentifier object 958230592Sken public XMLResourceIdentifier getCurrentResourceIdentifier() { 959230592Sken return fResourceIdentifier; 960230592Sken } 961230592Sken 962230592Sken /** 963230592Sken * Sets the entity handler. When an entity starts and ends, the 964230592Sken * entity handler is notified of the change. 965230592Sken * 966230592Sken * @param entityHandler The new entity handler. 967230592Sken */ 968230592Sken 969230592Sken public void setEntityHandler(com.sun.org.apache.xerces.internal.impl.XMLEntityHandler entityHandler) { 970230592Sken fEntityHandler = entityHandler; 971230592Sken } // setEntityHandler(XMLEntityHandler) 972230592Sken 973230592Sken //this function returns StaxXMLInputSource 974230592Sken public StaxXMLInputSource resolveEntityAsPerStax(XMLResourceIdentifier resourceIdentifier) throws java.io.IOException{ 975230592Sken 976230592Sken if(resourceIdentifier == null ) return null; 977230592Sken 978230592Sken String publicId = resourceIdentifier.getPublicId(); 979230592Sken String literalSystemId = resourceIdentifier.getLiteralSystemId(); 980230592Sken String baseSystemId = resourceIdentifier.getBaseSystemId(); 981230592Sken String expandedSystemId = resourceIdentifier.getExpandedSystemId(); 982230592Sken // if no base systemId given, assume that it's relative 983230592Sken // to the systemId of the current scanned entity 984230592Sken // Sometimes the system id is not (properly) expanded. 985230592Sken // We need to expand the system id if: 986230592Sken // a. the expanded one was null; or 987230592Sken // b. the base system id was null, but becomes non-null from the current entity. 988230592Sken boolean needExpand = (expandedSystemId == null); 989230592Sken // REVISIT: why would the baseSystemId ever be null? if we 990230592Sken // didn't have to make this check we wouldn't have to reuse the 991230592Sken // fXMLResourceIdentifier object... 992230592Sken if (baseSystemId == null && fCurrentEntity != null && fCurrentEntity.entityLocation != null) { 993230592Sken baseSystemId = fCurrentEntity.entityLocation.getExpandedSystemId(); 994230592Sken if (baseSystemId != null) 995230592Sken needExpand = true; 996230592Sken } 997230592Sken if (needExpand) 998230592Sken expandedSystemId = expandSystemId(literalSystemId, baseSystemId,false); 999230592Sken 1000230592Sken // give the entity resolver a chance 1001230592Sken StaxXMLInputSource staxInputSource = null; 1002230592Sken XMLInputSource xmlInputSource = null; 1003230592Sken 1004230592Sken XMLResourceIdentifierImpl ri = null; 1005230592Sken 1006230592Sken if (resourceIdentifier instanceof XMLResourceIdentifierImpl) { 1007230592Sken ri = (XMLResourceIdentifierImpl)resourceIdentifier; 1008230592Sken } else { 1009230592Sken fResourceIdentifier.clear(); 1010230592Sken ri = fResourceIdentifier; 1011230592Sken } 1012230592Sken ri.setValues(publicId, literalSystemId, baseSystemId, expandedSystemId); 1013230592Sken if(DEBUG_RESOLVER){ 1014230592Sken System.out.println("BEFORE Calling resolveEntity") ; 1015230592Sken } 1016230592Sken 1017230592Sken fISCreatedByResolver = false; 1018230592Sken //either of Stax or Xerces would be null 1019230592Sken if(fStaxEntityResolver != null){ 1020230592Sken staxInputSource = fStaxEntityResolver.resolveEntity(ri); 1021230592Sken if(staxInputSource != null) { 1022230592Sken fISCreatedByResolver = true; 1023230592Sken } 1024230592Sken } 1025230592Sken 1026230592Sken if(fEntityResolver != null){ 1027230592Sken xmlInputSource = fEntityResolver.resolveEntity(ri); 1028230592Sken if(xmlInputSource != null) { 1029230592Sken fISCreatedByResolver = true; 1030230592Sken } 1031230592Sken } 1032230592Sken 1033230592Sken if(xmlInputSource != null){ 1034230592Sken //wrap this XMLInputSource to StaxInputSource 1035230592Sken staxInputSource = new StaxXMLInputSource(xmlInputSource, fISCreatedByResolver); 1036230592Sken } 1037230592Sken 1038230592Sken if (staxInputSource == null && fUseCatalog) { 1039230592Sken if (fCatalogFeatures == null) { 1040230592Sken fCatalogFeatures = JdkXmlUtils.getCatalogFeatures(fDefer, fCatalogFile, fPrefer, fResolve); 1041230592Sken } 1042230592Sken fCatalogFile = fCatalogFeatures.get(Feature.FILES); 1043230592Sken if (fCatalogFile != null) { 1044230592Sken try { 1045230592Sken if (fCatalogResolver == null) { 1046230592Sken fCatalogResolver = CatalogManager.catalogResolver(fCatalogFeatures); 1047230592Sken } 1048230592Sken InputSource is = fCatalogResolver.resolveEntity(publicId, literalSystemId); 1049230592Sken if (is != null && !is.isEmpty()) { 1050230592Sken staxInputSource = new StaxXMLInputSource(new XMLInputSource(is, true), true); 1051230592Sken } 1052230592Sken } catch (CatalogException e) { 1053230592Sken fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,"CatalogException", 1054230592Sken new Object[]{SecuritySupport.sanitizePath(fCatalogFile)}, 1055230592Sken XMLErrorReporter.SEVERITY_FATAL_ERROR, e ); 1056230592Sken } 1057230592Sken } 1058230592Sken } 1059230592Sken 1060230592Sken // do default resolution 1061230592Sken //this works for both stax & Xerces, if staxInputSource is null, 1062230592Sken //it means parser need to revert to default resolution 1063230592Sken if (staxInputSource == null) { 1064230592Sken // REVISIT: when systemId is null, I think we should return null. 1065230592Sken // is this the right solution? -SG 1066230592Sken //if (systemId != null) 1067230592Sken staxInputSource = new StaxXMLInputSource( 1068230592Sken new XMLInputSource(publicId, literalSystemId, baseSystemId, true), false); 1069230592Sken }else if(staxInputSource.hasXMLStreamOrXMLEventReader()){ 1070230592Sken //Waiting for the clarification from EG. - nb 1071230592Sken } 1072230592Sken 1073230592Sken if (DEBUG_RESOLVER) { 1074230592Sken System.err.println("XMLEntityManager.resolveEntity(" + publicId + ")"); 1075230592Sken System.err.println(" = " + xmlInputSource); 1076230592Sken } 1077230592Sken 1078230592Sken return staxInputSource; 1079230592Sken 1080230592Sken } 1081230592Sken 1082230592Sken /** 1083230592Sken * Resolves the specified public and system identifiers. This 1084230592Sken * method first attempts to resolve the entity based on the 1085230592Sken * EntityResolver registered by the application. If no entity 1086230592Sken * resolver is registered or if the registered entity handler 1087230592Sken * is unable to resolve the entity, then default entity 1088230592Sken * resolution will occur. 1089230592Sken * 1090230592Sken * @param publicId The public identifier of the entity. 1091230592Sken * @param systemId The system identifier of the entity. 1092230592Sken * @param baseSystemId The base system identifier of the entity. 1093230592Sken * This is the system identifier of the current 1094230592Sken * entity and is used to expand the system 1095230592Sken * identifier when the system identifier is a 1096230592Sken * relative URI. 1097230592Sken * 1098230592Sken * @return Returns an input source that wraps the resolved entity. 1099230592Sken * This method will never return null. 1100230592Sken * 1101230592Sken * @throws IOException Thrown on i/o error. 1102230592Sken * @throws XNIException Thrown by entity resolver to signal an error. 1103230592Sken */ 1104230592Sken public XMLInputSource resolveEntity(XMLResourceIdentifier resourceIdentifier) throws IOException, XNIException { 1105230592Sken if(resourceIdentifier == null ) return null; 1106230592Sken String publicId = resourceIdentifier.getPublicId(); 1107230592Sken String literalSystemId = resourceIdentifier.getLiteralSystemId(); 1108230592Sken String baseSystemId = resourceIdentifier.getBaseSystemId(); 1109230592Sken String expandedSystemId = resourceIdentifier.getExpandedSystemId(); 1110230592Sken 1111230592Sken // if no base systemId given, assume that it's relative 1112230592Sken // to the systemId of the current scanned entity 1113230592Sken // Sometimes the system id is not (properly) expanded. 1114230592Sken // We need to expand the system id if: 1115230592Sken // a. the expanded one was null; or 1116230592Sken // b. the base system id was null, but becomes non-null from the current entity. 1117230592Sken boolean needExpand = (expandedSystemId == null); 1118230592Sken // REVISIT: why would the baseSystemId ever be null? if we 1119230592Sken // didn't have to make this check we wouldn't have to reuse the 1120230592Sken // fXMLResourceIdentifier object... 1121230592Sken if (baseSystemId == null && fCurrentEntity != null && fCurrentEntity.entityLocation != null) { 1122230592Sken baseSystemId = fCurrentEntity.entityLocation.getExpandedSystemId(); 1123230592Sken if (baseSystemId != null) 1124230592Sken needExpand = true; 1125230592Sken } 1126230592Sken if (needExpand) 1127230592Sken expandedSystemId = expandSystemId(literalSystemId, baseSystemId,false); 1128230592Sken 1129230592Sken // give the entity resolver a chance 1130230592Sken XMLInputSource xmlInputSource = null; 1131230592Sken 1132230592Sken if (fEntityResolver != null) { 1133230592Sken resourceIdentifier.setBaseSystemId(baseSystemId); 1134230592Sken resourceIdentifier.setExpandedSystemId(expandedSystemId); 1135230592Sken xmlInputSource = fEntityResolver.resolveEntity(resourceIdentifier); 1136230592Sken } 1137230592Sken 1138230592Sken if (xmlInputSource == null && fUseCatalog) { 1139230592Sken if (fCatalogFeatures == null) { 1140230592Sken fCatalogFeatures = JdkXmlUtils.getCatalogFeatures(fDefer, fCatalogFile, fPrefer, fResolve); 1141230592Sken } 1142230592Sken fCatalogFile = fCatalogFeatures.get(Feature.FILES); 1143230592Sken if (fCatalogFile != null) { 1144230592Sken /* 1145230592Sken since the method can be called from various processors, both 1146230592Sken EntityResolver and URIResolver are used to attempt to find 1147230592Sken a match 1148230592Sken */ 1149230592Sken InputSource is = null; 1150230592Sken try { 1151230592Sken if (fCatalogResolver == null) { 1152230592Sken fCatalogResolver = CatalogManager.catalogResolver(fCatalogFeatures); 1153230592Sken } 1154230592Sken String pid = (publicId != null? publicId : resourceIdentifier.getNamespace()); 1155230592Sken if (pid != null || literalSystemId != null) { 1156230592Sken is = fCatalogResolver.resolveEntity(pid, literalSystemId); 1157230592Sken } 1158230592Sken } catch (CatalogException e) {} 1159230592Sken 1160230592Sken if (is != null && !is.isEmpty()) { 1161230592Sken xmlInputSource = new XMLInputSource(is, true); 1162230592Sken } else if (literalSystemId != null) { 1163230592Sken if (fCatalogResolver == null) { 1164230592Sken fCatalogResolver = CatalogManager.catalogResolver(fCatalogFeatures); 1165230592Sken } 1166230592Sken 1167230592Sken Source source = null; 1168230592Sken try { 1169230592Sken source = fCatalogResolver.resolve(literalSystemId, baseSystemId); 1170230592Sken } catch (CatalogException e) { 1171230592Sken throw new XNIException(e); 1172230592Sken } 1173230592Sken if (source != null && !source.isEmpty()) { 1174230592Sken xmlInputSource = new XMLInputSource(publicId, source.getSystemId(), baseSystemId, true); 1175230592Sken } 1176230592Sken } 1177230592Sken } 1178230592Sken } 1179230592Sken 1180230592Sken // do default resolution 1181230592Sken // REVISIT: what's the correct behavior if the user provided an entity 1182230592Sken // resolver (fEntityResolver != null), but resolveEntity doesn't return 1183230592Sken // an input source (xmlInputSource == null)? 1184230592Sken // do we do default resolution, or do we just return null? -SG 1185230592Sken if (xmlInputSource == null) { 1186230592Sken // REVISIT: when systemId is null, I think we should return null. 1187230592Sken // is this the right solution? -SG 1188230592Sken //if (systemId != null) 1189230592Sken xmlInputSource = new XMLInputSource(publicId, literalSystemId, baseSystemId, false); 1190230592Sken } 1191230592Sken 1192230592Sken if (DEBUG_RESOLVER) { 1193230592Sken System.err.println("XMLEntityManager.resolveEntity(" + publicId + ")"); 1194230592Sken System.err.println(" = " + xmlInputSource); 1195230592Sken } 1196230592Sken 1197230592Sken return xmlInputSource; 1198230592Sken 1199230592Sken } // resolveEntity(XMLResourceIdentifier):XMLInputSource 1200230592Sken 1201230592Sken /** 1202230592Sken * Starts a named entity. 1203230592Sken * 1204230592Sken * @param isGE flag to indicate whether the entity is a General Entity 1205230592Sken * @param entityName The name of the entity to start. 1206230592Sken * @param literal True if this entity is started within a literal 1207230592Sken * value. 1208230592Sken * 1209230592Sken * @throws IOException Thrown on i/o error. 1210230592Sken * @throws XNIException Thrown by entity handler to signal an error. 1211230592Sken */ 1212230592Sken public void startEntity(boolean isGE, String entityName, boolean literal) 1213230592Sken throws IOException, XNIException { 1214230592Sken 1215230592Sken // was entity declared? 1216230592Sken Entity entity = fEntityStorage.getEntity(entityName); 1217230592Sken if (entity == null) { 1218230592Sken if (fEntityHandler != null) { 1219230592Sken String encoding = null; 1220230592Sken fResourceIdentifier.clear(); 1221230592Sken fEntityAugs.removeAllItems(); 1222230592Sken fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE); 1223230592Sken fEntityHandler.startEntity(entityName, fResourceIdentifier, encoding, fEntityAugs); 1224230592Sken fEntityAugs.removeAllItems(); 1225230592Sken fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE); 1226230592Sken fEntityHandler.endEntity(entityName, fEntityAugs); 1227230592Sken } 1228230592Sken return; 1229230592Sken } 1230230592Sken 1231230592Sken // should we skip external entities? 1232230592Sken boolean external = entity.isExternal(); 1233230592Sken Entity.ExternalEntity externalEntity = null; 1234230592Sken String extLitSysId = null, extBaseSysId = null, expandedSystemId = null; 1235230592Sken if (external) { 1236230592Sken externalEntity = (Entity.ExternalEntity)entity; 1237230592Sken extLitSysId = (externalEntity.entityLocation != null ? externalEntity.entityLocation.getLiteralSystemId() : null); 1238230592Sken extBaseSysId = (externalEntity.entityLocation != null ? externalEntity.entityLocation.getBaseSystemId() : null); 1239230592Sken expandedSystemId = expandSystemId(extLitSysId, extBaseSysId); 1240230592Sken boolean unparsed = entity.isUnparsed(); 1241230592Sken boolean parameter = entityName.startsWith("%"); 1242230592Sken boolean general = !parameter; 1243230592Sken if (unparsed || (general && !fExternalGeneralEntities) || 1244230592Sken (parameter && !fExternalParameterEntities) || 1245230592Sken !fSupportDTD || !fSupportExternalEntities) { 1246230592Sken 1247230592Sken if (fEntityHandler != null) { 1248230592Sken fResourceIdentifier.clear(); 1249230592Sken final String encoding = null; 1250230592Sken fResourceIdentifier.setValues( 1251230592Sken (externalEntity.entityLocation != null ? externalEntity.entityLocation.getPublicId() : null), 1252230592Sken extLitSysId, extBaseSysId, expandedSystemId); 1253230592Sken fEntityAugs.removeAllItems(); 1254230592Sken fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE); 1255230592Sken fEntityHandler.startEntity(entityName, fResourceIdentifier, encoding, fEntityAugs); 1256230592Sken fEntityAugs.removeAllItems(); 1257230592Sken fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE); 1258230592Sken fEntityHandler.endEntity(entityName, fEntityAugs); 1259230592Sken } 1260230592Sken return; 1261230592Sken } 1262230592Sken } 1263230592Sken 1264230592Sken // is entity recursive? 1265230592Sken int size = fEntityStack.size(); 1266230592Sken for (int i = size; i >= 0; i--) { 1267230592Sken Entity activeEntity = i == size 1268230592Sken ? fCurrentEntity 1269230592Sken : fEntityStack.elementAt(i); 1270230592Sken if (activeEntity.name == entityName) { 1271230592Sken String path = entityName; 1272230592Sken for (int j = i + 1; j < size; j++) { 1273230592Sken activeEntity = fEntityStack.elementAt(j); 1274230592Sken path = path + " -> " + activeEntity.name; 1275230592Sken } 1276230592Sken path = path + " -> " + fCurrentEntity.name; 1277230592Sken path = path + " -> " + entityName; 1278230592Sken fErrorReporter.reportError(this.getEntityScanner(),XMLMessageFormatter.XML_DOMAIN, 1279230592Sken "RecursiveReference", 1280230592Sken new Object[] { entityName, path }, 1281230592Sken XMLErrorReporter.SEVERITY_FATAL_ERROR); 1282230592Sken 1283230592Sken if (fEntityHandler != null) { 1284230592Sken fResourceIdentifier.clear(); 1285230592Sken final String encoding = null; 1286230592Sken if (external) { 1287230592Sken fResourceIdentifier.setValues( 1288230592Sken (externalEntity.entityLocation != null ? externalEntity.entityLocation.getPublicId() : null), 1289230592Sken extLitSysId, extBaseSysId, expandedSystemId); 1290230592Sken } 1291230592Sken fEntityAugs.removeAllItems(); 1292230592Sken fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE); 1293230592Sken fEntityHandler.startEntity(entityName, fResourceIdentifier, encoding, fEntityAugs); 1294230592Sken fEntityAugs.removeAllItems(); 1295230592Sken fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE); 1296230592Sken fEntityHandler.endEntity(entityName, fEntityAugs); 1297230592Sken } 1298230592Sken 1299230592Sken return; 1300230592Sken } 1301230592Sken } 1302230592Sken 1303230592Sken // resolve external entity 1304230592Sken StaxXMLInputSource staxInputSource = null; 1305230592Sken XMLInputSource xmlInputSource = null ; 1306230592Sken 1307230592Sken if (external) { 1308230592Sken staxInputSource = resolveEntityAsPerStax(externalEntity.entityLocation); 1309230592Sken /** xxx: Waiting from the EG 1310230592Sken * //simply return if there was entity resolver registered and application 1311230592Sken * //returns either XMLStreamReader or XMLEventReader. 1312230592Sken * if(staxInputSource.hasXMLStreamOrXMLEventReader()) return ; 1313230592Sken */ 1314230592Sken xmlInputSource = staxInputSource.getXMLInputSource() ; 1315230592Sken if (!fISCreatedByResolver) { 1316230592Sken //let the not-LoadExternalDTD or not-SupportDTD process to handle the situation 1317230592Sken if (fLoadExternalDTD) { 1318230592Sken String accessError = SecuritySupport.checkAccess(expandedSystemId, fAccessExternalDTD, Constants.ACCESS_EXTERNAL_ALL); 1319230592Sken if (accessError != null) { 1320230592Sken fErrorReporter.reportError(this.getEntityScanner(),XMLMessageFormatter.XML_DOMAIN, 1321230592Sken "AccessExternalEntity", 1322230592Sken new Object[] { SecuritySupport.sanitizePath(expandedSystemId), accessError }, 1323230592Sken XMLErrorReporter.SEVERITY_FATAL_ERROR); 1324230592Sken } 1325230592Sken } 1326230592Sken } 1327230592Sken } 1328230592Sken // wrap internal entity 1329230592Sken else { 1330230592Sken Entity.InternalEntity internalEntity = (Entity.InternalEntity)entity; 1331230592Sken Reader reader = new StringReader(internalEntity.text); 1332230592Sken xmlInputSource = new XMLInputSource(null, null, null, reader, null); 1333230592Sken } 1334230592Sken 1335230592Sken // start the entity 1336230592Sken startEntity(isGE, entityName, xmlInputSource, literal, external); 1337230592Sken 1338230592Sken } // startEntity(String,boolean) 1339230592Sken 1340230592Sken /** 1341230592Sken * Starts the document entity. The document entity has the "[xml]" 1342230592Sken * pseudo-name. 1343230592Sken * 1344230592Sken * @param xmlInputSource The input source of the document entity. 1345230592Sken * 1346230592Sken * @throws IOException Thrown on i/o error. 1347230592Sken * @throws XNIException Thrown by entity handler to signal an error. 1348230592Sken */ 1349230592Sken public void startDocumentEntity(XMLInputSource xmlInputSource) 1350230592Sken throws IOException, XNIException { 1351230592Sken startEntity(false, XMLEntity, xmlInputSource, false, true); 1352230592Sken } // startDocumentEntity(XMLInputSource) 1353230592Sken 1354230592Sken //xxx these methods are not required. 1355230592Sken /** 1356230592Sken * Starts the DTD entity. The DTD entity has the "[dtd]" 1357230592Sken * pseudo-name. 1358230592Sken * 1359230592Sken * @param xmlInputSource The input source of the DTD entity. 1360230592Sken * 1361230592Sken * @throws IOException Thrown on i/o error. 1362230592Sken * @throws XNIException Thrown by entity handler to signal an error. 1363230592Sken */ 1364230592Sken public void startDTDEntity(XMLInputSource xmlInputSource) 1365230592Sken throws IOException, XNIException { 1366230592Sken startEntity(false, DTDEntity, xmlInputSource, false, true); 1367230592Sken } // startDTDEntity(XMLInputSource) 1368230592Sken 1369230592Sken // indicate start of external subset so that 1370230592Sken // location of entity decls can be tracked 1371230592Sken public void startExternalSubset() { 1372230592Sken fInExternalSubset = true; 1373230592Sken } 1374230592Sken 1375230592Sken public void endExternalSubset() { 1376230592Sken fInExternalSubset = false; 1377230592Sken } 1378230592Sken 1379230592Sken /** 1380230592Sken * Starts an entity. 1381230592Sken * <p> 1382230592Sken * This method can be used to insert an application defined XML 1383230592Sken * entity stream into the parsing stream. 1384230592Sken * 1385230592Sken * @param isGE flag to indicate whether the entity is a General Entity 1386230592Sken * @param name The name of the entity. 1387230592Sken * @param xmlInputSource The input source of the entity. 1388230592Sken * @param literal True if this entity is started within a 1389230592Sken * literal value. 1390230592Sken * @param isExternal whether this entity should be treated as an internal or external entity. 1391230592Sken * 1392230592Sken * @throws IOException Thrown on i/o error. 1393230592Sken * @throws XNIException Thrown by entity handler to signal an error. 1394230592Sken */ 1395230592Sken public void startEntity(boolean isGE, String name, 1396230592Sken XMLInputSource xmlInputSource, 1397230592Sken boolean literal, boolean isExternal) 1398230592Sken throws IOException, XNIException { 1399230592Sken 1400230592Sken String encoding = setupCurrentEntity(isGE, name, xmlInputSource, literal, isExternal); 1401230592Sken 1402230592Sken //when entity expansion limit is set by the Application, we need to 1403230592Sken //check for the entity expansion limit set by the parser, if number of entity 1404230592Sken //expansions exceeds the entity expansion limit, parser will throw fatal error. 1405230592Sken // Note that this represents the nesting level of open entities. 1406230592Sken fEntityExpansionCount++; 1407230592Sken if(fLimitAnalyzer != null) { 1408230592Sken fLimitAnalyzer.addValue(entityExpansionIndex, name, 1); 1409230592Sken } 1410230592Sken if( fSecurityManager != null && fSecurityManager.isOverLimit(entityExpansionIndex, fLimitAnalyzer)){ 1411230592Sken fSecurityManager.debugPrint(fLimitAnalyzer); 1412230592Sken fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,"EntityExpansionLimit", 1413230592Sken new Object[]{fSecurityManager.getLimitValueByIndex(entityExpansionIndex)}, 1414230592Sken XMLErrorReporter.SEVERITY_FATAL_ERROR ); 1415230592Sken // is there anything better to do than reset the counter? 1416230592Sken // at least one can envision debugging applications where this might 1417230592Sken // be useful... 1418230592Sken fEntityExpansionCount = 0; 1419230592Sken } 1420230592Sken 1421230592Sken // call handler 1422230592Sken if (fEntityHandler != null) { 1423230592Sken fEntityHandler.startEntity(name, fResourceIdentifier, encoding, null); 1424230592Sken } 1425230592Sken 1426230592Sken } // startEntity(String,XMLInputSource) 1427230592Sken 1428230592Sken /** 1429230592Sken * Return the current entity being scanned. Current entity is SET using startEntity function. 1430230592Sken * @return Entity.ScannedEntity 1431230592Sken */ 1432230592Sken 1433230592Sken public Entity.ScannedEntity getCurrentEntity(){ 1434230592Sken return fCurrentEntity ; 1435230592Sken } 1436230592Sken 1437230592Sken /** 1438230592Sken * Return the top level entity handled by this manager, or null 1439230592Sken * if no entity was added. 1440230592Sken */ 1441230592Sken public Entity.ScannedEntity getTopLevelEntity() { 1442230592Sken return (Entity.ScannedEntity) 1443230592Sken (fEntityStack.empty() ? null : fEntityStack.elementAt(0)); 1444230592Sken } 1445230592Sken 1446230592Sken 1447230592Sken /** 1448230592Sken * Close all opened InputStreams and Readers opened by this parser. 1449230592Sken */ 1450230592Sken public void closeReaders() { 1451230592Sken /** this call actually does nothing, readers are closed in the endEntity method 1452230592Sken * through the current entity. 1453230592Sken * The change seems to have happened during the jdk6 development with the 1454230592Sken * addition of StAX 1455230592Sken **/ 1456230592Sken } 1457230592Sken 1458230592Sken public void endEntity() throws IOException, XNIException { 1459230592Sken 1460230592Sken // call handler 1461230592Sken if (DEBUG_BUFFER) { 1462230592Sken System.out.print("(endEntity: "); 1463230592Sken print(); 1464230592Sken System.out.println(); 1465230592Sken } 1466230592Sken //pop the entity from the stack 1467230592Sken Entity.ScannedEntity entity = fEntityStack.size() > 0 ? (Entity.ScannedEntity)fEntityStack.pop() : null ; 1468230592Sken 1469230592Sken /** need to close the reader first since the program can end 1470230592Sken * prematurely (e.g. fEntityHandler.endEntity may throw exception) 1471230592Sken * leaving the reader open 1472230592Sken */ 1473230592Sken //close the reader 1474230592Sken if(fCurrentEntity != null){ 1475230592Sken //close the reader 1476230592Sken try{ 1477230592Sken if (fLimitAnalyzer != null) { 1478230592Sken fLimitAnalyzer.endEntity(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT, fCurrentEntity.name); 1479230592Sken if (fCurrentEntity.name.equals("[xml]")) { 1480230592Sken fSecurityManager.debugPrint(fLimitAnalyzer); 1481230592Sken } 1482230592Sken } 1483230592Sken fCurrentEntity.close(); 1484230592Sken }catch(IOException ex){ 1485230592Sken throw new XNIException(ex); 1486230592Sken } 1487230592Sken } 1488230592Sken 1489230592Sken if (fEntityHandler != null) { 1490230592Sken //so this is the last opened entity, signal it to current fEntityHandler using Augmentation 1491230592Sken if(entity == null){ 1492230592Sken fEntityAugs.removeAllItems(); 1493230592Sken fEntityAugs.putItem(Constants.LAST_ENTITY, Boolean.TRUE); 1494230592Sken fEntityHandler.endEntity(fCurrentEntity.name, fEntityAugs); 1495230592Sken fEntityAugs.removeAllItems(); 1496230592Sken }else{ 1497230592Sken fEntityHandler.endEntity(fCurrentEntity.name, null); 1498230592Sken } 1499230592Sken } 1500230592Sken //check if it is a document entity 1501230592Sken boolean documentEntity = fCurrentEntity.name == XMLEntity; 1502230592Sken 1503230592Sken //set popped entity as current entity 1504230592Sken fCurrentEntity = entity; 1505230592Sken fEntityScanner.setCurrentEntity(fCurrentEntity); 1506230592Sken 1507230592Sken //check if there are any entity left in the stack -- if there are 1508230592Sken //no entries EOF has been reached. 1509230592Sken // throw exception when it is the last entity but it is not a document entity 1510230592Sken 1511230592Sken if(fCurrentEntity == null & !documentEntity){ 1512230592Sken throw new EOFException() ; 1513230592Sken } 1514230592Sken 1515230592Sken if (DEBUG_BUFFER) { 1516230592Sken System.out.print(")endEntity: "); 1517230592Sken print(); 1518230592Sken System.out.println(); 1519230592Sken } 1520230592Sken 1521230592Sken } // endEntity() 1522230592Sken 1523230592Sken 1524230592Sken // 1525230592Sken // XMLComponent methods 1526230592Sken // 1527230592Sken public void reset(PropertyManager propertyManager){ 1528230592Sken // xerces properties 1529230592Sken fSymbolTable = (SymbolTable)propertyManager.getProperty(Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY); 1530230592Sken fErrorReporter = (XMLErrorReporter)propertyManager.getProperty(Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY); 1531230592Sken try { 1532230592Sken fStaxEntityResolver = (StaxEntityResolverWrapper)propertyManager.getProperty(STAX_ENTITY_RESOLVER); 1533230592Sken } catch (XMLConfigurationException e) { 1534230592Sken fStaxEntityResolver = null; 1535230592Sken } 1536230592Sken 1537230592Sken fSupportDTD = ((Boolean)propertyManager.getProperty(XMLInputFactory.SUPPORT_DTD)); 1538230592Sken fReplaceEntityReferences = ((Boolean)propertyManager.getProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES)); 1539230592Sken fSupportExternalEntities = ((Boolean)propertyManager.getProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES)); 1540230592Sken 1541230592Sken // Zephyr feature ignore-external-dtd is the opposite of Xerces' load-external-dtd 1542230592Sken fLoadExternalDTD = !((Boolean)propertyManager.getProperty(Constants.ZEPHYR_PROPERTY_PREFIX + Constants.IGNORE_EXTERNAL_DTD)); 1543230592Sken 1544230592Sken //Use Catalog 1545230592Sken fUseCatalog = (Boolean)propertyManager.getProperty(XMLConstants.USE_CATALOG); 1546230592Sken fCatalogFile = (String)propertyManager.getProperty(JdkXmlUtils.CATALOG_FILES); 1547230592Sken fDefer = (String)propertyManager.getProperty(JdkXmlUtils.CATALOG_DEFER); 1548230592Sken fPrefer = (String)propertyManager.getProperty(JdkXmlUtils.CATALOG_PREFER); 1549230592Sken fResolve = (String)propertyManager.getProperty(JdkXmlUtils.CATALOG_RESOLVE); 1550230592Sken 1551230592Sken // JAXP 1.5 feature 1552230592Sken XMLSecurityPropertyManager spm = (XMLSecurityPropertyManager) propertyManager.getProperty(XML_SECURITY_PROPERTY_MANAGER); 1553230592Sken fAccessExternalDTD = spm.getValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_DTD); 1554230592Sken 1555230592Sken fSecurityManager = (XMLSecurityManager)propertyManager.getProperty(SECURITY_MANAGER); 1556230592Sken 1557230592Sken fLimitAnalyzer = new XMLLimitAnalyzer(); 1558230592Sken //reset fEntityStorage 1559230592Sken fEntityStorage.reset(propertyManager); 1560230592Sken //reset XMLEntityReaderImpl 1561230592Sken fEntityScanner.reset(propertyManager); 1562230592Sken 1563230592Sken // initialize state 1564230592Sken //fStandalone = false; 1565230592Sken fEntities.clear(); 1566230592Sken fEntityStack.removeAllElements(); 1567230592Sken fCurrentEntity = null; 1568230592Sken fValidation = false; 1569230592Sken fExternalGeneralEntities = true; 1570230592Sken fExternalParameterEntities = true; 1571230592Sken fAllowJavaEncodings = true ; 1572230592Sken } 1573230592Sken 1574230592Sken /** 1575230592Sken * Resets the component. The component can query the component manager 1576230592Sken * about any features and properties that affect the operation of the 1577230592Sken * component. 1578230592Sken * 1579230592Sken * @param componentManager The component manager. 1580230592Sken * 1581230592Sken * @throws SAXException Thrown by component on initialization error. 1582230592Sken * For example, if a feature or property is 1583230592Sken * required for the operation of the component, the 1584230592Sken * component manager may throw a 1585230592Sken * SAXNotRecognizedException or a 1586230592Sken * SAXNotSupportedException. 1587230592Sken */ 1588230592Sken public void reset(XMLComponentManager componentManager) 1589230592Sken throws XMLConfigurationException { 1590230592Sken 1591230592Sken boolean parser_settings = componentManager.getFeature(PARSER_SETTINGS, true); 1592230592Sken 1593230592Sken if (!parser_settings) { 1594230592Sken // parser settings have not been changed 1595230592Sken reset(); 1596230592Sken if(fEntityScanner != null){ 1597230592Sken fEntityScanner.reset(componentManager); 1598230592Sken } 1599230592Sken if(fEntityStorage != null){ 1600230592Sken fEntityStorage.reset(componentManager); 1601230592Sken } 1602230592Sken return; 1603230592Sken } 1604230592Sken 1605230592Sken // sax features 1606230592Sken fValidation = componentManager.getFeature(VALIDATION, false); 1607230592Sken fExternalGeneralEntities = componentManager.getFeature(EXTERNAL_GENERAL_ENTITIES, true); 1608230592Sken fExternalParameterEntities = componentManager.getFeature(EXTERNAL_PARAMETER_ENTITIES, true); 1609230592Sken 1610230592Sken // xerces features 1611230592Sken fAllowJavaEncodings = componentManager.getFeature(ALLOW_JAVA_ENCODINGS, false); 1612230592Sken fWarnDuplicateEntityDef = componentManager.getFeature(WARN_ON_DUPLICATE_ENTITYDEF, false); 1613230592Sken fStrictURI = componentManager.getFeature(STANDARD_URI_CONFORMANT, false); 1614230592Sken fLoadExternalDTD = componentManager.getFeature(LOAD_EXTERNAL_DTD, true); 1615230592Sken 1616230592Sken // xerces properties 1617230592Sken fSymbolTable = (SymbolTable)componentManager.getProperty(SYMBOL_TABLE); 1618230592Sken fErrorReporter = (XMLErrorReporter)componentManager.getProperty(ERROR_REPORTER); 1619230592Sken fEntityResolver = (XMLEntityResolver)componentManager.getProperty(ENTITY_RESOLVER, null); 1620230592Sken fStaxEntityResolver = (StaxEntityResolverWrapper)componentManager.getProperty(STAX_ENTITY_RESOLVER, null); 1621230592Sken fValidationManager = (ValidationManager)componentManager.getProperty(VALIDATION_MANAGER, null); 1622230592Sken fSecurityManager = (XMLSecurityManager)componentManager.getProperty(SECURITY_MANAGER, null); 1623230592Sken entityExpansionIndex = fSecurityManager.getIndex(Constants.JDK_ENTITY_EXPANSION_LIMIT); 1624230592Sken 1625230592Sken //StAX Property 1626230592Sken fSupportDTD = true; 1627230592Sken fReplaceEntityReferences = true; 1628230592Sken fSupportExternalEntities = true; 1629230592Sken 1630230592Sken // JAXP 1.5 feature 1631230592Sken XMLSecurityPropertyManager spm = (XMLSecurityPropertyManager) componentManager.getProperty(XML_SECURITY_PROPERTY_MANAGER, null); 1632230592Sken if (spm == null) { 1633230592Sken spm = new XMLSecurityPropertyManager(); 1634230592Sken } 1635230592Sken fAccessExternalDTD = spm.getValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_DTD); 1636230592Sken 1637230592Sken //Use Catalog 1638230592Sken fUseCatalog = componentManager.getFeature(XMLConstants.USE_CATALOG, true); 1639230592Sken fCatalogFile = (String)componentManager.getProperty(JdkXmlUtils.CATALOG_FILES); 1640230592Sken fDefer = (String)componentManager.getProperty(JdkXmlUtils.CATALOG_DEFER); 1641230592Sken fPrefer = (String)componentManager.getProperty(JdkXmlUtils.CATALOG_PREFER); 1642230592Sken fResolve = (String)componentManager.getProperty(JdkXmlUtils.CATALOG_RESOLVE); 1643230592Sken 1644230592Sken //reset general state 1645230592Sken reset(); 1646230592Sken 1647230592Sken fEntityScanner.reset(componentManager); 1648230592Sken fEntityStorage.reset(componentManager); 1649230592Sken 1650230592Sken } // reset(XMLComponentManager) 1651230592Sken 1652230592Sken // reset general state. Should not be called other than by 1653230592Sken // a class acting as a component manager but not 1654230592Sken // implementing that interface for whatever reason. 1655230592Sken public void reset() { 1656230592Sken fLimitAnalyzer = new XMLLimitAnalyzer(); 1657230592Sken // initialize state 1658230592Sken fStandalone = false; 1659230592Sken fEntities.clear(); 1660230592Sken fEntityStack.removeAllElements(); 1661230592Sken fEntityExpansionCount = 0; 1662230592Sken 1663230592Sken fCurrentEntity = null; 1664230592Sken // reset scanner 1665230592Sken if(fXML10EntityScanner != null){ 1666230592Sken fXML10EntityScanner.reset(fSymbolTable, this, fErrorReporter); 1667230592Sken } 1668230592Sken if(fXML11EntityScanner != null) { 1669230592Sken fXML11EntityScanner.reset(fSymbolTable, this, fErrorReporter); 1670230592Sken } 1671230592Sken 1672230592Sken // DEBUG 1673230592Sken if (DEBUG_ENTITIES) { 1674230592Sken addInternalEntity("text", "Hello, World."); 1675230592Sken addInternalEntity("empty-element", "<foo/>"); 1676230592Sken addInternalEntity("balanced-element", "<foo></foo>"); 1677230592Sken addInternalEntity("balanced-element-with-text", "<foo>Hello, World</foo>"); 1678230592Sken addInternalEntity("balanced-element-with-entity", "<foo>&text;</foo>"); 1679230592Sken addInternalEntity("unbalanced-entity", "<foo>"); 1680230592Sken addInternalEntity("recursive-entity", "<foo>&recursive-entity2;</foo>"); 1681230592Sken addInternalEntity("recursive-entity2", "<bar>&recursive-entity3;</bar>"); 1682230592Sken addInternalEntity("recursive-entity3", "<baz>&recursive-entity;</baz>"); 1683230592Sken try { 1684230592Sken addExternalEntity("external-text", null, "external-text.ent", "test/external-text.xml"); 1685230592Sken addExternalEntity("external-balanced-element", null, "external-balanced-element.ent", "test/external-balanced-element.xml"); 1686230592Sken addExternalEntity("one", null, "ent/one.ent", "test/external-entity.xml"); 1687230592Sken addExternalEntity("two", null, "ent/two.ent", "test/ent/one.xml"); 1688230592Sken } 1689230592Sken catch (IOException ex) { 1690230592Sken // should never happen 1691230592Sken } 1692230592Sken } 1693230592Sken 1694230592Sken fEntityHandler = null; 1695230592Sken 1696230592Sken // reset scanner 1697230592Sken //if(fEntityScanner!=null) 1698230592Sken // fEntityScanner.reset(fSymbolTable, this,fErrorReporter); 1699230592Sken 1700230592Sken } 1701230592Sken /** 1702230592Sken * Returns a list of feature identifiers that are recognized by 1703230592Sken * this component. This method may return null if no features 1704230592Sken * are recognized by this component. 1705230592Sken */ 1706230592Sken public String[] getRecognizedFeatures() { 1707230592Sken return RECOGNIZED_FEATURES.clone(); 1708230592Sken } // getRecognizedFeatures():String[] 1709230592Sken 1710230592Sken /** 1711230592Sken * Sets the state of a feature. This method is called by the component 1712230592Sken * manager any time after reset when a feature changes state. 1713230592Sken * <p> 1714230592Sken * <strong>Note:</strong> Components should silently ignore features 1715230592Sken * that do not affect the operation of the component. 1716230592Sken * 1717230592Sken * @param featureId The feature identifier. 1718230592Sken * @param state The state of the feature. 1719230592Sken * 1720230592Sken * @throws SAXNotRecognizedException The component should not throw 1721230592Sken * this exception. 1722230592Sken * @throws SAXNotSupportedException The component should not throw 1723230592Sken * this exception. 1724230592Sken */ 1725230592Sken public void setFeature(String featureId, boolean state) 1726230592Sken throws XMLConfigurationException { 1727230592Sken 1728230592Sken // xerces features 1729230592Sken if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) { 1730230592Sken final int suffixLength = featureId.length() - Constants.XERCES_FEATURE_PREFIX.length(); 1731230592Sken if (suffixLength == Constants.ALLOW_JAVA_ENCODINGS_FEATURE.length() && 1732230592Sken featureId.endsWith(Constants.ALLOW_JAVA_ENCODINGS_FEATURE)) { 1733230592Sken fAllowJavaEncodings = state; 1734230592Sken } 1735230592Sken if (suffixLength == Constants.LOAD_EXTERNAL_DTD_FEATURE.length() && 1736230592Sken featureId.endsWith(Constants.LOAD_EXTERNAL_DTD_FEATURE)) { 1737230592Sken fLoadExternalDTD = state; 1738230592Sken return; 1739230592Sken } 1740230592Sken } else if (featureId.equals(XMLConstants.USE_CATALOG)) { 1741230592Sken fUseCatalog = state; 1742230592Sken } 1743230592Sken 1744230592Sken } // setFeature(String,boolean) 1745230592Sken 1746230592Sken /** 1747230592Sken * Sets the value of a property. This method is called by the component 1748230592Sken * manager any time after reset when a property changes value. 1749230592Sken * <p> 1750230592Sken * <strong>Note:</strong> Components should silently ignore properties 1751230592Sken * that do not affect the operation of the component. 1752230592Sken * 1753230592Sken * @param propertyId The property identifier. 1754230592Sken * @param value The value of the property. 1755230592Sken * 1756230592Sken * @throws SAXNotRecognizedException The component should not throw 1757230592Sken * this exception. 1758230592Sken * @throws SAXNotSupportedException The component should not throw 1759230592Sken * this exception. 1760230592Sken */ 1761230592Sken public void setProperty(String propertyId, Object value){ 1762230592Sken // Xerces properties 1763230592Sken if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) { 1764230592Sken final int suffixLength = propertyId.length() - Constants.XERCES_PROPERTY_PREFIX.length(); 1765230592Sken 1766230592Sken if (suffixLength == Constants.SYMBOL_TABLE_PROPERTY.length() && 1767230592Sken propertyId.endsWith(Constants.SYMBOL_TABLE_PROPERTY)) { 1768230592Sken fSymbolTable = (SymbolTable)value; 1769230592Sken return; 1770230592Sken } 1771230592Sken if (suffixLength == Constants.ERROR_REPORTER_PROPERTY.length() && 1772230592Sken propertyId.endsWith(Constants.ERROR_REPORTER_PROPERTY)) { 1773230592Sken fErrorReporter = (XMLErrorReporter)value; 1774230592Sken return; 1775230592Sken } 1776230592Sken if (suffixLength == Constants.ENTITY_RESOLVER_PROPERTY.length() && 1777230592Sken propertyId.endsWith(Constants.ENTITY_RESOLVER_PROPERTY)) { 1778230592Sken fEntityResolver = (XMLEntityResolver)value; 1779230592Sken return; 1780230592Sken } 1781230592Sken if (suffixLength == Constants.BUFFER_SIZE_PROPERTY.length() && 1782230592Sken propertyId.endsWith(Constants.BUFFER_SIZE_PROPERTY)) { 1783230592Sken Integer bufferSize = (Integer)value; 1784230592Sken if (bufferSize != null && 1785230592Sken bufferSize.intValue() > DEFAULT_XMLDECL_BUFFER_SIZE) { 1786230592Sken fBufferSize = bufferSize.intValue(); 1787230592Sken fEntityScanner.setBufferSize(fBufferSize); 1788230592Sken fBufferPool.setExternalBufferSize(fBufferSize); 1789230592Sken } 1790230592Sken } 1791230592Sken if (suffixLength == Constants.SECURITY_MANAGER_PROPERTY.length() && 1792230592Sken propertyId.endsWith(Constants.SECURITY_MANAGER_PROPERTY)) { 1793230592Sken fSecurityManager = (XMLSecurityManager)value; 1794230592Sken } 1795230592Sken } 1796230592Sken 1797230592Sken //JAXP 1.5 properties 1798230592Sken if (propertyId.equals(XML_SECURITY_PROPERTY_MANAGER)) 1799230592Sken { 1800230592Sken XMLSecurityPropertyManager spm = (XMLSecurityPropertyManager)value; 1801230592Sken fAccessExternalDTD = spm.getValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_DTD); 1802230592Sken return; 1803230592Sken } 1804230592Sken 1805230592Sken //Catalog properties 1806230592Sken if (propertyId.equals(JdkXmlUtils.CATALOG_FILES)) { 1807230592Sken fCatalogFile = (String)value; 1808230592Sken } else if (propertyId.equals(JdkXmlUtils.CATALOG_DEFER)) { 1809230592Sken fDefer = (String)value; 1810230592Sken } else if (propertyId.equals(JdkXmlUtils.CATALOG_PREFER)) { 1811230592Sken fPrefer = (String)value; 1812230592Sken } else if (propertyId.equals(JdkXmlUtils.CATALOG_RESOLVE)) { 1813230592Sken fResolve = (String)value; 1814230592Sken } 1815230592Sken } 1816230592Sken 1817230592Sken public void setLimitAnalyzer(XMLLimitAnalyzer fLimitAnalyzer) { 1818230592Sken this.fLimitAnalyzer = fLimitAnalyzer; 1819230592Sken } 1820230592Sken 1821230592Sken /** 1822230592Sken * Returns a list of property identifiers that are recognized by 1823230592Sken * this component. This method may return null if no properties 1824230592Sken * are recognized by this component. 1825230592Sken */ 1826230592Sken public String[] getRecognizedProperties() { 1827230592Sken return RECOGNIZED_PROPERTIES.clone(); 1828230592Sken } // getRecognizedProperties():String[] 1829230592Sken /** 1830230592Sken * Returns the default state for a feature, or null if this 1831230592Sken * component does not want to report a default value for this 1832230592Sken * feature. 1833230592Sken * 1834230592Sken * @param featureId The feature identifier. 1835230592Sken * 1836230592Sken * @since Xerces 2.2.0 1837230592Sken */ 1838230592Sken public Boolean getFeatureDefault(String featureId) { 1839230592Sken for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) { 1840230592Sken if (RECOGNIZED_FEATURES[i].equals(featureId)) { 1841230592Sken return FEATURE_DEFAULTS[i]; 1842230592Sken } 1843230592Sken } 1844230592Sken return null; 1845230592Sken } // getFeatureDefault(String):Boolean 1846230592Sken 1847230592Sken /** 1848230592Sken * Returns the default state for a property, or null if this 1849230592Sken * component does not want to report a default value for this 1850230592Sken * property. 1851230592Sken * 1852230592Sken * @param propertyId The property identifier. 1853230592Sken * 1854230592Sken * @since Xerces 2.2.0 1855230592Sken */ 1856230592Sken public Object getPropertyDefault(String propertyId) { 1857230592Sken for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) { 1858230592Sken if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) { 1859230592Sken return PROPERTY_DEFAULTS[i]; 1860230592Sken } 1861230592Sken } 1862230592Sken return null; 1863230592Sken } // getPropertyDefault(String):Object 1864230592Sken 1865230592Sken // 1866230592Sken // Public static methods 1867230592Sken // 1868230592Sken 1869230592Sken /** 1870230592Sken * Expands a system id and returns the system id as a URI, if 1871230592Sken * it can be expanded. A return value of null means that the 1872230592Sken * identifier is already expanded. An exception thrown 1873230592Sken * indicates a failure to expand the id. 1874230592Sken * 1875230592Sken * @param systemId The systemId to be expanded. 1876230592Sken * 1877230592Sken * @return Returns the URI string representing the expanded system 1878230592Sken * identifier. A null value indicates that the given 1879230592Sken * system identifier is already expanded. 1880230592Sken * 1881230592Sken */ 1882230592Sken public static String expandSystemId(String systemId) { 1883230592Sken return expandSystemId(systemId, null); 1884230592Sken } // expandSystemId(String):String 1885230592Sken 1886230592Sken // 1887230592Sken // Public static methods 1888230592Sken // 1889230592Sken 1890230592Sken // current value of the "user.dir" property 1891230592Sken private static String gUserDir; 1892230592Sken // cached URI object for the current value of the escaped "user.dir" property stored as a URI 1893230592Sken private static URI gUserDirURI; 1894230592Sken // which ASCII characters need to be escaped 1895230592Sken private static boolean gNeedEscaping[] = new boolean[128]; 1896230592Sken // the first hex character if a character needs to be escaped 1897230592Sken private static char gAfterEscaping1[] = new char[128]; 1898230592Sken // the second hex character if a character needs to be escaped 1899230592Sken private static char gAfterEscaping2[] = new char[128]; 1900230592Sken private static char[] gHexChs = {'0', '1', '2', '3', '4', '5', '6', '7', 1901230592Sken '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; 1902230592Sken // initialize the above 3 arrays 1903230592Sken static { 1904230592Sken for (int i = 0; i <= 0x1f; i++) { 1905230592Sken gNeedEscaping[i] = true; 1906230592Sken gAfterEscaping1[i] = gHexChs[i >> 4]; 1907230592Sken gAfterEscaping2[i] = gHexChs[i & 0xf]; 1908230592Sken } 1909230592Sken gNeedEscaping[0x7f] = true; 1910230592Sken gAfterEscaping1[0x7f] = '7'; 1911230592Sken gAfterEscaping2[0x7f] = 'F'; 1912230592Sken char[] escChs = {' ', '<', '>', '#', '%', '"', '{', '}', 1913230592Sken '|', '\\', '^', '~', '[', ']', '`'}; 1914230592Sken int len = escChs.length; 1915230592Sken char ch; 1916230592Sken for (int i = 0; i < len; i++) { 1917230592Sken ch = escChs[i]; 1918230592Sken gNeedEscaping[ch] = true; 1919230592Sken gAfterEscaping1[ch] = gHexChs[ch >> 4]; 1920230592Sken gAfterEscaping2[ch] = gHexChs[ch & 0xf]; 1921230592Sken } 1922230592Sken } 1923230592Sken 1924230592Sken // To escape the "user.dir" system property, by using %HH to represent 1925230592Sken // special ASCII characters: 0x00~0x1F, 0x7F, ' ', '<', '>', '#', '%' 1926230592Sken // and '"'. It's a static method, so needs to be synchronized. 1927230592Sken // this method looks heavy, but since the system property isn't expected 1928230592Sken // to change often, so in most cases, we only need to return the URI 1929230592Sken // that was escaped before. 1930230592Sken // According to the URI spec, non-ASCII characters (whose value >= 128) 1931230592Sken // need to be escaped too. 1932230592Sken // REVISIT: don't know how to escape non-ASCII characters, especially 1933230592Sken // which encoding to use. Leave them for now. 1934230592Sken private static synchronized URI getUserDir() throws URI.MalformedURIException { 1935230592Sken // get the user.dir property 1936230592Sken String userDir = ""; 1937230592Sken try { 1938230592Sken userDir = SecuritySupport.getSystemProperty("user.dir"); 1939230592Sken } 1940230592Sken catch (SecurityException se) { 1941230592Sken } 1942230592Sken 1943230592Sken // return empty string if property value is empty string. 1944230592Sken if (userDir.length() == 0) 1945230592Sken return new URI("file", "", "", null, null); 1946230592Sken // compute the new escaped value if the new property value doesn't 1947230592Sken // match the previous one 1948230592Sken if (gUserDirURI != null && userDir.equals(gUserDir)) { 1949230592Sken return gUserDirURI; 1950230592Sken } 1951230592Sken 1952230592Sken // record the new value as the global property value 1953230592Sken gUserDir = userDir; 1954230592Sken 1955230592Sken char separator = java.io.File.separatorChar; 1956230592Sken userDir = userDir.replace(separator, '/'); 1957230592Sken 1958230592Sken int len = userDir.length(), ch; 1959230592Sken StringBuilder buffer = new StringBuilder(len*3); 1960230592Sken // change C:/blah to /C:/blah 1961230592Sken if (len >= 2 && userDir.charAt(1) == ':') { 1962230592Sken ch = Character.toUpperCase(userDir.charAt(0)); 1963230592Sken if (ch >= 'A' && ch <= 'Z') { 1964230592Sken buffer.append('/'); 1965230592Sken } 1966230592Sken } 1967230592Sken 1968230592Sken // for each character in the path 1969230592Sken int i = 0; 1970230592Sken for (; i < len; i++) { 1971230592Sken ch = userDir.charAt(i); 1972230592Sken // if it's not an ASCII character, break here, and use UTF-8 encoding 1973230592Sken if (ch >= 128) 1974230592Sken break; 1975230592Sken if (gNeedEscaping[ch]) { 1976230592Sken buffer.append('%'); 1977230592Sken buffer.append(gAfterEscaping1[ch]); 1978230592Sken buffer.append(gAfterEscaping2[ch]); 1979230592Sken // record the fact that it's escaped 1980230592Sken } 1981230592Sken else { 1982230592Sken buffer.append((char)ch); 1983230592Sken } 1984230592Sken } 1985230592Sken 1986230592Sken // we saw some non-ascii character 1987230592Sken if (i < len) { 1988230592Sken // get UTF-8 bytes for the remaining sub-string 1989230592Sken byte[] bytes = null; 1990230592Sken byte b; 1991230592Sken try { 1992230592Sken bytes = userDir.substring(i).getBytes("UTF-8"); 1993230592Sken } catch (java.io.UnsupportedEncodingException e) { 1994230592Sken // should never happen 1995230592Sken return new URI("file", "", userDir, null, null); 1996230592Sken } 1997230592Sken len = bytes.length; 1998230592Sken 1999230592Sken // for each byte 2000230592Sken for (i = 0; i < len; i++) { 2001230592Sken b = bytes[i]; 2002230592Sken // for non-ascii character: make it positive, then escape 2003230592Sken if (b < 0) { 2004230592Sken ch = b + 256; 2005230592Sken buffer.append('%'); 2006230592Sken buffer.append(gHexChs[ch >> 4]); 2007230592Sken buffer.append(gHexChs[ch & 0xf]); 2008230592Sken } 2009230592Sken else if (gNeedEscaping[b]) { 2010230592Sken buffer.append('%'); 2011230592Sken buffer.append(gAfterEscaping1[b]); 2012230592Sken buffer.append(gAfterEscaping2[b]); 2013230592Sken } 2014230592Sken else { 2015230592Sken buffer.append((char)b); 2016230592Sken } 2017230592Sken } 2018230592Sken } 2019230592Sken 2020230592Sken // change blah/blah to blah/blah/ 2021230592Sken if (!userDir.endsWith("/")) 2022230592Sken buffer.append('/'); 2023230592Sken 2024230592Sken gUserDirURI = new URI("file", "", buffer.toString(), null, null); 2025230592Sken 2026230592Sken return gUserDirURI; 2027230592Sken } 2028230592Sken 2029230592Sken public static OutputStream createOutputStream(String uri) throws IOException { 2030230592Sken // URI was specified. Handle relative URIs. 2031230592Sken final String expanded = XMLEntityManager.expandSystemId(uri, null, true); 2032230592Sken final URL url = new URL(expanded != null ? expanded : uri); 2033230592Sken OutputStream out = null; 2034230592Sken String protocol = url.getProtocol(); 2035230592Sken String host = url.getHost(); 2036230592Sken // Use FileOutputStream if this URI is for a local file. 2037230592Sken if (protocol.equals("file") 2038230592Sken && (host == null || host.length() == 0 || host.equals("localhost"))) { 2039230592Sken File file = new File(getPathWithoutEscapes(url.getPath())); 2040230592Sken if (!file.exists()) { 2041230592Sken File parent = file.getParentFile(); 2042230592Sken if (parent != null && !parent.exists()) { 2043230592Sken parent.mkdirs(); 2044230592Sken } 2045230592Sken } 2046230592Sken out = new FileOutputStream(file); 2047213702Smdf } 2048212420Sken // Try to write to some other kind of URI. Some protocols 2049212420Sken // won't support this, though HTTP should work. 2050212420Sken else { 2051212420Sken URLConnection urlCon = url.openConnection(); 2052212420Sken urlCon.setDoInput(false); 2053212420Sken urlCon.setDoOutput(true); 2054230592Sken urlCon.setUseCaches(false); // Enable tunneling. 2055212420Sken if (urlCon instanceof HttpURLConnection) { 2056212420Sken // The DOM L3 REC says if we are writing to an HTTP URI 2057212420Sken // it is to be done with an HTTP PUT. 2058212420Sken HttpURLConnection httpCon = (HttpURLConnection) urlCon; 2059212420Sken httpCon.setRequestMethod("PUT"); 2060212420Sken } 2061212420Sken out = urlCon.getOutputStream(); 2062212420Sken } 2063212420Sken return out; 2064212420Sken } 2065212420Sken 2066212420Sken private static String getPathWithoutEscapes(String origPath) { 2067212420Sken if (origPath != null && origPath.length() != 0 && origPath.indexOf('%') != -1) { 2068212420Sken // Locate the escape characters 2069212420Sken StringTokenizer tokenizer = new StringTokenizer(origPath, "%"); 2070212420Sken StringBuilder result = new StringBuilder(origPath.length()); 2071212420Sken int size = tokenizer.countTokens(); 2072212420Sken result.append(tokenizer.nextToken()); 2073212420Sken for(int i = 1; i < size; ++i) { 2074212420Sken String token = tokenizer.nextToken(); 2075212420Sken // Decode the 2 digit hexadecimal number following % in '%nn' 2076212420Sken result.append((char)Integer.valueOf(token.substring(0, 2), 16).intValue()); 2077212420Sken result.append(token.substring(2)); 2078212420Sken } 2079212420Sken return result.toString(); 2080212420Sken } 2081212420Sken return origPath; 2082212420Sken } 2083212420Sken 2084212420Sken /** 2085212420Sken * Absolutizes a URI using the current value 2086212420Sken * of the "user.dir" property as the base URI. If 2087212420Sken * the URI is already absolute, this is a no-op. 2088212420Sken * 2089212420Sken * @param uri the URI to absolutize 2090212420Sken */ 2091212420Sken public static void absolutizeAgainstUserDir(URI uri) 2092212420Sken throws URI.MalformedURIException { 2093212420Sken uri.absolutize(getUserDir()); 2094212420Sken } 2095212420Sken 2096212420Sken /** 2097212420Sken * Expands a system id and returns the system id as a URI, if 2098212420Sken * it can be expanded. A return value of null means that the 2099212420Sken * identifier is already expanded. An exception thrown 2100212420Sken * indicates a failure to expand the id. 2101212420Sken * 2102212420Sken * @param systemId The systemId to be expanded. 2103212420Sken * 2104212420Sken * @return Returns the URI string representing the expanded system 2105212420Sken * identifier. A null value indicates that the given 2106212420Sken * system identifier is already expanded. 2107212420Sken * 2108212420Sken */ 2109212420Sken public static String expandSystemId(String systemId, String baseSystemId) { 2110230592Sken 2111230592Sken // check for bad parameters id 2112230592Sken if (systemId == null || systemId.length() == 0) { 2113230592Sken return systemId; 2114230592Sken } 2115230592Sken // if id already expanded, return 2116230592Sken try { 2117230592Sken URI uri = new URI(systemId); 2118230592Sken if (uri != null) { 2119230592Sken return systemId; 2120230592Sken } 2121230592Sken } catch (URI.MalformedURIException e) { 2122230592Sken // continue on... 2123230592Sken } 2124230592Sken // normalize id 2125230592Sken String id = fixURI(systemId); 2126230592Sken 2127230592Sken // normalize base 2128230592Sken URI base = null; 2129230592Sken URI uri = null; 2130230592Sken try { 2131230592Sken if (baseSystemId == null || baseSystemId.length() == 0 || 2132230592Sken baseSystemId.equals(systemId)) { 2133230592Sken String dir = getUserDir().toString(); 2134230592Sken base = new URI("file", "", dir, null, null); 2135230592Sken } else { 2136230592Sken try { 2137230592Sken base = new URI(fixURI(baseSystemId)); 2138230592Sken } catch (URI.MalformedURIException e) { 2139230592Sken if (baseSystemId.indexOf(':') != -1) { 2140230592Sken // for xml schemas we might have baseURI with 2141230592Sken // a specified drive 2142230592Sken base = new URI("file", "", fixURI(baseSystemId), null, null); 2143230592Sken } else { 2144230592Sken String dir = getUserDir().toString(); 2145230592Sken dir = dir + fixURI(baseSystemId); 2146230592Sken base = new URI("file", "", dir, null, null); 2147230592Sken } 2148230592Sken } 2149230592Sken } 2150230592Sken // expand id 2151230592Sken uri = new URI(base, id); 2152230592Sken } catch (Exception e) { 2153230592Sken // let it go through 2154230592Sken 2155230592Sken } 2156230592Sken 2157230592Sken if (uri == null) { 2158230592Sken return systemId; 2159230592Sken } 2160230592Sken return uri.toString(); 2161230592Sken 2162230592Sken } // expandSystemId(String,String):String 2163230592Sken 2164230592Sken /** 2165230592Sken * Expands a system id and returns the system id as a URI, if 2166230592Sken * it can be expanded. A return value of null means that the 2167230592Sken * identifier is already expanded. An exception thrown 2168230592Sken * indicates a failure to expand the id. 2169230592Sken * 2170230592Sken * @param systemId The systemId to be expanded. 2171230592Sken * 2172230592Sken * @return Returns the URI string representing the expanded system 2173230592Sken * identifier. A null value indicates that the given 2174230592Sken * system identifier is already expanded. 2175230592Sken * 2176230592Sken */ 2177230592Sken public static String expandSystemId(String systemId, String baseSystemId, 2178230592Sken boolean strict) 2179230592Sken throws URI.MalformedURIException { 2180230592Sken 2181230592Sken // check if there is a system id before 2182230592Sken // trying to expand it. 2183230592Sken if (systemId == null) { 2184230592Sken return null; 2185230592Sken } 2186230592Sken 2187230592Sken // system id has to be a valid URI 2188230592Sken if (strict) { 2189230592Sken try { 2190230592Sken // if it's already an absolute one, return it 2191230592Sken new URI(systemId); 2192230592Sken return systemId; 2193230592Sken } 2194230592Sken catch (URI.MalformedURIException ex) { 2195230592Sken } 2196230592Sken URI base = null; 2197230592Sken // if there isn't a base uri, use the working directory 2198230592Sken if (baseSystemId == null || baseSystemId.length() == 0) { 2199230592Sken base = new URI("file", "", getUserDir().toString(), null, null); 2200230592Sken } 2201230592Sken // otherwise, use the base uri 2202212420Sken else { 2203212420Sken try { 2204212420Sken base = new URI(baseSystemId); 2205212420Sken } 2206212420Sken catch (URI.MalformedURIException e) { 2207212420Sken // assume "base" is also a relative uri 2208212420Sken String dir = getUserDir().toString(); 2209212420Sken dir = dir + baseSystemId; 2210213702Smdf base = new URI("file", "", dir, null, null); 2211213702Smdf } 2212212420Sken } 2213213702Smdf // absolutize the system id using the base 2214213702Smdf URI uri = new URI(base, systemId); 2215213702Smdf // return the string rep of the new uri (an absolute one) 2216213702Smdf return uri.toString(); 2217213702Smdf 2218213702Smdf // if any exception is thrown, it'll get thrown to the caller. 2219213702Smdf } 2220213702Smdf 2221213702Smdf // Assume the URIs are well-formed. If it turns out they're not, try fixing them up. 2222213702Smdf try { 2223213702Smdf return expandSystemIdStrictOff(systemId, baseSystemId); 2224213702Smdf } 2225213702Smdf catch (URI.MalformedURIException e) { 2226213702Smdf /** Xerces URI rejects unicode, try java.net.URI 2227213702Smdf * this is not ideal solution, but it covers known cases which either 2228213702Smdf * Xerces URI or java.net.URI can handle alone 2229213702Smdf * will file bug against java.net.URI 2230213702Smdf */ 2231213702Smdf try { 2232213702Smdf return expandSystemIdStrictOff1(systemId, baseSystemId); 2233213702Smdf } catch (URISyntaxException ex) { 2234213702Smdf // continue on... 2235213702Smdf } 2236213702Smdf } 2237213702Smdf // check for bad parameters id 2238213702Smdf if (systemId.length() == 0) { 2239213702Smdf return systemId; 2240213702Smdf } 2241213702Smdf 2242213702Smdf // normalize id 2243213702Smdf String id = fixURI(systemId); 2244213702Smdf 2245213702Smdf // normalize base 2246213702Smdf URI base = null; 2247213702Smdf URI uri = null; 2248213702Smdf try { 2249213702Smdf if (baseSystemId == null || baseSystemId.length() == 0 || 2250213702Smdf baseSystemId.equals(systemId)) { 2251213702Smdf base = getUserDir(); 2252213702Smdf } 2253213702Smdf else { 2254213702Smdf try { 2255213702Smdf base = new URI(fixURI(baseSystemId).trim()); 2256213702Smdf } 2257213702Smdf catch (URI.MalformedURIException e) { 2258213702Smdf if (baseSystemId.indexOf(':') != -1) { 2259213702Smdf // for xml schemas we might have baseURI with 2260213702Smdf // a specified drive 2261213702Smdf base = new URI("file", "", fixURI(baseSystemId).trim(), null, null); 2262213702Smdf } 2263213702Smdf else { 2264213702Smdf base = new URI(getUserDir(), fixURI(baseSystemId)); 2265213702Smdf } 2266213702Smdf } 2267213702Smdf } 2268213702Smdf // expand id 2269213702Smdf uri = new URI(base, id.trim()); 2270213702Smdf } 2271213702Smdf catch (Exception e) { 2272213702Smdf // let it go through 2273213702Smdf 2274213702Smdf } 2275213702Smdf 2276213702Smdf if (uri == null) { 2277213702Smdf return systemId; 2278213702Smdf } 2279213702Smdf return uri.toString(); 2280213702Smdf 2281213702Smdf } // expandSystemId(String,String,boolean):String 2282212420Sken 2283212420Sken /** 2284212420Sken * Helper method for expandSystemId(String,String,boolean):String 2285213702Smdf */ 2286213702Smdf private static String expandSystemIdStrictOn(String systemId, String baseSystemId) 2287213702Smdf throws URI.MalformedURIException { 2288213702Smdf 2289213702Smdf URI systemURI = new URI(systemId, true); 2290213702Smdf // If it's already an absolute one, return it 2291213702Smdf if (systemURI.isAbsoluteURI()) { 2292213702Smdf return systemId; 2293213702Smdf } 2294213702Smdf 2295213702Smdf // If there isn't a base URI, use the working directory 2296212420Sken URI baseURI = null; 2297213702Smdf if (baseSystemId == null || baseSystemId.length() == 0) { 2298212420Sken baseURI = getUserDir(); 2299213702Smdf } 2300213702Smdf else { 2301213702Smdf baseURI = new URI(baseSystemId, true); 2302213702Smdf if (!baseURI.isAbsoluteURI()) { 2303213702Smdf // assume "base" is also a relative uri 2304213702Smdf baseURI.absolutize(getUserDir()); 2305213702Smdf } 2306213702Smdf } 2307213702Smdf 2308213702Smdf // absolutize the system identifier using the base URI 2309212420Sken systemURI.absolutize(baseURI); 2310213702Smdf 2311213702Smdf // return the string rep of the new uri (an absolute one) 2312213702Smdf return systemURI.toString(); 2313213702Smdf 2314213702Smdf // if any exception is thrown, it'll get thrown to the caller. 2315213702Smdf 2316213702Smdf } // expandSystemIdStrictOn(String,String):String 2317213702Smdf 2318213702Smdf /** 2319213702Smdf * Helper method for expandSystemId(String,String,boolean):String 2320213702Smdf */ 2321213702Smdf private static String expandSystemIdStrictOff(String systemId, String baseSystemId) 2322213702Smdf throws URI.MalformedURIException { 2323213702Smdf 2324213702Smdf URI systemURI = new URI(systemId, true); 2325213702Smdf // If it's already an absolute one, return it 2326213702Smdf if (systemURI.isAbsoluteURI()) { 2327213702Smdf if (systemURI.getScheme().length() > 1) { 2328213702Smdf return systemId; 2329213702Smdf } 2330213702Smdf /** 2331213702Smdf * If the scheme's length is only one character, 2332213702Smdf * it's likely that this was intended as a file 2333213702Smdf * path. Fixing this up in expandSystemId to 2334213702Smdf * maintain backwards compatibility. 2335213702Smdf */ 2336213702Smdf throw new URI.MalformedURIException(); 2337212420Sken } 2338212420Sken 2339212420Sken // If there isn't a base URI, use the working directory 2340212420Sken URI baseURI = null; 2341213702Smdf if (baseSystemId == null || baseSystemId.length() == 0) { 2342213702Smdf baseURI = getUserDir(); 2343213702Smdf } 2344213702Smdf else { 2345213702Smdf baseURI = new URI(baseSystemId, true); 2346213702Smdf if (!baseURI.isAbsoluteURI()) { 2347213702Smdf // assume "base" is also a relative uri 2348213702Smdf baseURI.absolutize(getUserDir()); 2349213702Smdf } 2350213702Smdf } 2351213702Smdf 2352213702Smdf // absolutize the system identifier using the base URI 2353213702Smdf systemURI.absolutize(baseURI); 2354213702Smdf 2355213702Smdf // return the string rep of the new uri (an absolute one) 2356213702Smdf return systemURI.toString(); 2357213702Smdf 2358213702Smdf // if any exception is thrown, it'll get thrown to the caller. 2359213702Smdf 2360213702Smdf } // expandSystemIdStrictOff(String,String):String 2361213702Smdf 2362213702Smdf private static String expandSystemIdStrictOff1(String systemId, String baseSystemId) 2363213702Smdf throws URISyntaxException, URI.MalformedURIException { 2364213702Smdf 2365213702Smdf java.net.URI systemURI = new java.net.URI(systemId); 2366213702Smdf // If it's already an absolute one, return it 2367213702Smdf if (systemURI.isAbsolute()) { 2368213702Smdf if (systemURI.getScheme().length() > 1) { 2369213702Smdf return systemId; 2370213702Smdf } 2371213702Smdf /** 2372213702Smdf * If the scheme's length is only one character, 2373213702Smdf * it's likely that this was intended as a file 2374213702Smdf * path. Fixing this up in expandSystemId to 2375213702Smdf * maintain backwards compatibility. 2376213702Smdf */ 2377213702Smdf throw new URISyntaxException(systemId, "the scheme's length is only one character"); 2378213702Smdf } 2379213702Smdf 2380213702Smdf // If there isn't a base URI, use the working directory 2381213702Smdf URI baseURI = null; 2382213702Smdf if (baseSystemId == null || baseSystemId.length() == 0) { 2383213702Smdf baseURI = getUserDir(); 2384213702Smdf } 2385213702Smdf else { 2386213702Smdf baseURI = new URI(baseSystemId, true); 2387213702Smdf if (!baseURI.isAbsoluteURI()) { 2388213702Smdf // assume "base" is also a relative uri 2389213702Smdf baseURI.absolutize(getUserDir()); 2390213702Smdf } 2391212420Sken } 2392213702Smdf 2393213702Smdf // absolutize the system identifier using the base URI 2394213702Smdf// systemURI.absolutize(baseURI); 2395213702Smdf systemURI = (new java.net.URI(baseURI.toString())).resolve(systemURI); 2396213702Smdf 2397213702Smdf // return the string rep of the new uri (an absolute one) 2398213702Smdf return systemURI.toString(); 2399213702Smdf 2400213702Smdf // if any exception is thrown, it'll get thrown to the caller. 2401213702Smdf 2402213702Smdf } // expandSystemIdStrictOff(String,String):String 2403213702Smdf 2404 // 2405 // Protected methods 2406 // 2407 2408 2409 /** 2410 * Returns the IANA encoding name that is auto-detected from 2411 * the bytes specified, with the endian-ness of that encoding where appropriate. 2412 * 2413 * @param b4 The first four bytes of the input. 2414 * @param count The number of bytes actually read. 2415 * @return a 2-element array: the first element, an IANA-encoding string, 2416 * the second element a Boolean which is true iff the document is big endian, false 2417 * if it's little-endian, and null if the distinction isn't relevant. 2418 */ 2419 protected Object[] getEncodingName(byte[] b4, int count) { 2420 2421 if (count < 2) { 2422 return defaultEncoding; 2423 } 2424 2425 // UTF-16, with BOM 2426 int b0 = b4[0] & 0xFF; 2427 int b1 = b4[1] & 0xFF; 2428 if (b0 == 0xFE && b1 == 0xFF) { 2429 // UTF-16, big-endian 2430 return new Object [] {"UTF-16BE", new Boolean(true)}; 2431 } 2432 if (b0 == 0xFF && b1 == 0xFE) { 2433 // UTF-16, little-endian 2434 return new Object [] {"UTF-16LE", new Boolean(false)}; 2435 } 2436 2437 // default to UTF-8 if we don't have enough bytes to make a 2438 // good determination of the encoding 2439 if (count < 3) { 2440 return defaultEncoding; 2441 } 2442 2443 // UTF-8 with a BOM 2444 int b2 = b4[2] & 0xFF; 2445 if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF) { 2446 return defaultEncoding; 2447 } 2448 2449 // default to UTF-8 if we don't have enough bytes to make a 2450 // good determination of the encoding 2451 if (count < 4) { 2452 return defaultEncoding; 2453 } 2454 2455 // other encodings 2456 int b3 = b4[3] & 0xFF; 2457 if (b0 == 0x00 && b1 == 0x00 && b2 == 0x00 && b3 == 0x3C) { 2458 // UCS-4, big endian (1234) 2459 return new Object [] {"ISO-10646-UCS-4", new Boolean(true)}; 2460 } 2461 if (b0 == 0x3C && b1 == 0x00 && b2 == 0x00 && b3 == 0x00) { 2462 // UCS-4, little endian (4321) 2463 return new Object [] {"ISO-10646-UCS-4", new Boolean(false)}; 2464 } 2465 if (b0 == 0x00 && b1 == 0x00 && b2 == 0x3C && b3 == 0x00) { 2466 // UCS-4, unusual octet order (2143) 2467 // REVISIT: What should this be? 2468 return new Object [] {"ISO-10646-UCS-4", null}; 2469 } 2470 if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x00) { 2471 // UCS-4, unusual octect order (3412) 2472 // REVISIT: What should this be? 2473 return new Object [] {"ISO-10646-UCS-4", null}; 2474 } 2475 if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x3F) { 2476 // UTF-16, big-endian, no BOM 2477 // (or could turn out to be UCS-2... 2478 // REVISIT: What should this be? 2479 return new Object [] {"UTF-16BE", new Boolean(true)}; 2480 } 2481 if (b0 == 0x3C && b1 == 0x00 && b2 == 0x3F && b3 == 0x00) { 2482 // UTF-16, little-endian, no BOM 2483 // (or could turn out to be UCS-2... 2484 return new Object [] {"UTF-16LE", new Boolean(false)}; 2485 } 2486 if (b0 == 0x4C && b1 == 0x6F && b2 == 0xA7 && b3 == 0x94) { 2487 // EBCDIC 2488 // a la xerces1, return CP037 instead of EBCDIC here 2489 return new Object [] {"CP037", null}; 2490 } 2491 2492 return defaultEncoding; 2493 2494 } // getEncodingName(byte[],int):Object[] 2495 2496 /** 2497 * Creates a reader capable of reading the given input stream in 2498 * the specified encoding. 2499 * 2500 * @param inputStream The input stream. 2501 * @param encoding The encoding name that the input stream is 2502 * encoded using. If the user has specified that 2503 * Java encoding names are allowed, then the 2504 * encoding name may be a Java encoding name; 2505 * otherwise, it is an ianaEncoding name. 2506 * @param isBigEndian For encodings (like uCS-4), whose names cannot 2507 * specify a byte order, this tells whether the order is bigEndian. null menas 2508 * unknown or not relevant. 2509 * 2510 * @return Returns a reader. 2511 */ 2512 protected Reader createReader(InputStream inputStream, String encoding, Boolean isBigEndian) 2513 throws IOException { 2514 2515 // normalize encoding name 2516 if (encoding == null) { 2517 encoding = "UTF-8"; 2518 } 2519 2520 // try to use an optimized reader 2521 String ENCODING = encoding.toUpperCase(Locale.ENGLISH); 2522 if (ENCODING.equals("UTF-8")) { 2523 if (DEBUG_ENCODINGS) { 2524 System.out.println("$$$ creating UTF8Reader"); 2525 } 2526 return new UTF8Reader(inputStream, fBufferSize, fErrorReporter.getMessageFormatter(XMLMessageFormatter.XML_DOMAIN), fErrorReporter.getLocale() ); 2527 } 2528 if (ENCODING.equals("US-ASCII")) { 2529 if (DEBUG_ENCODINGS) { 2530 System.out.println("$$$ creating ASCIIReader"); 2531 } 2532 return new ASCIIReader(inputStream, fBufferSize, fErrorReporter.getMessageFormatter(XMLMessageFormatter.XML_DOMAIN), fErrorReporter.getLocale()); 2533 } 2534 if(ENCODING.equals("ISO-10646-UCS-4")) { 2535 if(isBigEndian != null) { 2536 boolean isBE = isBigEndian.booleanValue(); 2537 if(isBE) { 2538 return new UCSReader(inputStream, UCSReader.UCS4BE); 2539 } else { 2540 return new UCSReader(inputStream, UCSReader.UCS4LE); 2541 } 2542 } else { 2543 fErrorReporter.reportError(this.getEntityScanner(),XMLMessageFormatter.XML_DOMAIN, 2544 "EncodingByteOrderUnsupported", 2545 new Object[] { encoding }, 2546 XMLErrorReporter.SEVERITY_FATAL_ERROR); 2547 } 2548 } 2549 if(ENCODING.equals("ISO-10646-UCS-2")) { 2550 if(isBigEndian != null) { // sould never happen with this encoding... 2551 boolean isBE = isBigEndian.booleanValue(); 2552 if(isBE) { 2553 return new UCSReader(inputStream, UCSReader.UCS2BE); 2554 } else { 2555 return new UCSReader(inputStream, UCSReader.UCS2LE); 2556 } 2557 } else { 2558 fErrorReporter.reportError(this.getEntityScanner(),XMLMessageFormatter.XML_DOMAIN, 2559 "EncodingByteOrderUnsupported", 2560 new Object[] { encoding }, 2561 XMLErrorReporter.SEVERITY_FATAL_ERROR); 2562 } 2563 } 2564 2565 // check for valid name 2566 boolean validIANA = XMLChar.isValidIANAEncoding(encoding); 2567 boolean validJava = XMLChar.isValidJavaEncoding(encoding); 2568 if (!validIANA || (fAllowJavaEncodings && !validJava)) { 2569 fErrorReporter.reportError(this.getEntityScanner(),XMLMessageFormatter.XML_DOMAIN, 2570 "EncodingDeclInvalid", 2571 new Object[] { encoding }, 2572 XMLErrorReporter.SEVERITY_FATAL_ERROR); 2573 // NOTE: AndyH suggested that, on failure, we use ISO Latin 1 2574 // because every byte is a valid ISO Latin 1 character. 2575 // It may not translate correctly but if we failed on 2576 // the encoding anyway, then we're expecting the content 2577 // of the document to be bad. This will just prevent an 2578 // invalid UTF-8 sequence to be detected. This is only 2579 // important when continue-after-fatal-error is turned 2580 // on. -Ac 2581 encoding = "ISO-8859-1"; 2582 } 2583 2584 // try to use a Java reader 2585 String javaEncoding = EncodingMap.getIANA2JavaMapping(ENCODING); 2586 if (javaEncoding == null) { 2587 if(fAllowJavaEncodings) { 2588 javaEncoding = encoding; 2589 } else { 2590 fErrorReporter.reportError(this.getEntityScanner(),XMLMessageFormatter.XML_DOMAIN, 2591 "EncodingDeclInvalid", 2592 new Object[] { encoding }, 2593 XMLErrorReporter.SEVERITY_FATAL_ERROR); 2594 // see comment above. 2595 javaEncoding = "ISO8859_1"; 2596 } 2597 } 2598 if (DEBUG_ENCODINGS) { 2599 System.out.print("$$$ creating Java InputStreamReader: encoding="+javaEncoding); 2600 if (javaEncoding == encoding) { 2601 System.out.print(" (IANA encoding)"); 2602 } 2603 System.out.println(); 2604 } 2605 return new BufferedReader( new InputStreamReader(inputStream, javaEncoding)); 2606 2607 } // createReader(InputStream,String, Boolean): Reader 2608 2609 2610 /** 2611 * Return the public identifier for the current document event. 2612 * <p> 2613 * The return value is the public identifier of the document 2614 * entity or of the external parsed entity in which the markup 2615 * triggering the event appears. 2616 * 2617 * @return A string containing the public identifier, or 2618 * null if none is available. 2619 */ 2620 public String getPublicId() { 2621 return (fCurrentEntity != null && fCurrentEntity.entityLocation != null) ? fCurrentEntity.entityLocation.getPublicId() : null; 2622 } // getPublicId():String 2623 2624 /** 2625 * Return the expanded system identifier for the current document event. 2626 * <p> 2627 * The return value is the expanded system identifier of the document 2628 * entity or of the external parsed entity in which the markup 2629 * triggering the event appears. 2630 * <p> 2631 * If the system identifier is a URL, the parser must resolve it 2632 * fully before passing it to the application. 2633 * 2634 * @return A string containing the expanded system identifier, or null 2635 * if none is available. 2636 */ 2637 public String getExpandedSystemId() { 2638 if (fCurrentEntity != null) { 2639 if (fCurrentEntity.entityLocation != null && 2640 fCurrentEntity.entityLocation.getExpandedSystemId() != null ) { 2641 return fCurrentEntity.entityLocation.getExpandedSystemId(); 2642 } else { 2643 // search for the first external entity on the stack 2644 int size = fEntityStack.size(); 2645 for (int i = size - 1; i >= 0 ; i--) { 2646 Entity.ScannedEntity externalEntity = 2647 (Entity.ScannedEntity)fEntityStack.elementAt(i); 2648 2649 if (externalEntity.entityLocation != null && 2650 externalEntity.entityLocation.getExpandedSystemId() != null) { 2651 return externalEntity.entityLocation.getExpandedSystemId(); 2652 } 2653 } 2654 } 2655 } 2656 return null; 2657 } // getExpandedSystemId():String 2658 2659 /** 2660 * Return the literal system identifier for the current document event. 2661 * <p> 2662 * The return value is the literal system identifier of the document 2663 * entity or of the external parsed entity in which the markup 2664 * triggering the event appears. 2665 * <p> 2666 * @return A string containing the literal system identifier, or null 2667 * if none is available. 2668 */ 2669 public String getLiteralSystemId() { 2670 if (fCurrentEntity != null) { 2671 if (fCurrentEntity.entityLocation != null && 2672 fCurrentEntity.entityLocation.getLiteralSystemId() != null ) { 2673 return fCurrentEntity.entityLocation.getLiteralSystemId(); 2674 } else { 2675 // search for the first external entity on the stack 2676 int size = fEntityStack.size(); 2677 for (int i = size - 1; i >= 0 ; i--) { 2678 Entity.ScannedEntity externalEntity = 2679 (Entity.ScannedEntity)fEntityStack.elementAt(i); 2680 2681 if (externalEntity.entityLocation != null && 2682 externalEntity.entityLocation.getLiteralSystemId() != null) { 2683 return externalEntity.entityLocation.getLiteralSystemId(); 2684 } 2685 } 2686 } 2687 } 2688 return null; 2689 } // getLiteralSystemId():String 2690 2691 /** 2692 * Return the line number where the current document event ends. 2693 * <p> 2694 * <strong>Warning:</strong> The return value from the method 2695 * is intended only as an approximation for the sake of error 2696 * reporting; it is not intended to provide sufficient information 2697 * to edit the character content of the original XML document. 2698 * <p> 2699 * The return value is an approximation of the line number 2700 * in the document entity or external parsed entity where the 2701 * markup triggering the event appears. 2702 * <p> 2703 * If possible, the SAX driver should provide the line position 2704 * of the first character after the text associated with the document 2705 * event. The first line in the document is line 1. 2706 * 2707 * @return The line number, or -1 if none is available. 2708 */ 2709 public int getLineNumber() { 2710 if (fCurrentEntity != null) { 2711 if (fCurrentEntity.isExternal()) { 2712 return fCurrentEntity.lineNumber; 2713 } else { 2714 // search for the first external entity on the stack 2715 int size = fEntityStack.size(); 2716 for (int i=size-1; i>0 ; i--) { 2717 Entity.ScannedEntity firstExternalEntity = (Entity.ScannedEntity)fEntityStack.elementAt(i); 2718 if (firstExternalEntity.isExternal()) { 2719 return firstExternalEntity.lineNumber; 2720 } 2721 } 2722 } 2723 } 2724 2725 return -1; 2726 2727 } // getLineNumber():int 2728 2729 /** 2730 * Return the column number where the current document event ends. 2731 * <p> 2732 * <strong>Warning:</strong> The return value from the method 2733 * is intended only as an approximation for the sake of error 2734 * reporting; it is not intended to provide sufficient information 2735 * to edit the character content of the original XML document. 2736 * <p> 2737 * The return value is an approximation of the column number 2738 * in the document entity or external parsed entity where the 2739 * markup triggering the event appears. 2740 * <p> 2741 * If possible, the SAX driver should provide the line position 2742 * of the first character after the text associated with the document 2743 * event. 2744 * <p> 2745 * If possible, the SAX driver should provide the line position 2746 * of the first character after the text associated with the document 2747 * event. The first column in each line is column 1. 2748 * 2749 * @return The column number, or -1 if none is available. 2750 */ 2751 public int getColumnNumber() { 2752 if (fCurrentEntity != null) { 2753 if (fCurrentEntity.isExternal()) { 2754 return fCurrentEntity.columnNumber; 2755 } else { 2756 // search for the first external entity on the stack 2757 int size = fEntityStack.size(); 2758 for (int i=size-1; i>0 ; i--) { 2759 Entity.ScannedEntity firstExternalEntity = (Entity.ScannedEntity)fEntityStack.elementAt(i); 2760 if (firstExternalEntity.isExternal()) { 2761 return firstExternalEntity.columnNumber; 2762 } 2763 } 2764 } 2765 } 2766 2767 return -1; 2768 } // getColumnNumber():int 2769 2770 2771 // 2772 // Protected static methods 2773 // 2774 2775 /** 2776 * Fixes a platform dependent filename to standard URI form. 2777 * 2778 * @param str The string to fix. 2779 * 2780 * @return Returns the fixed URI string. 2781 */ 2782 protected static String fixURI(String str) { 2783 2784 // handle platform dependent strings 2785 str = str.replace(java.io.File.separatorChar, '/'); 2786 2787 // Windows fix 2788 if (str.length() >= 2) { 2789 char ch1 = str.charAt(1); 2790 // change "C:blah" to "/C:blah" 2791 if (ch1 == ':') { 2792 char ch0 = Character.toUpperCase(str.charAt(0)); 2793 if (ch0 >= 'A' && ch0 <= 'Z') { 2794 str = "/" + str; 2795 } 2796 } 2797 // change "//blah" to "file://blah" 2798 else if (ch1 == '/' && str.charAt(0) == '/') { 2799 str = "file:" + str; 2800 } 2801 } 2802 2803 // replace spaces in file names with %20. 2804 // Original comment from JDK5: the following algorithm might not be 2805 // very performant, but people who want to use invalid URI's have to 2806 // pay the price. 2807 int pos = str.indexOf(' '); 2808 if (pos >= 0) { 2809 StringBuilder sb = new StringBuilder(str.length()); 2810 // put characters before ' ' into the string builder 2811 for (int i = 0; i < pos; i++) 2812 sb.append(str.charAt(i)); 2813 // and %20 for the space 2814 sb.append("%20"); 2815 // for the remamining part, also convert ' ' to "%20". 2816 for (int i = pos+1; i < str.length(); i++) { 2817 if (str.charAt(i) == ' ') 2818 sb.append("%20"); 2819 else 2820 sb.append(str.charAt(i)); 2821 } 2822 str = sb.toString(); 2823 } 2824 2825 // done 2826 return str; 2827 2828 } // fixURI(String):String 2829 2830 2831 // 2832 // Package visible methods 2833 // 2834 /** Prints the contents of the buffer. */ 2835 final void print() { 2836 if (DEBUG_BUFFER) { 2837 if (fCurrentEntity != null) { 2838 System.out.print('['); 2839 System.out.print(fCurrentEntity.count); 2840 System.out.print(' '); 2841 System.out.print(fCurrentEntity.position); 2842 if (fCurrentEntity.count > 0) { 2843 System.out.print(" \""); 2844 for (int i = 0; i < fCurrentEntity.count; i++) { 2845 if (i == fCurrentEntity.position) { 2846 System.out.print('^'); 2847 } 2848 char c = fCurrentEntity.ch[i]; 2849 switch (c) { 2850 case '\n': { 2851 System.out.print("\\n"); 2852 break; 2853 } 2854 case '\r': { 2855 System.out.print("\\r"); 2856 break; 2857 } 2858 case '\t': { 2859 System.out.print("\\t"); 2860 break; 2861 } 2862 case '\\': { 2863 System.out.print("\\\\"); 2864 break; 2865 } 2866 default: { 2867 System.out.print(c); 2868 } 2869 } 2870 } 2871 if (fCurrentEntity.position == fCurrentEntity.count) { 2872 System.out.print('^'); 2873 } 2874 System.out.print('"'); 2875 } 2876 System.out.print(']'); 2877 System.out.print(" @ "); 2878 System.out.print(fCurrentEntity.lineNumber); 2879 System.out.print(','); 2880 System.out.print(fCurrentEntity.columnNumber); 2881 } else { 2882 System.out.print("*NO CURRENT ENTITY*"); 2883 } 2884 } 2885 } // print() 2886 2887 /** 2888 * Buffer used in entity manager to reuse character arrays instead 2889 * of creating new ones every time. 2890 * 2891 * @xerces.internal 2892 * 2893 * @author Ankit Pasricha, IBM 2894 */ 2895 private static class CharacterBuffer { 2896 2897 /** character buffer */ 2898 private char[] ch; 2899 2900 /** whether the buffer is for an external or internal scanned entity */ 2901 private boolean isExternal; 2902 2903 public CharacterBuffer(boolean isExternal, int size) { 2904 this.isExternal = isExternal; 2905 ch = new char[size]; 2906 } 2907 } 2908 2909 2910 /** 2911 * Stores a number of character buffers and provides it to the entity 2912 * manager to use when an entity is seen. 2913 * 2914 * @xerces.internal 2915 * 2916 * @author Ankit Pasricha, IBM 2917 */ 2918 private static class CharacterBufferPool { 2919 2920 private static final int DEFAULT_POOL_SIZE = 3; 2921 2922 private CharacterBuffer[] fInternalBufferPool; 2923 private CharacterBuffer[] fExternalBufferPool; 2924 2925 private int fExternalBufferSize; 2926 private int fInternalBufferSize; 2927 private int poolSize; 2928 2929 private int fInternalTop; 2930 private int fExternalTop; 2931 2932 public CharacterBufferPool(int externalBufferSize, int internalBufferSize) { 2933 this(DEFAULT_POOL_SIZE, externalBufferSize, internalBufferSize); 2934 } 2935 2936 public CharacterBufferPool(int poolSize, int externalBufferSize, int internalBufferSize) { 2937 fExternalBufferSize = externalBufferSize; 2938 fInternalBufferSize = internalBufferSize; 2939 this.poolSize = poolSize; 2940 init(); 2941 } 2942 2943 /** Initializes buffer pool. **/ 2944 private void init() { 2945 fInternalBufferPool = new CharacterBuffer[poolSize]; 2946 fExternalBufferPool = new CharacterBuffer[poolSize]; 2947 fInternalTop = -1; 2948 fExternalTop = -1; 2949 } 2950 2951 /** Retrieves buffer from pool. **/ 2952 public CharacterBuffer getBuffer(boolean external) { 2953 if (external) { 2954 if (fExternalTop > -1) { 2955 return fExternalBufferPool[fExternalTop--]; 2956 } 2957 else { 2958 return new CharacterBuffer(true, fExternalBufferSize); 2959 } 2960 } 2961 else { 2962 if (fInternalTop > -1) { 2963 return fInternalBufferPool[fInternalTop--]; 2964 } 2965 else { 2966 return new CharacterBuffer(false, fInternalBufferSize); 2967 } 2968 } 2969 } 2970 2971 /** Returns buffer to pool. **/ 2972 public void returnToPool(CharacterBuffer buffer) { 2973 if (buffer.isExternal) { 2974 if (fExternalTop < fExternalBufferPool.length - 1) { 2975 fExternalBufferPool[++fExternalTop] = buffer; 2976 } 2977 } 2978 else if (fInternalTop < fInternalBufferPool.length - 1) { 2979 fInternalBufferPool[++fInternalTop] = buffer; 2980 } 2981 } 2982 2983 /** Sets the size of external buffers and dumps the old pool. **/ 2984 public void setExternalBufferSize(int bufferSize) { 2985 fExternalBufferSize = bufferSize; 2986 fExternalBufferPool = new CharacterBuffer[poolSize]; 2987 fExternalTop = -1; 2988 } 2989 } 2990 2991 /** 2992 * This class wraps the byte inputstreams we're presented with. 2993 * We need it because java.io.InputStreams don't provide 2994 * functionality to reread processed bytes, and they have a habit 2995 * of reading more than one character when you call their read() 2996 * methods. This means that, once we discover the true (declared) 2997 * encoding of a document, we can neither backtrack to read the 2998 * whole doc again nor start reading where we are with a new 2999 * reader. 3000 * 3001 * This class allows rewinding an inputStream by allowing a mark 3002 * to be set, and the stream reset to that position. <strong>The 3003 * class assumes that it needs to read one character per 3004 * invocation when it's read() method is inovked, but uses the 3005 * underlying InputStream's read(char[], offset length) method--it 3006 * won't buffer data read this way!</strong> 3007 * 3008 * @xerces.internal 3009 * 3010 * @author Neil Graham, IBM 3011 * @author Glenn Marcy, IBM 3012 */ 3013 3014 protected final class RewindableInputStream extends InputStream { 3015 3016 private InputStream fInputStream; 3017 private byte[] fData; 3018 private int fStartOffset; 3019 private int fEndOffset; 3020 private int fOffset; 3021 private int fLength; 3022 private int fMark; 3023 3024 public RewindableInputStream(InputStream is) { 3025 fData = new byte[DEFAULT_XMLDECL_BUFFER_SIZE]; 3026 fInputStream = is; 3027 fStartOffset = 0; 3028 fEndOffset = -1; 3029 fOffset = 0; 3030 fLength = 0; 3031 fMark = 0; 3032 } 3033 3034 public void setStartOffset(int offset) { 3035 fStartOffset = offset; 3036 } 3037 3038 public void rewind() { 3039 fOffset = fStartOffset; 3040 } 3041 3042 public int read() throws IOException { 3043 int b = 0; 3044 if (fOffset < fLength) { 3045 return fData[fOffset++] & 0xff; 3046 } 3047 if (fOffset == fEndOffset) { 3048 return -1; 3049 } 3050 if (fOffset == fData.length) { 3051 byte[] newData = new byte[fOffset << 1]; 3052 System.arraycopy(fData, 0, newData, 0, fOffset); 3053 fData = newData; 3054 } 3055 b = fInputStream.read(); 3056 if (b == -1) { 3057 fEndOffset = fOffset; 3058 return -1; 3059 } 3060 fData[fLength++] = (byte)b; 3061 fOffset++; 3062 return b & 0xff; 3063 } 3064 3065 public int read(byte[] b, int off, int len) throws IOException { 3066 int bytesLeft = fLength - fOffset; 3067 if (bytesLeft == 0) { 3068 if (fOffset == fEndOffset) { 3069 return -1; 3070 } 3071 3072 /** 3073 * //System.out.println("fCurrentEntitty = " + fCurrentEntity ); 3074 * //System.out.println("fInputStream = " + fInputStream ); 3075 * // better get some more for the voracious reader... */ 3076 3077 if(fCurrentEntity.mayReadChunks || !fCurrentEntity.xmlDeclChunkRead) { 3078 3079 if (!fCurrentEntity.xmlDeclChunkRead) 3080 { 3081 fCurrentEntity.xmlDeclChunkRead = true; 3082 len = Entity.ScannedEntity.DEFAULT_XMLDECL_BUFFER_SIZE; 3083 } 3084 return fInputStream.read(b, off, len); 3085 } 3086 3087 int returnedVal = read(); 3088 if(returnedVal == -1) { 3089 fEndOffset = fOffset; 3090 return -1; 3091 } 3092 b[off] = (byte)returnedVal; 3093 return 1; 3094 3095 } 3096 if (len < bytesLeft) { 3097 if (len <= 0) { 3098 return 0; 3099 } 3100 } else { 3101 len = bytesLeft; 3102 } 3103 if (b != null) { 3104 System.arraycopy(fData, fOffset, b, off, len); 3105 } 3106 fOffset += len; 3107 return len; 3108 } 3109 3110 public long skip(long n) 3111 throws IOException { 3112 int bytesLeft; 3113 if (n <= 0) { 3114 return 0; 3115 } 3116 bytesLeft = fLength - fOffset; 3117 if (bytesLeft == 0) { 3118 if (fOffset == fEndOffset) { 3119 return 0; 3120 } 3121 return fInputStream.skip(n); 3122 } 3123 if (n <= bytesLeft) { 3124 fOffset += n; 3125 return n; 3126 } 3127 fOffset += bytesLeft; 3128 if (fOffset == fEndOffset) { 3129 return bytesLeft; 3130 } 3131 n -= bytesLeft; 3132 /* 3133 * In a manner of speaking, when this class isn't permitting more 3134 * than one byte at a time to be read, it is "blocking". The 3135 * available() method should indicate how much can be read without 3136 * blocking, so while we're in this mode, it should only indicate 3137 * that bytes in its buffer are available; otherwise, the result of 3138 * available() on the underlying InputStream is appropriate. 3139 */ 3140 return fInputStream.skip(n) + bytesLeft; 3141 } 3142 3143 public int available() throws IOException { 3144 int bytesLeft = fLength - fOffset; 3145 if (bytesLeft == 0) { 3146 if (fOffset == fEndOffset) { 3147 return -1; 3148 } 3149 return fCurrentEntity.mayReadChunks ? fInputStream.available() 3150 : 0; 3151 } 3152 return bytesLeft; 3153 } 3154 3155 public void mark(int howMuch) { 3156 fMark = fOffset; 3157 } 3158 3159 public void reset() { 3160 fOffset = fMark; 3161 //test(); 3162 } 3163 3164 public boolean markSupported() { 3165 return true; 3166 } 3167 3168 public void close() throws IOException { 3169 if (fInputStream != null) { 3170 fInputStream.close(); 3171 fInputStream = null; 3172 } 3173 } 3174 } // end of RewindableInputStream class 3175 3176 public void test(){ 3177 //System.out.println("TESTING: Added familytree to entityManager"); 3178 //Usecase1 3179 fEntityStorage.addExternalEntity("entityUsecase1",null, 3180 "/space/home/stax/sun/6thJan2004/zephyr/data/test.txt", 3181 "/space/home/stax/sun/6thJan2004/zephyr/data/entity.xml"); 3182 3183 //Usecase2 3184 fEntityStorage.addInternalEntity("entityUsecase2","<Test>value</Test>"); 3185 fEntityStorage.addInternalEntity("entityUsecase3","value3"); 3186 fEntityStorage.addInternalEntity("text", "Hello World."); 3187 fEntityStorage.addInternalEntity("empty-element", "<foo/>"); 3188 fEntityStorage.addInternalEntity("balanced-element", "<foo></foo>"); 3189 fEntityStorage.addInternalEntity("balanced-element-with-text", "<foo>Hello, World</foo>"); 3190 fEntityStorage.addInternalEntity("balanced-element-with-entity", "<foo>&text;</foo>"); 3191 fEntityStorage.addInternalEntity("unbalanced-entity", "<foo>"); 3192 fEntityStorage.addInternalEntity("recursive-entity", "<foo>&recursive-entity2;</foo>"); 3193 fEntityStorage.addInternalEntity("recursive-entity2", "<bar>&recursive-entity3;</bar>"); 3194 fEntityStorage.addInternalEntity("recursive-entity3", "<baz>&recursive-entity;</baz>"); 3195 fEntityStorage.addInternalEntity("ch","©"); 3196 fEntityStorage.addInternalEntity("ch1","T"); 3197 fEntityStorage.addInternalEntity("% ch2","param"); 3198 } 3199 3200} // class XMLEntityManager 3201