1/*
2 * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
3 */
4/*
5 * Licensed to the Apache Software Foundation (ASF) under one or more
6 * contributor license agreements.  See the NOTICE file distributed with
7 * this work for additional information regarding copyright ownership.
8 * The ASF licenses this file to You under the Apache License, Version 2.0
9 * (the "License"); you may not use this file except in compliance with
10 * the License.  You may obtain a copy of the License at
11 *
12 *      http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21package com.sun.org.apache.xerces.internal.impl ;
22
23import com.sun.org.apache.xerces.internal.impl.io.ASCIIReader;
24import com.sun.org.apache.xerces.internal.impl.io.UCSReader;
25import com.sun.org.apache.xerces.internal.impl.io.UTF8Reader;
26import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
27import com.sun.org.apache.xerces.internal.impl.validation.ValidationManager;
28import com.sun.org.apache.xerces.internal.util.*;
29import com.sun.org.apache.xerces.internal.util.URI;
30import com.sun.org.apache.xerces.internal.utils.SecuritySupport;
31import com.sun.org.apache.xerces.internal.utils.XMLLimitAnalyzer;
32import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager;
33import com.sun.org.apache.xerces.internal.utils.XMLSecurityPropertyManager;
34import com.sun.org.apache.xerces.internal.xni.Augmentations;
35import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier;
36import com.sun.org.apache.xerces.internal.xni.XNIException;
37import com.sun.org.apache.xerces.internal.xni.parser.*;
38import com.sun.xml.internal.stream.Entity;
39import com.sun.xml.internal.stream.StaxEntityResolverWrapper;
40import com.sun.xml.internal.stream.StaxXMLInputSource;
41import com.sun.xml.internal.stream.XMLEntityStorage;
42import java.io.*;
43import java.net.HttpURLConnection;
44import java.net.URISyntaxException;
45import java.net.URL;
46import java.net.URLConnection;
47import java.util.HashMap;
48import java.util.Iterator;
49import java.util.Locale;
50import java.util.Map;
51import java.util.Stack;
52import java.util.StringTokenizer;
53import javax.xml.XMLConstants;
54import javax.xml.catalog.CatalogException;
55import javax.xml.catalog.CatalogFeatures;
56import javax.xml.catalog.CatalogFeatures.Feature;
57import javax.xml.catalog.CatalogManager;
58import javax.xml.catalog.CatalogResolver;
59import javax.xml.stream.XMLInputFactory;
60import javax.xml.transform.Source;
61import jdk.xml.internal.JdkXmlUtils;
62import org.xml.sax.InputSource;
63
64
65/**
66 * Will keep track of current entity.
67 *
68 * The entity manager handles the registration of general and parameter
69 * entities; resolves entities; and starts entities. The entity manager
70 * is a central component in a standard parser configuration and this
71 * class works directly with the entity scanner to manage the underlying
72 * xni.
73 * <p>
74 * This component requires the following features and properties from the
75 * component manager that uses it:
76 * <ul>
77 *  <li>http://xml.org/sax/features/validation</li>
78 *  <li>http://xml.org/sax/features/external-general-entities</li>
79 *  <li>http://xml.org/sax/features/external-parameter-entities</li>
80 *  <li>http://apache.org/xml/features/allow-java-encodings</li>
81 *  <li>http://apache.org/xml/properties/internal/symbol-table</li>
82 *  <li>http://apache.org/xml/properties/internal/error-reporter</li>
83 *  <li>http://apache.org/xml/properties/internal/entity-resolver</li>
84 * </ul>
85 *
86 *
87 * @author Andy Clark, IBM
88 * @author Arnaud  Le Hors, IBM
89 * @author K.Venugopal SUN Microsystems
90 * @author Neeraj Bajaj SUN Microsystems
91 * @author Sunitha Reddy SUN Microsystems
92 */
93public class XMLEntityManager implements XMLComponent, XMLEntityResolver {
94
95    //
96    // Constants
97    //
98
99    /** Default buffer size (2048). */
100    public static final int DEFAULT_BUFFER_SIZE = 8192;
101
102    /** Default buffer size before we've finished with the XMLDecl:  */
103    public static final int DEFAULT_XMLDECL_BUFFER_SIZE = 64;
104
105    /** Default internal entity buffer size (1024). */
106    public static final int DEFAULT_INTERNAL_BUFFER_SIZE = 1024;
107
108    // feature identifiers
109
110    /** Feature identifier: validation. */
111    protected static final String VALIDATION =
112            Constants.SAX_FEATURE_PREFIX + Constants.VALIDATION_FEATURE;
113
114    /**
115     * standard uri conformant (strict uri).
116     * http://apache.org/xml/features/standard-uri-conformant
117     */
118    protected boolean fStrictURI;
119
120
121    /** Feature identifier: external general entities. */
122    protected static final String EXTERNAL_GENERAL_ENTITIES =
123            Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_GENERAL_ENTITIES_FEATURE;
124
125    /** Feature identifier: external parameter entities. */
126    protected static final String EXTERNAL_PARAMETER_ENTITIES =
127            Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_PARAMETER_ENTITIES_FEATURE;
128
129    /** Feature identifier: allow Java encodings. */
130    protected static final String ALLOW_JAVA_ENCODINGS =
131            Constants.XERCES_FEATURE_PREFIX + Constants.ALLOW_JAVA_ENCODINGS_FEATURE;
132
133    /** Feature identifier: warn on duplicate EntityDef */
134    protected static final String WARN_ON_DUPLICATE_ENTITYDEF =
135            Constants.XERCES_FEATURE_PREFIX +Constants.WARN_ON_DUPLICATE_ENTITYDEF_FEATURE;
136
137    /** Feature identifier: load external DTD. */
138    protected static final String LOAD_EXTERNAL_DTD =
139            Constants.XERCES_FEATURE_PREFIX + Constants.LOAD_EXTERNAL_DTD_FEATURE;
140
141    // property identifiers
142
143    /** Property identifier: symbol table. */
144    protected static final String SYMBOL_TABLE =
145            Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY;
146
147    /** Property identifier: error reporter. */
148    protected static final String ERROR_REPORTER =
149            Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;
150
151    /** Feature identifier: standard uri conformant */
152    protected static final String STANDARD_URI_CONFORMANT =
153            Constants.XERCES_FEATURE_PREFIX +Constants.STANDARD_URI_CONFORMANT_FEATURE;
154
155    /** Property identifier: entity resolver. */
156    protected static final String ENTITY_RESOLVER =
157            Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY;
158
159    protected static final String STAX_ENTITY_RESOLVER =
160            Constants.XERCES_PROPERTY_PREFIX + Constants.STAX_ENTITY_RESOLVER_PROPERTY;
161
162    // property identifier:  ValidationManager
163    protected static final String VALIDATION_MANAGER =
164            Constants.XERCES_PROPERTY_PREFIX + Constants.VALIDATION_MANAGER_PROPERTY;
165
166    /** property identifier: buffer size. */
167    protected static final String BUFFER_SIZE =
168            Constants.XERCES_PROPERTY_PREFIX + Constants.BUFFER_SIZE_PROPERTY;
169
170    /** property identifier: security manager. */
171    protected static final String SECURITY_MANAGER =
172        Constants.XERCES_PROPERTY_PREFIX + Constants.SECURITY_MANAGER_PROPERTY;
173
174    protected static final String PARSER_SETTINGS =
175        Constants.XERCES_FEATURE_PREFIX + Constants.PARSER_SETTINGS;
176
177    /** Property identifier: Security property manager. */
178    private static final String XML_SECURITY_PROPERTY_MANAGER =
179            Constants.XML_SECURITY_PROPERTY_MANAGER;
180
181    /** access external dtd: file protocol */
182    static final String EXTERNAL_ACCESS_DEFAULT = Constants.EXTERNAL_ACCESS_DEFAULT;
183
184    // recognized features and properties
185
186    /** Recognized features. */
187    private static final String[] RECOGNIZED_FEATURES = {
188                VALIDATION,
189                EXTERNAL_GENERAL_ENTITIES,
190                EXTERNAL_PARAMETER_ENTITIES,
191                ALLOW_JAVA_ENCODINGS,
192                WARN_ON_DUPLICATE_ENTITYDEF,
193                STANDARD_URI_CONFORMANT,
194                XMLConstants.USE_CATALOG
195    };
196
197    /** Feature defaults. */
198    private static final Boolean[] FEATURE_DEFAULTS = {
199                null,
200                Boolean.TRUE,
201                Boolean.TRUE,
202                Boolean.TRUE,
203                Boolean.FALSE,
204                Boolean.FALSE,
205                JdkXmlUtils.USE_CATALOG_DEFAULT
206    };
207
208    /** Recognized properties. */
209    private static final String[] RECOGNIZED_PROPERTIES = {
210                SYMBOL_TABLE,
211                ERROR_REPORTER,
212                ENTITY_RESOLVER,
213                VALIDATION_MANAGER,
214                BUFFER_SIZE,
215                SECURITY_MANAGER,
216                XML_SECURITY_PROPERTY_MANAGER,
217                JdkXmlUtils.CATALOG_DEFER,
218                JdkXmlUtils.CATALOG_FILES,
219                JdkXmlUtils.CATALOG_PREFER,
220                JdkXmlUtils.CATALOG_RESOLVE,
221                JdkXmlUtils.CDATA_CHUNK_SIZE
222    };
223
224    /** Property defaults. */
225    private static final Object[] PROPERTY_DEFAULTS = {
226                null,
227                null,
228                null,
229                null,
230                DEFAULT_BUFFER_SIZE,
231                null,
232                null,
233                null,
234                null,
235                null,
236                null,
237                JdkXmlUtils.CDATA_CHUNK_SIZE_DEFAULT
238    };
239
240    private static final String XMLEntity = "[xml]".intern();
241    private static final String DTDEntity = "[dtd]".intern();
242
243    // debugging
244
245    /**
246     * Debug printing of buffer. This debugging flag works best when you
247     * resize the DEFAULT_BUFFER_SIZE down to something reasonable like
248     * 64 characters.
249     */
250    private static final boolean DEBUG_BUFFER = false;
251
252    /** warn on duplicate Entity declaration.
253     *  http://apache.org/xml/features/warn-on-duplicate-entitydef
254     */
255    protected boolean fWarnDuplicateEntityDef;
256
257    /** Debug some basic entities. */
258    private static final boolean DEBUG_ENTITIES = false;
259
260    /** Debug switching readers for encodings. */
261    private static final boolean DEBUG_ENCODINGS = false;
262
263    // should be diplayed trace resolving messages
264    private static final boolean DEBUG_RESOLVER = false ;
265
266    //
267    // Data
268    //
269
270    // features
271
272    /**
273     * Validation. This feature identifier is:
274     * http://xml.org/sax/features/validation
275     */
276    protected boolean fValidation;
277
278    /**
279     * External general entities. This feature identifier is:
280     * http://xml.org/sax/features/external-general-entities
281     */
282    protected boolean fExternalGeneralEntities;
283
284    /**
285     * External parameter entities. This feature identifier is:
286     * http://xml.org/sax/features/external-parameter-entities
287     */
288    protected boolean fExternalParameterEntities;
289
290    /**
291     * Allow Java encoding names. This feature identifier is:
292     * http://apache.org/xml/features/allow-java-encodings
293     */
294    protected boolean fAllowJavaEncodings = true ;
295
296    /** Load external DTD. */
297    protected boolean fLoadExternalDTD = true;
298
299    // properties
300
301    /**
302     * Symbol table. This property identifier is:
303     * http://apache.org/xml/properties/internal/symbol-table
304     */
305    protected SymbolTable fSymbolTable;
306
307    /**
308     * Error reporter. This property identifier is:
309     * http://apache.org/xml/properties/internal/error-reporter
310     */
311    protected XMLErrorReporter fErrorReporter;
312
313    /**
314     * Entity resolver. This property identifier is:
315     * http://apache.org/xml/properties/internal/entity-resolver
316     */
317    protected XMLEntityResolver fEntityResolver;
318
319    /** Stax Entity Resolver. This property identifier is XMLInputFactory.ENTITY_RESOLVER */
320
321    protected StaxEntityResolverWrapper fStaxEntityResolver;
322
323    /** Property Manager. This is used from Stax */
324    protected PropertyManager fPropertyManager ;
325
326    /** StAX properties */
327    boolean fSupportDTD = true;
328    boolean fReplaceEntityReferences = true;
329    boolean fSupportExternalEntities = true;
330
331    /** used to restrict external access */
332    protected String fAccessExternalDTD = EXTERNAL_ACCESS_DEFAULT;
333
334    // settings
335
336    /**
337     * Validation manager. This property identifier is:
338     * http://apache.org/xml/properties/internal/validation-manager
339     */
340    protected ValidationManager fValidationManager;
341
342    // settings
343
344    /**
345     * Buffer size. We get this value from a property. The default size
346     * is used if the input buffer size property is not specified.
347     * REVISIT: do we need a property for internal entity buffer size?
348     */
349    protected int fBufferSize = DEFAULT_BUFFER_SIZE;
350
351    /** Security Manager */
352    protected XMLSecurityManager fSecurityManager = null;
353
354    protected XMLLimitAnalyzer fLimitAnalyzer = null;
355
356    protected int entityExpansionIndex;
357
358    /**
359     * True if the document entity is standalone. This should really
360     * only be set by the document source (e.g. XMLDocumentScanner).
361     */
362    protected boolean fStandalone;
363
364    // are the entities being parsed in the external subset?
365    // NOTE:  this *is not* the same as whether they're external entities!
366    protected boolean fInExternalSubset = false;
367
368
369    // handlers
370    /** Entity handler. */
371    protected XMLEntityHandler fEntityHandler;
372
373    /** Current entity scanner */
374    protected XMLEntityScanner fEntityScanner ;
375
376    /** XML 1.0 entity scanner. */
377    protected XMLEntityScanner fXML10EntityScanner;
378
379    /** XML 1.1 entity scanner. */
380    protected XMLEntityScanner fXML11EntityScanner;
381
382    /** count of entities expanded: */
383    protected int fEntityExpansionCount = 0;
384
385    // entities
386
387    /** Entities. */
388    protected Map<String, Entity> fEntities = new HashMap<>();
389
390    /** Entity stack. */
391    protected Stack<Entity> fEntityStack = new Stack<>();
392
393    /** Current entity. */
394    protected Entity.ScannedEntity fCurrentEntity = null;
395
396    /** identify if the InputSource is created by a resolver */
397    boolean fISCreatedByResolver = false;
398
399    // shared context
400
401    protected XMLEntityStorage fEntityStorage ;
402
403    protected final Object [] defaultEncoding = new Object[]{"UTF-8", null};
404
405
406    // temp vars
407
408    /** Resource identifer. */
409    private final XMLResourceIdentifierImpl fResourceIdentifier = new XMLResourceIdentifierImpl();
410
411    /** Augmentations for entities. */
412    private final Augmentations fEntityAugs = new AugmentationsImpl();
413
414    /** Pool of character buffers. */
415    private CharacterBufferPool fBufferPool = new CharacterBufferPool(fBufferSize, DEFAULT_INTERNAL_BUFFER_SIZE);
416
417    /** indicate whether Catalog should be used for resolving external resources */
418    private boolean fUseCatalog = true;
419    CatalogFeatures fCatalogFeatures;
420    CatalogResolver fCatalogResolver;
421
422    private String fCatalogFile;
423    private String fDefer;
424    private String fPrefer;
425    private String fResolve;
426
427    //
428    // Constructors
429    //
430
431    /**
432     * If this constructor is used to create the object, reset() should be invoked on this object
433     */
434    public XMLEntityManager() {
435        //for entity managers not created by parsers
436        fSecurityManager = new XMLSecurityManager(true);
437        fEntityStorage = new XMLEntityStorage(this) ;
438        setScannerVersion(Constants.XML_VERSION_1_0);
439    } // <init>()
440
441    /** Default constructor. */
442    public XMLEntityManager(PropertyManager propertyManager) {
443        fPropertyManager = propertyManager ;
444        //pass a reference to current entity being scanned
445        //fEntityStorage = new XMLEntityStorage(fCurrentEntity) ;
446        fEntityStorage = new XMLEntityStorage(this) ;
447        fEntityScanner = new XMLEntityScanner(propertyManager, this) ;
448        reset(propertyManager);
449    } // <init>()
450
451    /**
452     * Adds an internal entity declaration.
453     * <p>
454     * <strong>Note:</strong> This method ignores subsequent entity
455     * declarations.
456     * <p>
457     * <strong>Note:</strong> The name should be a unique symbol. The
458     * SymbolTable can be used for this purpose.
459     *
460     * @param name The name of the entity.
461     * @param text The text of the entity.
462     *
463     * @see SymbolTable
464     */
465    public void addInternalEntity(String name, String text) {
466        if (!fEntities.containsKey(name)) {
467            Entity entity = new Entity.InternalEntity(name, text, fInExternalSubset);
468            fEntities.put(name, entity);
469        } else{
470            if(fWarnDuplicateEntityDef){
471                fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
472                        "MSG_DUPLICATE_ENTITY_DEFINITION",
473                        new Object[]{ name },
474                        XMLErrorReporter.SEVERITY_WARNING );
475            }
476        }
477
478    } // addInternalEntity(String,String)
479
480    /**
481     * Adds an external entity declaration.
482     * <p>
483     * <strong>Note:</strong> This method ignores subsequent entity
484     * declarations.
485     * <p>
486     * <strong>Note:</strong> The name should be a unique symbol. The
487     * SymbolTable can be used for this purpose.
488     *
489     * @param name         The name of the entity.
490     * @param publicId     The public identifier of the entity.
491     * @param literalSystemId     The system identifier of the entity.
492     * @param baseSystemId The base system identifier of the entity.
493     *                     This is the system identifier of the entity
494     *                     where <em>the entity being added</em> and
495     *                     is used to expand the system identifier when
496     *                     the system identifier is a relative URI.
497     *                     When null the system identifier of the first
498     *                     external entity on the stack is used instead.
499     *
500     * @see SymbolTable
501     */
502    public void addExternalEntity(String name,
503            String publicId, String literalSystemId,
504            String baseSystemId) throws IOException {
505        if (!fEntities.containsKey(name)) {
506            if (baseSystemId == null) {
507                // search for the first external entity on the stack
508                int size = fEntityStack.size();
509                if (size == 0 && fCurrentEntity != null && fCurrentEntity.entityLocation != null) {
510                    baseSystemId = fCurrentEntity.entityLocation.getExpandedSystemId();
511                }
512                for (int i = size - 1; i >= 0 ; i--) {
513                    Entity.ScannedEntity externalEntity =
514                            (Entity.ScannedEntity)fEntityStack.elementAt(i);
515                    if (externalEntity.entityLocation != null && externalEntity.entityLocation.getExpandedSystemId() != null) {
516                        baseSystemId = externalEntity.entityLocation.getExpandedSystemId();
517                        break;
518                    }
519                }
520            }
521            Entity entity = new Entity.ExternalEntity(name,
522                    new XMLEntityDescriptionImpl(name, publicId, literalSystemId, baseSystemId,
523                    expandSystemId(literalSystemId, baseSystemId, false)), null, fInExternalSubset);
524            fEntities.put(name, entity);
525        } else{
526            if(fWarnDuplicateEntityDef){
527                fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
528                        "MSG_DUPLICATE_ENTITY_DEFINITION",
529                        new Object[]{ name },
530                        XMLErrorReporter.SEVERITY_WARNING );
531            }
532        }
533
534    } // addExternalEntity(String,String,String,String)
535
536
537    /**
538     * Adds an unparsed entity declaration.
539     * <p>
540     * <strong>Note:</strong> This method ignores subsequent entity
541     * declarations.
542     * <p>
543     * <strong>Note:</strong> The name should be a unique symbol. The
544     * SymbolTable can be used for this purpose.
545     *
546     * @param name     The name of the entity.
547     * @param publicId The public identifier of the entity.
548     * @param systemId The system identifier of the entity.
549     * @param notation The name of the notation.
550     *
551     * @see SymbolTable
552     */
553    public void addUnparsedEntity(String name,
554            String publicId, String systemId,
555            String baseSystemId, String notation) {
556        if (!fEntities.containsKey(name)) {
557            Entity.ExternalEntity entity = new Entity.ExternalEntity(name,
558                    new XMLEntityDescriptionImpl(name, publicId, systemId, baseSystemId, null),
559                    notation, fInExternalSubset);
560            fEntities.put(name, entity);
561        } else{
562            if(fWarnDuplicateEntityDef){
563                fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
564                        "MSG_DUPLICATE_ENTITY_DEFINITION",
565                        new Object[]{ name },
566                        XMLErrorReporter.SEVERITY_WARNING );
567            }
568        }
569    } // addUnparsedEntity(String,String,String,String)
570
571
572    /** get the entity storage object from entity manager */
573    public XMLEntityStorage getEntityStore(){
574        return fEntityStorage ;
575    }
576
577    /** return the entity responsible for reading the entity */
578    public XMLEntityScanner getEntityScanner(){
579        if(fEntityScanner == null) {
580            // default to 1.0
581            if(fXML10EntityScanner == null) {
582                fXML10EntityScanner = new XMLEntityScanner();
583            }
584            fXML10EntityScanner.reset(fSymbolTable, this, fErrorReporter);
585            fEntityScanner = fXML10EntityScanner;
586        }
587        return fEntityScanner;
588
589    }
590
591    public void setScannerVersion(short version) {
592
593        if(version == Constants.XML_VERSION_1_0) {
594            if(fXML10EntityScanner == null) {
595                fXML10EntityScanner = new XMLEntityScanner();
596            }
597            fXML10EntityScanner.reset(fSymbolTable, this, fErrorReporter);
598            fEntityScanner = fXML10EntityScanner;
599            fEntityScanner.setCurrentEntity(fCurrentEntity);
600        } else {
601            if(fXML11EntityScanner == null) {
602                fXML11EntityScanner = new XML11EntityScanner();
603            }
604            fXML11EntityScanner.reset(fSymbolTable, this, fErrorReporter);
605            fEntityScanner = fXML11EntityScanner;
606            fEntityScanner.setCurrentEntity(fCurrentEntity);
607        }
608
609    }
610
611    /**
612     * This method uses the passed-in XMLInputSource to make
613     * fCurrentEntity usable for reading.
614     *
615     * @param reference flag to indicate whether the entity is an Entity Reference.
616     * @param name  name of the entity (XML is it's the document entity)
617     * @param xmlInputSource    the input source, with sufficient information
618     *      to begin scanning characters.
619     * @param literal        True if this entity is started within a
620     *                       literal value.
621     * @param isExternal    whether this entity should be treated as an internal or external entity.
622     * @throws IOException  if anything can't be read
623     *  XNIException    If any parser-specific goes wrong.
624     * @return the encoding of the new entity or null if a character stream was employed
625     */
626    public String setupCurrentEntity(boolean reference, String name, XMLInputSource xmlInputSource,
627            boolean literal, boolean isExternal)
628            throws IOException, XNIException {
629        // get information
630
631        final String publicId = xmlInputSource.getPublicId();
632        String literalSystemId = xmlInputSource.getSystemId();
633        String baseSystemId = xmlInputSource.getBaseSystemId();
634        String encoding = xmlInputSource.getEncoding();
635        final boolean encodingExternallySpecified = (encoding != null);
636        Boolean isBigEndian = null;
637
638        // create reader
639        InputStream stream = null;
640        Reader reader = xmlInputSource.getCharacterStream();
641
642        // First chance checking strict URI
643        String expandedSystemId = expandSystemId(literalSystemId, baseSystemId, fStrictURI);
644        if (baseSystemId == null) {
645            baseSystemId = expandedSystemId;
646        }
647        if (reader == null) {
648            stream = xmlInputSource.getByteStream();
649            if (stream == null) {
650                URL location = new URL(expandedSystemId);
651                URLConnection connect = location.openConnection();
652                if (!(connect instanceof HttpURLConnection)) {
653                    stream = connect.getInputStream();
654                }
655                else {
656                    boolean followRedirects = true;
657
658                    // setup URLConnection if we have an HTTPInputSource
659                    if (xmlInputSource instanceof HTTPInputSource) {
660                        final HttpURLConnection urlConnection = (HttpURLConnection) connect;
661                        final HTTPInputSource httpInputSource = (HTTPInputSource) xmlInputSource;
662
663                        // set request properties
664                        Iterator<Map.Entry<String, String>> propIter = httpInputSource.getHTTPRequestProperties();
665                        while (propIter.hasNext()) {
666                            Map.Entry<String, String> entry = propIter.next();
667                            urlConnection.setRequestProperty(entry.getKey(), entry.getValue());
668                        }
669
670                        // set preference for redirection
671                        followRedirects = httpInputSource.getFollowHTTPRedirects();
672                        if (!followRedirects) {
673                            urlConnection.setInstanceFollowRedirects(followRedirects);
674                        }
675                    }
676
677                    stream = connect.getInputStream();
678
679                    // REVISIT: If the URLConnection has external encoding
680                    // information, we should be reading it here. It's located
681                    // in the charset parameter of Content-Type. -- mrglavas
682
683                    if (followRedirects) {
684                        String redirect = connect.getURL().toString();
685                        // E43: Check if the URL was redirected, and then
686                        // update literal and expanded system IDs if needed.
687                        if (!redirect.equals(expandedSystemId)) {
688                            literalSystemId = redirect;
689                            expandedSystemId = redirect;
690                        }
691                    }
692                }
693            }
694
695            // wrap this stream in RewindableInputStream
696            stream = new RewindableInputStream(stream);
697
698            // perform auto-detect of encoding if necessary
699            if (encoding == null) {
700                // read first four bytes and determine encoding
701                final byte[] b4 = new byte[4];
702                int count = 0;
703                for (; count<4; count++ ) {
704                    b4[count] = (byte)stream.read();
705                }
706                if (count == 4) {
707                    Object [] encodingDesc = getEncodingName(b4, count);
708                    encoding = (String)(encodingDesc[0]);
709                    isBigEndian = (Boolean)(encodingDesc[1]);
710
711                    stream.reset();
712                    // Special case UTF-8 files with BOM created by Microsoft
713                    // tools. It's more efficient to consume the BOM than make
714                    // the reader perform extra checks. -Ac
715                    if (count > 2 && encoding.equals("UTF-8")) {
716                        int b0 = b4[0] & 0xFF;
717                        int b1 = b4[1] & 0xFF;
718                        int b2 = b4[2] & 0xFF;
719                        if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF) {
720                            // ignore first three bytes...
721                            stream.skip(3);
722                        }
723                    }
724                    reader = createReader(stream, encoding, isBigEndian);
725                } else {
726                    reader = createReader(stream, encoding, isBigEndian);
727                }
728            }
729
730            // use specified encoding
731            else {
732                encoding = encoding.toUpperCase(Locale.ENGLISH);
733
734                // If encoding is UTF-8, consume BOM if one is present.
735                if (encoding.equals("UTF-8")) {
736                    final int[] b3 = new int[3];
737                    int count = 0;
738                    for (; count < 3; ++count) {
739                        b3[count] = stream.read();
740                        if (b3[count] == -1)
741                            break;
742                    }
743                    if (count == 3) {
744                        if (b3[0] != 0xEF || b3[1] != 0xBB || b3[2] != 0xBF) {
745                            // First three bytes are not BOM, so reset.
746                            stream.reset();
747                        }
748                    } else {
749                        stream.reset();
750                    }
751                }
752                // If encoding is UTF-16, we still need to read the first four bytes
753                // in order to discover the byte order.
754                else if (encoding.equals("UTF-16")) {
755                    final int[] b4 = new int[4];
756                    int count = 0;
757                    for (; count < 4; ++count) {
758                        b4[count] = stream.read();
759                        if (b4[count] == -1)
760                            break;
761                    }
762                    stream.reset();
763
764                    String utf16Encoding = "UTF-16";
765                    if (count >= 2) {
766                        final int b0 = b4[0];
767                        final int b1 = b4[1];
768                        if (b0 == 0xFE && b1 == 0xFF) {
769                            // UTF-16, big-endian
770                            utf16Encoding = "UTF-16BE";
771                            isBigEndian = Boolean.TRUE;
772                        }
773                        else if (b0 == 0xFF && b1 == 0xFE) {
774                            // UTF-16, little-endian
775                            utf16Encoding = "UTF-16LE";
776                            isBigEndian = Boolean.FALSE;
777                        }
778                        else if (count == 4) {
779                            final int b2 = b4[2];
780                            final int b3 = b4[3];
781                            if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x3F) {
782                                // UTF-16, big-endian, no BOM
783                                utf16Encoding = "UTF-16BE";
784                                isBigEndian = Boolean.TRUE;
785                            }
786                            if (b0 == 0x3C && b1 == 0x00 && b2 == 0x3F && b3 == 0x00) {
787                                // UTF-16, little-endian, no BOM
788                                utf16Encoding = "UTF-16LE";
789                                isBigEndian = Boolean.FALSE;
790                            }
791                        }
792                    }
793                    reader = createReader(stream, utf16Encoding, isBigEndian);
794                }
795                // If encoding is UCS-4, we still need to read the first four bytes
796                // in order to discover the byte order.
797                else if (encoding.equals("ISO-10646-UCS-4")) {
798                    final int[] b4 = new int[4];
799                    int count = 0;
800                    for (; count < 4; ++count) {
801                        b4[count] = stream.read();
802                        if (b4[count] == -1)
803                            break;
804                    }
805                    stream.reset();
806
807                    // Ignore unusual octet order for now.
808                    if (count == 4) {
809                        // UCS-4, big endian (1234)
810                        if (b4[0] == 0x00 && b4[1] == 0x00 && b4[2] == 0x00 && b4[3] == 0x3C) {
811                            isBigEndian = Boolean.TRUE;
812                        }
813                        // UCS-4, little endian (1234)
814                        else if (b4[0] == 0x3C && b4[1] == 0x00 && b4[2] == 0x00 && b4[3] == 0x00) {
815                            isBigEndian = Boolean.FALSE;
816                        }
817                    }
818                }
819                // If encoding is UCS-2, we still need to read the first four bytes
820                // in order to discover the byte order.
821                else if (encoding.equals("ISO-10646-UCS-2")) {
822                    final int[] b4 = new int[4];
823                    int count = 0;
824                    for (; count < 4; ++count) {
825                        b4[count] = stream.read();
826                        if (b4[count] == -1)
827                            break;
828                    }
829                    stream.reset();
830
831                    if (count == 4) {
832                        // UCS-2, big endian
833                        if (b4[0] == 0x00 && b4[1] == 0x3C && b4[2] == 0x00 && b4[3] == 0x3F) {
834                            isBigEndian = Boolean.TRUE;
835                        }
836                        // UCS-2, little endian
837                        else if (b4[0] == 0x3C && b4[1] == 0x00 && b4[2] == 0x3F && b4[3] == 0x00) {
838                            isBigEndian = Boolean.FALSE;
839                        }
840                    }
841                }
842
843                reader = createReader(stream, encoding, isBigEndian);
844            }
845
846            // read one character at a time so we don't jump too far
847            // ahead, converting characters from the byte stream in
848            // the wrong encoding
849            if (DEBUG_ENCODINGS) {
850                System.out.println("$$$ no longer wrapping reader in OneCharReader");
851            }
852            //reader = new OneCharReader(reader);
853        }
854
855        // We've seen a new Reader.
856        // Push it on the stack so we can close it later.
857        //fOwnReaders.add(reader);
858
859        // push entity on stack
860        if (fCurrentEntity != null) {
861            fEntityStack.push(fCurrentEntity);
862        }
863
864        // create entity
865        /* if encoding is specified externally, 'encoding' information present
866         * in the prolog of the XML document is not considered. Hence, prolog can
867         * be read in Chunks of data instead of byte by byte.
868         */
869        fCurrentEntity = new Entity.ScannedEntity(reference, name,
870                new XMLResourceIdentifierImpl(publicId, literalSystemId, baseSystemId, expandedSystemId),
871                stream, reader, encoding, literal, encodingExternallySpecified, isExternal);
872        fCurrentEntity.setEncodingExternallySpecified(encodingExternallySpecified);
873        fEntityScanner.setCurrentEntity(fCurrentEntity);
874        fResourceIdentifier.setValues(publicId, literalSystemId, baseSystemId, expandedSystemId);
875        if (fLimitAnalyzer != null) {
876            fLimitAnalyzer.startEntity(name);
877        }
878        return encoding;
879    } //setupCurrentEntity(String, XMLInputSource, boolean, boolean):  String
880
881
882    /**
883     * Checks whether an entity given by name is external.
884     *
885     * @param entityName The name of the entity to check.
886     * @return True if the entity is external, false otherwise
887     * (including when the entity is not declared).
888     */
889    public boolean isExternalEntity(String entityName) {
890
891        Entity entity = fEntities.get(entityName);
892        if (entity == null) {
893            return false;
894        }
895        return entity.isExternal();
896    }
897
898    /**
899     * Checks whether the declaration of an entity given by name is
900     * // in the external subset.
901     *
902     * @param entityName The name of the entity to check.
903     * @return True if the entity was declared in the external subset, false otherwise
904     *           (including when the entity is not declared).
905     */
906    public boolean isEntityDeclInExternalSubset(String entityName) {
907
908        Entity entity = fEntities.get(entityName);
909        if (entity == null) {
910            return false;
911        }
912        return entity.isEntityDeclInExternalSubset();
913    }
914
915
916
917    //
918    // Public methods
919    //
920
921    /**
922     * Sets whether the document entity is standalone.
923     *
924     * @param standalone True if document entity is standalone.
925     */
926    public void setStandalone(boolean standalone) {
927        fStandalone = standalone;
928    }
929    // setStandalone(boolean)
930
931    /** Returns true if the document entity is standalone. */
932    public boolean isStandalone() {
933        return fStandalone;
934    }  //isStandalone():boolean
935
936    public boolean isDeclaredEntity(String entityName) {
937
938        Entity entity = fEntities.get(entityName);
939        return entity != null;
940    }
941
942    public boolean isUnparsedEntity(String entityName) {
943
944        Entity entity = fEntities.get(entityName);
945        if (entity == null) {
946            return false;
947        }
948        return entity.isUnparsed();
949    }
950
951
952
953    // this simply returns the fResourceIdentifier object;
954    // this should only be used with caution by callers that
955    // carefully manage the entity manager's behaviour, so that
956    // this doesn't returning meaningless or misleading data.
957    // @return  a reference to the current fResourceIdentifier object
958    public XMLResourceIdentifier getCurrentResourceIdentifier() {
959        return fResourceIdentifier;
960    }
961
962    /**
963     * Sets the entity handler. When an entity starts and ends, the
964     * entity handler is notified of the change.
965     *
966     * @param entityHandler The new entity handler.
967     */
968
969    public void setEntityHandler(com.sun.org.apache.xerces.internal.impl.XMLEntityHandler entityHandler) {
970        fEntityHandler = entityHandler;
971    } // setEntityHandler(XMLEntityHandler)
972
973    //this function returns StaxXMLInputSource
974    public StaxXMLInputSource resolveEntityAsPerStax(XMLResourceIdentifier resourceIdentifier) throws java.io.IOException{
975
976        if(resourceIdentifier == null ) return null;
977
978        String publicId = resourceIdentifier.getPublicId();
979        String literalSystemId = resourceIdentifier.getLiteralSystemId();
980        String baseSystemId = resourceIdentifier.getBaseSystemId();
981        String expandedSystemId = resourceIdentifier.getExpandedSystemId();
982        // if no base systemId given, assume that it's relative
983        // to the systemId of the current scanned entity
984        // Sometimes the system id is not (properly) expanded.
985        // We need to expand the system id if:
986        // a. the expanded one was null; or
987        // b. the base system id was null, but becomes non-null from the current entity.
988        boolean needExpand = (expandedSystemId == null);
989        // REVISIT:  why would the baseSystemId ever be null?  if we
990        // didn't have to make this check we wouldn't have to reuse the
991        // fXMLResourceIdentifier object...
992        if (baseSystemId == null && fCurrentEntity != null && fCurrentEntity.entityLocation != null) {
993            baseSystemId = fCurrentEntity.entityLocation.getExpandedSystemId();
994            if (baseSystemId != null)
995                needExpand = true;
996        }
997        if (needExpand)
998            expandedSystemId = expandSystemId(literalSystemId, baseSystemId,false);
999
1000        // give the entity resolver a chance
1001        StaxXMLInputSource staxInputSource = null;
1002        XMLInputSource xmlInputSource = null;
1003
1004        XMLResourceIdentifierImpl ri = null;
1005
1006        if (resourceIdentifier instanceof XMLResourceIdentifierImpl) {
1007            ri = (XMLResourceIdentifierImpl)resourceIdentifier;
1008        } else {
1009            fResourceIdentifier.clear();
1010            ri = fResourceIdentifier;
1011        }
1012        ri.setValues(publicId, literalSystemId, baseSystemId, expandedSystemId);
1013        if(DEBUG_RESOLVER){
1014            System.out.println("BEFORE Calling resolveEntity") ;
1015        }
1016
1017        fISCreatedByResolver = false;
1018        //either of Stax or Xerces would be null
1019        if(fStaxEntityResolver != null){
1020            staxInputSource = fStaxEntityResolver.resolveEntity(ri);
1021            if(staxInputSource != null) {
1022                fISCreatedByResolver = true;
1023            }
1024        }
1025
1026        if(fEntityResolver != null){
1027            xmlInputSource = fEntityResolver.resolveEntity(ri);
1028            if(xmlInputSource != null) {
1029                fISCreatedByResolver = true;
1030            }
1031        }
1032
1033        if(xmlInputSource != null){
1034            //wrap this XMLInputSource to StaxInputSource
1035            staxInputSource = new StaxXMLInputSource(xmlInputSource, fISCreatedByResolver);
1036        }
1037
1038        if (staxInputSource == null && fUseCatalog) {
1039            if (fCatalogFeatures == null) {
1040                fCatalogFeatures = JdkXmlUtils.getCatalogFeatures(fDefer, fCatalogFile, fPrefer, fResolve);
1041            }
1042            fCatalogFile = fCatalogFeatures.get(Feature.FILES);
1043            if (fCatalogFile != null) {
1044                try {
1045                    if (fCatalogResolver == null) {
1046                        fCatalogResolver = CatalogManager.catalogResolver(fCatalogFeatures);
1047                    }
1048                    InputSource is = fCatalogResolver.resolveEntity(publicId, literalSystemId);
1049                    if (is != null && !is.isEmpty()) {
1050                        staxInputSource = new StaxXMLInputSource(new XMLInputSource(is, true), true);
1051                    }
1052                } catch (CatalogException e) {
1053                    fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,"CatalogException",
1054                    new Object[]{SecuritySupport.sanitizePath(fCatalogFile)},
1055                    XMLErrorReporter.SEVERITY_FATAL_ERROR, e );
1056                }
1057            }
1058        }
1059
1060        // do default resolution
1061        //this works for both stax & Xerces, if staxInputSource is null,
1062        //it means parser need to revert to default resolution
1063        if (staxInputSource == null) {
1064            // REVISIT: when systemId is null, I think we should return null.
1065            //          is this the right solution? -SG
1066            //if (systemId != null)
1067            staxInputSource = new StaxXMLInputSource(
1068                    new XMLInputSource(publicId, literalSystemId, baseSystemId, true), false);
1069        }else if(staxInputSource.hasXMLStreamOrXMLEventReader()){
1070            //Waiting for the clarification from EG. - nb
1071        }
1072
1073        if (DEBUG_RESOLVER) {
1074            System.err.println("XMLEntityManager.resolveEntity(" + publicId + ")");
1075            System.err.println(" = " + xmlInputSource);
1076        }
1077
1078        return staxInputSource;
1079
1080    }
1081
1082    /**
1083     * Resolves the specified public and system identifiers. This
1084     * method first attempts to resolve the entity based on the
1085     * EntityResolver registered by the application. If no entity
1086     * resolver is registered or if the registered entity handler
1087     * is unable to resolve the entity, then default entity
1088     * resolution will occur.
1089     *
1090     * @param publicId     The public identifier of the entity.
1091     * @param systemId     The system identifier of the entity.
1092     * @param baseSystemId The base system identifier of the entity.
1093     *                     This is the system identifier of the current
1094     *                     entity and is used to expand the system
1095     *                     identifier when the system identifier is a
1096     *                     relative URI.
1097     *
1098     * @return Returns an input source that wraps the resolved entity.
1099     *         This method will never return null.
1100     *
1101     * @throws IOException  Thrown on i/o error.
1102     * @throws XNIException Thrown by entity resolver to signal an error.
1103     */
1104    public XMLInputSource resolveEntity(XMLResourceIdentifier resourceIdentifier) throws IOException, XNIException {
1105        if(resourceIdentifier == null ) return null;
1106        String publicId = resourceIdentifier.getPublicId();
1107        String literalSystemId = resourceIdentifier.getLiteralSystemId();
1108        String baseSystemId = resourceIdentifier.getBaseSystemId();
1109        String expandedSystemId = resourceIdentifier.getExpandedSystemId();
1110
1111        // if no base systemId given, assume that it's relative
1112        // to the systemId of the current scanned entity
1113        // Sometimes the system id is not (properly) expanded.
1114        // We need to expand the system id if:
1115        // a. the expanded one was null; or
1116        // b. the base system id was null, but becomes non-null from the current entity.
1117        boolean needExpand = (expandedSystemId == null);
1118        // REVISIT:  why would the baseSystemId ever be null?  if we
1119        // didn't have to make this check we wouldn't have to reuse the
1120        // fXMLResourceIdentifier object...
1121        if (baseSystemId == null && fCurrentEntity != null && fCurrentEntity.entityLocation != null) {
1122            baseSystemId = fCurrentEntity.entityLocation.getExpandedSystemId();
1123            if (baseSystemId != null)
1124                needExpand = true;
1125        }
1126        if (needExpand)
1127            expandedSystemId = expandSystemId(literalSystemId, baseSystemId,false);
1128
1129        // give the entity resolver a chance
1130        XMLInputSource xmlInputSource = null;
1131
1132        if (fEntityResolver != null) {
1133            resourceIdentifier.setBaseSystemId(baseSystemId);
1134            resourceIdentifier.setExpandedSystemId(expandedSystemId);
1135            xmlInputSource = fEntityResolver.resolveEntity(resourceIdentifier);
1136        }
1137
1138        if (xmlInputSource == null && fUseCatalog) {
1139            if (fCatalogFeatures == null) {
1140                fCatalogFeatures = JdkXmlUtils.getCatalogFeatures(fDefer, fCatalogFile, fPrefer, fResolve);
1141            }
1142            fCatalogFile = fCatalogFeatures.get(Feature.FILES);
1143            if (fCatalogFile != null) {
1144                /*
1145                 since the method can be called from various processors, both
1146                 EntityResolver and URIResolver are used to attempt to find
1147                 a match
1148                */
1149                InputSource is = null;
1150                try {
1151                    if (fCatalogResolver == null) {
1152                        fCatalogResolver = CatalogManager.catalogResolver(fCatalogFeatures);
1153                    }
1154                    String pid = (publicId != null? publicId : resourceIdentifier.getNamespace());
1155                    if (pid != null || literalSystemId != null) {
1156                        is = fCatalogResolver.resolveEntity(pid, literalSystemId);
1157                    }
1158                } catch (CatalogException e) {}
1159
1160                if (is != null && !is.isEmpty()) {
1161                    xmlInputSource = new XMLInputSource(is, true);
1162                } else if (literalSystemId != null) {
1163                    if (fCatalogResolver == null) {
1164                        fCatalogResolver = CatalogManager.catalogResolver(fCatalogFeatures);
1165                    }
1166
1167                    Source source = null;
1168                    try {
1169                        source = fCatalogResolver.resolve(literalSystemId, baseSystemId);
1170                    } catch (CatalogException e) {
1171                        throw new XNIException(e);
1172                    }
1173                    if (source != null && !source.isEmpty()) {
1174                        xmlInputSource = new XMLInputSource(publicId, source.getSystemId(), baseSystemId, true);
1175                    }
1176                }
1177            }
1178        }
1179
1180        // do default resolution
1181        // REVISIT: what's the correct behavior if the user provided an entity
1182        // resolver (fEntityResolver != null), but resolveEntity doesn't return
1183        // an input source (xmlInputSource == null)?
1184        // do we do default resolution, or do we just return null? -SG
1185        if (xmlInputSource == null) {
1186            // REVISIT: when systemId is null, I think we should return null.
1187            //          is this the right solution? -SG
1188            //if (systemId != null)
1189            xmlInputSource = new XMLInputSource(publicId, literalSystemId, baseSystemId, false);
1190        }
1191
1192        if (DEBUG_RESOLVER) {
1193            System.err.println("XMLEntityManager.resolveEntity(" + publicId + ")");
1194            System.err.println(" = " + xmlInputSource);
1195        }
1196
1197        return xmlInputSource;
1198
1199    } // resolveEntity(XMLResourceIdentifier):XMLInputSource
1200
1201    /**
1202     * Starts a named entity.
1203     *
1204     * @param isGE flag to indicate whether the entity is a General Entity
1205     * @param entityName The name of the entity to start.
1206     * @param literal    True if this entity is started within a literal
1207     *                   value.
1208     *
1209     * @throws IOException  Thrown on i/o error.
1210     * @throws XNIException Thrown by entity handler to signal an error.
1211     */
1212    public void startEntity(boolean isGE, String entityName, boolean literal)
1213    throws IOException, XNIException {
1214
1215        // was entity declared?
1216        Entity entity = fEntityStorage.getEntity(entityName);
1217        if (entity == null) {
1218            if (fEntityHandler != null) {
1219                String encoding = null;
1220                fResourceIdentifier.clear();
1221                fEntityAugs.removeAllItems();
1222                fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
1223                fEntityHandler.startEntity(entityName, fResourceIdentifier, encoding, fEntityAugs);
1224                fEntityAugs.removeAllItems();
1225                fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
1226                fEntityHandler.endEntity(entityName, fEntityAugs);
1227            }
1228            return;
1229        }
1230
1231        // should we skip external entities?
1232        boolean external = entity.isExternal();
1233        Entity.ExternalEntity externalEntity = null;
1234        String extLitSysId = null, extBaseSysId = null, expandedSystemId = null;
1235        if (external) {
1236            externalEntity = (Entity.ExternalEntity)entity;
1237            extLitSysId = (externalEntity.entityLocation != null ? externalEntity.entityLocation.getLiteralSystemId() : null);
1238            extBaseSysId = (externalEntity.entityLocation != null ? externalEntity.entityLocation.getBaseSystemId() : null);
1239            expandedSystemId = expandSystemId(extLitSysId, extBaseSysId);
1240            boolean unparsed = entity.isUnparsed();
1241            boolean parameter = entityName.startsWith("%");
1242            boolean general = !parameter;
1243            if (unparsed || (general && !fExternalGeneralEntities) ||
1244                    (parameter && !fExternalParameterEntities) ||
1245                    !fSupportDTD || !fSupportExternalEntities) {
1246
1247                if (fEntityHandler != null) {
1248                    fResourceIdentifier.clear();
1249                    final String encoding = null;
1250                    fResourceIdentifier.setValues(
1251                            (externalEntity.entityLocation != null ? externalEntity.entityLocation.getPublicId() : null),
1252                            extLitSysId, extBaseSysId, expandedSystemId);
1253                    fEntityAugs.removeAllItems();
1254                    fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
1255                    fEntityHandler.startEntity(entityName, fResourceIdentifier, encoding, fEntityAugs);
1256                    fEntityAugs.removeAllItems();
1257                    fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
1258                    fEntityHandler.endEntity(entityName, fEntityAugs);
1259                }
1260                return;
1261            }
1262        }
1263
1264        // is entity recursive?
1265        int size = fEntityStack.size();
1266        for (int i = size; i >= 0; i--) {
1267            Entity activeEntity = i == size
1268                    ? fCurrentEntity
1269                    : fEntityStack.elementAt(i);
1270            if (activeEntity.name == entityName) {
1271                String path = entityName;
1272                for (int j = i + 1; j < size; j++) {
1273                    activeEntity = fEntityStack.elementAt(j);
1274                    path = path + " -> " + activeEntity.name;
1275                }
1276                path = path + " -> " + fCurrentEntity.name;
1277                path = path + " -> " + entityName;
1278                fErrorReporter.reportError(this.getEntityScanner(),XMLMessageFormatter.XML_DOMAIN,
1279                        "RecursiveReference",
1280                        new Object[] { entityName, path },
1281                        XMLErrorReporter.SEVERITY_FATAL_ERROR);
1282
1283                        if (fEntityHandler != null) {
1284                            fResourceIdentifier.clear();
1285                            final String encoding = null;
1286                            if (external) {
1287                                fResourceIdentifier.setValues(
1288                                        (externalEntity.entityLocation != null ? externalEntity.entityLocation.getPublicId() : null),
1289                                        extLitSysId, extBaseSysId, expandedSystemId);
1290                            }
1291                            fEntityAugs.removeAllItems();
1292                            fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
1293                            fEntityHandler.startEntity(entityName, fResourceIdentifier, encoding, fEntityAugs);
1294                            fEntityAugs.removeAllItems();
1295                            fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
1296                            fEntityHandler.endEntity(entityName, fEntityAugs);
1297                        }
1298
1299                        return;
1300            }
1301        }
1302
1303        // resolve external entity
1304        StaxXMLInputSource staxInputSource = null;
1305        XMLInputSource xmlInputSource = null ;
1306
1307        if (external) {
1308            staxInputSource = resolveEntityAsPerStax(externalEntity.entityLocation);
1309            /** xxx:  Waiting from the EG
1310             * //simply return if there was entity resolver registered and application
1311             * //returns either XMLStreamReader or XMLEventReader.
1312             * if(staxInputSource.hasXMLStreamOrXMLEventReader()) return ;
1313             */
1314            xmlInputSource = staxInputSource.getXMLInputSource() ;
1315            if (!fISCreatedByResolver) {
1316                //let the not-LoadExternalDTD or not-SupportDTD process to handle the situation
1317                if (fLoadExternalDTD) {
1318                    String accessError = SecuritySupport.checkAccess(expandedSystemId, fAccessExternalDTD, Constants.ACCESS_EXTERNAL_ALL);
1319                    if (accessError != null) {
1320                        fErrorReporter.reportError(this.getEntityScanner(),XMLMessageFormatter.XML_DOMAIN,
1321                                "AccessExternalEntity",
1322                                new Object[] { SecuritySupport.sanitizePath(expandedSystemId), accessError },
1323                                XMLErrorReporter.SEVERITY_FATAL_ERROR);
1324                    }
1325                }
1326            }
1327        }
1328        // wrap internal entity
1329        else {
1330            Entity.InternalEntity internalEntity = (Entity.InternalEntity)entity;
1331            Reader reader = new StringReader(internalEntity.text);
1332            xmlInputSource = new XMLInputSource(null, null, null, reader, null);
1333        }
1334
1335        // start the entity
1336        startEntity(isGE, entityName, xmlInputSource, literal, external);
1337
1338    } // startEntity(String,boolean)
1339
1340    /**
1341     * Starts the document entity. The document entity has the "[xml]"
1342     * pseudo-name.
1343     *
1344     * @param xmlInputSource The input source of the document entity.
1345     *
1346     * @throws IOException  Thrown on i/o error.
1347     * @throws XNIException Thrown by entity handler to signal an error.
1348     */
1349    public void startDocumentEntity(XMLInputSource xmlInputSource)
1350    throws IOException, XNIException {
1351        startEntity(false, XMLEntity, xmlInputSource, false, true);
1352    } // startDocumentEntity(XMLInputSource)
1353
1354    //xxx these methods are not required.
1355    /**
1356     * Starts the DTD entity. The DTD entity has the "[dtd]"
1357     * pseudo-name.
1358     *
1359     * @param xmlInputSource The input source of the DTD entity.
1360     *
1361     * @throws IOException  Thrown on i/o error.
1362     * @throws XNIException Thrown by entity handler to signal an error.
1363     */
1364    public void startDTDEntity(XMLInputSource xmlInputSource)
1365    throws IOException, XNIException {
1366        startEntity(false, DTDEntity, xmlInputSource, false, true);
1367    } // startDTDEntity(XMLInputSource)
1368
1369    // indicate start of external subset so that
1370    // location of entity decls can be tracked
1371    public void startExternalSubset() {
1372        fInExternalSubset = true;
1373    }
1374
1375    public void endExternalSubset() {
1376        fInExternalSubset = false;
1377    }
1378
1379    /**
1380     * Starts an entity.
1381     * <p>
1382     * This method can be used to insert an application defined XML
1383     * entity stream into the parsing stream.
1384     *
1385     * @param isGE flag to indicate whether the entity is a General Entity
1386     * @param name           The name of the entity.
1387     * @param xmlInputSource The input source of the entity.
1388     * @param literal        True if this entity is started within a
1389     *                       literal value.
1390     * @param isExternal    whether this entity should be treated as an internal or external entity.
1391     *
1392     * @throws IOException  Thrown on i/o error.
1393     * @throws XNIException Thrown by entity handler to signal an error.
1394     */
1395    public void startEntity(boolean isGE, String name,
1396            XMLInputSource xmlInputSource,
1397            boolean literal, boolean isExternal)
1398            throws IOException, XNIException {
1399
1400        String encoding = setupCurrentEntity(isGE, name, xmlInputSource, literal, isExternal);
1401
1402        //when entity expansion limit is set by the Application, we need to
1403        //check for the entity expansion limit set by the parser, if number of entity
1404        //expansions exceeds the entity expansion limit, parser will throw fatal error.
1405        // Note that this represents the nesting level of open entities.
1406        fEntityExpansionCount++;
1407        if(fLimitAnalyzer != null) {
1408           fLimitAnalyzer.addValue(entityExpansionIndex, name, 1);
1409        }
1410        if( fSecurityManager != null && fSecurityManager.isOverLimit(entityExpansionIndex, fLimitAnalyzer)){
1411            fSecurityManager.debugPrint(fLimitAnalyzer);
1412            fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,"EntityExpansionLimit",
1413                    new Object[]{fSecurityManager.getLimitValueByIndex(entityExpansionIndex)},
1414                                             XMLErrorReporter.SEVERITY_FATAL_ERROR );
1415            // is there anything better to do than reset the counter?
1416            // at least one can envision debugging applications where this might
1417            // be useful...
1418            fEntityExpansionCount = 0;
1419        }
1420
1421        // call handler
1422        if (fEntityHandler != null) {
1423            fEntityHandler.startEntity(name, fResourceIdentifier, encoding, null);
1424        }
1425
1426    } // startEntity(String,XMLInputSource)
1427
1428    /**
1429     * Return the current entity being scanned. Current entity is SET using startEntity function.
1430     * @return Entity.ScannedEntity
1431     */
1432
1433    public Entity.ScannedEntity getCurrentEntity(){
1434        return fCurrentEntity ;
1435    }
1436
1437    /**
1438     * Return the top level entity handled by this manager, or null
1439     * if no entity was added.
1440     */
1441    public Entity.ScannedEntity getTopLevelEntity() {
1442        return (Entity.ScannedEntity)
1443            (fEntityStack.empty() ? null : fEntityStack.elementAt(0));
1444    }
1445
1446
1447    /**
1448     * Close all opened InputStreams and Readers opened by this parser.
1449     */
1450    public void closeReaders() {
1451        /** this call actually does nothing, readers are closed in the endEntity method
1452         * through the current entity.
1453         * The change seems to have happened during the jdk6 development with the
1454         * addition of StAX
1455        **/
1456    }
1457
1458    public void endEntity() throws IOException, XNIException {
1459
1460        // call handler
1461        if (DEBUG_BUFFER) {
1462            System.out.print("(endEntity: ");
1463            print();
1464            System.out.println();
1465        }
1466        //pop the entity from the stack
1467        Entity.ScannedEntity entity = fEntityStack.size() > 0 ? (Entity.ScannedEntity)fEntityStack.pop() : null ;
1468
1469        /** need to close the reader first since the program can end
1470         *  prematurely (e.g. fEntityHandler.endEntity may throw exception)
1471         *  leaving the reader open
1472         */
1473        //close the reader
1474        if(fCurrentEntity != null){
1475            //close the reader
1476            try{
1477                if (fLimitAnalyzer != null) {
1478                    fLimitAnalyzer.endEntity(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT, fCurrentEntity.name);
1479                    if (fCurrentEntity.name.equals("[xml]")) {
1480                        fSecurityManager.debugPrint(fLimitAnalyzer);
1481                    }
1482                }
1483                fCurrentEntity.close();
1484            }catch(IOException ex){
1485                throw new XNIException(ex);
1486            }
1487        }
1488
1489        if (fEntityHandler != null) {
1490            //so this is the last opened entity, signal it to current fEntityHandler using Augmentation
1491            if(entity == null){
1492                fEntityAugs.removeAllItems();
1493                fEntityAugs.putItem(Constants.LAST_ENTITY, Boolean.TRUE);
1494                fEntityHandler.endEntity(fCurrentEntity.name, fEntityAugs);
1495                fEntityAugs.removeAllItems();
1496            }else{
1497                fEntityHandler.endEntity(fCurrentEntity.name, null);
1498            }
1499        }
1500        //check if it is a document entity
1501        boolean documentEntity = fCurrentEntity.name == XMLEntity;
1502
1503        //set popped entity as current entity
1504        fCurrentEntity = entity;
1505        fEntityScanner.setCurrentEntity(fCurrentEntity);
1506
1507        //check if there are any entity left in the stack -- if there are
1508        //no entries EOF has been reached.
1509        // throw exception when it is the last entity but it is not a document entity
1510
1511        if(fCurrentEntity == null & !documentEntity){
1512            throw new EOFException() ;
1513        }
1514
1515        if (DEBUG_BUFFER) {
1516            System.out.print(")endEntity: ");
1517            print();
1518            System.out.println();
1519        }
1520
1521    } // endEntity()
1522
1523
1524    //
1525    // XMLComponent methods
1526    //
1527    public void reset(PropertyManager propertyManager){
1528        // xerces properties
1529        fSymbolTable = (SymbolTable)propertyManager.getProperty(Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY);
1530        fErrorReporter = (XMLErrorReporter)propertyManager.getProperty(Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY);
1531        try {
1532            fStaxEntityResolver = (StaxEntityResolverWrapper)propertyManager.getProperty(STAX_ENTITY_RESOLVER);
1533        } catch (XMLConfigurationException e) {
1534            fStaxEntityResolver = null;
1535        }
1536
1537        fSupportDTD = ((Boolean)propertyManager.getProperty(XMLInputFactory.SUPPORT_DTD));
1538        fReplaceEntityReferences = ((Boolean)propertyManager.getProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES));
1539        fSupportExternalEntities = ((Boolean)propertyManager.getProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES));
1540
1541        // Zephyr feature ignore-external-dtd is the opposite of Xerces' load-external-dtd
1542        fLoadExternalDTD = !((Boolean)propertyManager.getProperty(Constants.ZEPHYR_PROPERTY_PREFIX + Constants.IGNORE_EXTERNAL_DTD));
1543
1544        //Use Catalog
1545        fUseCatalog = (Boolean)propertyManager.getProperty(XMLConstants.USE_CATALOG);
1546        fCatalogFile = (String)propertyManager.getProperty(JdkXmlUtils.CATALOG_FILES);
1547        fDefer = (String)propertyManager.getProperty(JdkXmlUtils.CATALOG_DEFER);
1548        fPrefer = (String)propertyManager.getProperty(JdkXmlUtils.CATALOG_PREFER);
1549        fResolve = (String)propertyManager.getProperty(JdkXmlUtils.CATALOG_RESOLVE);
1550
1551        // JAXP 1.5 feature
1552        XMLSecurityPropertyManager spm = (XMLSecurityPropertyManager) propertyManager.getProperty(XML_SECURITY_PROPERTY_MANAGER);
1553        fAccessExternalDTD = spm.getValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_DTD);
1554
1555        fSecurityManager = (XMLSecurityManager)propertyManager.getProperty(SECURITY_MANAGER);
1556
1557        fLimitAnalyzer = new XMLLimitAnalyzer();
1558        //reset fEntityStorage
1559        fEntityStorage.reset(propertyManager);
1560        //reset XMLEntityReaderImpl
1561        fEntityScanner.reset(propertyManager);
1562
1563        // initialize state
1564        //fStandalone = false;
1565        fEntities.clear();
1566        fEntityStack.removeAllElements();
1567        fCurrentEntity = null;
1568        fValidation = false;
1569        fExternalGeneralEntities = true;
1570        fExternalParameterEntities = true;
1571        fAllowJavaEncodings = true ;
1572    }
1573
1574    /**
1575     * Resets the component. The component can query the component manager
1576     * about any features and properties that affect the operation of the
1577     * component.
1578     *
1579     * @param componentManager The component manager.
1580     *
1581     * @throws SAXException Thrown by component on initialization error.
1582     *                      For example, if a feature or property is
1583     *                      required for the operation of the component, the
1584     *                      component manager may throw a
1585     *                      SAXNotRecognizedException or a
1586     *                      SAXNotSupportedException.
1587     */
1588    public void reset(XMLComponentManager componentManager)
1589    throws XMLConfigurationException {
1590
1591        boolean parser_settings = componentManager.getFeature(PARSER_SETTINGS, true);
1592
1593        if (!parser_settings) {
1594            // parser settings have not been changed
1595            reset();
1596            if(fEntityScanner != null){
1597                fEntityScanner.reset(componentManager);
1598            }
1599            if(fEntityStorage != null){
1600                fEntityStorage.reset(componentManager);
1601            }
1602            return;
1603        }
1604
1605        // sax features
1606        fValidation = componentManager.getFeature(VALIDATION, false);
1607        fExternalGeneralEntities = componentManager.getFeature(EXTERNAL_GENERAL_ENTITIES, true);
1608        fExternalParameterEntities = componentManager.getFeature(EXTERNAL_PARAMETER_ENTITIES, true);
1609
1610        // xerces features
1611        fAllowJavaEncodings = componentManager.getFeature(ALLOW_JAVA_ENCODINGS, false);
1612        fWarnDuplicateEntityDef = componentManager.getFeature(WARN_ON_DUPLICATE_ENTITYDEF, false);
1613        fStrictURI = componentManager.getFeature(STANDARD_URI_CONFORMANT, false);
1614        fLoadExternalDTD = componentManager.getFeature(LOAD_EXTERNAL_DTD, true);
1615
1616        // xerces properties
1617        fSymbolTable = (SymbolTable)componentManager.getProperty(SYMBOL_TABLE);
1618        fErrorReporter = (XMLErrorReporter)componentManager.getProperty(ERROR_REPORTER);
1619        fEntityResolver = (XMLEntityResolver)componentManager.getProperty(ENTITY_RESOLVER, null);
1620        fStaxEntityResolver = (StaxEntityResolverWrapper)componentManager.getProperty(STAX_ENTITY_RESOLVER, null);
1621        fValidationManager = (ValidationManager)componentManager.getProperty(VALIDATION_MANAGER, null);
1622        fSecurityManager = (XMLSecurityManager)componentManager.getProperty(SECURITY_MANAGER, null);
1623        entityExpansionIndex = fSecurityManager.getIndex(Constants.JDK_ENTITY_EXPANSION_LIMIT);
1624
1625        //StAX Property
1626        fSupportDTD = true;
1627        fReplaceEntityReferences = true;
1628        fSupportExternalEntities = true;
1629
1630        // JAXP 1.5 feature
1631        XMLSecurityPropertyManager spm = (XMLSecurityPropertyManager) componentManager.getProperty(XML_SECURITY_PROPERTY_MANAGER, null);
1632        if (spm == null) {
1633            spm = new XMLSecurityPropertyManager();
1634        }
1635        fAccessExternalDTD = spm.getValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_DTD);
1636
1637        //Use Catalog
1638        fUseCatalog = componentManager.getFeature(XMLConstants.USE_CATALOG, true);
1639        fCatalogFile = (String)componentManager.getProperty(JdkXmlUtils.CATALOG_FILES);
1640        fDefer = (String)componentManager.getProperty(JdkXmlUtils.CATALOG_DEFER);
1641        fPrefer = (String)componentManager.getProperty(JdkXmlUtils.CATALOG_PREFER);
1642        fResolve = (String)componentManager.getProperty(JdkXmlUtils.CATALOG_RESOLVE);
1643
1644        //reset general state
1645        reset();
1646
1647        fEntityScanner.reset(componentManager);
1648        fEntityStorage.reset(componentManager);
1649
1650    } // reset(XMLComponentManager)
1651
1652    // reset general state.  Should not be called other than by
1653    // a class acting as a component manager but not
1654    // implementing that interface for whatever reason.
1655    public void reset() {
1656        fLimitAnalyzer = new XMLLimitAnalyzer();
1657        // initialize state
1658        fStandalone = false;
1659        fEntities.clear();
1660        fEntityStack.removeAllElements();
1661        fEntityExpansionCount = 0;
1662
1663        fCurrentEntity = null;
1664        // reset scanner
1665        if(fXML10EntityScanner != null){
1666            fXML10EntityScanner.reset(fSymbolTable, this, fErrorReporter);
1667        }
1668        if(fXML11EntityScanner != null) {
1669            fXML11EntityScanner.reset(fSymbolTable, this, fErrorReporter);
1670        }
1671
1672        // DEBUG
1673        if (DEBUG_ENTITIES) {
1674            addInternalEntity("text", "Hello, World.");
1675            addInternalEntity("empty-element", "<foo/>");
1676            addInternalEntity("balanced-element", "<foo></foo>");
1677            addInternalEntity("balanced-element-with-text", "<foo>Hello, World</foo>");
1678            addInternalEntity("balanced-element-with-entity", "<foo>&text;</foo>");
1679            addInternalEntity("unbalanced-entity", "<foo>");
1680            addInternalEntity("recursive-entity", "<foo>&recursive-entity2;</foo>");
1681            addInternalEntity("recursive-entity2", "<bar>&recursive-entity3;</bar>");
1682            addInternalEntity("recursive-entity3", "<baz>&recursive-entity;</baz>");
1683            try {
1684                addExternalEntity("external-text", null, "external-text.ent", "test/external-text.xml");
1685                addExternalEntity("external-balanced-element", null, "external-balanced-element.ent", "test/external-balanced-element.xml");
1686                addExternalEntity("one", null, "ent/one.ent", "test/external-entity.xml");
1687                addExternalEntity("two", null, "ent/two.ent", "test/ent/one.xml");
1688            }
1689            catch (IOException ex) {
1690                // should never happen
1691            }
1692        }
1693
1694        fEntityHandler = null;
1695
1696        // reset scanner
1697        //if(fEntityScanner!=null)
1698          //  fEntityScanner.reset(fSymbolTable, this,fErrorReporter);
1699
1700    }
1701    /**
1702     * Returns a list of feature identifiers that are recognized by
1703     * this component. This method may return null if no features
1704     * are recognized by this component.
1705     */
1706    public String[] getRecognizedFeatures() {
1707        return RECOGNIZED_FEATURES.clone();
1708    } // getRecognizedFeatures():String[]
1709
1710    /**
1711     * Sets the state of a feature. This method is called by the component
1712     * manager any time after reset when a feature changes state.
1713     * <p>
1714     * <strong>Note:</strong> Components should silently ignore features
1715     * that do not affect the operation of the component.
1716     *
1717     * @param featureId The feature identifier.
1718     * @param state     The state of the feature.
1719     *
1720     * @throws SAXNotRecognizedException The component should not throw
1721     *                                   this exception.
1722     * @throws SAXNotSupportedException The component should not throw
1723     *                                  this exception.
1724     */
1725    public void setFeature(String featureId, boolean state)
1726    throws XMLConfigurationException {
1727
1728        // xerces features
1729        if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) {
1730            final int suffixLength = featureId.length() - Constants.XERCES_FEATURE_PREFIX.length();
1731            if (suffixLength == Constants.ALLOW_JAVA_ENCODINGS_FEATURE.length() &&
1732                featureId.endsWith(Constants.ALLOW_JAVA_ENCODINGS_FEATURE)) {
1733                fAllowJavaEncodings = state;
1734            }
1735            if (suffixLength == Constants.LOAD_EXTERNAL_DTD_FEATURE.length() &&
1736                featureId.endsWith(Constants.LOAD_EXTERNAL_DTD_FEATURE)) {
1737                fLoadExternalDTD = state;
1738                return;
1739            }
1740        } else if (featureId.equals(XMLConstants.USE_CATALOG)) {
1741            fUseCatalog = state;
1742        }
1743
1744    } // setFeature(String,boolean)
1745
1746    /**
1747     * Sets the value of a property. This method is called by the component
1748     * manager any time after reset when a property changes value.
1749     * <p>
1750     * <strong>Note:</strong> Components should silently ignore properties
1751     * that do not affect the operation of the component.
1752     *
1753     * @param propertyId The property identifier.
1754     * @param value      The value of the property.
1755     *
1756     * @throws SAXNotRecognizedException The component should not throw
1757     *                                   this exception.
1758     * @throws SAXNotSupportedException The component should not throw
1759     *                                  this exception.
1760     */
1761    public void setProperty(String propertyId, Object value){
1762        // Xerces properties
1763        if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) {
1764            final int suffixLength = propertyId.length() - Constants.XERCES_PROPERTY_PREFIX.length();
1765
1766            if (suffixLength == Constants.SYMBOL_TABLE_PROPERTY.length() &&
1767                propertyId.endsWith(Constants.SYMBOL_TABLE_PROPERTY)) {
1768                fSymbolTable = (SymbolTable)value;
1769                return;
1770            }
1771            if (suffixLength == Constants.ERROR_REPORTER_PROPERTY.length() &&
1772                propertyId.endsWith(Constants.ERROR_REPORTER_PROPERTY)) {
1773                fErrorReporter = (XMLErrorReporter)value;
1774                return;
1775            }
1776            if (suffixLength == Constants.ENTITY_RESOLVER_PROPERTY.length() &&
1777                propertyId.endsWith(Constants.ENTITY_RESOLVER_PROPERTY)) {
1778                fEntityResolver = (XMLEntityResolver)value;
1779                return;
1780            }
1781            if (suffixLength == Constants.BUFFER_SIZE_PROPERTY.length() &&
1782                propertyId.endsWith(Constants.BUFFER_SIZE_PROPERTY)) {
1783                Integer bufferSize = (Integer)value;
1784                if (bufferSize != null &&
1785                    bufferSize.intValue() > DEFAULT_XMLDECL_BUFFER_SIZE) {
1786                    fBufferSize = bufferSize.intValue();
1787                    fEntityScanner.setBufferSize(fBufferSize);
1788                    fBufferPool.setExternalBufferSize(fBufferSize);
1789                }
1790            }
1791            if (suffixLength == Constants.SECURITY_MANAGER_PROPERTY.length() &&
1792                propertyId.endsWith(Constants.SECURITY_MANAGER_PROPERTY)) {
1793                fSecurityManager = (XMLSecurityManager)value;
1794            }
1795        }
1796
1797        //JAXP 1.5 properties
1798        if (propertyId.equals(XML_SECURITY_PROPERTY_MANAGER))
1799        {
1800            XMLSecurityPropertyManager spm = (XMLSecurityPropertyManager)value;
1801            fAccessExternalDTD = spm.getValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_DTD);
1802            return;
1803        }
1804
1805        //Catalog properties
1806        if (propertyId.equals(JdkXmlUtils.CATALOG_FILES)) {
1807            fCatalogFile = (String)value;
1808        } else if (propertyId.equals(JdkXmlUtils.CATALOG_DEFER)) {
1809            fDefer = (String)value;
1810        } else if (propertyId.equals(JdkXmlUtils.CATALOG_PREFER)) {
1811            fPrefer = (String)value;
1812        } else if (propertyId.equals(JdkXmlUtils.CATALOG_RESOLVE)) {
1813            fResolve = (String)value;
1814        }
1815    }
1816
1817    public void setLimitAnalyzer(XMLLimitAnalyzer fLimitAnalyzer) {
1818        this.fLimitAnalyzer = fLimitAnalyzer;
1819    }
1820
1821    /**
1822     * Returns a list of property identifiers that are recognized by
1823     * this component. This method may return null if no properties
1824     * are recognized by this component.
1825     */
1826    public String[] getRecognizedProperties() {
1827        return RECOGNIZED_PROPERTIES.clone();
1828    } // getRecognizedProperties():String[]
1829    /**
1830     * Returns the default state for a feature, or null if this
1831     * component does not want to report a default value for this
1832     * feature.
1833     *
1834     * @param featureId The feature identifier.
1835     *
1836     * @since Xerces 2.2.0
1837     */
1838    public Boolean getFeatureDefault(String featureId) {
1839        for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
1840            if (RECOGNIZED_FEATURES[i].equals(featureId)) {
1841                return FEATURE_DEFAULTS[i];
1842            }
1843        }
1844        return null;
1845    } // getFeatureDefault(String):Boolean
1846
1847    /**
1848     * Returns the default state for a property, or null if this
1849     * component does not want to report a default value for this
1850     * property.
1851     *
1852     * @param propertyId The property identifier.
1853     *
1854     * @since Xerces 2.2.0
1855     */
1856    public Object getPropertyDefault(String propertyId) {
1857        for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
1858            if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
1859                return PROPERTY_DEFAULTS[i];
1860            }
1861        }
1862        return null;
1863    } // getPropertyDefault(String):Object
1864
1865    //
1866    // Public static methods
1867    //
1868
1869    /**
1870     * Expands a system id and returns the system id as a URI, if
1871     * it can be expanded. A return value of null means that the
1872     * identifier is already expanded. An exception thrown
1873     * indicates a failure to expand the id.
1874     *
1875     * @param systemId The systemId to be expanded.
1876     *
1877     * @return Returns the URI string representing the expanded system
1878     *         identifier. A null value indicates that the given
1879     *         system identifier is already expanded.
1880     *
1881     */
1882    public static String expandSystemId(String systemId) {
1883        return expandSystemId(systemId, null);
1884    } // expandSystemId(String):String
1885
1886    //
1887    // Public static methods
1888    //
1889
1890    // current value of the "user.dir" property
1891    private static String gUserDir;
1892    // cached URI object for the current value of the escaped "user.dir" property stored as a URI
1893    private static URI gUserDirURI;
1894    // which ASCII characters need to be escaped
1895    private static boolean gNeedEscaping[] = new boolean[128];
1896    // the first hex character if a character needs to be escaped
1897    private static char gAfterEscaping1[] = new char[128];
1898    // the second hex character if a character needs to be escaped
1899    private static char gAfterEscaping2[] = new char[128];
1900    private static char[] gHexChs = {'0', '1', '2', '3', '4', '5', '6', '7',
1901                                     '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
1902    // initialize the above 3 arrays
1903    static {
1904        for (int i = 0; i <= 0x1f; i++) {
1905            gNeedEscaping[i] = true;
1906            gAfterEscaping1[i] = gHexChs[i >> 4];
1907            gAfterEscaping2[i] = gHexChs[i & 0xf];
1908        }
1909        gNeedEscaping[0x7f] = true;
1910        gAfterEscaping1[0x7f] = '7';
1911        gAfterEscaping2[0x7f] = 'F';
1912        char[] escChs = {' ', '<', '>', '#', '%', '"', '{', '}',
1913                         '|', '\\', '^', '~', '[', ']', '`'};
1914        int len = escChs.length;
1915        char ch;
1916        for (int i = 0; i < len; i++) {
1917            ch = escChs[i];
1918            gNeedEscaping[ch] = true;
1919            gAfterEscaping1[ch] = gHexChs[ch >> 4];
1920            gAfterEscaping2[ch] = gHexChs[ch & 0xf];
1921        }
1922    }
1923
1924    // To escape the "user.dir" system property, by using %HH to represent
1925    // special ASCII characters: 0x00~0x1F, 0x7F, ' ', '<', '>', '#', '%'
1926    // and '"'. It's a static method, so needs to be synchronized.
1927    // this method looks heavy, but since the system property isn't expected
1928    // to change often, so in most cases, we only need to return the URI
1929    // that was escaped before.
1930    // According to the URI spec, non-ASCII characters (whose value >= 128)
1931    // need to be escaped too.
1932    // REVISIT: don't know how to escape non-ASCII characters, especially
1933    // which encoding to use. Leave them for now.
1934    private static synchronized URI getUserDir() throws URI.MalformedURIException {
1935        // get the user.dir property
1936        String userDir = "";
1937        try {
1938            userDir = SecuritySupport.getSystemProperty("user.dir");
1939        }
1940        catch (SecurityException se) {
1941        }
1942
1943        // return empty string if property value is empty string.
1944        if (userDir.length() == 0)
1945            return new URI("file", "", "", null, null);
1946        // compute the new escaped value if the new property value doesn't
1947        // match the previous one
1948        if (gUserDirURI != null && userDir.equals(gUserDir)) {
1949            return gUserDirURI;
1950        }
1951
1952        // record the new value as the global property value
1953        gUserDir = userDir;
1954
1955        char separator = java.io.File.separatorChar;
1956        userDir = userDir.replace(separator, '/');
1957
1958        int len = userDir.length(), ch;
1959        StringBuilder buffer = new StringBuilder(len*3);
1960        // change C:/blah to /C:/blah
1961        if (len >= 2 && userDir.charAt(1) == ':') {
1962            ch = Character.toUpperCase(userDir.charAt(0));
1963            if (ch >= 'A' && ch <= 'Z') {
1964                buffer.append('/');
1965            }
1966        }
1967
1968        // for each character in the path
1969        int i = 0;
1970        for (; i < len; i++) {
1971            ch = userDir.charAt(i);
1972            // if it's not an ASCII character, break here, and use UTF-8 encoding
1973            if (ch >= 128)
1974                break;
1975            if (gNeedEscaping[ch]) {
1976                buffer.append('%');
1977                buffer.append(gAfterEscaping1[ch]);
1978                buffer.append(gAfterEscaping2[ch]);
1979                // record the fact that it's escaped
1980            }
1981            else {
1982                buffer.append((char)ch);
1983            }
1984        }
1985
1986        // we saw some non-ascii character
1987        if (i < len) {
1988            // get UTF-8 bytes for the remaining sub-string
1989            byte[] bytes = null;
1990            byte b;
1991            try {
1992                bytes = userDir.substring(i).getBytes("UTF-8");
1993            } catch (java.io.UnsupportedEncodingException e) {
1994                // should never happen
1995                return new URI("file", "", userDir, null, null);
1996            }
1997            len = bytes.length;
1998
1999            // for each byte
2000            for (i = 0; i < len; i++) {
2001                b = bytes[i];
2002                // for non-ascii character: make it positive, then escape
2003                if (b < 0) {
2004                    ch = b + 256;
2005                    buffer.append('%');
2006                    buffer.append(gHexChs[ch >> 4]);
2007                    buffer.append(gHexChs[ch & 0xf]);
2008                }
2009                else if (gNeedEscaping[b]) {
2010                    buffer.append('%');
2011                    buffer.append(gAfterEscaping1[b]);
2012                    buffer.append(gAfterEscaping2[b]);
2013                }
2014                else {
2015                    buffer.append((char)b);
2016                }
2017            }
2018        }
2019
2020        // change blah/blah to blah/blah/
2021        if (!userDir.endsWith("/"))
2022            buffer.append('/');
2023
2024        gUserDirURI = new URI("file", "", buffer.toString(), null, null);
2025
2026        return gUserDirURI;
2027    }
2028
2029    public static OutputStream createOutputStream(String uri) throws IOException {
2030        // URI was specified. Handle relative URIs.
2031        final String expanded = XMLEntityManager.expandSystemId(uri, null, true);
2032        final URL url = new URL(expanded != null ? expanded : uri);
2033        OutputStream out = null;
2034        String protocol = url.getProtocol();
2035        String host = url.getHost();
2036        // Use FileOutputStream if this URI is for a local file.
2037        if (protocol.equals("file")
2038                && (host == null || host.length() == 0 || host.equals("localhost"))) {
2039            File file = new File(getPathWithoutEscapes(url.getPath()));
2040            if (!file.exists()) {
2041                File parent = file.getParentFile();
2042                if (parent != null && !parent.exists()) {
2043                    parent.mkdirs();
2044                }
2045            }
2046            out = new FileOutputStream(file);
2047        }
2048        // Try to write to some other kind of URI. Some protocols
2049        // won't support this, though HTTP should work.
2050        else {
2051            URLConnection urlCon = url.openConnection();
2052            urlCon.setDoInput(false);
2053            urlCon.setDoOutput(true);
2054            urlCon.setUseCaches(false); // Enable tunneling.
2055            if (urlCon instanceof HttpURLConnection) {
2056                // The DOM L3 REC says if we are writing to an HTTP URI
2057                // it is to be done with an HTTP PUT.
2058                HttpURLConnection httpCon = (HttpURLConnection) urlCon;
2059                httpCon.setRequestMethod("PUT");
2060            }
2061            out = urlCon.getOutputStream();
2062        }
2063        return out;
2064    }
2065
2066    private static String getPathWithoutEscapes(String origPath) {
2067        if (origPath != null && origPath.length() != 0 && origPath.indexOf('%') != -1) {
2068            // Locate the escape characters
2069            StringTokenizer tokenizer = new StringTokenizer(origPath, "%");
2070            StringBuilder result = new StringBuilder(origPath.length());
2071            int size = tokenizer.countTokens();
2072            result.append(tokenizer.nextToken());
2073            for(int i = 1; i < size; ++i) {
2074                String token = tokenizer.nextToken();
2075                // Decode the 2 digit hexadecimal number following % in '%nn'
2076                result.append((char)Integer.valueOf(token.substring(0, 2), 16).intValue());
2077                result.append(token.substring(2));
2078            }
2079            return result.toString();
2080        }
2081        return origPath;
2082    }
2083
2084    /**
2085     * Absolutizes a URI using the current value
2086     * of the "user.dir" property as the base URI. If
2087     * the URI is already absolute, this is a no-op.
2088     *
2089     * @param uri the URI to absolutize
2090     */
2091    public static void absolutizeAgainstUserDir(URI uri)
2092        throws URI.MalformedURIException {
2093        uri.absolutize(getUserDir());
2094    }
2095
2096    /**
2097     * Expands a system id and returns the system id as a URI, if
2098     * it can be expanded. A return value of null means that the
2099     * identifier is already expanded. An exception thrown
2100     * indicates a failure to expand the id.
2101     *
2102     * @param systemId The systemId to be expanded.
2103     *
2104     * @return Returns the URI string representing the expanded system
2105     *         identifier. A null value indicates that the given
2106     *         system identifier is already expanded.
2107     *
2108     */
2109    public static String expandSystemId(String systemId, String baseSystemId) {
2110
2111        // check for bad parameters id
2112        if (systemId == null || systemId.length() == 0) {
2113            return systemId;
2114        }
2115        // if id already expanded, return
2116        try {
2117            URI uri = new URI(systemId);
2118            if (uri != null) {
2119                return systemId;
2120            }
2121        } catch (URI.MalformedURIException e) {
2122            // continue on...
2123        }
2124        // normalize id
2125        String id = fixURI(systemId);
2126
2127        // normalize base
2128        URI base = null;
2129        URI uri = null;
2130        try {
2131            if (baseSystemId == null || baseSystemId.length() == 0 ||
2132                    baseSystemId.equals(systemId)) {
2133                String dir = getUserDir().toString();
2134                base = new URI("file", "", dir, null, null);
2135            } else {
2136                try {
2137                    base = new URI(fixURI(baseSystemId));
2138                } catch (URI.MalformedURIException e) {
2139                    if (baseSystemId.indexOf(':') != -1) {
2140                        // for xml schemas we might have baseURI with
2141                        // a specified drive
2142                        base = new URI("file", "", fixURI(baseSystemId), null, null);
2143                    } else {
2144                        String dir = getUserDir().toString();
2145                        dir = dir + fixURI(baseSystemId);
2146                        base = new URI("file", "", dir, null, null);
2147                    }
2148                }
2149            }
2150            // expand id
2151            uri = new URI(base, id);
2152        } catch (Exception e) {
2153            // let it go through
2154
2155        }
2156
2157        if (uri == null) {
2158            return systemId;
2159        }
2160        return uri.toString();
2161
2162    } // expandSystemId(String,String):String
2163
2164    /**
2165     * Expands a system id and returns the system id as a URI, if
2166     * it can be expanded. A return value of null means that the
2167     * identifier is already expanded. An exception thrown
2168     * indicates a failure to expand the id.
2169     *
2170     * @param systemId The systemId to be expanded.
2171     *
2172     * @return Returns the URI string representing the expanded system
2173     *         identifier. A null value indicates that the given
2174     *         system identifier is already expanded.
2175     *
2176     */
2177    public static String expandSystemId(String systemId, String baseSystemId,
2178                                        boolean strict)
2179            throws URI.MalformedURIException {
2180
2181        // check if there is a system id before
2182        // trying to expand it.
2183        if (systemId == null) {
2184            return null;
2185        }
2186
2187        // system id has to be a valid URI
2188        if (strict) {
2189            try {
2190                // if it's already an absolute one, return it
2191                new URI(systemId);
2192                return systemId;
2193            }
2194            catch (URI.MalformedURIException ex) {
2195            }
2196            URI base = null;
2197            // if there isn't a base uri, use the working directory
2198            if (baseSystemId == null || baseSystemId.length() == 0) {
2199                base = new URI("file", "", getUserDir().toString(), null, null);
2200            }
2201            // otherwise, use the base uri
2202            else {
2203                try {
2204                    base = new URI(baseSystemId);
2205                }
2206                catch (URI.MalformedURIException e) {
2207                    // assume "base" is also a relative uri
2208                    String dir = getUserDir().toString();
2209                    dir = dir + baseSystemId;
2210                    base = new URI("file", "", dir, null, null);
2211                }
2212            }
2213            // absolutize the system id using the base
2214            URI uri = new URI(base, systemId);
2215            // return the string rep of the new uri (an absolute one)
2216            return uri.toString();
2217
2218            // if any exception is thrown, it'll get thrown to the caller.
2219        }
2220
2221        // Assume the URIs are well-formed. If it turns out they're not, try fixing them up.
2222        try {
2223             return expandSystemIdStrictOff(systemId, baseSystemId);
2224        }
2225        catch (URI.MalformedURIException e) {
2226            /** Xerces URI rejects unicode, try java.net.URI
2227             * this is not ideal solution, but it covers known cases which either
2228             * Xerces URI or java.net.URI can handle alone
2229             * will file bug against java.net.URI
2230             */
2231            try {
2232                return expandSystemIdStrictOff1(systemId, baseSystemId);
2233            } catch (URISyntaxException ex) {
2234                // continue on...
2235            }
2236        }
2237        // check for bad parameters id
2238        if (systemId.length() == 0) {
2239            return systemId;
2240        }
2241
2242        // normalize id
2243        String id = fixURI(systemId);
2244
2245        // normalize base
2246        URI base = null;
2247        URI uri = null;
2248        try {
2249            if (baseSystemId == null || baseSystemId.length() == 0 ||
2250                baseSystemId.equals(systemId)) {
2251                base = getUserDir();
2252            }
2253            else {
2254                try {
2255                    base = new URI(fixURI(baseSystemId).trim());
2256                }
2257                catch (URI.MalformedURIException e) {
2258                    if (baseSystemId.indexOf(':') != -1) {
2259                        // for xml schemas we might have baseURI with
2260                        // a specified drive
2261                        base = new URI("file", "", fixURI(baseSystemId).trim(), null, null);
2262                    }
2263                    else {
2264                        base = new URI(getUserDir(), fixURI(baseSystemId));
2265                    }
2266                }
2267             }
2268             // expand id
2269             uri = new URI(base, id.trim());
2270        }
2271        catch (Exception e) {
2272            // let it go through
2273
2274        }
2275
2276        if (uri == null) {
2277            return systemId;
2278        }
2279        return uri.toString();
2280
2281    } // expandSystemId(String,String,boolean):String
2282
2283    /**
2284     * Helper method for expandSystemId(String,String,boolean):String
2285     */
2286    private static String expandSystemIdStrictOn(String systemId, String baseSystemId)
2287        throws URI.MalformedURIException {
2288
2289        URI systemURI = new URI(systemId, true);
2290        // If it's already an absolute one, return it
2291        if (systemURI.isAbsoluteURI()) {
2292            return systemId;
2293        }
2294
2295        // If there isn't a base URI, use the working directory
2296        URI baseURI = null;
2297        if (baseSystemId == null || baseSystemId.length() == 0) {
2298            baseURI = getUserDir();
2299        }
2300        else {
2301            baseURI = new URI(baseSystemId, true);
2302            if (!baseURI.isAbsoluteURI()) {
2303                // assume "base" is also a relative uri
2304                baseURI.absolutize(getUserDir());
2305            }
2306        }
2307
2308        // absolutize the system identifier using the base URI
2309        systemURI.absolutize(baseURI);
2310
2311        // return the string rep of the new uri (an absolute one)
2312        return systemURI.toString();
2313
2314        // if any exception is thrown, it'll get thrown to the caller.
2315
2316    } // expandSystemIdStrictOn(String,String):String
2317
2318    /**
2319     * Helper method for expandSystemId(String,String,boolean):String
2320     */
2321    private static String expandSystemIdStrictOff(String systemId, String baseSystemId)
2322        throws URI.MalformedURIException {
2323
2324        URI systemURI = new URI(systemId, true);
2325        // If it's already an absolute one, return it
2326        if (systemURI.isAbsoluteURI()) {
2327            if (systemURI.getScheme().length() > 1) {
2328                return systemId;
2329            }
2330            /**
2331             * If the scheme's length is only one character,
2332             * it's likely that this was intended as a file
2333             * path. Fixing this up in expandSystemId to
2334             * maintain backwards compatibility.
2335             */
2336            throw new URI.MalformedURIException();
2337        }
2338
2339        // If there isn't a base URI, use the working directory
2340        URI baseURI = null;
2341        if (baseSystemId == null || baseSystemId.length() == 0) {
2342            baseURI = getUserDir();
2343        }
2344        else {
2345            baseURI = new URI(baseSystemId, true);
2346            if (!baseURI.isAbsoluteURI()) {
2347                // assume "base" is also a relative uri
2348                baseURI.absolutize(getUserDir());
2349            }
2350        }
2351
2352        // absolutize the system identifier using the base URI
2353        systemURI.absolutize(baseURI);
2354
2355        // return the string rep of the new uri (an absolute one)
2356        return systemURI.toString();
2357
2358        // if any exception is thrown, it'll get thrown to the caller.
2359
2360    } // expandSystemIdStrictOff(String,String):String
2361
2362    private static String expandSystemIdStrictOff1(String systemId, String baseSystemId)
2363        throws URISyntaxException, URI.MalformedURIException {
2364
2365            java.net.URI systemURI = new java.net.URI(systemId);
2366        // If it's already an absolute one, return it
2367        if (systemURI.isAbsolute()) {
2368            if (systemURI.getScheme().length() > 1) {
2369                return systemId;
2370            }
2371            /**
2372             * If the scheme's length is only one character,
2373             * it's likely that this was intended as a file
2374             * path. Fixing this up in expandSystemId to
2375             * maintain backwards compatibility.
2376             */
2377            throw new URISyntaxException(systemId, "the scheme's length is only one character");
2378        }
2379
2380        // If there isn't a base URI, use the working directory
2381        URI baseURI = null;
2382        if (baseSystemId == null || baseSystemId.length() == 0) {
2383            baseURI = getUserDir();
2384        }
2385        else {
2386            baseURI = new URI(baseSystemId, true);
2387            if (!baseURI.isAbsoluteURI()) {
2388                // assume "base" is also a relative uri
2389                baseURI.absolutize(getUserDir());
2390            }
2391        }
2392
2393        // absolutize the system identifier using the base URI
2394//        systemURI.absolutize(baseURI);
2395        systemURI = (new java.net.URI(baseURI.toString())).resolve(systemURI);
2396
2397        // return the string rep of the new uri (an absolute one)
2398        return systemURI.toString();
2399
2400        // if any exception is thrown, it'll get thrown to the caller.
2401
2402    } // expandSystemIdStrictOff(String,String):String
2403
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", true};
2431        }
2432        if (b0 == 0xFF && b1 == 0xFE) {
2433            // UTF-16, little-endian
2434            return new Object [] {"UTF-16LE", 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", 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", 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", 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", 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","&#x00A9;");
3196        fEntityStorage.addInternalEntity("ch1","&#84;");
3197        fEntityStorage.addInternalEntity("% ch2","param");
3198    }
3199
3200} // class XMLEntityManager
3201