KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xerces > impl > XMLEntityManager


1 /*
2  * Copyright 1999-2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.xerces.impl;
18
19 import java.io.IOException JavaDoc;
20 import java.io.InputStream JavaDoc;
21 import java.io.InputStreamReader JavaDoc;
22 import java.io.Reader JavaDoc;
23 import java.io.StringReader JavaDoc;
24 import java.lang.reflect.Method JavaDoc;
25 import java.net.HttpURLConnection JavaDoc;
26 import java.net.URL JavaDoc;
27 import java.net.URLConnection JavaDoc;
28 import java.util.Hashtable JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.Locale JavaDoc;
31 import java.util.Map JavaDoc;
32 import java.util.Stack JavaDoc;
33
34 import org.apache.xerces.impl.io.ASCIIReader;
35 import org.apache.xerces.impl.io.UCSReader;
36 import org.apache.xerces.impl.io.UTF8Reader;
37 import org.apache.xerces.impl.msg.XMLMessageFormatter;
38 import org.apache.xerces.impl.validation.ValidationManager;
39 import org.apache.xerces.util.AugmentationsImpl;
40 import org.apache.xerces.util.EncodingMap;
41 import org.apache.xerces.util.HTTPInputSource;
42 import org.apache.xerces.util.SecurityManager;
43 import org.apache.xerces.util.SymbolTable;
44 import org.apache.xerces.util.URI;
45 import org.apache.xerces.util.XMLChar;
46 import org.apache.xerces.util.XMLEntityDescriptionImpl;
47 import org.apache.xerces.util.XMLResourceIdentifierImpl;
48 import org.apache.xerces.xni.Augmentations;
49 import org.apache.xerces.xni.XMLResourceIdentifier;
50 import org.apache.xerces.xni.XNIException;
51 import org.apache.xerces.xni.parser.XMLComponent;
52 import org.apache.xerces.xni.parser.XMLComponentManager;
53 import org.apache.xerces.xni.parser.XMLConfigurationException;
54 import org.apache.xerces.xni.parser.XMLEntityResolver;
55 import org.apache.xerces.xni.parser.XMLInputSource;
56
57 /**
58  * The entity manager handles the registration of general and parameter
59  * entities; resolves entities; and starts entities. The entity manager
60  * is a central component in a standard parser configuration and this
61  * class works directly with the entity scanner to manage the underlying
62  * xni.
63  * <p>
64  * This component requires the following features and properties from the
65  * component manager that uses it:
66  * <ul>
67  * <li>http://xml.org/sax/features/validation</li>
68  * <li>http://xml.org/sax/features/external-general-entities</li>
69  * <li>http://xml.org/sax/features/external-parameter-entities</li>
70  * <li>http://apache.org/xml/features/allow-java-encodings</li>
71  * <li>http://apache.org/xml/properties/internal/symbol-table</li>
72  * <li>http://apache.org/xml/properties/internal/error-reporter</li>
73  * <li>http://apache.org/xml/properties/internal/entity-resolver</li>
74  * </ul>
75  *
76  * @xerces.internal
77  *
78  * @author Andy Clark, IBM
79  * @author Arnaud Le Hors, IBM
80  *
81  * @version $Id: XMLEntityManager.java,v 1.94 2005/04/19 03:18:18 mrglavas Exp $
82  */

83 public class XMLEntityManager
84     implements XMLComponent, XMLEntityResolver {
85
86     //
87
// Constants
88
//
89

90     /** Default buffer size (2048). */
91     public static final int DEFAULT_BUFFER_SIZE = 2048;
92
93     /** Default buffer size before we've finished with the XMLDecl: */
94     public static final int DEFAULT_XMLDECL_BUFFER_SIZE = 64;
95
96     /** Default internal entity buffer size (512). */
97     public static final int DEFAULT_INTERNAL_BUFFER_SIZE = 512;
98
99     // feature identifiers
100

101     /** Feature identifier: validation. */
102     protected static final String JavaDoc VALIDATION =
103         Constants.SAX_FEATURE_PREFIX + Constants.VALIDATION_FEATURE;
104
105     /** Feature identifier: external general entities. */
106     protected static final String JavaDoc EXTERNAL_GENERAL_ENTITIES =
107         Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_GENERAL_ENTITIES_FEATURE;
108
109     /** Feature identifier: external parameter entities. */
110     protected static final String JavaDoc EXTERNAL_PARAMETER_ENTITIES =
111         Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_PARAMETER_ENTITIES_FEATURE;
112
113     /** Feature identifier: allow Java encodings. */
114     protected static final String JavaDoc ALLOW_JAVA_ENCODINGS =
115         Constants.XERCES_FEATURE_PREFIX + Constants.ALLOW_JAVA_ENCODINGS_FEATURE;
116
117     /** Feature identifier: warn on duplicate EntityDef */
118     protected static final String JavaDoc WARN_ON_DUPLICATE_ENTITYDEF =
119     Constants.XERCES_FEATURE_PREFIX +Constants.WARN_ON_DUPLICATE_ENTITYDEF_FEATURE;
120
121     /** Feature identifier: standard uri conformant */
122     protected static final String JavaDoc STANDARD_URI_CONFORMANT =
123     Constants.XERCES_FEATURE_PREFIX +Constants.STANDARD_URI_CONFORMANT_FEATURE;
124     
125     protected static final String JavaDoc PARSER_SETTINGS =
126         Constants.XERCES_FEATURE_PREFIX + Constants.PARSER_SETTINGS;
127
128     // property identifiers
129

130     /** Property identifier: symbol table. */
131     protected static final String JavaDoc SYMBOL_TABLE =
132         Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY;
133
134     /** Property identifier: error reporter. */
135     protected static final String JavaDoc ERROR_REPORTER =
136         Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;
137
138     /** Property identifier: entity resolver. */
139     protected static final String JavaDoc ENTITY_RESOLVER =
140         Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY;
141
142     // property identifier: ValidationManager
143
protected static final String JavaDoc VALIDATION_MANAGER =
144         Constants.XERCES_PROPERTY_PREFIX + Constants.VALIDATION_MANAGER_PROPERTY;
145
146     /** property identifier: buffer size. */
147     protected static final String JavaDoc BUFFER_SIZE =
148         Constants.XERCES_PROPERTY_PREFIX + Constants.BUFFER_SIZE_PROPERTY;
149
150     /** property identifier: security manager. */
151     protected static final String JavaDoc SECURITY_MANAGER =
152         Constants.XERCES_PROPERTY_PREFIX + Constants.SECURITY_MANAGER_PROPERTY;
153
154     // recognized features and properties
155

156     /** Recognized features. */
157     private static final String JavaDoc[] RECOGNIZED_FEATURES = {
158         VALIDATION,
159         EXTERNAL_GENERAL_ENTITIES,
160         EXTERNAL_PARAMETER_ENTITIES,
161         ALLOW_JAVA_ENCODINGS,
162         WARN_ON_DUPLICATE_ENTITYDEF,
163         STANDARD_URI_CONFORMANT
164     };
165
166     /** Feature defaults. */
167     private static final Boolean JavaDoc[] FEATURE_DEFAULTS = {
168         null,
169         Boolean.TRUE,
170         Boolean.TRUE,
171         Boolean.FALSE,
172         Boolean.FALSE,
173         Boolean.FALSE
174     };
175
176     /** Recognized properties. */
177     private static final String JavaDoc[] RECOGNIZED_PROPERTIES = {
178         SYMBOL_TABLE,
179         ERROR_REPORTER,
180         ENTITY_RESOLVER,
181         VALIDATION_MANAGER,
182         BUFFER_SIZE,
183         SECURITY_MANAGER,
184     };
185
186     /** Property defaults. */
187     private static final Object JavaDoc[] PROPERTY_DEFAULTS = {
188         null,
189         null,
190         null,
191         null,
192         new Integer JavaDoc(DEFAULT_BUFFER_SIZE),
193         null,
194     };
195
196     private static final String JavaDoc XMLEntity = "[xml]".intern();
197     private static final String JavaDoc DTDEntity = "[dtd]".intern();
198     
199     // debugging
200

201     /**
202      * Debug printing of buffer. This debugging flag works best when you
203      * resize the DEFAULT_BUFFER_SIZE down to something reasonable like
204      * 64 characters.
205      */

206     private static final boolean DEBUG_BUFFER = false;
207
208     /** Debug some basic entities. */
209     private static final boolean DEBUG_ENTITIES = false;
210
211     /** Debug switching readers for encodings. */
212     private static final boolean DEBUG_ENCODINGS = false;
213
214     // should be diplayed trace resolving messages
215
private static final boolean DEBUG_RESOLVER = false;
216
217     //
218
// Data
219
//
220

221     // features
222

223     /**
224      * Validation. This feature identifier is:
225      * http://xml.org/sax/features/validation
226      */

227     protected boolean fValidation;
228
229     /**
230      * External general entities. This feature identifier is:
231      * http://xml.org/sax/features/external-general-entities
232      */

233     protected boolean fExternalGeneralEntities = true;
234
235     /**
236      * External parameter entities. This feature identifier is:
237      * http://xml.org/sax/features/external-parameter-entities
238      */

239     protected boolean fExternalParameterEntities = true;
240
241     /**
242      * Allow Java encoding names. This feature identifier is:
243      * http://apache.org/xml/features/allow-java-encodings
244      */

245     protected boolean fAllowJavaEncodings;
246
247     /** warn on duplicate Entity declaration.
248      * http://apache.org/xml/features/warn-on-duplicate-entitydef
249      */

250     protected boolean fWarnDuplicateEntityDef;
251
252     /**
253      * standard uri conformant (strict uri).
254      * http://apache.org/xml/features/standard-uri-conformant
255      */

256     protected boolean fStrictURI;
257
258     // properties
259

260     /**
261      * Symbol table. This property identifier is:
262      * http://apache.org/xml/properties/internal/symbol-table
263      */

264     protected SymbolTable fSymbolTable;
265
266     /**
267      * Error reporter. This property identifier is:
268      * http://apache.org/xml/properties/internal/error-reporter
269      */

270     protected XMLErrorReporter fErrorReporter;
271
272     /**
273      * Entity resolver. This property identifier is:
274      * http://apache.org/xml/properties/internal/entity-resolver
275      */

276     protected XMLEntityResolver fEntityResolver;
277
278     /**
279      * Validation manager. This property identifier is:
280      * http://apache.org/xml/properties/internal/validation-manager
281      */

282     protected ValidationManager fValidationManager;
283
284     // settings
285

286     /**
287      * Buffer size. We get this value from a property. The default size
288      * is used if the input buffer size property is not specified.
289      * REVISIT: do we need a property for internal entity buffer size?
290      */

291     protected int fBufferSize = DEFAULT_BUFFER_SIZE;
292
293     // stores defaults for entity expansion limit if it has
294
// been set on the configuration.
295
protected SecurityManager JavaDoc fSecurityManager = null;
296
297     /**
298      * True if the document entity is standalone. This should really
299      * only be set by the document source (e.g. XMLDocumentScanner).
300      */

301     protected boolean fStandalone;
302
303     // are the entities being parsed in the external subset?
304
// NOTE: this *is not* the same as whether they're external entities!
305
protected boolean fInExternalSubset = false;
306
307     // handlers
308

309     /** Entity handler. */
310     protected XMLEntityHandler fEntityHandler;
311
312     // scanner
313

314     /** Current entity scanner. */
315     protected XMLEntityScanner fEntityScanner;
316
317     /** XML 1.0 entity scanner. */
318     protected XMLEntityScanner fXML10EntityScanner;
319
320     /** XML 1.1 entity scanner. */
321     protected XMLEntityScanner fXML11EntityScanner;
322
323     // entity expansion limit (contains useful data if and only if
324
// fSecurityManager is non-null)
325
protected int fEntityExpansionLimit = 0;
326     // entity currently being expanded:
327
protected int fEntityExpansionCount = 0;
328
329     // entities
330

331     /** Entities. */
332     protected Hashtable JavaDoc fEntities = new Hashtable JavaDoc();
333
334     /** Entity stack. */
335     protected Stack JavaDoc fEntityStack = new Stack JavaDoc();
336
337     /** Current entity. */
338     protected ScannedEntity fCurrentEntity;
339
340     // shared context
341

342     /** Shared declared entities. */
343     protected Hashtable JavaDoc fDeclaredEntities;
344
345     // temp vars
346

347     /** Resource identifer. */
348     private final XMLResourceIdentifierImpl fResourceIdentifier = new XMLResourceIdentifierImpl();
349     
350     /** Augmentations for entities. */
351     private final Augmentations fEntityAugs = new AugmentationsImpl();
352     
353     /** Pool of character buffers. */
354     private CharacterBufferPool fBufferPool = new CharacterBufferPool(fBufferSize, DEFAULT_INTERNAL_BUFFER_SIZE);
355
356     //
357
// Constructors
358
//
359

360     /** Default constructor. */
361     public XMLEntityManager() {
362         this(null);
363     } // <init>()
364

365     /**
366      * Constructs an entity manager that shares the specified entity
367      * declarations during each parse.
368      * <p>
369      * <strong>REVISIT:</strong> We might want to think about the "right"
370      * way to expose the list of declared entities. For now, the knowledge
371      * how to access the entity declarations is implicit.
372      */

373     public XMLEntityManager(XMLEntityManager entityManager) {
374
375
376         // save shared entity declarations
377
fDeclaredEntities = entityManager != null
378                           ? entityManager.getDeclaredEntities() : null;
379
380         setScannerVersion(Constants.XML_VERSION_1_0);
381     } // <init>(XMLEntityManager)
382

383     //
384
// Public methods
385
//
386

387     /**
388      * Sets whether the document entity is standalone.
389      *
390      * @param standalone True if document entity is standalone.
391      */

392     public void setStandalone(boolean standalone) {
393         fStandalone = standalone;
394     } // setStandalone(boolean)
395

396     /** Returns true if the document entity is standalone. */
397     public boolean isStandalone() {
398         return fStandalone;
399     } // isStandalone():boolean
400

401     /**
402      * Sets the entity handler. When an entity starts and ends, the
403      * entity handler is notified of the change.
404      *
405      * @param entityHandler The new entity handler.
406      */

407     public void setEntityHandler(XMLEntityHandler entityHandler) {
408         fEntityHandler = entityHandler;
409     } // setEntityHandler(XMLEntityHandler)
410

411     // this simply returns the fResourceIdentifier object;
412
// this should only be used with caution by callers that
413
// carefully manage the entity manager's behaviour, so that
414
// this doesn't returning meaningless or misleading data.
415
// @return a reference to the current fResourceIdentifier object
416
public XMLResourceIdentifier getCurrentResourceIdentifier() {
417         return fResourceIdentifier;
418     }
419
420     // this simply returns the fCurrentEntity object;
421
// this should only be used with caution by callers that
422
// carefully manage the entity manager's behaviour, so that
423
// this doesn't returning meaningless or misleading data.
424
// @return a reference to the current fCurrentEntity object
425
public ScannedEntity getCurrentEntity() {
426         return fCurrentEntity;
427     }
428
429     /**
430      * Adds an internal entity declaration.
431      * <p>
432      * <strong>Note:</strong> This method ignores subsequent entity
433      * declarations.
434      * <p>
435      * <strong>Note:</strong> The name should be a unique symbol. The
436      * SymbolTable can be used for this purpose.
437      *
438      * @param name The name of the entity.
439      * @param text The text of the entity.
440      *
441      * @see SymbolTable
442      */

443     public void addInternalEntity(String JavaDoc name, String JavaDoc text) {
444         if (!fEntities.containsKey(name)) {
445             Entity entity = new InternalEntity(name, text, fInExternalSubset);
446             fEntities.put(name, entity);
447         }
448         else{
449             if(fWarnDuplicateEntityDef){
450                 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
451                                              "MSG_DUPLICATE_ENTITY_DEFINITION",
452                                              new Object JavaDoc[]{ name },
453                                              XMLErrorReporter.SEVERITY_WARNING );
454             }
455         }
456
457     } // addInternalEntity(String,String)
458

459     /**
460      * Adds an external entity declaration.
461      * <p>
462      * <strong>Note:</strong> This method ignores subsequent entity
463      * declarations.
464      * <p>
465      * <strong>Note:</strong> The name should be a unique symbol. The
466      * SymbolTable can be used for this purpose.
467      *
468      * @param name The name of the entity.
469      * @param publicId The public identifier of the entity.
470      * @param literalSystemId The system identifier of the entity.
471      * @param baseSystemId The base system identifier of the entity.
472      * This is the system identifier of the entity
473      * where <em>the entity being added</em> and
474      * is used to expand the system identifier when
475      * the system identifier is a relative URI.
476      * When null the system identifier of the first
477      * external entity on the stack is used instead.
478      *
479      * @see SymbolTable
480      */

481     public void addExternalEntity(String JavaDoc name,
482                                   String JavaDoc publicId, String JavaDoc literalSystemId,
483                                   String JavaDoc baseSystemId) throws IOException JavaDoc {
484         if (!fEntities.containsKey(name)) {
485             if (baseSystemId == null) {
486                 // search for the first external entity on the stack
487
int size = fEntityStack.size();
488                 if (size == 0 && fCurrentEntity != null && fCurrentEntity.entityLocation != null) {
489                     baseSystemId = fCurrentEntity.entityLocation.getExpandedSystemId();
490                 }
491                 for (int i = size - 1; i >= 0 ; i--) {
492                     ScannedEntity externalEntity =
493                         (ScannedEntity)fEntityStack.elementAt(i);
494                     if (externalEntity.entityLocation != null && externalEntity.entityLocation.getExpandedSystemId() != null) {
495                         baseSystemId = externalEntity.entityLocation.getExpandedSystemId();
496                         break;
497                     }
498                 }
499             }
500             Entity entity = new ExternalEntity(name,
501                 new XMLEntityDescriptionImpl(name, publicId, literalSystemId, baseSystemId,
502                 expandSystemId(literalSystemId, baseSystemId, false)), null, fInExternalSubset);
503             fEntities.put(name, entity);
504         }
505         else{
506             if(fWarnDuplicateEntityDef){
507                 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
508                                              "MSG_DUPLICATE_ENTITY_DEFINITION",
509                                              new Object JavaDoc[]{ name },
510                                              XMLErrorReporter.SEVERITY_WARNING );
511             }
512         }
513
514     } // addExternalEntity(String,String,String,String)
515

516     /**
517      * Checks whether an entity given by name is external.
518      *
519      * @param entityName The name of the entity to check.
520      * @return True if the entity is external, false otherwise
521      * (including when the entity is not declared).
522      */

523     public boolean isExternalEntity(String JavaDoc entityName) {
524
525         Entity entity = (Entity)fEntities.get(entityName);
526         if (entity == null) {
527             return false;
528         }
529         return entity.isExternal();
530     }
531
532     /**
533      * Checks whether the declaration of an entity given by name is
534      // in the external subset.
535      *
536      * @param entityName The name of the entity to check.
537      * @return True if the entity was declared in the external subset, false otherwise
538      * (including when the entity is not declared).
539      */

540     public boolean isEntityDeclInExternalSubset(String JavaDoc entityName) {
541
542         Entity entity = (Entity)fEntities.get(entityName);
543         if (entity == null) {
544             return false;
545         }
546         return entity.isEntityDeclInExternalSubset();
547     }
548
549     /**
550      * Adds an unparsed entity declaration.
551      * <p>
552      * <strong>Note:</strong> This method ignores subsequent entity
553      * declarations.
554      * <p>
555      * <strong>Note:</strong> The name should be a unique symbol. The
556      * SymbolTable can be used for this purpose.
557      *
558      * @param name The name of the entity.
559      * @param publicId The public identifier of the entity.
560      * @param systemId The system identifier of the entity.
561      * @param notation The name of the notation.
562      *
563      * @see SymbolTable
564      */

565     public void addUnparsedEntity(String JavaDoc name,
566                                   String JavaDoc publicId, String JavaDoc systemId,
567                                   String JavaDoc baseSystemId, String JavaDoc notation) {
568         if (!fEntities.containsKey(name)) {
569             Entity entity = new ExternalEntity(name,
570                 new XMLEntityDescriptionImpl(name, publicId, systemId, baseSystemId, null),
571                 notation, fInExternalSubset);
572             fEntities.put(name, entity);
573         }
574         else{
575             if(fWarnDuplicateEntityDef){
576                 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
577                                              "MSG_DUPLICATE_ENTITY_DEFINITION",
578                                              new Object JavaDoc[]{ name },
579                                              XMLErrorReporter.SEVERITY_WARNING );
580             }
581         }
582     } // addUnparsedEntity(String,String,String,String)
583

584     /**
585      * Checks whether an entity given by name is unparsed.
586      *
587      * @param entityName The name of the entity to check.
588      * @return True if the entity is unparsed, false otherwise
589      * (including when the entity is not declared).
590      */

591     public boolean isUnparsedEntity(String JavaDoc entityName) {
592
593         Entity entity = (Entity)fEntities.get(entityName);
594         if (entity == null) {
595             return false;
596         }
597         return entity.isUnparsed();
598     }
599
600     /**
601      * Checks whether an entity given by name is declared.
602      *
603      * @param entityName The name of the entity to check.
604      * @return True if the entity is declared, false otherwise.
605      */

606     public boolean isDeclaredEntity(String JavaDoc entityName) {
607
608         Entity entity = (Entity)fEntities.get(entityName);
609         return entity != null;
610     }
611
612     /**
613      * Resolves the specified public and system identifiers. This
614      * method first attempts to resolve the entity based on the
615      * EntityResolver registered by the application. If no entity
616      * resolver is registered or if the registered entity handler
617      * is unable to resolve the entity, then default entity
618      * resolution will occur.
619      *
620      * @param publicId The public identifier of the entity.
621      * @param systemId The system identifier of the entity.
622      * @param baseSystemId The base system identifier of the entity.
623      * This is the system identifier of the current
624      * entity and is used to expand the system
625      * identifier when the system identifier is a
626      * relative URI.
627      *
628      * @return Returns an input source that wraps the resolved entity.
629      * This method will never return null.
630      *
631      * @throws IOException Thrown on i/o error.
632      * @throws XNIException Thrown by entity resolver to signal an error.
633      */

634     public XMLInputSource resolveEntity(XMLResourceIdentifier resourceIdentifier)
635             throws IOException JavaDoc, XNIException {
636         if(resourceIdentifier == null ) return null;
637         String JavaDoc publicId = resourceIdentifier.getPublicId();
638         String JavaDoc literalSystemId = resourceIdentifier.getLiteralSystemId();
639         String JavaDoc baseSystemId = resourceIdentifier.getBaseSystemId();
640         String JavaDoc expandedSystemId = resourceIdentifier.getExpandedSystemId();
641         // if no base systemId given, assume that it's relative
642
// to the systemId of the current scanned entity
643
// Sometimes the system id is not (properly) expanded.
644
// We need to expand the system id if:
645
// a. the expanded one was null; or
646
// b. the base system id was null, but becomes non-null from the current entity.
647
boolean needExpand = (expandedSystemId == null);
648         // REVISIT: why would the baseSystemId ever be null? if we
649
// didn't have to make this check we wouldn't have to reuse the
650
// fXMLResourceIdentifier object...
651
if (baseSystemId == null && fCurrentEntity != null && fCurrentEntity.entityLocation != null) {
652             baseSystemId = fCurrentEntity.entityLocation.getExpandedSystemId();
653             if (baseSystemId != null)
654                 needExpand = true;
655          }
656          if (needExpand)
657             expandedSystemId = expandSystemId(literalSystemId, baseSystemId, false);
658
659        // give the entity resolver a chance
660
XMLInputSource xmlInputSource = null;
661         if (fEntityResolver != null) {
662             resourceIdentifier.setBaseSystemId(baseSystemId);
663             resourceIdentifier.setExpandedSystemId(expandedSystemId);
664             xmlInputSource = fEntityResolver.resolveEntity(resourceIdentifier);
665         }
666
667         // do default resolution
668
// REVISIT: what's the correct behavior if the user provided an entity
669
// resolver (fEntityResolver != null), but resolveEntity doesn't return
670
// an input source (xmlInputSource == null)?
671
// do we do default resolution, or do we just return null? -SG
672
if (xmlInputSource == null) {
673             // REVISIT: when systemId is null, I think we should return null.
674
// is this the right solution? -SG
675
//if (systemId != null)
676
xmlInputSource = new XMLInputSource(publicId, literalSystemId, baseSystemId);
677         }
678
679         if (DEBUG_RESOLVER) {
680             System.err.println("XMLEntityManager.resolveEntity(" + publicId + ")");
681             System.err.println(" = " + xmlInputSource);
682         }
683
684         return xmlInputSource;
685
686     } // resolveEntity(XMLResourceIdentifier):XMLInputSource
687

688     /**
689      * Starts a named entity.
690      *
691      * @param entityName The name of the entity to start.
692      * @param literal True if this entity is started within a literal
693      * value.
694      *
695      * @throws IOException Thrown on i/o error.
696      * @throws XNIException Thrown by entity handler to signal an error.
697      */

698     public void startEntity(String JavaDoc entityName, boolean literal)
699         throws IOException JavaDoc, XNIException {
700
701         // was entity declared?
702
Entity entity = (Entity)fEntities.get(entityName);
703         if (entity == null) {
704             if (fEntityHandler != null) {
705                 String JavaDoc encoding = null;
706                 fResourceIdentifier.clear();
707                 fEntityAugs.removeAllItems();
708                 fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
709                 fEntityHandler.startEntity(entityName, fResourceIdentifier, encoding, fEntityAugs);
710                 fEntityAugs.removeAllItems();
711                 fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
712                 fEntityHandler.endEntity(entityName, fEntityAugs);
713             }
714             return;
715         }
716
717         // should we skip external entities?
718
boolean external = entity.isExternal();
719         if (external && (fValidationManager == null || !fValidationManager.isCachedDTD())) {
720             boolean unparsed = entity.isUnparsed();
721             boolean parameter = entityName.startsWith("%");
722             boolean general = !parameter;
723             if (unparsed || (general && !fExternalGeneralEntities) ||
724                 (parameter && !fExternalParameterEntities)) {
725                 if (fEntityHandler != null) {
726                     fResourceIdentifier.clear();
727                     final String JavaDoc encoding = null;
728                     ExternalEntity externalEntity = (ExternalEntity)entity;
729                     //REVISIT: since we're storing expandedSystemId in the
730
// externalEntity, how could this have got here if it wasn't already
731
// expanded??? - neilg
732
String JavaDoc extLitSysId = (externalEntity.entityLocation != null ? externalEntity.entityLocation.getLiteralSystemId() : null);
733                     String JavaDoc extBaseSysId = (externalEntity.entityLocation != null ? externalEntity.entityLocation.getBaseSystemId() : null);
734                     String JavaDoc expandedSystemId = expandSystemId(extLitSysId, extBaseSysId, false);
735                     fResourceIdentifier.setValues(
736                             (externalEntity.entityLocation != null ? externalEntity.entityLocation.getPublicId() : null),
737                             extLitSysId, extBaseSysId, expandedSystemId);
738                     fEntityAugs.removeAllItems();
739                     fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
740                     fEntityHandler.startEntity(entityName, fResourceIdentifier, encoding, fEntityAugs);
741                     fEntityAugs.removeAllItems();
742                     fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
743                     fEntityHandler.endEntity(entityName, fEntityAugs);
744                 }
745                 return;
746             }
747         }
748
749         // is entity recursive?
750
int size = fEntityStack.size();
751         for (int i = size; i >= 0; i--) {
752             Entity activeEntity = i == size
753                                 ? fCurrentEntity
754                                 : (Entity)fEntityStack.elementAt(i);
755             if (activeEntity.name == entityName) {
756                 StringBuffer JavaDoc path = new StringBuffer JavaDoc(entityName);
757                 for (int j = i + 1; j < size; j++) {
758                     activeEntity = (Entity)fEntityStack.elementAt(j);
759                     path.append(" -> ");
760                     path.append(activeEntity.name);
761                 }
762                 path.append(" -> ");
763                 path.append(fCurrentEntity.name);
764                 path.append(" -> ");
765                 path.append(entityName);
766                 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
767                                            "RecursiveReference",
768                                            new Object JavaDoc[] { entityName, path.toString() },
769                                            XMLErrorReporter.SEVERITY_FATAL_ERROR);
770                 if (fEntityHandler != null) {
771                     fResourceIdentifier.clear();
772                     final String JavaDoc encoding = null;
773                     if (external) {
774                         ExternalEntity externalEntity = (ExternalEntity)entity;
775                         // REVISIT: for the same reason above...
776
String JavaDoc extLitSysId = (externalEntity.entityLocation != null ? externalEntity.entityLocation.getLiteralSystemId() : null);
777                         String JavaDoc extBaseSysId = (externalEntity.entityLocation != null ? externalEntity.entityLocation.getBaseSystemId() : null);
778                         String JavaDoc expandedSystemId = expandSystemId(extLitSysId, extBaseSysId, false);
779                         fResourceIdentifier.setValues(
780                                 (externalEntity.entityLocation != null ? externalEntity.entityLocation.getPublicId() : null),
781                                 extLitSysId, extBaseSysId, expandedSystemId);
782                     }
783                     fEntityAugs.removeAllItems();
784                     fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
785                     fEntityHandler.startEntity(entityName, fResourceIdentifier, encoding, fEntityAugs);
786                     fEntityAugs.removeAllItems();
787                     fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
788                     fEntityHandler.endEntity(entityName, fEntityAugs);
789                 }
790                 return;
791             }
792         }
793
794         // resolve external entity
795
XMLInputSource xmlInputSource = null;
796         if (external) {
797             ExternalEntity externalEntity = (ExternalEntity)entity;
798             xmlInputSource = resolveEntity(externalEntity.entityLocation);
799         }
800
801         // wrap internal entity
802
else {
803             InternalEntity internalEntity = (InternalEntity)entity;
804             Reader JavaDoc reader = new StringReader JavaDoc(internalEntity.text);
805             xmlInputSource = new XMLInputSource(null, null, null, reader, null);
806         }
807
808         // start the entity
809
startEntity(entityName, xmlInputSource, literal, external);
810
811     } // startEntity(String,boolean)
812

813     /**
814      * Starts the document entity. The document entity has the "[xml]"
815      * pseudo-name.
816      *
817      * @param xmlInputSource The input source of the document entity.
818      *
819      * @throws IOException Thrown on i/o error.
820      * @throws XNIException Thrown by entity handler to signal an error.
821      */

822     public void startDocumentEntity(XMLInputSource xmlInputSource)
823         throws IOException JavaDoc, XNIException {
824         startEntity(XMLEntity, xmlInputSource, false, true);
825     } // startDocumentEntity(XMLInputSource)
826

827     /**
828      * Starts the DTD entity. The DTD entity has the "[dtd]"
829      * pseudo-name.
830      *
831      * @param xmlInputSource The input source of the DTD entity.
832      *
833      * @throws IOException Thrown on i/o error.
834      * @throws XNIException Thrown by entity handler to signal an error.
835      */

836     public void startDTDEntity(XMLInputSource xmlInputSource)
837         throws IOException JavaDoc, XNIException {
838         startEntity(DTDEntity, xmlInputSource, false, true);
839     } // startDTDEntity(XMLInputSource)
840

841     // indicate start of external subset so that
842
// location of entity decls can be tracked
843
public void startExternalSubset() {
844         fInExternalSubset = true;
845     }
846
847     public void endExternalSubset() {
848         fInExternalSubset = false;
849     }
850
851     /**
852      * Starts an entity.
853      * <p>
854      * This method can be used to insert an application defined XML
855      * entity stream into the parsing stream.
856      *
857      * @param name The name of the entity.
858      * @param xmlInputSource The input source of the entity.
859      * @param literal True if this entity is started within a
860      * literal value.
861      * @param isExternal whether this entity should be treated as an internal or external entity.
862      *
863      * @throws IOException Thrown on i/o error.
864      * @throws XNIException Thrown by entity handler to signal an error.
865      */

866     public void startEntity(String JavaDoc name,
867                             XMLInputSource xmlInputSource,
868                             boolean literal, boolean isExternal)
869         throws IOException JavaDoc, XNIException {
870
871         String JavaDoc encoding = setupCurrentEntity(name, xmlInputSource, literal, isExternal);
872
873         //when entity expansion limit is set by the Application, we need to
874
//check for the entity expansion limit set by the parser, if number of entity
875
//expansions exceeds the entity expansion limit, parser will throw fatal error.
876
// Note that this is intentionally unbalanced; it counts
877
// the number of expansions *per document*.
878
if( fSecurityManager != null && fEntityExpansionCount++ > fEntityExpansionLimit ){
879             fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
880                                              "EntityExpansionLimitExceeded",
881                                              new Object JavaDoc[]{new Integer JavaDoc(fEntityExpansionLimit) },
882                                              XMLErrorReporter.SEVERITY_FATAL_ERROR );
883             // is there anything better to do than reset the counter?
884
// at least one can envision debugging applications where this might
885
// be useful...
886
fEntityExpansionCount = 0;
887         }
888         
889         // call handler
890
if (fEntityHandler != null) {
891             fEntityHandler.startEntity(name, fResourceIdentifier, encoding, null);
892         }
893
894     } // startEntity(String,XMLInputSource)
895

896     /**
897      * This method uses the passed-in XMLInputSource to make
898      * fCurrentEntity usable for reading.
899      * @param name name of the entity (XML is it's the document entity)
900      * @param xmlInputSource the input source, with sufficient information
901      * to begin scanning characters.
902      * @param literal True if this entity is started within a
903      * literal value.
904      * @param isExternal whether this entity should be treated as an internal or external entity.
905      * @throws IOException if anything can't be read
906      * XNIException If any parser-specific goes wrong.
907      * @return the encoding of the new entity or null if a character stream was employed
908      */

909     public String JavaDoc setupCurrentEntity(String JavaDoc name, XMLInputSource xmlInputSource,
910                 boolean literal, boolean isExternal)
911             throws IOException JavaDoc, XNIException {
912         // get information
913

914         final String JavaDoc publicId = xmlInputSource.getPublicId();
915         String JavaDoc literalSystemId = xmlInputSource.getSystemId();
916         String JavaDoc baseSystemId = xmlInputSource.getBaseSystemId();
917         String JavaDoc encoding = xmlInputSource.getEncoding();
918         final boolean encodingExternallySpecified = (encoding != null);
919         Boolean JavaDoc isBigEndian = null;
920
921         // create reader
922
InputStream JavaDoc stream = null;
923         Reader JavaDoc reader = xmlInputSource.getCharacterStream();
924         // First chance checking strict URI
925
String JavaDoc expandedSystemId = expandSystemId(literalSystemId, baseSystemId, fStrictURI);
926         if (baseSystemId == null) {
927             baseSystemId = expandedSystemId;
928         }
929         if (reader == null) {
930             stream = xmlInputSource.getByteStream();
931             if (stream == null) {
932                 URL JavaDoc location = new URL JavaDoc(expandedSystemId);
933                 URLConnection JavaDoc connect = location.openConnection();
934                 if (!(connect instanceof HttpURLConnection JavaDoc)) {
935                     stream = connect.getInputStream();
936                 }
937                 else {
938                     boolean followRedirects = true;
939                     
940                     // setup URLConnection if we have an HTTPInputSource
941
if (xmlInputSource instanceof HTTPInputSource) {
942                         final HttpURLConnection JavaDoc urlConnection = (HttpURLConnection JavaDoc) connect;
943                         final HTTPInputSource httpInputSource = (HTTPInputSource) xmlInputSource;
944                         
945                         // set request properties
946
Iterator JavaDoc propIter = httpInputSource.getHTTPRequestProperties();
947                         while (propIter.hasNext()) {
948                             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) propIter.next();
949                             urlConnection.setRequestProperty((String JavaDoc) entry.getKey(), (String JavaDoc) entry.getValue());
950                         }
951                         
952                         // set preference for redirection
953
followRedirects = httpInputSource.getFollowHTTPRedirects();
954                         if (!followRedirects) {
955                             setInstanceFollowRedirects(urlConnection, followRedirects);
956                         }
957                     }
958                     
959                     stream = connect.getInputStream();
960                     
961                     // REVISIT: If the URLConnection has external encoding
962
// information, we should be reading it here. It's located
963
// in the charset parameter of Content-Type. -- mrglavas
964

965                     if (followRedirects) {
966                         String JavaDoc redirect = connect.getURL().toString();
967                         // E43: Check if the URL was redirected, and then
968
// update literal and expanded system IDs if needed.
969
if (!redirect.equals(expandedSystemId)) {
970                             literalSystemId = redirect;
971                             expandedSystemId = redirect;
972                         }
973                     }
974                 }
975             }
976             // wrap this stream in RewindableInputStream
977
stream = new RewindableInputStream(stream);
978
979             // perform auto-detect of encoding if necessary
980
if (encoding == null) {
981                 // read first four bytes and determine encoding
982
final byte[] b4 = new byte[4];
983                 int count = 0;
984                 for (; count<4; count++ ) {
985                     b4[count] = (byte)stream.read();
986                 }
987                 if (count == 4) {
988                     Object JavaDoc [] encodingDesc = getEncodingName(b4, count);
989                     encoding = (String JavaDoc)(encodingDesc[0]);
990                     isBigEndian = (Boolean JavaDoc)(encodingDesc[1]);
991
992                     stream.reset();
993                     int offset = 0;
994                     // Special case UTF-8 files with BOM created by Microsoft
995
// tools. It's more efficient to consume the BOM than make
996
// the reader perform extra checks. -Ac
997
if (count > 2 && encoding.equals("UTF-8")) {
998                         int b0 = b4[0] & 0xFF;
999                         int b1 = b4[1] & 0xFF;
1000                        int b2 = b4[2] & 0xFF;
1001                        if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF) {
1002                            // ignore first three bytes...
1003
stream.skip(3);
1004                        }
1005                    }
1006                    reader = createReader(stream, encoding, isBigEndian);
1007                }
1008                else {
1009                    reader = createReader(stream, encoding, isBigEndian);
1010                }
1011            }
1012
1013            // use specified encoding
1014
else {
1015                encoding = encoding.toUpperCase(Locale.ENGLISH);
1016                
1017                // If encoding is UTF-8, consume BOM if one is present.
1018
if (encoding.equals("UTF-8")) {
1019                    final int[] b3 = new int[3];
1020                    int count = 0;
1021                    for (; count < 3; ++count) {
1022                        b3[count] = stream.read();
1023                        if (b3[count] == -1)
1024                            break;
1025                    }
1026                    if (count == 3) {
1027                        if (b3[0] != 0xEF || b3[1] != 0xBB || b3[2] != 0xBF) {
1028                            // First three bytes are not BOM, so reset.
1029
stream.reset();
1030                        }
1031                    }
1032                    else {
1033                        stream.reset();
1034                    }
1035                    reader = createReader(stream, encoding, isBigEndian);
1036                }
1037                // If encoding is UTF-16, we still need to read the first four bytes
1038
// in order to discover the byte order.
1039
else if (encoding.equals("UTF-16")) {
1040                    final int[] b4 = new int[4];
1041                    int count = 0;
1042                    for (; count < 4; ++count) {
1043                        b4[count] = stream.read();
1044                        if (b4[count] == -1)
1045                            break;
1046                    }
1047                    stream.reset();
1048                    
1049                    String JavaDoc utf16Encoding = "UTF-16";
1050                    if (count >= 2) {
1051                        final int b0 = b4[0];
1052                        final int b1 = b4[1];
1053                        if (b0 == 0xFE && b1 == 0xFF) {
1054                            // UTF-16, big-endian
1055
utf16Encoding = "UTF-16BE";
1056                            isBigEndian = Boolean.TRUE;
1057                        }
1058                        else if (b0 == 0xFF && b1 == 0xFE) {
1059                            // UTF-16, little-endian
1060
utf16Encoding = "UTF-16LE";
1061                            isBigEndian = Boolean.FALSE;
1062                        }
1063                        else if (count == 4) {
1064                            final int b2 = b4[2];
1065                            final int b3 = b4[3];
1066                            if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x3F) {
1067                                // UTF-16, big-endian, no BOM
1068
utf16Encoding = "UTF-16BE";
1069                                isBigEndian = Boolean.TRUE;
1070                            }
1071                            if (b0 == 0x3C && b1 == 0x00 && b2 == 0x3F && b3 == 0x00) {
1072                                // UTF-16, little-endian, no BOM
1073
utf16Encoding = "UTF-16LE";
1074                                isBigEndian = Boolean.FALSE;
1075                            }
1076                        }
1077                    }
1078                    reader = createReader(stream, utf16Encoding, isBigEndian);
1079                }
1080                // If encoding is UCS-4, we still need to read the first four bytes
1081
// in order to discover the byte order.
1082
else if (encoding.equals("ISO-10646-UCS-4")) {
1083                    final int[] b4 = new int[4];
1084                    int count = 0;
1085                    for (; count < 4; ++count) {
1086                        b4[count] = stream.read();
1087                        if (b4[count] == -1)
1088                            break;
1089                    }
1090                    stream.reset();
1091
1092                    // Ignore unusual octet order for now.
1093
if (count == 4) {
1094                        // UCS-4, big endian (1234)
1095
if (b4[0] == 0x00 && b4[1] == 0x00 && b4[2] == 0x00 && b4[3] == 0x3C) {
1096                            isBigEndian = Boolean.TRUE;
1097                        }
1098                        // UCS-4, little endian (1234)
1099
else if (b4[0] == 0x3C && b4[1] == 0x00 && b4[2] == 0x00 && b4[3] == 0x00) {
1100                            isBigEndian = Boolean.FALSE;
1101                        }
1102                    }
1103                    reader = createReader(stream, encoding, isBigEndian);
1104                }
1105                // If encoding is UCS-2, we still need to read the first four bytes
1106
// in order to discover the byte order.
1107
else if (encoding.equals("ISO-10646-UCS-2")) {
1108                    final int[] b4 = new int[4];
1109                    int count = 0;
1110                    for (; count < 4; ++count) {
1111                        b4[count] = stream.read();
1112                        if (b4[count] == -1)
1113                            break;
1114                    }
1115                    stream.reset();
1116
1117                    if (count == 4) {
1118                        // UCS-2, big endian
1119
if (b4[0] == 0x00 && b4[1] == 0x3C && b4[2] == 0x00 && b4[3] == 0x3F) {
1120                            isBigEndian = Boolean.TRUE;
1121                        }
1122                        // UCS-2, little endian
1123
else if (b4[0] == 0x3C && b4[1] == 0x00 && b4[2] == 0x3F && b4[3] == 0x00) {
1124                            isBigEndian = Boolean.FALSE;
1125                        }
1126                    }
1127                    reader = createReader(stream, encoding, isBigEndian);
1128                }
1129                else {
1130                    reader = createReader(stream, encoding, isBigEndian);
1131                }
1132            }
1133
1134            // read one character at a time so we don't jump too far
1135
// ahead, converting characters from the byte stream in
1136
// the wrong encoding
1137
if (DEBUG_ENCODINGS) {
1138                System.out.println("$$$ no longer wrapping reader in OneCharReader");
1139            }
1140            //reader = new OneCharReader(reader);
1141
}
1142
1143        // We've seen a new Reader.
1144
// Push it on the stack so we can close it later.
1145
fReaderStack.push(reader);
1146
1147        // push entity on stack
1148
if (fCurrentEntity != null) {
1149            fEntityStack.push(fCurrentEntity);
1150        }
1151
1152        // create entity
1153
fCurrentEntity = new ScannedEntity(name,
1154                new XMLResourceIdentifierImpl(publicId, literalSystemId, baseSystemId, expandedSystemId),
1155                stream, reader, encoding, literal, false, isExternal);
1156        fCurrentEntity.setEncodingExternallySpecified(encodingExternallySpecified);
1157        fEntityScanner.setCurrentEntity(fCurrentEntity);
1158        fResourceIdentifier.setValues(publicId, literalSystemId, baseSystemId, expandedSystemId);
1159        return encoding;
1160    } //setupCurrentEntity(String, XMLInputSource, boolean, boolean): String
1161

1162    // set version of scanner to use
1163
public void setScannerVersion(short version) {
1164        if(version == Constants.XML_VERSION_1_0) {
1165            if(fXML10EntityScanner == null) {
1166                fXML10EntityScanner = new XMLEntityScanner();
1167            }
1168            fXML10EntityScanner.reset(fSymbolTable, this, fErrorReporter);
1169            fEntityScanner = fXML10EntityScanner;
1170            fEntityScanner.setCurrentEntity(fCurrentEntity);
1171        } else {
1172            if(fXML11EntityScanner == null) {
1173                fXML11EntityScanner = new XML11EntityScanner();
1174            }
1175            fXML11EntityScanner.reset(fSymbolTable, this, fErrorReporter);
1176            fEntityScanner = fXML11EntityScanner;
1177            fEntityScanner.setCurrentEntity(fCurrentEntity);
1178        }
1179    } // setScannerVersion(short)
1180

1181    /** Returns the entity scanner. */
1182    public XMLEntityScanner getEntityScanner() {
1183        if(fEntityScanner == null) {
1184            // default to 1.0
1185
if(fXML10EntityScanner == null) {
1186                fXML10EntityScanner = new XMLEntityScanner();
1187            }
1188            fXML10EntityScanner.reset(fSymbolTable, this, fErrorReporter);
1189            fEntityScanner = fXML10EntityScanner;
1190        }
1191        return fEntityScanner;
1192    } // getEntityScanner():XMLEntityScanner
1193

1194    // A stack containing all the open readers
1195
protected Stack JavaDoc fReaderStack = new Stack JavaDoc();
1196
1197    /**
1198     * Close all opened InputStreams and Readers opened by this parser.
1199     */

1200    public void closeReaders() {
1201        // close all readers
1202
for (int i = fReaderStack.size()-1; i >= 0; i--) {
1203            try {
1204                ((Reader JavaDoc)fReaderStack.pop()).close();
1205            } catch (IOException JavaDoc e) {
1206                // ignore
1207
}
1208        }
1209    }
1210
1211    //
1212
// XMLComponent methods
1213
//
1214

1215    /**
1216     * Resets the component. The component can query the component manager
1217     * about any features and properties that affect the operation of the
1218     * component.
1219     *
1220     * @param componentManager The component manager.
1221     *
1222     * @throws SAXException Thrown by component on initialization error.
1223     * For example, if a feature or property is
1224     * required for the operation of the component, the
1225     * component manager may throw a
1226     * SAXNotRecognizedException or a
1227     * SAXNotSupportedException.
1228     */

1229    public void reset(XMLComponentManager componentManager)
1230        throws XMLConfigurationException {
1231            
1232        boolean parser_settings;
1233        try {
1234                parser_settings = componentManager.getFeature(PARSER_SETTINGS);
1235        } catch (XMLConfigurationException e) {
1236                parser_settings = true;
1237        }
1238
1239        if (!parser_settings) {
1240            // parser settings have not been changed
1241
reset();
1242            return;
1243        }
1244
1245        // sax features
1246
try {
1247            fValidation = componentManager.getFeature(VALIDATION);
1248        }
1249        catch (XMLConfigurationException e) {
1250            fValidation = false;
1251        }
1252        try {
1253            fExternalGeneralEntities = componentManager.getFeature(EXTERNAL_GENERAL_ENTITIES);
1254        }
1255        catch (XMLConfigurationException e) {
1256            fExternalGeneralEntities = true;
1257        }
1258        try {
1259            fExternalParameterEntities = componentManager.getFeature(EXTERNAL_PARAMETER_ENTITIES);
1260        }
1261        catch (XMLConfigurationException e) {
1262            fExternalParameterEntities = true;
1263        }
1264
1265        // xerces features
1266
try {
1267            fAllowJavaEncodings = componentManager.getFeature(ALLOW_JAVA_ENCODINGS);
1268        }
1269        catch (XMLConfigurationException e) {
1270            fAllowJavaEncodings = false;
1271        }
1272
1273        try {
1274            fWarnDuplicateEntityDef = componentManager.getFeature(WARN_ON_DUPLICATE_ENTITYDEF);
1275        }
1276        catch (XMLConfigurationException e) {
1277            fWarnDuplicateEntityDef = false;
1278        }
1279
1280        try {
1281            fStrictURI = componentManager.getFeature(STANDARD_URI_CONFORMANT);
1282        }
1283        catch (XMLConfigurationException e) {
1284            fStrictURI = false;
1285        }
1286
1287        // xerces properties
1288
fSymbolTable = (SymbolTable)componentManager.getProperty(SYMBOL_TABLE);
1289        fErrorReporter = (XMLErrorReporter)componentManager.getProperty(ERROR_REPORTER);
1290        try {
1291            fEntityResolver = (XMLEntityResolver)componentManager.getProperty(ENTITY_RESOLVER);
1292        }
1293        catch (XMLConfigurationException e) {
1294            fEntityResolver = null;
1295        }
1296        try {
1297            fValidationManager = (ValidationManager)componentManager.getProperty(VALIDATION_MANAGER);
1298        }
1299        catch (XMLConfigurationException e) {
1300            fValidationManager = null;
1301        }
1302        try {
1303            fSecurityManager = (SecurityManager JavaDoc)componentManager.getProperty(SECURITY_MANAGER);
1304        }
1305        catch (XMLConfigurationException e) {
1306            fSecurityManager = null;
1307        }
1308
1309        // reset general state
1310
reset();
1311        
1312    } // reset(XMLComponentManager)
1313

1314    // reset general state. Should not be called other than by
1315
// a class acting as a component manager but not
1316
// implementing that interface for whatever reason.
1317
public void reset() {
1318        fEntityExpansionLimit = (fSecurityManager != null)?fSecurityManager.getEntityExpansionLimit():0;
1319
1320        // initialize state
1321
fStandalone = false;
1322        fEntities.clear();
1323        fEntityStack.removeAllElements();
1324        fEntityExpansionCount = 0;
1325
1326        fCurrentEntity = null;
1327        // reset scanner
1328
if(fXML10EntityScanner != null){
1329            fXML10EntityScanner.reset(fSymbolTable, this, fErrorReporter);
1330        }
1331        if(fXML11EntityScanner != null) {
1332            fXML11EntityScanner.reset(fSymbolTable, this, fErrorReporter);
1333        }
1334
1335        // DEBUG
1336
if (DEBUG_ENTITIES) {
1337            addInternalEntity("text", "Hello, World.");
1338            addInternalEntity("empty-element", "<foo/>");
1339            addInternalEntity("balanced-element", "<foo></foo>");
1340            addInternalEntity("balanced-element-with-text", "<foo>Hello, World</foo>");
1341            addInternalEntity("balanced-element-with-entity", "<foo>&text;</foo>");
1342            addInternalEntity("unbalanced-entity", "<foo>");
1343            addInternalEntity("recursive-entity", "<foo>&recursive-entity2;</foo>");
1344            addInternalEntity("recursive-entity2", "<bar>&recursive-entity3;</bar>");
1345            addInternalEntity("recursive-entity3", "<baz>&recursive-entity;</baz>");
1346            try {
1347                addExternalEntity("external-text", null, "external-text.ent", "test/external-text.xml");
1348                addExternalEntity("external-balanced-element", null, "external-balanced-element.ent", "test/external-balanced-element.xml");
1349                addExternalEntity("one", null, "ent/one.ent", "test/external-entity.xml");
1350                addExternalEntity("two", null, "ent/two.ent", "test/ent/one.xml");
1351            }
1352            catch (IOException JavaDoc ex) {
1353                // should never happen
1354
}
1355        }
1356
1357        // copy declared entities
1358
if (fDeclaredEntities != null) {
1359            java.util.Enumeration JavaDoc keys = fDeclaredEntities.keys();
1360            while (keys.hasMoreElements()) {
1361                Object JavaDoc key = keys.nextElement();
1362                Object JavaDoc value = fDeclaredEntities.get(key);
1363                fEntities.put(key, value);
1364            }
1365        }
1366        fEntityHandler = null;
1367
1368    } // reset(XMLComponentManager)
1369

1370    /**
1371     * Returns a list of feature identifiers that are recognized by
1372     * this component. This method may return null if no features
1373     * are recognized by this component.
1374     */

1375    public String JavaDoc[] getRecognizedFeatures() {
1376        return (String JavaDoc[])(RECOGNIZED_FEATURES.clone());
1377    } // getRecognizedFeatures():String[]
1378

1379    /**
1380     * Sets the state of a feature. This method is called by the component
1381     * manager any time after reset when a feature changes state.
1382     * <p>
1383     * <strong>Note:</strong> Components should silently ignore features
1384     * that do not affect the operation of the component.
1385     *
1386     * @param featureId The feature identifier.
1387     * @param state The state of the feature.
1388     *
1389     * @throws SAXNotRecognizedException The component should not throw
1390     * this exception.
1391     * @throws SAXNotSupportedException The component should not throw
1392     * this exception.
1393     */

1394    public void setFeature(String JavaDoc featureId, boolean state)
1395        throws XMLConfigurationException {
1396
1397        // xerces features
1398
if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) {
1399            final int suffixLength = featureId.length() - Constants.XERCES_FEATURE_PREFIX.length();
1400            if (suffixLength == Constants.ALLOW_JAVA_ENCODINGS_FEATURE.length() &&
1401                featureId.endsWith(Constants.ALLOW_JAVA_ENCODINGS_FEATURE)) {
1402                fAllowJavaEncodings = state;
1403            }
1404        }
1405
1406    } // setFeature(String,boolean)
1407

1408    /**
1409     * Returns a list of property identifiers that are recognized by
1410     * this component. This method may return null if no properties
1411     * are recognized by this component.
1412     */

1413    public String JavaDoc[] getRecognizedProperties() {
1414        return (String JavaDoc[])(RECOGNIZED_PROPERTIES.clone());
1415    } // getRecognizedProperties():String[]
1416

1417    /**
1418     * Sets the value of a property. This method is called by the component
1419     * manager any time after reset when a property changes value.
1420     * <p>
1421     * <strong>Note:</strong> Components should silently ignore properties
1422     * that do not affect the operation of the component.
1423     *
1424     * @param propertyId The property identifier.
1425     * @param value The value of the property.
1426     *
1427     * @throws SAXNotRecognizedException The component should not throw
1428     * this exception.
1429     * @throws SAXNotSupportedException The component should not throw
1430     * this exception.
1431     */

1432    public void setProperty(String JavaDoc propertyId, Object JavaDoc value)
1433        throws XMLConfigurationException {
1434
1435        // Xerces properties
1436
if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) {
1437            final int suffixLength = propertyId.length() - Constants.XERCES_PROPERTY_PREFIX.length();
1438            
1439            if (suffixLength == Constants.SYMBOL_TABLE_PROPERTY.length() &&
1440                propertyId.endsWith(Constants.SYMBOL_TABLE_PROPERTY)) {
1441                fSymbolTable = (SymbolTable)value;
1442                return;
1443            }
1444            if (suffixLength == Constants.ERROR_REPORTER_PROPERTY.length() &&
1445                propertyId.endsWith(Constants.ERROR_REPORTER_PROPERTY)) {
1446                fErrorReporter = (XMLErrorReporter)value;
1447                return;
1448            }
1449            if (suffixLength == Constants.ENTITY_RESOLVER_PROPERTY.length() &&
1450                propertyId.endsWith(Constants.ENTITY_RESOLVER_PROPERTY)) {
1451                fEntityResolver = (XMLEntityResolver)value;
1452                return;
1453            }
1454            if (suffixLength == Constants.BUFFER_SIZE_PROPERTY.length() &&
1455                propertyId.endsWith(Constants.BUFFER_SIZE_PROPERTY)) {
1456                Integer JavaDoc bufferSize = (Integer JavaDoc)value;
1457                if (bufferSize != null &&
1458                    bufferSize.intValue() > DEFAULT_XMLDECL_BUFFER_SIZE) {
1459                    fBufferSize = bufferSize.intValue();
1460                    fEntityScanner.setBufferSize(fBufferSize);
1461                    fBufferPool.setExternalBufferSize(fBufferSize);
1462                }
1463            }
1464            if (suffixLength == Constants.SECURITY_MANAGER_PROPERTY.length() &&
1465                propertyId.endsWith(Constants.SECURITY_MANAGER_PROPERTY)) {
1466                fSecurityManager = (SecurityManager JavaDoc)value;
1467                fEntityExpansionLimit = (fSecurityManager != null)?fSecurityManager.getEntityExpansionLimit():0;
1468            }
1469        }
1470
1471    } // setProperty(String,Object)
1472

1473    /**
1474     * Returns the default state for a feature, or null if this
1475     * component does not want to report a default value for this
1476     * feature.
1477     *
1478     * @param featureId The feature identifier.
1479     *
1480     * @since Xerces 2.2.0
1481     */

1482    public Boolean JavaDoc getFeatureDefault(String JavaDoc featureId) {
1483        for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
1484            if (RECOGNIZED_FEATURES[i].equals(featureId)) {
1485                return FEATURE_DEFAULTS[i];
1486            }
1487        }
1488        return null;
1489    } // getFeatureDefault(String):Boolean
1490

1491    /**
1492     * Returns the default state for a property, or null if this
1493     * component does not want to report a default value for this
1494     * property.
1495     *
1496     * @param propertyId The property identifier.
1497     *
1498     * @since Xerces 2.2.0
1499     */

1500    public Object JavaDoc getPropertyDefault(String JavaDoc propertyId) {
1501        for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
1502            if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
1503                return PROPERTY_DEFAULTS[i];
1504            }
1505        }
1506        return null;
1507    } // getPropertyDefault(String):Object
1508

1509    //
1510
// Public static methods
1511
//
1512

1513    // current value of the "user.dir" property
1514
private static String JavaDoc gUserDir;
1515    // cached URI object for the current value of the escaped "user.dir" property stored as a URI
1516
private static URI gUserDirURI;
1517    // which ASCII characters need to be escaped
1518
private static boolean gNeedEscaping[] = new boolean[128];
1519    // the first hex character if a character needs to be escaped
1520
private static char gAfterEscaping1[] = new char[128];
1521    // the second hex character if a character needs to be escaped
1522
private static char gAfterEscaping2[] = new char[128];
1523    private static char[] gHexChs = {'0', '1', '2', '3', '4', '5', '6', '7',
1524                                     '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
1525    // initialize the above 3 arrays
1526
static {
1527        for (int i = 0; i <= 0x1f; i++) {
1528            gNeedEscaping[i] = true;
1529            gAfterEscaping1[i] = gHexChs[i >> 4];
1530            gAfterEscaping2[i] = gHexChs[i & 0xf];
1531        }
1532        gNeedEscaping[0x7f] = true;
1533        gAfterEscaping1[0x7f] = '7';
1534        gAfterEscaping2[0x7f] = 'F';
1535        char[] escChs = {' ', '<', '>', '#', '%', '"', '{', '}',
1536                         '|', '\\', '^', '~', '[', ']', '`'};
1537        int len = escChs.length;
1538        char ch;
1539        for (int i = 0; i < len; i++) {
1540            ch = escChs[i];
1541            gNeedEscaping[ch] = true;
1542            gAfterEscaping1[ch] = gHexChs[ch >> 4];
1543            gAfterEscaping2[ch] = gHexChs[ch & 0xf];
1544        }
1545    }
1546    // To escape the "user.dir" system property, by using %HH to represent
1547
// special ASCII characters: 0x00~0x1F, 0x7F, ' ', '<', '>', '#', '%'
1548
// and '"'. It's a static method, so needs to be synchronized.
1549
// this method looks heavy, but since the system property isn't expected
1550
// to change often, so in most cases, we only need to return the URI
1551
// that was escaped before.
1552
// According to the URI spec, non-ASCII characters (whose value >= 128)
1553
// need to be escaped too.
1554
// REVISIT: don't know how to escape non-ASCII characters, especially
1555
// which encoding to use. Leave them for now.
1556
private static synchronized URI getUserDir() throws URI.MalformedURIException {
1557        // get the user.dir property
1558
String JavaDoc userDir = "";
1559        try {
1560            userDir = System.getProperty("user.dir");
1561        }
1562        catch (SecurityException JavaDoc se) {
1563        }
1564
1565        // return empty string if property value is empty string.
1566
if (userDir.length() == 0)
1567            return new URI("file", "", "", null, null);
1568        
1569        // compute the new escaped value if the new property value doesn't
1570
// match the previous one
1571
if (gUserDirURI != null && userDir.equals(gUserDir)) {
1572            return gUserDirURI;
1573        }
1574
1575        // record the new value as the global property value
1576
gUserDir = userDir;
1577
1578        char separator = java.io.File.separatorChar;
1579        userDir = userDir.replace(separator, '/');
1580
1581        int len = userDir.length(), ch;
1582        StringBuffer JavaDoc buffer = new StringBuffer JavaDoc(len*3);
1583        // change C:/blah to /C:/blah
1584
if (len >= 2 && userDir.charAt(1) == ':') {
1585            ch = Character.toUpperCase(userDir.charAt(0));
1586            if (ch >= 'A' && ch <= 'Z') {
1587                buffer.append('/');
1588            }
1589        }
1590
1591        // for each character in the path
1592
int i = 0;
1593        for (; i < len; i++) {
1594            ch = userDir.charAt(i);
1595            // if it's not an ASCII character, break here, and use UTF-8 encoding
1596
if (ch >= 128)
1597                break;
1598            if (gNeedEscaping[ch]) {
1599                buffer.append('%');
1600                buffer.append(gAfterEscaping1[ch]);
1601                buffer.append(gAfterEscaping2[ch]);
1602                // record the fact that it's escaped
1603
}
1604            else {
1605                buffer.append((char)ch);
1606            }
1607        }
1608
1609        // we saw some non-ascii character
1610
if (i < len) {
1611            // get UTF-8 bytes for the remaining sub-string
1612
byte[] bytes = null;
1613            byte b;
1614            try {
1615                bytes = userDir.substring(i).getBytes("UTF-8");
1616            } catch (java.io.UnsupportedEncodingException JavaDoc e) {
1617                // should never happen
1618
return new URI("file", "", userDir, null, null);
1619            }
1620            len = bytes.length;
1621
1622            // for each byte
1623
for (i = 0; i < len; i++) {
1624                b = bytes[i];
1625                // for non-ascii character: make it positive, then escape
1626
if (b < 0) {
1627                    ch = b + 256;
1628                    buffer.append('%');
1629                    buffer.append(gHexChs[ch >> 4]);
1630                    buffer.append(gHexChs[ch & 0xf]);
1631                }
1632                else if (gNeedEscaping[b]) {
1633                    buffer.append('%');
1634                    buffer.append(gAfterEscaping1[b]);
1635                    buffer.append(gAfterEscaping2[b]);
1636                }
1637                else {
1638                    buffer.append((char)b);
1639                }
1640            }
1641        }
1642
1643        // change blah/blah to blah/blah/
1644
if (!userDir.endsWith("/"))
1645            buffer.append('/');
1646        
1647        gUserDirURI = new URI("file", "", buffer.toString(), null, null);
1648
1649        return gUserDirURI;
1650    }
1651    
1652    /**
1653     * Absolutizes a URI using the current value
1654     * of the "user.dir" property as the base URI. If
1655     * the URI is already absolute, this is a no-op.
1656     *
1657     * @param uri the URI to absolutize
1658     */

1659    public static void absolutizeAgainstUserDir(URI uri)
1660        throws URI.MalformedURIException {
1661        uri.absolutize(getUserDir());
1662    }
1663
1664    /**
1665     * Expands a system id and returns the system id as a URI, if
1666     * it can be expanded. A return value of null means that the
1667     * identifier is already expanded. An exception thrown
1668     * indicates a failure to expand the id.
1669     *
1670     * @param systemId The systemId to be expanded.
1671     *
1672     * @return Returns the URI string representing the expanded system
1673     * identifier. A null value indicates that the given
1674     * system identifier is already expanded.
1675     *
1676     */

1677    public static String JavaDoc expandSystemId(String JavaDoc systemId, String JavaDoc baseSystemId,
1678                                        boolean strict)
1679            throws URI.MalformedURIException {
1680
1681        // check if there is a system id before
1682
// trying to expand it.
1683
if (systemId == null) {
1684            return null;
1685        }
1686        
1687        // system id has to be a valid URI
1688
if (strict) {
1689            return expandSystemIdStrictOn(systemId, baseSystemId);
1690        }
1691
1692        // Assume the URIs are well-formed. If it turns out they're not, try fixing them up.
1693
try {
1694            return expandSystemIdStrictOff(systemId, baseSystemId);
1695        }
1696        catch (URI.MalformedURIException e) {
1697            // continue on...
1698
}
1699        
1700        // check for bad parameters id
1701
if (systemId.length() == 0) {
1702            return systemId;
1703        }
1704        
1705        // normalize id
1706
String JavaDoc id = fixURI(systemId);
1707
1708        // normalize base
1709
URI base = null;
1710        URI uri = null;
1711        try {
1712            if (baseSystemId == null || baseSystemId.length() == 0 ||
1713                baseSystemId.equals(systemId)) {
1714                base = getUserDir();
1715            }
1716            else {
1717                try {
1718                    base = new URI(fixURI(baseSystemId).trim());
1719                }
1720                catch (URI.MalformedURIException e) {
1721                    if (baseSystemId.indexOf(':') != -1) {
1722                        // for xml schemas we might have baseURI with
1723
// a specified drive
1724
base = new URI("file", "", fixURI(baseSystemId).trim(), null, null);
1725                    }
1726                    else {
1727                        base = new URI(getUserDir(), fixURI(baseSystemId));
1728                    }
1729                }
1730             }
1731             // expand id
1732
uri = new URI(base, id.trim());
1733        }
1734        catch (Exception JavaDoc e) {
1735            // let it go through
1736

1737        }
1738
1739        if (uri == null) {
1740            return systemId;
1741        }
1742        return uri.toString();
1743
1744    } // expandSystemId(String,String,boolean):String
1745

1746    /**
1747     * Helper method for expandSystemId(String,String,boolean):String
1748     */

1749    private static String JavaDoc expandSystemIdStrictOn(String JavaDoc systemId, String JavaDoc baseSystemId)
1750        throws URI.MalformedURIException {
1751        
1752        URI systemURI = new URI(systemId, true);
1753        // If it's already an absolute one, return it
1754
if (systemURI.isAbsoluteURI()) {
1755            return systemId;
1756        }
1757        
1758        // If there isn't a base URI, use the working directory
1759
URI baseURI = null;
1760        if (baseSystemId == null || baseSystemId.length() == 0) {
1761            baseURI = getUserDir();
1762        }
1763        else {
1764            baseURI = new URI(baseSystemId, true);
1765            if (!baseURI.isAbsoluteURI()) {
1766                // assume "base" is also a relative uri
1767
baseURI.absolutize(getUserDir());
1768            }
1769        }
1770        
1771        // absolutize the system identifier using the base URI
1772
systemURI.absolutize(baseURI);
1773        
1774        // return the string rep of the new uri (an absolute one)
1775
return systemURI.toString();
1776        
1777        // if any exception is thrown, it'll get thrown to the caller.
1778

1779    } // expandSystemIdStrictOn(String,String):String
1780

1781    /**
1782     * Helper method for expandSystemId(String,String,boolean):String
1783     */

1784    private static String JavaDoc expandSystemIdStrictOff(String JavaDoc systemId, String JavaDoc baseSystemId)
1785        throws URI.MalformedURIException {
1786        
1787        URI systemURI = new URI(systemId, true);
1788        // If it's already an absolute one, return it
1789
if (systemURI.isAbsoluteURI()) {
1790            if (systemURI.getScheme().length() > 1) {
1791                return systemId;
1792            }
1793            /**
1794             * If the scheme's length is only one character,
1795             * it's likely that this was intended as a file
1796             * path. Fixing this up in expandSystemId to
1797             * maintain backwards compatibility.
1798             */

1799            throw new URI.MalformedURIException();
1800        }
1801        
1802        // If there isn't a base URI, use the working directory
1803
URI baseURI = null;
1804        if (baseSystemId == null || baseSystemId.length() == 0) {
1805            baseURI = getUserDir();
1806        }
1807        else {
1808            baseURI = new URI(baseSystemId, true);
1809            if (!baseURI.isAbsoluteURI()) {
1810                // assume "base" is also a relative uri
1811
baseURI.absolutize(getUserDir());
1812            }
1813        }
1814        
1815        // absolutize the system identifier using the base URI
1816
systemURI.absolutize(baseURI);
1817        
1818        // return the string rep of the new uri (an absolute one)
1819
return systemURI.toString();
1820        
1821        // if any exception is thrown, it'll get thrown to the caller.
1822

1823    } // expandSystemIdStrictOff(String,String):String
1824

1825    /**
1826     * Attempt to set whether redirects will be followed for an <code>HttpURLConnection</code>.
1827     * This may fail on earlier JDKs which do not support setting this preference.
1828     */

1829    public static void setInstanceFollowRedirects(HttpURLConnection JavaDoc urlCon, boolean followRedirects) {
1830        try {
1831            Method JavaDoc method = HttpURLConnection JavaDoc.class.getMethod("setInstanceFollowRedirects", new Class JavaDoc[] {Boolean.TYPE});
1832            method.invoke(urlCon, new Object JavaDoc[] {followRedirects ? Boolean.TRUE : Boolean.FALSE});
1833        }
1834        // setInstanceFollowRedirects doesn't exist.
1835
catch (Exception JavaDoc exc) {}
1836    }
1837
1838    //
1839
// Protected methods
1840
//
1841

1842    /**
1843     * Ends an entity.
1844     *
1845     * @throws XNIException Thrown by entity handler to signal an error.
1846     */

1847    void endEntity() throws XNIException {
1848
1849        // call handler
1850
if (DEBUG_BUFFER) {
1851            System.out.print("(endEntity: ");
1852            print(fCurrentEntity);
1853            System.out.println();
1854        }
1855        if (fEntityHandler != null) {
1856            fEntityHandler.endEntity(fCurrentEntity.name, null);
1857        }
1858        
1859        // Close the reader for the current entity once we're
1860
// done with it, and remove it from our stack. If parsing
1861
// is halted at some point, the rest of the readers on
1862
// the stack will be closed during cleanup.
1863
try {
1864            fCurrentEntity.reader.close();
1865        }
1866        catch (IOException JavaDoc e) {
1867            // ignore
1868
}
1869        // REVISIT: We should never encounter underflow if the calls
1870
// to startEntity and endEntity are balanced, but guard
1871
// against the EmptyStackException for now. -- mrglavas
1872
if(!fReaderStack.isEmpty()) {
1873            fReaderStack.pop();
1874        }
1875
1876        //Release the character buffer back to the pool for reuse
1877
fBufferPool.returnToPool(fCurrentEntity.fBuffer);
1878        
1879        // Pop entity stack.
1880
fCurrentEntity = fEntityStack.size() > 0
1881                       ? (ScannedEntity)fEntityStack.pop() : null;
1882        fEntityScanner.setCurrentEntity(fCurrentEntity);
1883        if (DEBUG_BUFFER) {
1884            System.out.print(")endEntity: ");
1885            print(fCurrentEntity);
1886            System.out.println();
1887        }
1888
1889    } // endEntity()
1890

1891    /**
1892     * Returns the IANA encoding name that is auto-detected from
1893     * the bytes specified, with the endian-ness of that encoding where appropriate.
1894     *
1895     * @param b4 The first four bytes of the input.
1896     * @param count The number of bytes actually read.
1897     * @return a 2-element array: the first element, an IANA-encoding string,
1898     * the second element a Boolean which is true iff the document is big endian, false
1899     * if it's little-endian, and null if the distinction isn't relevant.
1900     */

1901    protected Object JavaDoc[] getEncodingName(byte[] b4, int count) {
1902
1903        if (count < 2) {
1904            return new Object JavaDoc[]{"UTF-8", null};
1905        }
1906
1907        // UTF-16, with BOM
1908
int b0 = b4[0] & 0xFF;
1909        int b1 = b4[1] & 0xFF;
1910        if (b0 == 0xFE && b1 == 0xFF) {
1911            // UTF-16, big-endian
1912
return new Object JavaDoc [] {"UTF-16BE", Boolean.TRUE};
1913        }
1914        if (b0 == 0xFF && b1 == 0xFE) {
1915            // UTF-16, little-endian
1916
return new Object JavaDoc [] {"UTF-16LE", Boolean.FALSE};
1917        }
1918
1919        // default to UTF-8 if we don't have enough bytes to make a
1920
// good determination of the encoding
1921
if (count < 3) {
1922            return new Object JavaDoc [] {"UTF-8", null};
1923        }
1924
1925        // UTF-8 with a BOM
1926
int b2 = b4[2] & 0xFF;
1927        if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF) {
1928            return new Object JavaDoc [] {"UTF-8", null};
1929        }
1930
1931        // default to UTF-8 if we don't have enough bytes to make a
1932
// good determination of the encoding
1933
if (count < 4) {
1934            return new Object JavaDoc [] {"UTF-8", null};
1935        }
1936
1937        // other encodings
1938
int b3 = b4[3] & 0xFF;
1939        if (b0 == 0x00 && b1 == 0x00 && b2 == 0x00 && b3 == 0x3C) {
1940            // UCS-4, big endian (1234)
1941
return new Object JavaDoc [] {"ISO-10646-UCS-4", Boolean.TRUE};
1942        }
1943        if (b0 == 0x3C && b1 == 0x00 && b2 == 0x00 && b3 == 0x00) {
1944            // UCS-4, little endian (4321)
1945
return new Object JavaDoc [] {"ISO-10646-UCS-4", Boolean.FALSE};
1946        }
1947        if (b0 == 0x00 && b1 == 0x00 && b2 == 0x3C && b3 == 0x00) {
1948            // UCS-4, unusual octet order (2143)
1949
// REVISIT: What should this be?
1950
return new Object JavaDoc [] {"ISO-10646-UCS-4", null};
1951        }
1952        if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x00) {
1953            // UCS-4, unusual octect order (3412)
1954
// REVISIT: What should this be?
1955
return new Object JavaDoc [] {"ISO-10646-UCS-4", null};
1956        }
1957        if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x3F) {
1958            // UTF-16, big-endian, no BOM
1959
// (or could turn out to be UCS-2...
1960
// REVISIT: What should this be?
1961
return new Object JavaDoc [] {"UTF-16BE", Boolean.TRUE};
1962        }
1963        if (b0 == 0x3C && b1 == 0x00 && b2 == 0x3F && b3 == 0x00) {
1964            // UTF-16, little-endian, no BOM
1965
// (or could turn out to be UCS-2...
1966
return new Object JavaDoc [] {"UTF-16LE", Boolean.FALSE};
1967        }
1968        if (b0 == 0x4C && b1 == 0x6F && b2 == 0xA7 && b3 == 0x94) {
1969            // EBCDIC
1970
// a la xerces1, return CP037 instead of EBCDIC here
1971
return new Object JavaDoc [] {"CP037", null};
1972        }
1973
1974        // default encoding
1975
return new Object JavaDoc [] {"UTF-8", null};
1976
1977    } // getEncodingName(byte[],int):Object[]
1978

1979    /**
1980     * Creates a reader capable of reading the given input stream in
1981     * the specified encoding.
1982     *
1983     * @param inputStream The input stream.
1984     * @param encoding The encoding name that the input stream is
1985     * encoded using. If the user has specified that
1986     * Java encoding names are allowed, then the
1987     * encoding name may be a Java encoding name;
1988     * otherwise, it is an ianaEncoding name.
1989     * @param isBigEndian For encodings (like uCS-4), whose names cannot
1990     * specify a byte order, this tells whether the order is bigEndian. null menas
1991     * unknown or not relevant.
1992     *
1993     * @return Returns a reader.
1994     */

1995    protected Reader JavaDoc createReader(InputStream JavaDoc inputStream, String JavaDoc encoding, Boolean JavaDoc isBigEndian)
1996        throws IOException JavaDoc {
1997
1998        // normalize encoding name
1999
if (encoding == null) {
2000            encoding = "UTF-8";
2001        }
2002
2003        // try to use an optimized reader
2004
String JavaDoc ENCODING = encoding.toUpperCase(Locale.ENGLISH);
2005        if (ENCODING.equals("UTF-8")) {
2006            if (DEBUG_ENCODINGS) {
2007                System.out.println("$$$ creating UTF8Reader");
2008            }
2009            return new UTF8Reader(inputStream, fBufferSize, fErrorReporter.getMessageFormatter(XMLMessageFormatter.XML_DOMAIN), fErrorReporter.getLocale() );
2010        }
2011        if(ENCODING.equals("ISO-10646-UCS-4")) {
2012            if(isBigEndian != null) {
2013                boolean isBE = isBigEndian.booleanValue();
2014                if(isBE) {
2015                    return new UCSReader(inputStream, UCSReader.UCS4BE);
2016                } else {
2017                    return new UCSReader(inputStream, UCSReader.UCS4LE);
2018                }
2019            } else {
2020                fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
2021                                       "EncodingByteOrderUnsupported",
2022                                       new Object JavaDoc[] { encoding },
2023                                       XMLErrorReporter.SEVERITY_FATAL_ERROR);
2024            }
2025        }
2026        if(ENCODING.equals("ISO-10646-UCS-2")) {
2027            if(isBigEndian != null) { // sould never happen with this encoding...
2028
boolean isBE = isBigEndian.booleanValue();
2029                if(isBE) {
2030                    return new UCSReader(inputStream, UCSReader.UCS2BE);
2031                } else {
2032                    return new UCSReader(inputStream, UCSReader.UCS2LE);
2033                }
2034            } else {
2035                fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
2036                                       "EncodingByteOrderUnsupported",
2037                                       new Object JavaDoc[] { encoding },
2038                                       XMLErrorReporter.SEVERITY_FATAL_ERROR);
2039            }
2040        }
2041
2042        // check for valid name
2043
boolean validIANA = XMLChar.isValidIANAEncoding(encoding);
2044        boolean validJava = XMLChar.isValidJavaEncoding(encoding);
2045        if (!validIANA || (fAllowJavaEncodings && !validJava)) {
2046            fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
2047                                       "EncodingDeclInvalid",
2048                                       new Object JavaDoc[] { encoding },
2049                                       XMLErrorReporter.SEVERITY_FATAL_ERROR);
2050            // NOTE: AndyH suggested that, on failure, we use ISO Latin 1
2051
// because every byte is a valid ISO Latin 1 character.
2052
// It may not translate correctly but if we failed on
2053
// the encoding anyway, then we're expecting the content
2054
// of the document to be bad. This will just prevent an
2055
// invalid UTF-8 sequence to be detected. This is only
2056
// important when continue-after-fatal-error is turned
2057
// on. -Ac
2058
encoding = "ISO-8859-1";
2059        }
2060
2061        // try to use a Java reader
2062
String JavaDoc javaEncoding = EncodingMap.getIANA2JavaMapping(ENCODING);
2063        if (javaEncoding == null) {
2064            if(fAllowJavaEncodings) {
2065            javaEncoding = encoding;
2066            } else {
2067                fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
2068                                       "EncodingDeclInvalid",
2069                                       new Object JavaDoc[] { encoding },
2070                                       XMLErrorReporter.SEVERITY_FATAL_ERROR);
2071                // see comment above.
2072
javaEncoding = "ISO8859_1";
2073            }
2074        }
2075        else if (javaEncoding.equals("ASCII")) {
2076            if (DEBUG_ENCODINGS) {
2077                System.out.println("$$$ creating ASCIIReader");
2078            }
2079            return new ASCIIReader(inputStream, fBufferSize, fErrorReporter.getMessageFormatter(XMLMessageFormatter.XML_DOMAIN), fErrorReporter.getLocale());
2080        }
2081        
2082        
2083        if (DEBUG_ENCODINGS) {
2084            System.out.print("$$$ creating Java InputStreamReader: encoding="+javaEncoding);
2085            if (javaEncoding == encoding) {
2086                System.out.print(" (IANA encoding)");
2087            }
2088            System.out.println();
2089        }
2090        return new InputStreamReader JavaDoc(inputStream, javaEncoding);
2091
2092    } // createReader(InputStream,String, Boolean): Reader
2093

2094    //
2095
// Protected static methods
2096
//
2097

2098    /**
2099     * Fixes a platform dependent filename to standard URI form.
2100     *
2101     * @param str The string to fix.
2102     *
2103     * @return Returns the fixed URI string.
2104     */

2105    protected static String JavaDoc fixURI(String JavaDoc str) {
2106
2107        // handle platform dependent strings
2108
str = str.replace(java.io.File.separatorChar, '/');
2109
2110        StringBuffer JavaDoc sb = null;
2111
2112        // Windows fix
2113
if (str.length() >= 2) {
2114            char ch1 = str.charAt(1);
2115            // change "C:blah" to "file:///C:blah"
2116
if (ch1 == ':') {
2117                char ch0 = Character.toUpperCase(str.charAt(0));
2118                if (ch0 >= 'A' && ch0 <= 'Z') {
2119                    sb = new StringBuffer JavaDoc(str.length() + 8);
2120                    sb.append("file:///");
2121                }
2122            }
2123            // change "//blah" to "file://blah"
2124
else if (ch1 == '/' && str.charAt(0) == '/') {
2125                sb = new StringBuffer JavaDoc(str.length() + 5);
2126                sb.append("file:");
2127            }
2128        }
2129
2130        int pos = str.indexOf(' ');
2131        // there is no space in the string
2132
// we just append "str" to the end of sb
2133
if (pos < 0) {
2134            if (sb != null) {
2135                sb.append(str);
2136                str = sb.toString();
2137            }
2138        }
2139        // otherwise, convert all ' ' to "%20".
2140
// Note: the following algorithm might not be very performant,
2141
// but people who want to use invalid URI's have to pay the price.
2142
else {
2143            if (sb == null)
2144                sb = new StringBuffer JavaDoc(str.length());
2145            // put characters before ' ' into the string buffer
2146
for (int i = 0; i < pos; i++)
2147                sb.append(str.charAt(i));
2148            // and %20 for the space
2149
sb.append("%20");
2150            // for the remamining part, also convert ' ' to "%20".
2151
for (int i = pos+1; i < str.length(); i++) {
2152                if (str.charAt(i) == ' ')
2153                    sb.append("%20");
2154                else
2155                    sb.append(str.charAt(i));
2156            }
2157            str = sb.toString();
2158        }
2159
2160        // done
2161
return str;
2162
2163    } // fixURI(String):String
2164

2165    //
2166
// Package visible methods
2167
//
2168

2169    /**
2170     * Returns the hashtable of declared entities.
2171     * <p>
2172     * <strong>REVISIT:</strong>
2173     * This should be done the "right" way by designing a better way to
2174     * enumerate the declared entities. For now, this method is needed
2175     * by the constructor that takes an XMLEntityManager parameter.
2176     */

2177    Hashtable JavaDoc getDeclaredEntities() {
2178        return fEntities;
2179    } // getDeclaredEntities():Hashtable
2180

2181    /** Prints the contents of the buffer. */
2182    static final void print(ScannedEntity currentEntity) {
2183        if (DEBUG_BUFFER) {
2184            if (currentEntity != null) {
2185                System.out.print('[');
2186                System.out.print(currentEntity.count);
2187                System.out.print(' ');
2188                System.out.print(currentEntity.position);
2189                if (currentEntity.count > 0) {
2190                    System.out.print(" \"");
2191                    for (int i = 0; i < currentEntity.count; i++) {
2192                        if (i == currentEntity.position) {
2193                            System.out.print('^');
2194                        }
2195                        char c = currentEntity.ch[i];
2196                        switch (c) {
2197                            case '\n': {
2198                                System.out.print("\\n");
2199                                break;
2200                            }
2201                            case '\r': {
2202                                System.out.print("\\r");
2203                                break;
2204                            }
2205                            case '\t': {
2206                                System.out.print("\\t");
2207                                break;
2208                            }
2209                            case '\\': {
2210                                System.out.print("\\\\");
2211                                break;
2212                            }
2213                            default: {
2214                                System.out.print(c);
2215                            }
2216                        }
2217                    }
2218                    if (currentEntity.position == currentEntity.count) {
2219                        System.out.print('^');
2220                    }
2221                    System.out.print('"');
2222                }
2223                System.out.print(']');
2224                System.out.print(" @ ");
2225                System.out.print(currentEntity.lineNumber);
2226                System.out.print(',');
2227                System.out.print(currentEntity.columnNumber);
2228            }
2229            else {
2230                System.out.print("*NO CURRENT ENTITY*");
2231            }
2232        }
2233    } // print(ScannedEntity)
2234

2235    //
2236
// Classes
2237
//
2238

2239    /**
2240     * Entity information.
2241     *
2242     * @xerces.internal
2243     *
2244     * @author Andy Clark, IBM
2245     */

2246    public static abstract class Entity {
2247
2248        //
2249
// Data
2250
//
2251

2252        /** Entity name. */
2253        public String JavaDoc name;
2254
2255        // whether this entity's declaration was found in the internal
2256
// or external subset
2257
public boolean inExternalSubset;
2258
2259        //
2260
// Constructors
2261
//
2262

2263        /** Default constructor. */
2264        public Entity() {
2265            clear();
2266        } // <init>()
2267

2268        /** Constructs an entity. */
2269        public Entity(String JavaDoc name, boolean inExternalSubset) {
2270            this.name = name;
2271            this.inExternalSubset = inExternalSubset;
2272        } // <init>(String)
2273

2274        //
2275
// Public methods
2276
//
2277

2278        /** Returns true if this entity was declared in the external subset. */
2279        public boolean isEntityDeclInExternalSubset () {
2280            return inExternalSubset;
2281        }
2282
2283        /** Returns true if this is an external entity. */
2284        public abstract boolean isExternal();
2285
2286        /** Returns true if this is an unparsed entity. */
2287        public abstract boolean isUnparsed();
2288
2289        /** Clears the entity. */
2290        public void clear() {
2291            name = null;
2292            inExternalSubset = false;
2293        } // clear()
2294

2295        /** Sets the values of the entity. */
2296        public void setValues(Entity entity) {
2297            name = entity.name;
2298            inExternalSubset = entity.inExternalSubset;
2299        } // setValues(Entity)
2300

2301    } // class Entity
2302

2303    /**
2304     * Internal entity.
2305     *
2306     * @xerces.internal
2307     *
2308     * @author Andy Clark, IBM
2309     */

2310    protected static class InternalEntity
2311        extends Entity {
2312
2313        //
2314
// Data
2315
//
2316

2317        /** Text value of entity. */
2318        public String JavaDoc text;
2319
2320        //
2321
// Constructors
2322
//
2323

2324        /** Default constructor. */
2325        public InternalEntity() {
2326            clear();
2327        } // <init>()
2328

2329        /** Constructs an internal entity. */
2330        public InternalEntity(String JavaDoc name, String JavaDoc text, boolean inExternalSubset) {
2331            super(name,inExternalSubset);
2332            this.text = text;
2333        } // <init>(String,String)
2334

2335        //
2336
// Entity methods
2337
//
2338

2339        /** Returns true if this is an external entity. */
2340        public final boolean isExternal() {
2341            return false;
2342        } // isExternal():boolean
2343

2344        /** Returns true if this is an unparsed entity. */
2345        public final boolean isUnparsed() {
2346            return false;
2347        } // isUnparsed():boolean
2348

2349        /** Clears the entity. */
2350        public void clear() {
2351            super.clear();
2352            text = null;
2353        } // clear()
2354

2355        /** Sets the values of the entity. */
2356        public void setValues(Entity entity) {
2357            super.setValues(entity);
2358            text = null;
2359        } // setValues(Entity)
2360

2361        /** Sets the values of the entity. */
2362        public void setValues(InternalEntity entity) {
2363            super.setValues(entity);
2364            text = entity.text;
2365        } // setValues(InternalEntity)
2366

2367    } // class InternalEntity
2368

2369    /**
2370     * External entity.
2371     *
2372     * @xerces.internal
2373     *
2374     * @author Andy Clark, IBM
2375     */

2376    protected static class ExternalEntity
2377        extends Entity {
2378
2379        //
2380
// Data
2381
//
2382

2383        /** container for all relevant entity location information. */
2384        public XMLResourceIdentifier entityLocation;
2385
2386        /** Notation name for unparsed entity. */
2387        public String JavaDoc notation;
2388
2389        //
2390
// Constructors
2391
//
2392

2393        /** Default constructor. */
2394        public ExternalEntity() {
2395            clear();
2396        } // <init>()
2397

2398        /** Constructs an internal entity. */
2399        public ExternalEntity(String JavaDoc name, XMLResourceIdentifier entityLocation,
2400                              String JavaDoc notation, boolean inExternalSubset) {
2401            super(name,inExternalSubset);
2402            this.entityLocation = entityLocation;
2403            this.notation = notation;
2404        } // <init>(String,XMLResourceIdentifier, String)
2405

2406        //
2407
// Entity methods
2408
//
2409

2410        /** Returns true if this is an external entity. */
2411        public final boolean isExternal() {
2412            return true;
2413        } // isExternal():boolean
2414

2415        /** Returns true if this is an unparsed entity. */
2416        public final boolean isUnparsed() {
2417            return notation != null;
2418        } // isUnparsed():boolean
2419

2420        /** Clears the entity. */
2421        public void clear() {
2422            super.clear();
2423            entityLocation = null;
2424            notation = null;
2425        } // clear()
2426

2427        /** Sets the values of the entity. */
2428        public void setValues(Entity entity) {
2429            super.setValues(entity);
2430            entityLocation = null;
2431            notation = null;
2432        } // setValues(Entity)
2433

2434        /** Sets the values of the entity. */
2435        public void setValues(ExternalEntity entity) {
2436            super.setValues(entity);
2437            entityLocation = entity.entityLocation;
2438            notation = entity.notation;
2439        } // setValues(ExternalEntity)
2440

2441    } // class ExternalEntity
2442

2443    /**
2444     * Entity state.
2445     *
2446     * @xerces.internal
2447     *
2448     * @author Andy Clark, IBM
2449     */

2450    public class ScannedEntity
2451        extends Entity {
2452
2453        //
2454
// Data
2455
//
2456

2457        // i/o
2458

2459        /** Input stream. */
2460        public InputStream JavaDoc stream;
2461
2462        /** Reader. */
2463        public Reader JavaDoc reader;
2464
2465        // locator information
2466

2467        /** entity location information */
2468        public XMLResourceIdentifier entityLocation;
2469
2470        /** Line number. */
2471        public int lineNumber = 1;
2472
2473        /** Column number. */
2474        public int columnNumber = 1;
2475
2476        // encoding
2477

2478        /** Auto-detected encoding. */
2479        public String JavaDoc encoding;
2480        
2481        /**
2482         * Encoding has been set externally, for example
2483         * using a SAX InputSource or a DOM LSInput.
2484         */

2485        boolean externallySpecifiedEncoding = false;
2486        
2487        // version
2488

2489        /** XML version. **/
2490        public String JavaDoc xmlVersion = "1.0";
2491        
2492        // status
2493

2494        /** True if in a literal. */
2495        public boolean literal;
2496
2497        // whether this is an external or internal scanned entity
2498
public boolean isExternal;
2499
2500        // buffer
2501

2502        /** Character buffer. */
2503        public char[] ch = null;
2504        
2505        /** Position in character buffer. */
2506        public int position;
2507        
2508        /** Base character offset for computing absolute character offset. */
2509        public int baseCharOffset;
2510        
2511        /** Start position in character buffer. */
2512        public int startPosition;
2513        
2514        /** Count of characters in buffer. */
2515        public int count;
2516
2517        // to allow the reader/inputStream to behave efficiently:
2518
public boolean mayReadChunks;
2519        
2520        /** Character buffer container. */
2521        private CharacterBuffer fBuffer;
2522        
2523
2524        //
2525
// Constructors
2526
//
2527

2528        /** Constructs a scanned entity. */
2529        public ScannedEntity(String JavaDoc name,
2530                             XMLResourceIdentifier entityLocation,
2531                             InputStream JavaDoc stream, Reader JavaDoc reader,
2532                             String JavaDoc encoding, boolean literal, boolean mayReadChunks, boolean isExternal) {
2533            super(name,XMLEntityManager.this.fInExternalSubset);
2534            this.entityLocation = entityLocation;
2535            this.stream = stream;
2536            this.reader = reader;
2537            this.encoding = encoding;
2538            this.literal = literal;
2539            this.mayReadChunks = mayReadChunks;
2540            this.isExternal = isExternal;
2541            this.fBuffer = fBufferPool.getBuffer(isExternal);
2542            this.ch = fBuffer.ch;
2543        } // <init>(StringXMLResourceIdentifier,InputStream,Reader,String,boolean, boolean)
2544

2545        //
2546
// Entity methods
2547
//
2548

2549        /** Returns true if this is an external entity. */
2550        public final boolean isExternal() {
2551            return isExternal;
2552        } // isExternal():boolean
2553

2554        /** Returns true if this is an unparsed entity. */
2555        public final boolean isUnparsed() {
2556            return false;
2557        } // isUnparsed():boolean
2558

2559        public void setReader(InputStream JavaDoc stream, String JavaDoc encoding, Boolean JavaDoc isBigEndian) throws IOException JavaDoc {
2560            reader = createReader(stream, encoding, isBigEndian);
2561        }
2562
2563        // return the expanded system ID of the
2564
// first external entity on the stack, null
2565
// otherwise.
2566
public String JavaDoc getExpandedSystemId() {
2567
2568            // search for the first external entity on the stack
2569
int size = fEntityStack.size();
2570            for (int i = size - 1; i >= 0 ; i--) {
2571               ScannedEntity externalEntity =
2572                    (ScannedEntity)fEntityStack.elementAt(i);
2573
2574                if (externalEntity.entityLocation != null &&
2575                        externalEntity.entityLocation.getExpandedSystemId() != null) {
2576                    return externalEntity.entityLocation.getExpandedSystemId();
2577                }
2578            }
2579            return null;
2580        }
2581
2582        // return literal systemId of
2583
// nearest external entity
2584
public String JavaDoc getLiteralSystemId() {
2585            // search for the first external entity on the stack
2586
int size = fEntityStack.size();
2587            for (int i = size - 1; i >= 0 ; i--) {
2588               ScannedEntity externalEntity =
2589                    (ScannedEntity)fEntityStack.elementAt(i);
2590
2591                if (externalEntity.entityLocation != null &&
2592                        externalEntity.entityLocation.getLiteralSystemId() != null) {
2593                    return externalEntity.entityLocation.getLiteralSystemId();
2594                }
2595            }
2596            return null;
2597        }
2598        
2599        // return line number of position in most
2600
// recent external entity
2601
public int getLineNumber() {
2602            // search for the first external entity on the stack
2603
int size = fEntityStack.size();
2604            for (int i=size-1; i>0 ; i--) {
2605                ScannedEntity firstExternalEntity = (ScannedEntity)fEntityStack.elementAt(i);
2606                if (firstExternalEntity.isExternal()) {
2607                    return firstExternalEntity.lineNumber;
2608                }
2609            }
2610            return -1;
2611        }
2612        
2613        // return column number of position in most
2614
// recent external entity
2615
public int getColumnNumber() {
2616            // search for the first external entity on the stack
2617
int size = fEntityStack.size();
2618            for (int i=size-1; i>0 ; i--) {
2619                ScannedEntity firstExternalEntity = (ScannedEntity)fEntityStack.elementAt(i);
2620                if (firstExternalEntity.isExternal()) {
2621                    return firstExternalEntity.columnNumber;
2622                }
2623            }
2624            return -1;
2625        }
2626        
2627        // return character offset of position in most
2628
// recent external entity
2629
public int getCharacterOffset() {
2630            // search for the first external entity on the stack
2631
int size = fEntityStack.size();
2632            for (int i=size-1; i>0 ; i--) {
2633                ScannedEntity firstExternalEntity = (ScannedEntity)fEntityStack.elementAt(i);
2634                if (firstExternalEntity.isExternal()) {
2635                    return firstExternalEntity.baseCharOffset + (firstExternalEntity.position - firstExternalEntity.startPosition);
2636                }
2637            }
2638            return -1;
2639        }
2640        
2641        // return encoding of most recent external entity
2642
public String JavaDoc getEncoding() {
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                ScannedEntity firstExternalEntity = (ScannedEntity)fEntityStack.elementAt(i);
2647                if (firstExternalEntity.isExternal()) {
2648                    return firstExternalEntity.encoding;
2649                }
2650            }
2651            return null;
2652        }
2653        
2654        // return xml version of most recent external entity
2655
public String JavaDoc getXMLVersion() {
2656            // search for the first external entity on the stack
2657
int size = fEntityStack.size();
2658            for (int i=size-1; i>0 ; i--) {
2659                ScannedEntity firstExternalEntity = (ScannedEntity)fEntityStack.elementAt(i);
2660                if (firstExternalEntity.isExternal()) {
2661                    return firstExternalEntity.xmlVersion;
2662                }
2663            }
2664            return null;
2665        }
2666        
2667        /** Returns whether the encoding of this entity was externally specified. **/
2668        public boolean isEncodingExternallySpecified() {
2669            return externallySpecifiedEncoding;
2670        }
2671        
2672        /** Sets whether the encoding of this entity was externally specified. **/
2673        public void setEncodingExternallySpecified(boolean value) {
2674            externallySpecifiedEncoding = value;
2675        }
2676        
2677        //
2678
// Object methods
2679
//
2680

2681        /** Returns a string representation of this object. */
2682        public String JavaDoc toString() {
2683            
2684            StringBuffer JavaDoc str = new StringBuffer JavaDoc();
2685            str.append("name=\""+name+'"');
2686            str.append(",ch=");
2687            str.append(ch);
2688            str.append(",position=" + position);
2689            str.append(",count=" + count);
2690            str.append(",baseCharOffset=" + baseCharOffset);
2691            str.append(",startPosition=" + startPosition);
2692            return str.toString();
2693            
2694        } // toString():String
2695

2696    } // class ScannedEntity
2697

2698    /**
2699     * Buffer used in entity manager to reuse character arrays instead
2700     * of creating new ones every time.
2701     *
2702     * @xerces.internal
2703     *
2704     * @author Ankit Pasricha, IBM
2705     */

2706    private static class CharacterBuffer {
2707
2708        /** character buffer */
2709        private char[] ch;
2710        
2711        /** whether the buffer is for an external or internal scanned entity */
2712        private boolean isExternal;
2713        
2714        public CharacterBuffer(boolean isExternal, int size) {
2715            this.isExternal = isExternal;
2716            ch = new char[size];
2717        }
2718    }
2719    
2720    /**
2721     * Stores a number of character buffers and provides it to the entity
2722     * manager to use when an entity is seen.
2723     *
2724     * @xerces.internal
2725     *
2726     * @author Ankit Pasricha, IBM
2727     */

2728    private static class CharacterBufferPool {
2729
2730        private static final int DEFAULT_POOL_SIZE = 3;
2731        
2732        private CharacterBuffer[] fInternalBufferPool;
2733        private CharacterBuffer[] fExternalBufferPool;
2734
2735        private int fExternalBufferSize;
2736        private int fInternalBufferSize;
2737        private int poolSize;
2738        
2739        private int fInternalTop;
2740        private int fExternalTop;
2741
2742        public CharacterBufferPool(int externalBufferSize, int internalBufferSize) {
2743            this(DEFAULT_POOL_SIZE, externalBufferSize, internalBufferSize);
2744        }
2745        
2746        public CharacterBufferPool(int poolSize, int externalBufferSize, int internalBufferSize) {
2747            fExternalBufferSize = externalBufferSize;
2748            fInternalBufferSize = internalBufferSize;
2749            this.poolSize = poolSize;
2750            init();
2751        }
2752        
2753        /** Initializes buffer pool. **/
2754        private void init() {
2755            fInternalBufferPool = new CharacterBuffer[poolSize];
2756            fExternalBufferPool = new CharacterBuffer[poolSize];
2757            fInternalTop = -1;
2758            fExternalTop = -1;
2759        }
2760
2761        /** Retrieves buffer from pool. **/
2762        public CharacterBuffer getBuffer(boolean external) {
2763            if (external) {
2764                if (fExternalTop > -1) {
2765                    return (CharacterBuffer)fExternalBufferPool[fExternalTop--];
2766                }
2767                else {
2768                    return new CharacterBuffer(true, fExternalBufferSize);
2769                }
2770            }
2771            else {
2772                if (fInternalTop > -1) {
2773                    return (CharacterBuffer)fInternalBufferPool[fInternalTop--];
2774                }
2775                else {
2776                    return new CharacterBuffer(false, fInternalBufferSize);
2777                }
2778            }
2779        }
2780        
2781        /** Returns buffer to pool. **/
2782        public void returnToPool(CharacterBuffer buffer) {
2783            if (buffer.isExternal) {
2784                if (fExternalTop < fExternalBufferPool.length - 1) {
2785                    fExternalBufferPool[++fExternalTop] = buffer;
2786                }
2787            }
2788            else if (fInternalTop < fInternalBufferPool.length - 1) {
2789                fInternalBufferPool[++fInternalTop] = buffer;
2790            }
2791        }
2792
2793        /** Sets the size of external buffers and dumps the old pool. **/
2794        public void setExternalBufferSize(int bufferSize) {
2795            fExternalBufferSize = bufferSize;
2796            fExternalBufferPool = new CharacterBuffer[poolSize];
2797            fExternalTop = -1;
2798        }
2799    }
2800
2801    /**
2802     * This class wraps the byte inputstreams we're presented with.
2803     * We need it because java.io.InputStreams don't provide
2804     * functionality to reread processed bytes, and they have a habit
2805     * of reading more than one character when you call their read()
2806     * methods. This means that, once we discover the true (declared)
2807     * encoding of a document, we can neither backtrack to read the
2808     * whole doc again nor start reading where we are with a new
2809     * reader.
2810     *
2811     * This class allows rewinding an inputStream by allowing a mark
2812     * to be set, and the stream reset to that position. <strong>The
2813     * class assumes that it needs to read one character per
2814     * invocation when it's read() method is inovked, but uses the
2815     * underlying InputStream's read(char[], offset length) method--it
2816     * won't buffer data read this way!</strong>
2817     *
2818     * @xerces.internal
2819     *
2820     * @author Neil Graham, IBM
2821     * @author Glenn Marcy, IBM
2822     */

2823    protected final class RewindableInputStream extends InputStream JavaDoc {
2824
2825        private InputStream JavaDoc fInputStream;
2826        private byte[] fData;
2827        private int fStartOffset;
2828        private int fEndOffset;
2829        private int fOffset;
2830        private int fLength;
2831        private int fMark;
2832
2833        public RewindableInputStream(InputStream JavaDoc is) {
2834            fData = new byte[DEFAULT_XMLDECL_BUFFER_SIZE];
2835            fInputStream = is;
2836            fStartOffset = 0;
2837            fEndOffset = -1;
2838            fOffset = 0;
2839            fLength = 0;
2840            fMark = 0;
2841        }
2842
2843        public void setStartOffset(int offset) {
2844            fStartOffset = offset;
2845        }
2846
2847        public void rewind() {
2848            fOffset = fStartOffset;
2849        }
2850
2851        public int read() throws IOException JavaDoc {
2852            int b = 0;
2853            if (fOffset < fLength) {
2854                return fData[fOffset++] & 0xff;
2855            }
2856            if (fOffset == fEndOffset) {
2857                return -1;
2858            }
2859            if (fOffset == fData.length) {
2860                byte[] newData = new byte[fOffset << 1];
2861                System.arraycopy(fData, 0, newData, 0, fOffset);
2862                fData = newData;
2863            }
2864            b = fInputStream.read();
2865            if (b == -1) {
2866                fEndOffset = fOffset;
2867                return -1;
2868            }
2869            fData[fLength++] = (byte)b;
2870            fOffset++;
2871            return b & 0xff;
2872        }
2873
2874        public int read(byte[] b, int off, int len) throws IOException JavaDoc {
2875            int bytesLeft = fLength - fOffset;
2876            if (bytesLeft == 0) {
2877                if (fOffset == fEndOffset) {
2878                    return -1;
2879                }
2880                // better get some more for the voracious reader...
2881
if(fCurrentEntity.mayReadChunks) {
2882                    return fInputStream.read(b, off, len);
2883                }
2884                int returnedVal = read();
2885                if(returnedVal == -1) {
2886                    fEndOffset = fOffset;
2887                    return -1;
2888                }
2889                b[off] = (byte)returnedVal;
2890                return 1;
2891            }
2892            if (len < bytesLeft) {
2893                if (len <= 0) {
2894                    return 0;
2895                }
2896            }
2897            else {
2898                len = bytesLeft;
2899            }
2900            if (b != null) {
2901                System.arraycopy(fData, fOffset, b, off, len);
2902            }
2903            fOffset += len;
2904            return len;
2905        }
2906
2907        public long skip(long n)
2908            throws IOException JavaDoc
2909        {
2910            int bytesLeft;
2911            if (n <= 0) {
2912                return 0;
2913            }
2914            bytesLeft = fLength - fOffset;
2915            if (bytesLeft == 0) {
2916                if (fOffset == fEndOffset) {
2917                    return 0;
2918                }
2919                return fInputStream.skip(n);
2920            }
2921            if (n <= bytesLeft) {
2922                fOffset += n;
2923                return n;
2924            }
2925            fOffset += bytesLeft;
2926            if (fOffset == fEndOffset) {
2927                return bytesLeft;
2928            }
2929            n -= bytesLeft;
2930           /*
2931            * In a manner of speaking, when this class isn't permitting more
2932            * than one byte at a time to be read, it is "blocking". The
2933            * available() method should indicate how much can be read without
2934            * blocking, so while we're in this mode, it should only indicate
2935            * that bytes in its buffer are available; otherwise, the result of
2936            * available() on the underlying InputStream is appropriate.
2937            */

2938            return fInputStream.skip(n) + bytesLeft;
2939        }
2940
2941        public int available() throws IOException JavaDoc {
2942            int bytesLeft = fLength - fOffset;
2943            if (bytesLeft == 0) {
2944                if (fOffset == fEndOffset) {
2945                    return -1;
2946                }
2947                return fCurrentEntity.mayReadChunks ? fInputStream.available()
2948                                                    : 0;
2949            }
2950            return bytesLeft;
2951        }
2952
2953        public void mark(int howMuch) {
2954            fMark = fOffset;
2955        }
2956
2957        public void reset() {
2958            fOffset = fMark;
2959        }
2960
2961        public boolean markSupported() {
2962            return true;
2963        }
2964
2965        public void close() throws IOException JavaDoc {
2966            if (fInputStream != null) {
2967                fInputStream.close();
2968                fInputStream = null;
2969            }
2970        }
2971    } // end of RewindableInputStream class
2972

2973} // class XMLEntityManager
2974
Popular Tags