KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > org > apache > xerces > internal > impl > XMLEntityManager


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  *
5  * Copyright (c) 1999-2004 The Apache Software Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Apache Software Foundation (http://www.apache.org/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Xerces" and "Apache Software Foundation" must
28  * not be used to endorse or promote products derived from this
29  * software without prior written permission. For written
30  * permission, please contact apache@apache.org.
31  *
32  * 5. Products derived from this software may not be called "Apache",
33  * nor may "Apache" appear in their name, without prior written
34  * permission of the Apache Software Foundation.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  * ====================================================================
49  *
50  * This software consists of voluntary contributions made by many
51  * individuals on behalf of the Apache Software Foundation and was
52  * originally based on software copyright (c) 1999, International
53  * Business Machines, Inc., http://www.apache.org. For more
54  * information on the Apache Software Foundation, please see
55  * <http://www.apache.org/>.
56  */

57
58 package com.sun.org.apache.xerces.internal.impl;
59
60 import java.io.IOException JavaDoc;
61 import java.io.InputStream JavaDoc;
62 import java.io.InputStreamReader JavaDoc;
63 import java.io.Reader JavaDoc;
64 import java.io.StringReader JavaDoc;
65 import java.net.HttpURLConnection JavaDoc;
66 import java.net.URL JavaDoc;
67 import java.net.URLConnection JavaDoc;
68 import java.util.Hashtable JavaDoc;
69 import java.util.Locale JavaDoc;
70 import java.util.Stack JavaDoc;
71 import java.util.Vector JavaDoc;
72
73 import com.sun.org.apache.xerces.internal.impl.io.ASCIIReader;
74 import com.sun.org.apache.xerces.internal.impl.io.UCSReader;
75 import com.sun.org.apache.xerces.internal.impl.io.UTF8Reader;
76 import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
77 import com.sun.org.apache.xerces.internal.impl.validation.ValidationManager;
78 import com.sun.org.apache.xerces.internal.util.AugmentationsImpl;
79 import com.sun.org.apache.xerces.internal.util.EncodingMap;
80 import com.sun.org.apache.xerces.internal.util.SymbolTable;
81 import com.sun.org.apache.xerces.internal.util.URI;
82 import com.sun.org.apache.xerces.internal.util.XMLChar;
83 import com.sun.org.apache.xerces.internal.util.XMLEntityDescriptionImpl;
84 import com.sun.org.apache.xerces.internal.util.XMLResourceIdentifierImpl;
85 import com.sun.org.apache.xerces.internal.util.SecurityManager;
86 import com.sun.org.apache.xerces.internal.xni.Augmentations;
87 import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier;
88 import com.sun.org.apache.xerces.internal.xni.XNIException;
89 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
90 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
91 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
92 import com.sun.org.apache.xerces.internal.xni.parser.XMLEntityResolver;
93 import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource;
94 import com.sun.org.apache.xerces.internal.xinclude.XIncludeHandler;
95 import com.sun.org.apache.xerces.internal.xinclude.XIncludeInputSource;
96 /**
97  * The entity manager handles the registration of general and parameter
98  * entities; resolves entities; and starts entities. The entity manager
99  * is a central component in a standard parser configuration and this
100  * class works directly with the entity scanner to manage the underlying
101  * xni.
102  * <p>
103  * This component requires the following features and properties from the
104  * component manager that uses it:
105  * <ul>
106  * <li>http://xml.org/sax/features/validation</li>
107  * <li>http://xml.org/sax/features/external-general-entities</li>
108  * <li>http://xml.org/sax/features/external-parameter-entities</li>
109  * <li>http://apache.org/xml/features/allow-java-encodings</li>
110  * <li>http://apache.org/xml/properties/internal/symbol-table</li>
111  * <li>http://apache.org/xml/properties/internal/error-reporter</li>
112  * <li>http://apache.org/xml/properties/internal/entity-resolver</li>
113  * <li>http://apache.org/xml/properties/security-manager</li>
114  * </ul>
115  *
116  *
117  * @author Andy Clark, IBM
118  * @author Arnaud Le Hors, IBM
119  *
120  * @version $Id: XMLEntityManager.java,v 1.79 2004/03/16 22:03:22 mrglavas Exp $
121  */

122 public class XMLEntityManager
123     implements XMLComponent, XMLEntityResolver {
124
125     //
126
// Constants
127
//
128

129     /** Default buffer size (2048). */
130     public static final int DEFAULT_BUFFER_SIZE = 2048;
131
132     /** Default buffer size before we've finished with the XMLDecl: */
133     public static final int DEFAULT_XMLDECL_BUFFER_SIZE = 64;
134
135     /** Default internal entity buffer size (1024). */
136     public static final int DEFAULT_INTERNAL_BUFFER_SIZE = 1024;
137
138     // feature identifiers
139

140     /** Feature identifier: validation. */
141     protected static final String JavaDoc VALIDATION =
142         Constants.SAX_FEATURE_PREFIX + Constants.VALIDATION_FEATURE;
143
144     /** Feature identifier: external general entities. */
145     protected static final String JavaDoc EXTERNAL_GENERAL_ENTITIES =
146         Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_GENERAL_ENTITIES_FEATURE;
147
148     /** Feature identifier: external parameter entities. */
149     protected static final String JavaDoc EXTERNAL_PARAMETER_ENTITIES =
150         Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_PARAMETER_ENTITIES_FEATURE;
151
152     /** Feature identifier: allow Java encodings. */
153     protected static final String JavaDoc ALLOW_JAVA_ENCODINGS =
154         Constants.XERCES_FEATURE_PREFIX + Constants.ALLOW_JAVA_ENCODINGS_FEATURE;
155
156     /** Feature identifier: warn on duplicate EntityDef */
157     protected static final String JavaDoc WARN_ON_DUPLICATE_ENTITYDEF =
158     Constants.XERCES_FEATURE_PREFIX +Constants.WARN_ON_DUPLICATE_ENTITYDEF_FEATURE;
159
160     /** Feature identifier: standard uri conformant */
161     protected static final String JavaDoc STANDARD_URI_CONFORMANT =
162     Constants.XERCES_FEATURE_PREFIX +Constants.STANDARD_URI_CONFORMANT_FEATURE;
163
164     protected static final String JavaDoc PARSER_SETTINGS =
165         Constants.XERCES_FEATURE_PREFIX + Constants.PARSER_SETTINGS;
166     
167     // property identifiers
168

169     /** Property identifier: symbol table. */
170     protected static final String JavaDoc SYMBOL_TABLE =
171         Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY;
172
173     /** Property identifier: error reporter. */
174     protected static final String JavaDoc ERROR_REPORTER =
175         Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;
176
177     /** Property identifier: entity resolver. */
178     protected static final String JavaDoc ENTITY_RESOLVER =
179         Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY;
180
181     // property identifier: ValidationManager
182
protected static final String JavaDoc VALIDATION_MANAGER =
183         Constants.XERCES_PROPERTY_PREFIX + Constants.VALIDATION_MANAGER_PROPERTY;
184
185     /** property identifier: buffer size. */
186     protected static final String JavaDoc BUFFER_SIZE =
187         Constants.XERCES_PROPERTY_PREFIX + Constants.BUFFER_SIZE_PROPERTY;
188
189     /** property identifier: security manager. */
190     protected static final String JavaDoc SECURITY_MANAGER =
191         Constants.XERCES_PROPERTY_PREFIX + Constants.SECURITY_MANAGER_PROPERTY;
192
193     // recognized features and properties
194

195     /** Recognized features. */
196     private static final String JavaDoc[] RECOGNIZED_FEATURES = {
197         VALIDATION,
198         EXTERNAL_GENERAL_ENTITIES,
199         EXTERNAL_PARAMETER_ENTITIES,
200         ALLOW_JAVA_ENCODINGS,
201         WARN_ON_DUPLICATE_ENTITYDEF,
202         STANDARD_URI_CONFORMANT
203     };
204
205     /** Feature defaults. */
206     private static final Boolean JavaDoc[] FEATURE_DEFAULTS = {
207         null,
208         Boolean.TRUE,
209         Boolean.TRUE,
210         Boolean.TRUE,
211         Boolean.FALSE,
212         Boolean.FALSE
213     };
214
215     /** Recognized properties. */
216     private static final String JavaDoc[] RECOGNIZED_PROPERTIES = {
217         SYMBOL_TABLE,
218         ERROR_REPORTER,
219         ENTITY_RESOLVER,
220         VALIDATION_MANAGER,
221         BUFFER_SIZE,
222         SECURITY_MANAGER,
223     };
224
225     /** Property defaults. */
226     private static final Object JavaDoc[] PROPERTY_DEFAULTS = {
227         null,
228         null,
229         null,
230         null,
231         new Integer JavaDoc(DEFAULT_BUFFER_SIZE),
232         null,
233     };
234
235     private static final String JavaDoc XMLEntity = "[xml]".intern();
236     private static final String JavaDoc DTDEntity = "[dtd]".intern();
237
238     // debugging
239

240     /**
241      * Debug printing of buffer. This debugging flag works best when you
242      * resize the DEFAULT_BUFFER_SIZE down to something reasonable like
243      * 64 characters.
244      */

245     private static final boolean DEBUG_BUFFER = false;
246
247     /** Debug some basic entities. */
248     private static final boolean DEBUG_ENTITIES = false;
249
250     /** Debug switching readers for encodings. */
251     private static final boolean DEBUG_ENCODINGS = false;
252
253     // should be diplayed trace resolving messages
254
private static final boolean DEBUG_RESOLVER = false;
255
256     //
257
// Data
258
//
259

260     // features
261

262     /**
263      * Validation. This feature identifier is:
264      * http://xml.org/sax/features/validation
265      */

266     protected boolean fValidation;
267
268     /**
269      * External general entities. This feature identifier is:
270      * http://xml.org/sax/features/external-general-entities
271      */

272     protected boolean fExternalGeneralEntities = true;
273
274     /**
275      * External parameter entities. This feature identifier is:
276      * http://xml.org/sax/features/external-parameter-entities
277      */

278     protected boolean fExternalParameterEntities = true;
279
280     /**
281      * Allow Java encoding names. This feature identifier is:
282      * http://apache.org/xml/features/allow-java-encodings
283      */

284     protected boolean fAllowJavaEncodings;
285
286     /** warn on duplicate Entity declaration.
287      * http://apache.org/xml/features/warn-on-duplicate-entitydef
288      */

289     protected boolean fWarnDuplicateEntityDef;
290
291     /**
292      * standard uri conformant (strict uri).
293      * http://apache.org/xml/features/standard-uri-conformant
294      */

295     protected boolean fStrictURI;
296
297     protected boolean fCharsetOverrideXmlEncoding;
298     // properties
299

300     /**
301      * Symbol table. This property identifier is:
302      * http://apache.org/xml/properties/internal/symbol-table
303      */

304     protected SymbolTable fSymbolTable;
305
306     /**
307      * Error reporter. This property identifier is:
308      * http://apache.org/xml/properties/internal/error-reporter
309      */

310     protected XMLErrorReporter fErrorReporter;
311
312     /**
313      * Entity resolver. This property identifier is:
314      * http://apache.org/xml/properties/internal/entity-resolver
315      */

316     protected XMLEntityResolver fEntityResolver;
317
318     /**
319      * Validation manager. This property identifier is:
320      * http://apache.org/xml/properties/internal/validation-manager
321      */

322     protected ValidationManager fValidationManager;
323
324     // settings
325

326     /**
327      * Buffer size. We get this value from a property. The default size
328      * is used if the input buffer size property is not specified.
329      * REVISIT: do we need a property for internal entity buffer size?
330      */

331     protected int fBufferSize = DEFAULT_BUFFER_SIZE;
332
333     // stores defaults for entity expansion limit if it has
334
// been set on the configuration.
335
protected SecurityManager JavaDoc fSecurityManager = null;
336
337     /**
338      * True if the document entity is standalone. This should really
339      * only be set by the document source (e.g. XMLDocumentScanner).
340      */

341     protected boolean fStandalone;
342
343     // are the entities being parsed in the external subset?
344
// NOTE: this *is not* the same as whether they're external entities!
345
protected boolean fInExternalSubset = false;
346
347     // handlers
348

349     /** Entity handler. */
350     protected XMLEntityHandler fEntityHandler;
351
352     // scanner
353

354     /** Current entity scanner. */
355     protected XMLEntityScanner fEntityScanner;
356
357     /** XML 1.0 entity scanner. */
358     protected XMLEntityScanner fXML10EntityScanner;
359
360     /** XML 1.1 entity scanner. */
361     protected XMLEntityScanner fXML11EntityScanner;
362
363     // entity expansion limit (contains useful data if and only if
364
// fSecurityManager is non-null)
365
protected int fEntityExpansionLimit = 0;
366     // entity currently being expanded:
367
protected int fEntityExpansionCount = 0;
368
369     // entities
370

371     /** Entities. */
372     protected Hashtable JavaDoc fEntities = new Hashtable JavaDoc();
373
374     /** Entity stack. */
375     protected Stack JavaDoc fEntityStack = new Stack JavaDoc();
376
377     /** Current entity. */
378     protected ScannedEntity fCurrentEntity;
379
380     // shared context
381

382     /** Shared declared entities. */
383     protected Hashtable JavaDoc fDeclaredEntities;
384
385     // temp vars
386

387     /** Resource identifer. */
388     private final XMLResourceIdentifierImpl fResourceIdentifier = new XMLResourceIdentifierImpl();
389     
390     /** Augmentations for entities. */
391     private final Augmentations fEntityAugs = new AugmentationsImpl();
392
393     //
394
// Constructors
395
//
396

397     /** Default constructor. */
398     public XMLEntityManager() {
399         this(null);
400     } // <init>()
401

402     /**
403      * Constructs an entity manager that shares the specified entity
404      * declarations during each parse.
405      * <p>
406      * <strong>REVISIT:</strong> We might want to think about the "right"
407      * way to expose the list of declared entities. For now, the knowledge
408      * how to access the entity declarations is implicit.
409      */

410     public XMLEntityManager(XMLEntityManager entityManager) {
411
412
413         // save shared entity declarations
414
fDeclaredEntities = entityManager != null
415                           ? entityManager.getDeclaredEntities() : null;
416
417         setScannerVersion(Constants.XML_VERSION_1_0);
418     } // <init>(XMLEntityManager)
419

420     //
421
// Public methods
422
//
423

424     /**
425      * Sets whether the document entity is standalone.
426      *
427      * @param standalone True if document entity is standalone.
428      */

429     public void setStandalone(boolean standalone) {
430         fStandalone = standalone;
431     } // setStandalone(boolean)
432

433     /** Returns true if the document entity is standalone. */
434     public boolean isStandalone() {
435         return fStandalone;
436     } // isStandalone():boolean
437

438     /**
439      * Sets the entity handler. When an entity starts and ends, the
440      * entity handler is notified of the change.
441      *
442      * @param entityHandler The new entity handler.
443      */

444     public void setEntityHandler(XMLEntityHandler entityHandler) {
445         fEntityHandler = entityHandler;
446     } // setEntityHandler(XMLEntityHandler)
447

448     // this simply returns the fResourceIdentifier object;
449
// this should only be used with caution by callers that
450
// carefully manage the entity manager's behaviour, so that
451
// this doesn't returning meaningless or misleading data.
452
// @return a reference to the current fResourceIdentifier object
453
public XMLResourceIdentifier getCurrentResourceIdentifier() {
454         return fResourceIdentifier;
455     }
456
457     // this simply returns the fCurrentEntity object;
458
// this should only be used with caution by callers that
459
// carefully manage the entity manager's behaviour, so that
460
// this doesn't returning meaningless or misleading data.
461
// @return a reference to the current fCurrentEntity object
462
public ScannedEntity getCurrentEntity() {
463         return fCurrentEntity;
464     }
465
466     /**
467      * Adds an internal entity declaration.
468      * <p>
469      * <strong>Note:</strong> This method ignores subsequent entity
470      * declarations.
471      * <p>
472      * <strong>Note:</strong> The name should be a unique symbol. The
473      * SymbolTable can be used for this purpose.
474      *
475      * @param name The name of the entity.
476      * @param text The text of the entity.
477      *
478      * @see SymbolTable
479      */

480     public void addInternalEntity(String JavaDoc name, String JavaDoc text) {
481         if (!fEntities.containsKey(name)) {
482             Entity entity = new InternalEntity(name, text, fInExternalSubset);
483             fEntities.put(name, entity);
484         }
485         else{
486             if(fWarnDuplicateEntityDef){
487                 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
488                                              "MSG_DUPLICATE_ENTITY_DEFINITION",
489                                              new Object JavaDoc[]{ name },
490                                              XMLErrorReporter.SEVERITY_WARNING );
491             }
492         }
493
494     } // addInternalEntity(String,String)
495

496     /**
497      * Adds an external entity declaration.
498      * <p>
499      * <strong>Note:</strong> This method ignores subsequent entity
500      * declarations.
501      * <p>
502      * <strong>Note:</strong> The name should be a unique symbol. The
503      * SymbolTable can be used for this purpose.
504      *
505      * @param name The name of the entity.
506      * @param publicId The public identifier of the entity.
507      * @param literalSystemId The system identifier of the entity.
508      * @param baseSystemId The base system identifier of the entity.
509      * This is the system identifier of the entity
510      * where <em>the entity being added</em> and
511      * is used to expand the system identifier when
512      * the system identifier is a relative URI.
513      * When null the system identifier of the first
514      * external entity on the stack is used instead.
515      *
516      * @see SymbolTable
517      */

518     public void addExternalEntity(String JavaDoc name,
519                                   String JavaDoc publicId, String JavaDoc literalSystemId,
520                                   String JavaDoc baseSystemId) throws IOException JavaDoc {
521         if (!fEntities.containsKey(name)) {
522             if (baseSystemId == null) {
523                 // search for the first external entity on the stack
524
int size = fEntityStack.size();
525                 if (size == 0 && fCurrentEntity != null && fCurrentEntity.entityLocation != null) {
526                     baseSystemId = fCurrentEntity.entityLocation.getExpandedSystemId();
527                 }
528                 for (int i = size - 1; i >= 0 ; i--) {
529                     ScannedEntity externalEntity =
530                         (ScannedEntity)fEntityStack.elementAt(i);
531                     if (externalEntity.entityLocation != null && externalEntity.entityLocation.getExpandedSystemId() != null) {
532                         baseSystemId = externalEntity.entityLocation.getExpandedSystemId();
533                         break;
534                     }
535                 }
536             }
537             Entity entity = new ExternalEntity(name,
538                 new XMLEntityDescriptionImpl(name, publicId, literalSystemId, baseSystemId,
539                 expandSystemId(literalSystemId, baseSystemId, false)), null, fInExternalSubset);
540             fEntities.put(name, entity);
541         }
542         else{
543             if(fWarnDuplicateEntityDef){
544                 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
545                                              "MSG_DUPLICATE_ENTITY_DEFINITION",
546                                              new Object JavaDoc[]{ name },
547                                              XMLErrorReporter.SEVERITY_WARNING );
548             }
549         }
550
551     } // addExternalEntity(String,String,String,String)
552

553     /**
554      * Checks whether an entity given by name is external.
555      *
556      * @param entityName The name of the entity to check.
557      * @return True if the entity is external, false otherwise
558      * (including when the entity is not declared).
559      */

560     public boolean isExternalEntity(String JavaDoc entityName) {
561
562         Entity entity = (Entity)fEntities.get(entityName);
563         if (entity == null) {
564             return false;
565         }
566         return entity.isExternal();
567     }
568
569     /**
570      * Checks whether the declaration of an entity given by name is
571      // in the external subset.
572      *
573      * @param entityName The name of the entity to check.
574      * @return True if the entity was declared in the external subset, false otherwise
575      * (including when the entity is not declared).
576      */

577     public boolean isEntityDeclInExternalSubset(String JavaDoc entityName) {
578
579         Entity entity = (Entity)fEntities.get(entityName);
580         if (entity == null) {
581             return false;
582         }
583         return entity.isEntityDeclInExternalSubset();
584     }
585
586     /**
587      * Adds an unparsed entity declaration.
588      * <p>
589      * <strong>Note:</strong> This method ignores subsequent entity
590      * declarations.
591      * <p>
592      * <strong>Note:</strong> The name should be a unique symbol. The
593      * SymbolTable can be used for this purpose.
594      *
595      * @param name The name of the entity.
596      * @param publicId The public identifier of the entity.
597      * @param systemId The system identifier of the entity.
598      * @param notation The name of the notation.
599      *
600      * @see SymbolTable
601      */

602     public void addUnparsedEntity(String JavaDoc name,
603                                   String JavaDoc publicId, String JavaDoc systemId,
604                                   String JavaDoc baseSystemId, String JavaDoc notation) {
605         if (!fEntities.containsKey(name)) {
606             Entity entity = new ExternalEntity(name,
607                 new XMLEntityDescriptionImpl(name, publicId, systemId, baseSystemId, null),
608                 notation, fInExternalSubset);
609             fEntities.put(name, entity);
610         }
611         else{
612             if(fWarnDuplicateEntityDef){
613                 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
614                                              "MSG_DUPLICATE_ENTITY_DEFINITION",
615                                              new Object JavaDoc[]{ name },
616                                              XMLErrorReporter.SEVERITY_WARNING );
617             }
618         }
619     } // addUnparsedEntity(String,String,String,String)
620

621     /**
622      * Checks whether an entity given by name is unparsed.
623      *
624      * @param entityName The name of the entity to check.
625      * @return True if the entity is unparsed, false otherwise
626      * (including when the entity is not declared).
627      */

628     public boolean isUnparsedEntity(String JavaDoc entityName) {
629
630         Entity entity = (Entity)fEntities.get(entityName);
631         if (entity == null) {
632             return false;
633         }
634         return entity.isUnparsed();
635     }
636
637     /**
638      * Checks whether an entity given by name is declared.
639      *
640      * @param entityName The name of the entity to check.
641      * @return True if the entity is declared, false otherwise.
642      */

643     public boolean isDeclaredEntity(String JavaDoc entityName) {
644
645         Entity entity = (Entity)fEntities.get(entityName);
646         return entity != null;
647     }
648
649     /**
650      * Resolves the specified public and system identifiers. This
651      * method first attempts to resolve the entity based on the
652      * EntityResolver registered by the application. If no entity
653      * resolver is registered or if the registered entity handler
654      * is unable to resolve the entity, then default entity
655      * resolution will occur.
656      *
657      * @param publicId The public identifier of the entity.
658      * @param systemId The system identifier of the entity.
659      * @param baseSystemId The base system identifier of the entity.
660      * This is the system identifier of the current
661      * entity and is used to expand the system
662      * identifier when the system identifier is a
663      * relative URI.
664      *
665      * @return Returns an input source that wraps the resolved entity.
666      * This method will never return null.
667      *
668      * @throws IOException Thrown on i/o error.
669      * @throws XNIException Thrown by entity resolver to signal an error.
670      */

671     public XMLInputSource resolveEntity(XMLResourceIdentifier resourceIdentifier)
672             throws IOException JavaDoc, XNIException {
673         if(resourceIdentifier == null ) return null;
674         String JavaDoc publicId = resourceIdentifier.getPublicId();
675         String JavaDoc literalSystemId = resourceIdentifier.getLiteralSystemId();
676         String JavaDoc baseSystemId = resourceIdentifier.getBaseSystemId();
677         String JavaDoc expandedSystemId = resourceIdentifier.getExpandedSystemId();
678         // if no base systemId given, assume that it's relative
679
// to the systemId of the current scanned entity
680
// Sometimes the system id is not (properly) expanded.
681
// We need to expand the system id if:
682
// a. the expanded one was null; or
683
// b. the base system id was null, but becomes non-null from the current entity.
684
boolean needExpand = (expandedSystemId == null);
685         // REVISIT: why would the baseSystemId ever be null? if we
686
// didn't have to make this check we wouldn't have to reuse the
687
// fXMLResourceIdentifier object...
688
if (baseSystemId == null && fCurrentEntity != null && fCurrentEntity.entityLocation != null) {
689             baseSystemId = fCurrentEntity.entityLocation.getExpandedSystemId();
690             if (baseSystemId != null)
691                 needExpand = true;
692          }
693          if (needExpand)
694             expandedSystemId = expandSystemId(literalSystemId, baseSystemId, false);
695
696        // give the entity resolver a chance
697
XMLInputSource xmlInputSource = null;
698         if (fEntityResolver != null) {
699             resourceIdentifier.setBaseSystemId(baseSystemId);
700             resourceIdentifier.setExpandedSystemId(expandedSystemId);
701             xmlInputSource = fEntityResolver.resolveEntity(resourceIdentifier);
702         }
703
704         // do default resolution
705
// REVISIT: what's the correct behavior if the user provided an entity
706
// resolver (fEntityResolver != null), but resolveEntity doesn't return
707
// an input source (xmlInputSource == null)?
708
// do we do default resolution, or do we just return null? -SG
709
if (xmlInputSource == null) {
710             // REVISIT: when systemId is null, I think we should return null.
711
// is this the right solution? -SG
712
//if (systemId != null)
713
xmlInputSource = new XMLInputSource(publicId, literalSystemId, baseSystemId);
714         }
715
716         if (DEBUG_RESOLVER) {
717             System.err.println("XMLEntityManager.resolveEntity(" + publicId + ")");
718             System.err.println(" = " + xmlInputSource);
719         }
720
721         return xmlInputSource;
722
723     } // resolveEntity(XMLResourceIdentifier):XMLInputSource
724

725     /**
726      * Starts a named entity.
727      *
728      * @param entityName The name of the entity to start.
729      * @param literal True if this entity is started within a literal
730      * value.
731      *
732      * @throws IOException Thrown on i/o error.
733      * @throws XNIException Thrown by entity handler to signal an error.
734      */

735     public void startEntity(String JavaDoc entityName, boolean literal)
736         throws IOException JavaDoc, XNIException {
737
738         // was entity declared?
739
Entity entity = (Entity)fEntities.get(entityName);
740         if (entity == null) {
741             if (fEntityHandler != null) {
742                 String JavaDoc encoding = null;
743                 fResourceIdentifier.clear();
744                 fEntityAugs.removeAllItems();
745                 fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
746                 fEntityHandler.startEntity(entityName, fResourceIdentifier, encoding, fEntityAugs);
747                 fEntityAugs.removeAllItems();
748                 fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
749                 fEntityHandler.endEntity(entityName, fEntityAugs);
750             }
751             return;
752         }
753
754         // should we skip external entities?
755
boolean external = entity.isExternal();
756         if (external && (fValidationManager == null || !fValidationManager.isCachedDTD())) {
757             boolean unparsed = entity.isUnparsed();
758             boolean parameter = entityName.startsWith("%");
759             boolean general = !parameter;
760             if (unparsed || (general && !fExternalGeneralEntities) ||
761                 (parameter && !fExternalParameterEntities)) {
762                 if (fEntityHandler != null) {
763                     fResourceIdentifier.clear();
764                     final String JavaDoc encoding = null;
765                     ExternalEntity externalEntity = (ExternalEntity)entity;
766                     //REVISIT: since we're storing expandedSystemId in the
767
// externalEntity, how could this have got here if it wasn't already
768
// expanded??? - neilg
769
String JavaDoc extLitSysId = (externalEntity.entityLocation != null ? externalEntity.entityLocation.getLiteralSystemId() : null);
770                     String JavaDoc extBaseSysId = (externalEntity.entityLocation != null ? externalEntity.entityLocation.getBaseSystemId() : null);
771                     String JavaDoc expandedSystemId = expandSystemId(extLitSysId, extBaseSysId, false);
772                     fResourceIdentifier.setValues(
773                             (externalEntity.entityLocation != null ? externalEntity.entityLocation.getPublicId() : null),
774                             extLitSysId, extBaseSysId, expandedSystemId);
775                     fEntityAugs.removeAllItems();
776                     fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
777                     fEntityHandler.startEntity(entityName, fResourceIdentifier, encoding, fEntityAugs);
778                     fEntityAugs.removeAllItems();
779                     fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
780                     fEntityHandler.endEntity(entityName, fEntityAugs);
781                 }
782                 return;
783             }
784         }
785
786         // is entity recursive?
787
int size = fEntityStack.size();
788         for (int i = size; i >= 0; i--) {
789             Entity activeEntity = i == size
790                                 ? fCurrentEntity
791                                 : (Entity)fEntityStack.elementAt(i);
792             if (activeEntity.name == entityName) {
793                 String JavaDoc path = entityName;
794                 for (int j = i + 1; j < size; j++) {
795                     activeEntity = (Entity)fEntityStack.elementAt(j);
796                     path = path + " -> " + activeEntity.name;
797                 }
798                 path = path + " -> " + fCurrentEntity.name;
799                 path = path + " -> " + entityName;
800                 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
801                                            "RecursiveReference",
802                                            new Object JavaDoc[] { entityName, path },
803                                            XMLErrorReporter.SEVERITY_FATAL_ERROR);
804                 if (fEntityHandler != null) {
805                     fResourceIdentifier.clear();
806                     final String JavaDoc encoding = null;
807                     if (external) {
808                         ExternalEntity externalEntity = (ExternalEntity)entity;
809                         // REVISIT: for the same reason above...
810
String JavaDoc extLitSysId = (externalEntity.entityLocation != null ? externalEntity.entityLocation.getLiteralSystemId() : null);
811                         String JavaDoc extBaseSysId = (externalEntity.entityLocation != null ? externalEntity.entityLocation.getBaseSystemId() : null);
812                         String JavaDoc expandedSystemId = expandSystemId(extLitSysId, extBaseSysId, false);
813                         fResourceIdentifier.setValues(
814                                 (externalEntity.entityLocation != null ? externalEntity.entityLocation.getPublicId() : null),
815                                 extLitSysId, extBaseSysId, expandedSystemId);
816                     }
817                     fEntityAugs.removeAllItems();
818                     fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
819                     fEntityHandler.startEntity(entityName, fResourceIdentifier, encoding, fEntityAugs);
820                     fEntityAugs.removeAllItems();
821                     fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
822                     fEntityHandler.endEntity(entityName, fEntityAugs);
823                 }
824                 return;
825             }
826         }
827
828         // resolve external entity
829
XMLInputSource xmlInputSource = null;
830         if (external) {
831             ExternalEntity externalEntity = (ExternalEntity)entity;
832             xmlInputSource = resolveEntity(externalEntity.entityLocation);
833         }
834
835         // wrap internal entity
836
else {
837             InternalEntity internalEntity = (InternalEntity)entity;
838             Reader JavaDoc reader = new StringReader JavaDoc(internalEntity.text);
839             xmlInputSource = new XMLInputSource(null, null, null, reader, null);
840         }
841
842         // start the entity
843
startEntity(entityName, xmlInputSource, literal, external);
844
845     } // startEntity(String,boolean)
846

847     /**
848      * Starts the document entity. The document entity has the "[xml]"
849      * pseudo-name.
850      *
851      * @param xmlInputSource The input source of the document entity.
852      *
853      * @throws IOException Thrown on i/o error.
854      * @throws XNIException Thrown by entity handler to signal an error.
855      */

856     public void startDocumentEntity(XMLInputSource xmlInputSource)
857         throws IOException JavaDoc, XNIException {
858         startEntity(XMLEntity, xmlInputSource, false, true);
859     } // startDocumentEntity(XMLInputSource)
860

861     /**
862      * Starts the DTD entity. The DTD entity has the "[dtd]"
863      * pseudo-name.
864      *
865      * @param xmlInputSource The input source of the DTD entity.
866      *
867      * @throws IOException Thrown on i/o error.
868      * @throws XNIException Thrown by entity handler to signal an error.
869      */

870     public void startDTDEntity(XMLInputSource xmlInputSource)
871         throws IOException JavaDoc, XNIException {
872         startEntity(DTDEntity, xmlInputSource, false, true);
873     } // startDTDEntity(XMLInputSource)
874

875     // indicate start of external subset so that
876
// location of entity decls can be tracked
877
public void startExternalSubset() {
878         fInExternalSubset = true;
879     }
880
881     public void endExternalSubset() {
882         fInExternalSubset = false;
883     }
884
885     /**
886      * Starts an entity.
887      * <p>
888      * This method can be used to insert an application defined XML
889      * entity stream into the parsing stream.
890      *
891      * @param name The name of the entity.
892      * @param xmlInputSource The input source of the entity.
893      * @param literal True if this entity is started within a
894      * literal value.
895      * @param isExternal whether this entity should be treated as an internal or external entity.
896      *
897      * @throws IOException Thrown on i/o error.
898      * @throws XNIException Thrown by entity handler to signal an error.
899      */

900     public void startEntity(String JavaDoc name,
901                             XMLInputSource xmlInputSource,
902                             boolean literal, boolean isExternal)
903         throws IOException JavaDoc, XNIException {
904
905         String JavaDoc encoding = setupCurrentEntity(name, xmlInputSource, literal, isExternal);
906        
907         //when entity expansion limit is set by the Application, we need to
908
//check for the entity expansion limit set by the parser, if number of entity
909
//expansions exceeds the entity expansion limit, parser will throw fatal error.
910
// Note that this represents the nesting level of open entities.
911
fEntityExpansionCount++;
912         if( fSecurityManager != null && fEntityExpansionCount > fEntityExpansionLimit ){
913             fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
914                                              "EntityExpansionLimitExceeded",
915                                              new Object JavaDoc[]{new Integer JavaDoc(fEntityExpansionLimit) },
916                                              XMLErrorReporter.SEVERITY_FATAL_ERROR );
917             // is there anything better to do than reset the counter?
918
// at least one can envision debugging applications where this might
919
// be useful...
920
fEntityExpansionCount = 0;
921         }
922
923         // call handler
924
if (fEntityHandler != null) {
925             fEntityHandler.startEntity(name, fResourceIdentifier, encoding, null);
926         }
927
928     } // startEntity(String,XMLInputSource)
929

930     /**
931      * This method uses the passed-in XMLInputSource to make
932      * fCurrentEntity usable for reading.
933      * @param name name of the entity (XML is it's the document entity)
934      * @param xmlInputSource the input source, with sufficient information
935      * to begin scanning characters.
936      * @param literal True if this entity is started within a
937      * literal value.
938      * @param isExternal whether this entity should be treated as an internal or external entity.
939      * @throws IOException if anything can't be read
940      * XNIException If any parser-specific goes wrong.
941      * @return the encoding of the new entity or null if a character stream was employed
942      */

943     public String JavaDoc setupCurrentEntity(String JavaDoc name, XMLInputSource xmlInputSource,
944                 boolean literal, boolean isExternal)
945             throws IOException JavaDoc, XNIException {
946         // get information
947

948         final String JavaDoc publicId = xmlInputSource.getPublicId();
949         String JavaDoc literalSystemId = xmlInputSource.getSystemId();
950         String JavaDoc baseSystemId = xmlInputSource.getBaseSystemId();
951         String JavaDoc encoding = xmlInputSource.getEncoding();
952         Boolean JavaDoc isBigEndian = null;
953         boolean declaredEncoding = false;
954
955         // create reader
956
InputStream JavaDoc stream = null;
957         Reader JavaDoc reader = xmlInputSource.getCharacterStream();
958         // First chance checking strict URI
959
String JavaDoc expandedSystemId = expandSystemId(literalSystemId, baseSystemId, fStrictURI);
960         if (baseSystemId == null) {
961             baseSystemId = expandedSystemId;
962         }
963         if (reader == null) {
964             stream = xmlInputSource.getByteStream();
965             if(stream != null && encoding != null)
966                 declaredEncoding = true;
967             if (stream == null) {
968                 URL JavaDoc location = new URL JavaDoc(expandedSystemId);
969                 URLConnection JavaDoc connect = location.openConnection();
970                 if (connect instanceof HttpURLConnection JavaDoc) {
971                     setHttpProperties(connect,xmlInputSource);
972                 }
973                 stream = connect.getInputStream();
974                 
975                 // REVISIT: If the URLConnection has external encoding
976
// information, we should be reading it here. It's located
977
// in the charset parameter of Content-Type. -- mrglavas
978
if (connect instanceof HttpURLConnection JavaDoc) {
979                     String JavaDoc redirect = connect.getURL().toString();
980                     // E43: Check if the URL was redirected, and then
981
// update literal and expanded system IDs if needed.
982
if (!redirect.equals(expandedSystemId)) {
983                         literalSystemId = redirect;
984                         expandedSystemId = redirect;
985                     }
986                 }
987             }
988             // wrap this stream in RewindableInputStream
989
stream = new RewindableInputStream(stream);
990
991             // perform auto-detect of encoding if necessary
992
if (encoding == null) {
993                 // read first four bytes and determine encoding
994
final byte[] b4 = new byte[4];
995                 int count = 0;
996                 for (; count<4; count++ ) {
997                     b4[count] = (byte)stream.read();
998                 }
999                 if (count == 4) {
1000                    Object JavaDoc [] encodingDesc = getEncodingName(b4, count);
1001                    encoding = (String JavaDoc)(encodingDesc[0]);
1002                    isBigEndian = (Boolean JavaDoc)(encodingDesc[1]);
1003
1004                    stream.reset();
1005                    // Special case UTF-8 files with BOM created by Microsoft
1006
// tools. It's more efficient to consume the BOM than make
1007
// the reader perform extra checks. -Ac
1008
if (count > 2 && encoding.equals("UTF-8")) {
1009                        int b0 = b4[0] & 0xFF;
1010                        int b1 = b4[1] & 0xFF;
1011                        int b2 = b4[2] & 0xFF;
1012                        if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF) {
1013                            // ignore first three bytes...
1014
stream.skip(3);
1015                        }
1016                    }
1017                    reader = createReader(stream, encoding, isBigEndian);
1018                }
1019                else {
1020                    reader = createReader(stream, encoding, isBigEndian);
1021                }
1022            }
1023
1024            // use specified encoding
1025
else {
1026                encoding = encoding.toUpperCase(Locale.ENGLISH);
1027
1028                // If encoding is UTF-8, consume BOM if one is present.
1029
if (encoding.equals("UTF-8")) {
1030                    final int[] b3 = new int[3];
1031                    int count = 0;
1032                    for (; count < 3; ++count) {
1033                        b3[count] = stream.read();
1034                        if (b3[count] == -1)
1035                            break;
1036                    }
1037                    if (count == 3) {
1038                        if (b3[0] != 0xEF || b3[1] != 0xBB || b3[2] != 0xBF) {
1039                            // First three bytes are not BOM, so reset.
1040
stream.reset();
1041                        }
1042                    }
1043                    else {
1044                        stream.reset();
1045                    }
1046                }
1047                // If encoding is UCS-4, we still need to read the first four bytes
1048
// in order to discover the byte order.
1049
else if (encoding.equals("ISO-10646-UCS-4")) {
1050                    final int[] b4 = new int[4];
1051                    int count = 0;
1052                    for (; count < 4; ++count) {
1053                        b4[count] = stream.read();
1054                        if (b4[count] == -1)
1055                            break;
1056                    }
1057                    stream.reset();
1058
1059                    // Ignore unusual octet order for now.
1060
if (count == 4) {
1061                        // UCS-4, big endian (1234)
1062
if (b4[0] == 0x00 && b4[1] == 0x00 && b4[2] == 0x00 && b4[3] == 0x3C) {
1063                            isBigEndian = Boolean.TRUE;
1064                        }
1065                        // UCS-4, little endian (1234)
1066
else if (b4[0] == 0x3C && b4[1] == 0x00 && b4[2] == 0x00 && b4[3] == 0x00) {
1067                            isBigEndian = Boolean.FALSE;
1068                        }
1069                    }
1070                }
1071                // If encoding is UCS-2, we still need to read the first four bytes
1072
// in order to discover the byte order.
1073
else if (encoding.equals("ISO-10646-UCS-2")) {
1074                    final int[] b4 = new int[4];
1075                    int count = 0;
1076                    for (; count < 4; ++count) {
1077                        b4[count] = stream.read();
1078                        if (b4[count] == -1)
1079                            break;
1080                    }
1081                    stream.reset();
1082
1083                    if (count == 4) {
1084                        // UCS-2, big endian
1085
if (b4[0] == 0x00 && b4[1] == 0x3C && b4[2] == 0x00 && b4[3] == 0x3F) {
1086                            isBigEndian = Boolean.TRUE;
1087                        }
1088                        // UCS-2, little endian
1089
else if (b4[0] == 0x3C && b4[1] == 0x00 && b4[2] == 0x3F && b4[3] == 0x00) {
1090                            isBigEndian = Boolean.FALSE;
1091                        }
1092                    }
1093                }
1094
1095                reader = createReader(stream, encoding, isBigEndian);
1096            }
1097
1098            // read one character at a time so we don't jump too far
1099
// ahead, converting characters from the byte stream in
1100
// the wrong encoding
1101
if (DEBUG_ENCODINGS) {
1102                System.out.println("$$$ no longer wrapping reader in OneCharReader");
1103            }
1104            //reader = new OneCharReader(reader);
1105
}
1106
1107        // We've seen a new Reader.
1108
// Push it on the stack so we can close it later.
1109
fReaderStack.push(reader);
1110
1111        // push entity on stack
1112
if (fCurrentEntity != null) {
1113            fEntityStack.push(fCurrentEntity);
1114        }
1115
1116        // create entity
1117
fCurrentEntity = new ScannedEntity(name,
1118                new XMLResourceIdentifierImpl(publicId, literalSystemId, baseSystemId, expandedSystemId),
1119                stream, reader, encoding, literal, false, isExternal);
1120        fCurrentEntity.setDeclaredEncoding(declaredEncoding);
1121        fEntityScanner.setCurrentEntity(fCurrentEntity);
1122        fResourceIdentifier.setValues(publicId, literalSystemId, baseSystemId, expandedSystemId);
1123        return encoding;
1124    } //setupCurrentEntity(String, XMLInputSource, boolean, boolean): String
1125

1126    void setHttpProperties(URLConnection JavaDoc conn, XMLInputSource source ){
1127
1128        if( source instanceof XIncludeInputSource ){
1129            String JavaDoc fAccept = null;
1130            String JavaDoc fAcceptCharset = null;
1131            String JavaDoc fAcceptLanguage = null;
1132        
1133            XIncludeInputSource xiSrc= (XIncludeInputSource)source;
1134        
1135            fAccept = (String JavaDoc)xiSrc.getProperty(XIncludeHandler.HTTP_ACCEPT);
1136            fAcceptCharset = (String JavaDoc)xiSrc.getProperty(XIncludeHandler.HTTP_ACCEPT_CHARSET);
1137            fAcceptLanguage = (String JavaDoc)xiSrc.getProperty(XIncludeHandler.HTTP_ACCEPT_LANGUAGE);
1138
1139            if( fAccept != null && fAccept.length() > 0) {
1140                conn.setRequestProperty(XIncludeHandler.HTTP_ACCEPT, fAccept);
1141            }
1142            if( fAcceptCharset != null && fAcceptCharset.length() > 0) {
1143                conn.setRequestProperty(XIncludeHandler.HTTP_ACCEPT_CHARSET, fAcceptCharset);
1144            }
1145            if( fAcceptLanguage != null && fAcceptLanguage.length() > 0) {
1146                conn.setRequestProperty(XIncludeHandler.HTTP_ACCEPT_LANGUAGE, fAcceptLanguage);
1147            }
1148        }
1149    }
1150
1151    // set version of scanner to use
1152
public void setScannerVersion(short version) {
1153        if(version == Constants.XML_VERSION_1_0) {
1154            if(fXML10EntityScanner == null) {
1155                fXML10EntityScanner = new XMLEntityScanner();
1156            }
1157            fXML10EntityScanner.reset(fSymbolTable, this, fErrorReporter);
1158            fEntityScanner = fXML10EntityScanner;
1159            fEntityScanner.setCurrentEntity(fCurrentEntity);
1160        } else {
1161            if(fXML11EntityScanner == null) {
1162                fXML11EntityScanner = new XML11EntityScanner();
1163            }
1164            fXML11EntityScanner.reset(fSymbolTable, this, fErrorReporter);
1165            fEntityScanner = fXML11EntityScanner;
1166            fEntityScanner.setCurrentEntity(fCurrentEntity);
1167        }
1168    } // setScannerVersion(short)
1169

1170    /** Returns the entity scanner. */
1171    public XMLEntityScanner getEntityScanner() {
1172        if(fEntityScanner == null) {
1173            // default to 1.0
1174
if(fXML10EntityScanner == null) {
1175                fXML10EntityScanner = new XMLEntityScanner();
1176            }
1177            fXML10EntityScanner.reset(fSymbolTable, this, fErrorReporter);
1178            fEntityScanner = fXML10EntityScanner;
1179        }
1180        return fEntityScanner;
1181    } // getEntityScanner():XMLEntityScanner
1182

1183    // A stack containing all the open readers
1184
protected Stack JavaDoc fReaderStack = new Stack JavaDoc();
1185
1186    /**
1187     * Close all opened InputStreams and Readers opened by this parser.
1188     */

1189    public void closeReaders() {
1190        // close all readers
1191
for (int i = fReaderStack.size()-1; i >= 0; i--) {
1192            try {
1193                ((Reader JavaDoc)fReaderStack.pop()).close();
1194            } catch (IOException JavaDoc e) {
1195                // ignore
1196
}
1197        }
1198    }
1199
1200    //
1201
// XMLComponent methods
1202
//
1203

1204    /**
1205     * Resets the component. The component can query the component manager
1206     * about any features and properties that affect the operation of the
1207     * component.
1208     *
1209     * @param componentManager The component manager.
1210     *
1211     * @throws SAXException Thrown by component on initialization error.
1212     * For example, if a feature or property is
1213     * required for the operation of the component, the
1214     * component manager may throw a
1215     * SAXNotRecognizedException or a
1216     * SAXNotSupportedException.
1217     */

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

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

1364    /**
1365     * Returns a list of feature identifiers that are recognized by
1366     * this component. This method may return null if no features
1367     * are recognized by this component.
1368     */

1369    public String JavaDoc[] getRecognizedFeatures() {
1370        return (String JavaDoc[])(RECOGNIZED_FEATURES.clone());
1371    } // getRecognizedFeatures():String[]
1372

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

1388    public void setFeature(String JavaDoc featureId, boolean state)
1389        throws XMLConfigurationException {
1390
1391        // xerces features
1392
if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) {
1393            final int suffixLength = featureId.length() - Constants.XERCES_FEATURE_PREFIX.length();
1394            if (suffixLength == Constants.ALLOW_JAVA_ENCODINGS_FEATURE.length() &&
1395                featureId.endsWith(Constants.ALLOW_JAVA_ENCODINGS_FEATURE)) {
1396                fAllowJavaEncodings = state;
1397            }
1398        }
1399
1400    } // setFeature(String,boolean)
1401

1402    /**
1403     * Returns a list of property identifiers that are recognized by
1404     * this component. This method may return null if no properties
1405     * are recognized by this component.
1406     */

1407    public String JavaDoc[] getRecognizedProperties() {
1408        return (String JavaDoc[])(RECOGNIZED_PROPERTIES.clone());
1409    } // getRecognizedProperties():String[]
1410

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

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

1466    /**
1467     * Returns the default state for a feature, or null if this
1468     * component does not want to report a default value for this
1469     * feature.
1470     *
1471     * @param featureId The feature identifier.
1472     *
1473     * @since Xerces 2.2.0
1474     */

1475    public Boolean JavaDoc getFeatureDefault(String JavaDoc featureId) {
1476        for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
1477            if (RECOGNIZED_FEATURES[i].equals(featureId)) {
1478                return FEATURE_DEFAULTS[i];
1479            }
1480        }
1481        return null;
1482    } // getFeatureDefault(String):Boolean
1483

1484    /**
1485     * Returns the default state for a property, or null if this
1486     * component does not want to report a default value for this
1487     * property.
1488     *
1489     * @param propertyId The property identifier.
1490     *
1491     * @since Xerces 2.2.0
1492     */

1493    public Object JavaDoc getPropertyDefault(String JavaDoc propertyId) {
1494        for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
1495            if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
1496                return PROPERTY_DEFAULTS[i];
1497            }
1498        }
1499        return null;
1500    } // getPropertyDefault(String):Object
1501

1502    //
1503
// Public static methods
1504
//
1505

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

1658    public static String JavaDoc expandSystemId(String JavaDoc systemId, String JavaDoc baseSystemId,
1659                                        boolean strict)
1660            throws URI.MalformedURIException {
1661
1662        // system id has to be a valid URI
1663
if (strict) {
1664            
1665            // check if there is a system id before
1666
// trying to expand it.
1667
if (systemId == null) {
1668                return null;
1669            }
1670            
1671            try {
1672                // if it's already an absolute one, return it
1673
new URI(systemId);
1674                return systemId;
1675            }
1676            catch (URI.MalformedURIException ex) {
1677            }
1678            URI base = null;
1679            // if there isn't a base uri, use the working directory
1680
if (baseSystemId == null || baseSystemId.length() == 0) {
1681                base = new URI("file", "", getUserDir(), null, null);
1682            }
1683            // otherwise, use the base uri
1684
else {
1685                try {
1686                    base = new URI(baseSystemId);
1687                }
1688                catch (URI.MalformedURIException e) {
1689                    // assume "base" is also a relative uri
1690
String JavaDoc dir = getUserDir();
1691                    dir = dir + baseSystemId;
1692                    base = new URI("file", "", dir, null, null);
1693                }
1694            }
1695            // absolutize the system id using the base
1696
URI uri = new URI(base, systemId);
1697            // return the string rep of the new uri (an absolute one)
1698
return uri.toString();
1699
1700            // if any exception is thrown, it'll get thrown to the caller.
1701
}
1702
1703        // check for bad parameters id
1704
if (systemId == null || systemId.length() == 0) {
1705            return systemId;
1706        }
1707        // if id already expanded, return
1708
try {
1709            URI uri = new URI(systemId.trim());
1710            if (uri != null) {
1711                return systemId;
1712            }
1713        }
1714        catch (URI.MalformedURIException e) {
1715            // continue on...
1716
}
1717        // normalize id
1718
String JavaDoc id = fixURI(systemId);
1719
1720        // normalize base
1721
URI base = null;
1722        URI uri = null;
1723        try {
1724            if (baseSystemId == null || baseSystemId.length() == 0 ||
1725                baseSystemId.equals(systemId)) {
1726                String JavaDoc dir = getUserDir();
1727                base = new URI("file", "", dir, null, null);
1728            }
1729            else {
1730                try {
1731                    base = new URI(fixURI(baseSystemId).trim());
1732                }
1733                catch (URI.MalformedURIException e) {
1734                    if (baseSystemId.indexOf(':') != -1) {
1735                        // for xml schemas we might have baseURI with
1736
// a specified drive
1737
base = new URI("file", "", fixURI(baseSystemId).trim(), null, null);
1738                    }
1739                    else {
1740                        String JavaDoc dir = getUserDir();
1741                        dir = dir + fixURI(baseSystemId);
1742                        base = new URI("file", "", dir, null, null);
1743                    }
1744                }
1745             }
1746             // expand id
1747
uri = new URI(base, id.trim());
1748        }
1749        catch (Exception JavaDoc e) {
1750            // let it go through
1751

1752        }
1753
1754        if (uri == null) {
1755            return systemId;
1756        }
1757        return uri.toString();
1758
1759    } // expandSystemId(String,String):String
1760

1761    //
1762
// Protected methods
1763
//
1764

1765    /**
1766     * Ends an entity.
1767     *
1768     * @throws XNIException Thrown by entity handler to signal an error.
1769     */

1770    void endEntity() throws XNIException {
1771        
1772        // call handler
1773
if (DEBUG_BUFFER) {
1774            System.out.print("(endEntity: ");
1775            print(fCurrentEntity);
1776            System.out.println();
1777        }
1778        if (fEntityHandler != null) {
1779            fEntityHandler.endEntity(fCurrentEntity.name, null);
1780        }
1781        
1782        // Close the reader for the current entity once we're
1783
// done with it, and remove it from our stack. If parsing
1784
// is halted at some point, the rest of the readers on
1785
// the stack will be closed during cleanup.
1786
try {
1787            fCurrentEntity.reader.close();
1788        }
1789        catch (IOException JavaDoc e) {
1790            // ignore
1791
}
1792        // REVISIT: We should never encounter underflow if the calls
1793
// to startEntity and endEntity are balanced, but guard
1794
// against the EmptyStackException for now. -- mrglavas
1795
if(!fReaderStack.isEmpty()) {
1796            fReaderStack.pop();
1797        }
1798
1799        // Pop entity stack.
1800
fCurrentEntity = fEntityStack.size() > 0
1801                       ? (ScannedEntity)fEntityStack.pop() : null;
1802        fEntityScanner.setCurrentEntity(fCurrentEntity);
1803        if (DEBUG_BUFFER) {
1804            System.out.print(")endEntity: ");
1805            print(fCurrentEntity);
1806            System.out.println();
1807        }
1808        
1809        // as entity is no longer open, decrement open entity expansion count
1810

1811    } // endEntity()
1812

1813    /**
1814     * Returns the IANA encoding name that is auto-detected from
1815     * the bytes specified, with the endian-ness of that encoding where appropriate.
1816     *
1817     * @param b4 The first four bytes of the input.
1818     * @param count The number of bytes actually read.
1819     * @return a 2-element array: the first element, an IANA-encoding string,
1820     * the second element a Boolean which is true iff the document is big endian, false
1821     * if it's little-endian, and null if the distinction isn't relevant.
1822     */

1823    protected Object JavaDoc[] getEncodingName(byte[] b4, int count) {
1824
1825        if (count < 2) {
1826            return new Object JavaDoc[]{"UTF-8", null};
1827        }
1828
1829        // UTF-16, with BOM
1830
int b0 = b4[0] & 0xFF;
1831        int b1 = b4[1] & 0xFF;
1832        if (b0 == 0xFE && b1 == 0xFF) {
1833            // UTF-16, big-endian
1834
return new Object JavaDoc [] {"UTF-16BE", new Boolean JavaDoc(true)};
1835        }
1836        if (b0 == 0xFF && b1 == 0xFE) {
1837            // UTF-16, little-endian
1838
return new Object JavaDoc [] {"UTF-16LE", new Boolean JavaDoc(false)};
1839        }
1840
1841        // default to UTF-8 if we don't have enough bytes to make a
1842
// good determination of the encoding
1843
if (count < 3) {
1844            return new Object JavaDoc [] {"UTF-8", null};
1845        }
1846
1847        // UTF-8 with a BOM
1848
int b2 = b4[2] & 0xFF;
1849        if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF) {
1850            return new Object JavaDoc [] {"UTF-8", null};
1851        }
1852
1853        // default to UTF-8 if we don't have enough bytes to make a
1854
// good determination of the encoding
1855
if (count < 4) {
1856            return new Object JavaDoc [] {"UTF-8", null};
1857        }
1858
1859        // other encodings
1860
int b3 = b4[3] & 0xFF;
1861        if (b0 == 0x00 && b1 == 0x00 && b2 == 0x00 && b3 == 0x3C) {
1862            // UCS-4, big endian (1234)
1863
return new Object JavaDoc [] {"ISO-10646-UCS-4", new Boolean JavaDoc(true)};
1864        }
1865        if (b0 == 0x3C && b1 == 0x00 && b2 == 0x00 && b3 == 0x00) {
1866            // UCS-4, little endian (4321)
1867
return new Object JavaDoc [] {"ISO-10646-UCS-4", new Boolean JavaDoc(false)};
1868        }
1869        if (b0 == 0x00 && b1 == 0x00 && b2 == 0x3C && b3 == 0x00) {
1870            // UCS-4, unusual octet order (2143)
1871
// REVISIT: What should this be?
1872
return new Object JavaDoc [] {"ISO-10646-UCS-4", null};
1873        }
1874        if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x00) {
1875            // UCS-4, unusual octect order (3412)
1876
// REVISIT: What should this be?
1877
return new Object JavaDoc [] {"ISO-10646-UCS-4", null};
1878        }
1879        if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x3F) {
1880            // UTF-16, big-endian, no BOM
1881
// (or could turn out to be UCS-2...
1882
// REVISIT: What should this be?
1883
return new Object JavaDoc [] {"UTF-16BE", new Boolean JavaDoc(true)};
1884        }
1885        if (b0 == 0x3C && b1 == 0x00 && b2 == 0x3F && b3 == 0x00) {
1886            // UTF-16, little-endian, no BOM
1887
// (or could turn out to be UCS-2...
1888
return new Object JavaDoc [] {"UTF-16LE", new Boolean JavaDoc(false)};
1889        }
1890        if (b0 == 0x4C && b1 == 0x6F && b2 == 0xA7 && b3 == 0x94) {
1891            // EBCDIC
1892
// a la xerces1, return CP037 instead of EBCDIC here
1893
return new Object JavaDoc [] {"CP037", null};
1894        }
1895
1896        // default encoding
1897
return new Object JavaDoc [] {"UTF-8", null};
1898
1899    } // getEncodingName(byte[],int):Object[]
1900

1901    /**
1902     * Creates a reader capable of reading the given input stream in
1903     * the specified encoding.
1904     *
1905     * @param inputStream The input stream.
1906     * @param encoding The encoding name that the input stream is
1907     * encoded using. If the user has specified that
1908     * Java encoding names are allowed, then the
1909     * encoding name may be a Java encoding name;
1910     * otherwise, it is an ianaEncoding name.
1911     * @param isBigEndian For encodings (like uCS-4), whose names cannot
1912     * specify a byte order, this tells whether the order is bigEndian. null menas
1913     * unknown or not relevant.
1914     *
1915     * @return Returns a reader.
1916     */

1917    protected Reader JavaDoc createReader(InputStream JavaDoc inputStream, String JavaDoc encoding, Boolean JavaDoc isBigEndian)
1918        throws IOException JavaDoc {
1919
1920        // normalize encoding name
1921
if (encoding == null) {
1922            encoding = "UTF-8";
1923        }
1924
1925        // try to use an optimized reader
1926
String JavaDoc ENCODING = encoding.toUpperCase(Locale.ENGLISH);
1927        if (ENCODING.equals("UTF-8")) {
1928            if (DEBUG_ENCODINGS) {
1929                System.out.println("$$$ creating UTF8Reader");
1930            }
1931            return new UTF8Reader(inputStream, fBufferSize, fErrorReporter.getMessageFormatter(XMLMessageFormatter.XML_DOMAIN), fErrorReporter.getLocale() );
1932        }
1933        if(ENCODING.equals("ISO-10646-UCS-4")) {
1934            if(isBigEndian != null) {
1935                boolean isBE = isBigEndian.booleanValue();
1936                if(isBE) {
1937                    return new UCSReader(inputStream, UCSReader.UCS4BE);
1938                } else {
1939                    return new UCSReader(inputStream, UCSReader.UCS4LE);
1940                }
1941            } else {
1942                fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1943                                       "EncodingByteOrderUnsupported",
1944                                       new Object JavaDoc[] { encoding },
1945                                       XMLErrorReporter.SEVERITY_FATAL_ERROR);
1946            }
1947        }
1948        if(ENCODING.equals("ISO-10646-UCS-2")) {
1949            if(isBigEndian != null) { // sould never happen with this encoding...
1950
boolean isBE = isBigEndian.booleanValue();
1951                if(isBE) {
1952                    return new UCSReader(inputStream, UCSReader.UCS2BE);
1953                } else {
1954                    return new UCSReader(inputStream, UCSReader.UCS2LE);
1955                }
1956            } else {
1957                fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1958                                       "EncodingByteOrderUnsupported",
1959                                       new Object JavaDoc[] { encoding },
1960                                       XMLErrorReporter.SEVERITY_FATAL_ERROR);
1961            }
1962        }
1963
1964        // check for valid name
1965
boolean validIANA = XMLChar.isValidIANAEncoding(encoding);
1966        boolean validJava = XMLChar.isValidJavaEncoding(encoding);
1967        if (!validIANA || (fAllowJavaEncodings && !validJava)) {
1968            fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1969                                       "EncodingDeclInvalid",
1970                                       new Object JavaDoc[] { encoding },
1971                                       XMLErrorReporter.SEVERITY_FATAL_ERROR);
1972            // NOTE: AndyH suggested that, on failure, we use ISO Latin 1
1973
// because every byte is a valid ISO Latin 1 character.
1974
// It may not translate correctly but if we failed on
1975
// the encoding anyway, then we're expecting the content
1976
// of the document to be bad. This will just prevent an
1977
// invalid UTF-8 sequence to be detected. This is only
1978
// important when continue-after-fatal-error is turned
1979
// on. -Ac
1980
encoding = "ISO-8859-1";
1981        }
1982
1983        // try to use a Java reader
1984
String JavaDoc javaEncoding = EncodingMap.getIANA2JavaMapping(ENCODING);
1985        if (javaEncoding == null) {
1986            if(fAllowJavaEncodings) {
1987            javaEncoding = encoding;
1988            } else {
1989                fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1990                                       "EncodingDeclInvalid",
1991                                       new Object JavaDoc[] { encoding },
1992                                       XMLErrorReporter.SEVERITY_FATAL_ERROR);
1993                // see comment above.
1994
javaEncoding = "ISO8859_1";
1995            }
1996        }
1997        else if (javaEncoding.equals("ASCII")) {
1998            if (DEBUG_ENCODINGS) {
1999                System.out.println("$$$ creating ASCIIReader");
2000            }
2001            return new ASCIIReader(inputStream, fBufferSize, fErrorReporter.getMessageFormatter(XMLMessageFormatter.XML_DOMAIN), fErrorReporter.getLocale());
2002        }
2003        
2004        
2005        if (DEBUG_ENCODINGS) {
2006            System.out.print("$$$ creating Java InputStreamReader: encoding="+javaEncoding);
2007            if (javaEncoding == encoding) {
2008                System.out.print(" (IANA encoding)");
2009            }
2010            System.out.println();
2011        }
2012        return new InputStreamReader JavaDoc(inputStream, javaEncoding);
2013
2014    } // createReader(InputStream,String, Boolean): Reader
2015

2016    //
2017
// Protected static methods
2018
//
2019

2020    /**
2021     * Fixes a platform dependent filename to standard URI form.
2022     *
2023     * @param str The string to fix.
2024     *
2025     * @return Returns the fixed URI string.
2026     */

2027    protected static String JavaDoc fixURI(String JavaDoc str) {
2028
2029        // handle platform dependent strings
2030
str = str.replace(java.io.File.separatorChar, '/');
2031
2032        StringBuffer JavaDoc sb = null;
2033
2034        // Windows fix
2035
if (str.length() >= 2) {
2036            char ch1 = str.charAt(1);
2037            // change "C:blah" to "/C:blah"
2038
if (ch1 == ':') {
2039                char ch0 = Character.toUpperCase(str.charAt(0));
2040                if (ch0 >= 'A' && ch0 <= 'Z') {
2041                    sb = new StringBuffer JavaDoc(str.length());
2042                    sb.append('/');
2043                }
2044            }
2045            // change "//blah" to "file://blah"
2046
else if (ch1 == '/' && str.charAt(0) == '/') {
2047                sb = new StringBuffer JavaDoc(str.length());
2048                sb.append("file:");
2049            }
2050        }
2051
2052        int pos = str.indexOf(' ');
2053        // there is no space in the string
2054
// we just append "str" to the end of sb
2055
if (pos < 0) {
2056            if (sb != null) {
2057                sb.append(str);
2058                str = sb.toString();
2059            }
2060        }
2061        // otherwise, convert all ' ' to "%20".
2062
// Note: the following algorithm might not be very performant,
2063
// but people who want to use invalid URI's have to pay the price.
2064
else {
2065            if (sb == null)
2066                sb = new StringBuffer JavaDoc(str.length());
2067            // put characters before ' ' into the string buffer
2068
for (int i = 0; i < pos; i++)
2069                sb.append(str.charAt(i));
2070            // and %20 for the space
2071
sb.append("%20");
2072            // for the remamining part, also convert ' ' to "%20".
2073
for (int i = pos+1; i < str.length(); i++) {
2074                if (str.charAt(i) == ' ')
2075                    sb.append("%20");
2076                else
2077                    sb.append(str.charAt(i));
2078            }
2079            str = sb.toString();
2080        }
2081
2082        // done
2083
return str;
2084
2085    } // fixURI(String):String
2086

2087    //
2088
// Package visible methods
2089
//
2090

2091    /**
2092     * Returns the hashtable of declared entities.
2093     * <p>
2094     * <strong>REVISIT:</strong>
2095     * This should be done the "right" way by designing a better way to
2096     * enumerate the declared entities. For now, this method is needed
2097     * by the constructor that takes an XMLEntityManager parameter.
2098     */

2099    Hashtable JavaDoc getDeclaredEntities() {
2100        return fEntities;
2101    } // getDeclaredEntities():Hashtable
2102

2103    /** Prints the contents of the buffer. */
2104    static final void print(ScannedEntity currentEntity) {
2105        if (DEBUG_BUFFER) {
2106            if (currentEntity != null) {
2107                System.out.print('[');
2108                System.out.print(currentEntity.count);
2109                System.out.print(' ');
2110                System.out.print(currentEntity.position);
2111                if (currentEntity.count > 0) {
2112                    System.out.print(" \"");
2113                    for (int i = 0; i < currentEntity.count; i++) {
2114                        if (i == currentEntity.position) {
2115                            System.out.print('^');
2116                        }
2117                        char c = currentEntity.ch[i];
2118                        switch (c) {
2119                            case '\n': {
2120                                System.out.print("\\n");
2121                                break;
2122                            }
2123                            case '\r': {
2124                                System.out.print("\\r");
2125                                break;
2126                            }
2127                            case '\t': {
2128                                System.out.print("\\t");
2129                                break;
2130                            }
2131                            case '\\': {
2132                                System.out.print("\\\\");
2133                                break;
2134                            }
2135                            default: {
2136                                System.out.print(c);
2137                            }
2138                        }
2139                    }
2140                    if (currentEntity.position == currentEntity.count) {
2141                        System.out.print('^');
2142                    }
2143                    System.out.print('"');
2144                }
2145                System.out.print(']');
2146                System.out.print(" @ ");
2147                System.out.print(currentEntity.lineNumber);
2148                System.out.print(',');
2149                System.out.print(currentEntity.columnNumber);
2150            }
2151            else {
2152                System.out.print("*NO CURRENT ENTITY*");
2153            }
2154        }
2155    } // print(ScannedEntity)
2156

2157    //
2158
// Classes
2159
//
2160

2161    /**
2162     * Entity information.
2163     *
2164     * @author Andy Clark, IBM
2165     */

2166    public static abstract class Entity {
2167
2168        //
2169
// Data
2170
//
2171

2172        /** Entity name. */
2173        public String JavaDoc name;
2174
2175        // whether this entity's declaration was found in the internal
2176
// or external subset
2177
public boolean inExternalSubset;
2178
2179        //
2180
// Constructors
2181
//
2182

2183        /** Default constructor. */
2184        public Entity() {
2185            clear();
2186        } // <init>()
2187

2188        /** Constructs an entity. */
2189        public Entity(String JavaDoc name, boolean inExternalSubset) {
2190            this.name = name;
2191            this.inExternalSubset = inExternalSubset;
2192        } // <init>(String)
2193

2194        //
2195
// Public methods
2196
//
2197

2198        /** Returns true if this entity was declared in the external subset. */
2199        public boolean isEntityDeclInExternalSubset () {
2200            return inExternalSubset;
2201        }
2202
2203        /** Returns true if this is an external entity. */
2204        public abstract boolean isExternal();
2205
2206        /** Returns true if this is an unparsed entity. */
2207        public abstract boolean isUnparsed();
2208
2209        /** Clears the entity. */
2210        public void clear() {
2211            name = null;
2212            inExternalSubset = false;
2213        } // clear()
2214

2215        /** Sets the values of the entity. */
2216        public void setValues(Entity entity) {
2217            name = entity.name;
2218            inExternalSubset = entity.inExternalSubset;
2219        } // setValues(Entity)
2220

2221    } // class Entity
2222

2223    /**
2224     * Internal entity.
2225     *
2226     * @author Andy Clark, IBM
2227     */

2228    protected static class InternalEntity
2229        extends Entity {
2230
2231        //
2232
// Data
2233
//
2234

2235        /** Text value of entity. */
2236        public String JavaDoc text;
2237
2238        //
2239
// Constructors
2240
//
2241

2242        /** Default constructor. */
2243        public InternalEntity() {
2244            clear();
2245        } // <init>()
2246

2247        /** Constructs an internal entity. */
2248        public InternalEntity(String JavaDoc name, String JavaDoc text, boolean inExternalSubset) {
2249            super(name,inExternalSubset);
2250            this.text = text;
2251        } // <init>(String,String)
2252

2253        //
2254
// Entity methods
2255
//
2256

2257        /** Returns true if this is an external entity. */
2258        public final boolean isExternal() {
2259            return false;
2260        } // isExternal():boolean
2261

2262        /** Returns true if this is an unparsed entity. */
2263        public final boolean isUnparsed() {
2264            return false;
2265        } // isUnparsed():boolean
2266

2267        /** Clears the entity. */
2268        public void clear() {
2269            super.clear();
2270            text = null;
2271        } // clear()
2272

2273        /** Sets the values of the entity. */
2274        public void setValues(Entity entity) {
2275            super.setValues(entity);
2276            text = null;
2277        } // setValues(Entity)
2278

2279        /** Sets the values of the entity. */
2280        public void setValues(InternalEntity entity) {
2281            super.setValues(entity);
2282            text = entity.text;
2283        } // setValues(InternalEntity)
2284

2285    } // class InternalEntity
2286

2287    /**
2288     * External entity.
2289     *
2290     * @author Andy Clark, IBM
2291     */

2292    protected static class ExternalEntity
2293        extends Entity {
2294
2295        //
2296
// Data
2297
//
2298

2299        /** container for all relevant entity location information. */
2300        public XMLResourceIdentifier entityLocation;
2301
2302        /** Notation name for unparsed entity. */
2303        public String JavaDoc notation;
2304
2305        //
2306
// Constructors
2307
//
2308

2309        /** Default constructor. */
2310        public ExternalEntity() {
2311            clear();
2312        } // <init>()
2313

2314        /** Constructs an internal entity. */
2315        public ExternalEntity(String JavaDoc name, XMLResourceIdentifier entityLocation,
2316                              String JavaDoc notation, boolean inExternalSubset) {
2317            super(name,inExternalSubset);
2318            this.entityLocation = entityLocation;
2319            this.notation = notation;
2320        } // <init>(String,XMLResourceIdentifier, String)
2321

2322        //
2323
// Entity methods
2324
//
2325

2326        /** Returns true if this is an external entity. */
2327        public final boolean isExternal() {
2328            return true;
2329        } // isExternal():boolean
2330

2331        /** Returns true if this is an unparsed entity. */
2332        public final boolean isUnparsed() {
2333            return notation != null;
2334        } // isUnparsed():boolean
2335

2336        /** Clears the entity. */
2337        public void clear() {
2338            super.clear();
2339            entityLocation = null;
2340            notation = null;
2341        } // clear()
2342

2343        /** Sets the values of the entity. */
2344        public void setValues(Entity entity) {
2345            super.setValues(entity);
2346            entityLocation = null;
2347            notation = null;
2348        } // setValues(Entity)
2349

2350        /** Sets the values of the entity. */
2351        public void setValues(ExternalEntity entity) {
2352            super.setValues(entity);
2353            entityLocation = entity.entityLocation;
2354            notation = entity.notation;
2355        } // setValues(ExternalEntity)
2356

2357    } // class ExternalEntity
2358

2359    /**
2360     * Entity state.
2361     *
2362     * @author Andy Clark, IBM
2363     */

2364    public class ScannedEntity
2365        extends Entity {
2366
2367        //
2368
// Data
2369
//
2370

2371        // i/o
2372

2373        /** Input stream. */
2374        public InputStream JavaDoc stream;
2375
2376        /** Reader. */
2377        public Reader JavaDoc reader;
2378
2379        // locator information
2380

2381        /** entity location information */
2382        public XMLResourceIdentifier entityLocation;
2383
2384        /** Line number. */
2385        public int lineNumber = 1;
2386
2387        /** Column number. */
2388        public int columnNumber = 1;
2389
2390        // encoding
2391

2392        /** Auto-detected encoding. */
2393        public String JavaDoc encoding;
2394
2395        /** Encoding has been set externally for eg: using DOMInput*/
2396        boolean declaredEncoding = false;
2397                
2398        // status
2399

2400        /** True if in a literal. */
2401        public boolean literal;
2402
2403        // whether this is an external or internal scanned entity
2404
public boolean isExternal;
2405
2406        // buffer
2407

2408        /** Character buffer. */
2409        public char[] ch = null;
2410
2411        /** Position in character buffer. */
2412        public int position;
2413
2414        /** Count of characters in buffer. */
2415        public int count;
2416
2417        // to allow the reader/inputStream to behave efficiently:
2418
public boolean mayReadChunks;
2419
2420        //
2421
// Constructors
2422
//
2423

2424        /** Constructs a scanned entity. */
2425        public ScannedEntity(String JavaDoc name,
2426                             XMLResourceIdentifier entityLocation,
2427                             InputStream JavaDoc stream, Reader JavaDoc reader,
2428                             String JavaDoc encoding, boolean literal, boolean mayReadChunks, boolean isExternal) {
2429            super(name,XMLEntityManager.this.fInExternalSubset);
2430            this.entityLocation = entityLocation;
2431            this.stream = stream;
2432            this.reader = reader;
2433            this.encoding = encoding;
2434            this.literal = literal;
2435            this.mayReadChunks = mayReadChunks;
2436            this.isExternal = isExternal;
2437            this.ch = new char[isExternal ? fBufferSize : DEFAULT_INTERNAL_BUFFER_SIZE];
2438        } // <init>(StringXMLResourceIdentifier,InputStream,Reader,String,boolean, boolean)
2439

2440        //
2441
// Entity methods
2442
//
2443

2444        /** Returns true if this is an external entity. */
2445        public final boolean isExternal() {
2446            return isExternal;
2447        } // isExternal():boolean
2448

2449        /** Returns true if this is an unparsed entity. */
2450        public final boolean isUnparsed() {
2451            return false;
2452        } // isUnparsed():boolean
2453

2454        public void setReader(InputStream JavaDoc stream, String JavaDoc encoding, Boolean JavaDoc isBigEndian) throws IOException JavaDoc {
2455            reader = createReader(stream, encoding, isBigEndian);
2456        }
2457
2458        // return the expanded system ID of the
2459
// first external entity on the stack, null
2460
// otherwise.
2461
public String JavaDoc getExpandedSystemId() {
2462
2463            // search for the first external entity on the stack
2464
int size = fEntityStack.size();
2465            for (int i = size - 1; i >= 0 ; i--) {
2466               ScannedEntity externalEntity =
2467                    (ScannedEntity)fEntityStack.elementAt(i);
2468
2469                if (externalEntity.entityLocation != null &&
2470                        externalEntity.entityLocation.getExpandedSystemId() != null) {
2471                    return externalEntity.entityLocation.getExpandedSystemId();
2472                }
2473            }
2474            return null;
2475        }
2476
2477        // return literal systemId of
2478
// nearest external entity
2479
public String JavaDoc getLiteralSystemId() {
2480            // search for the first external entity on the stack
2481
int size = fEntityStack.size();
2482            for (int i = size - 1; i >= 0 ; i--) {
2483               ScannedEntity externalEntity =
2484                    (ScannedEntity)fEntityStack.elementAt(i);
2485
2486                if (externalEntity.entityLocation != null &&
2487                        externalEntity.entityLocation.getLiteralSystemId() != null) {
2488                    return externalEntity.entityLocation.getLiteralSystemId();
2489                }
2490            }
2491            return null;
2492        }
2493
2494        // return line number of position in most
2495
// recent external entity
2496
public int getLineNumber() {
2497            // search for the first external entity on the stack
2498
int size = fEntityStack.size();
2499            for (int i=size-1; i>0 ; i--) {
2500               ScannedEntity firstExternalEntity = (ScannedEntity)fEntityStack.elementAt(i);
2501                if (firstExternalEntity.isExternal()) {
2502                    return firstExternalEntity.lineNumber;
2503                }
2504            }
2505            return -1;
2506        }
2507
2508        // return column number of position in most
2509
// recent external entity
2510
public int getColumnNumber() {
2511            // search for the first external entity on the stack
2512
int size = fEntityStack.size();
2513            for (int i=size-1; i>0 ; i--) {
2514               ScannedEntity firstExternalEntity = (ScannedEntity)fEntityStack.elementAt(i);
2515                if (firstExternalEntity.isExternal()) {
2516                    return firstExternalEntity.columnNumber;
2517                }
2518            }
2519            return -1;
2520        }
2521
2522        // return encoding of most recent external entity
2523
public String JavaDoc getEncoding() {
2524            // search for the first external entity on the stack
2525
int size = fEntityStack.size();
2526            for (int i=size-1; i>0 ; i--) {
2527               ScannedEntity firstExternalEntity = (ScannedEntity)fEntityStack.elementAt(i);
2528                if (firstExternalEntity.isExternal()) {
2529                    return firstExternalEntity.encoding;
2530                }
2531            }
2532            return null;
2533        }
2534
2535        //
2536
// Object methods
2537
//
2538

2539        /** Returns a string representation of this object. */
2540        public String JavaDoc toString() {
2541
2542            StringBuffer JavaDoc str = new StringBuffer JavaDoc();
2543            str.append("name=\""+name+'"');
2544            str.append(",ch=");
2545            str.append(ch);
2546            str.append(",position="+position);
2547            str.append(",count="+count);
2548            return str.toString();
2549
2550        } // toString():String
2551

2552        public boolean isDeclaredEncoding() {
2553            return declaredEncoding;
2554        }
2555       
2556        public void setDeclaredEncoding(boolean value) {
2557            declaredEncoding = value;
2558        }
2559
2560    } // class ScannedEntity
2561

2562    // This class wraps the byte inputstreams we're presented with.
2563
// We need it because java.io.InputStreams don't provide
2564
// functionality to reread processed bytes, and they have a habit
2565
// of reading more than one character when you call their read()
2566
// methods. This means that, once we discover the true (declared)
2567
// encoding of a document, we can neither backtrack to read the
2568
// whole doc again nor start reading where we are with a new
2569
// reader.
2570
//
2571
// This class allows rewinding an inputStream by allowing a mark
2572
// to be set, and the stream reset to that position. <strong>The
2573
// class assumes that it needs to read one character per
2574
// invocation when it's read() method is inovked, but uses the
2575
// underlying InputStream's read(char[], offset length) method--it
2576
// won't buffer data read this way!</strong>
2577
//
2578
// @author Neil Graham, IBM
2579
// @author Glenn Marcy, IBM
2580

2581    protected final class RewindableInputStream extends InputStream JavaDoc {
2582
2583        private InputStream JavaDoc fInputStream;
2584        private byte[] fData;
2585        private int fStartOffset;
2586        private int fEndOffset;
2587        private int fOffset;
2588        private int fLength;
2589        private int fMark;
2590
2591        public RewindableInputStream(InputStream JavaDoc is) {
2592            fData = new byte[DEFAULT_XMLDECL_BUFFER_SIZE];
2593            fInputStream = is;
2594            fStartOffset = 0;
2595            fEndOffset = -1;
2596            fOffset = 0;
2597            fLength = 0;
2598            fMark = 0;
2599        }
2600
2601        public void setStartOffset(int offset) {
2602            fStartOffset = offset;
2603        }
2604
2605        public void rewind() {
2606            fOffset = fStartOffset;
2607        }
2608
2609        public int read() throws IOException JavaDoc {
2610            int b = 0;
2611            if (fOffset < fLength) {
2612                return fData[fOffset++] & 0xff;
2613            }
2614            if (fOffset == fEndOffset) {
2615                return -1;
2616            }
2617            if (fOffset == fData.length) {
2618                byte[] newData = new byte[fOffset << 1];
2619                System.arraycopy(fData, 0, newData, 0, fOffset);
2620                fData = newData;
2621            }
2622            b = fInputStream.read();
2623            if (b == -1) {
2624                fEndOffset = fOffset;
2625                return -1;
2626            }
2627            fData[fLength++] = (byte)b;
2628            fOffset++;
2629            return b & 0xff;
2630        }
2631
2632        public int read(byte[] b, int off, int len) throws IOException JavaDoc {
2633            int bytesLeft = fLength - fOffset;
2634            if (bytesLeft == 0) {
2635                if (fOffset == fEndOffset) {
2636                    return -1;
2637                }
2638                // better get some more for the voracious reader...
2639
if(fCurrentEntity.mayReadChunks) {
2640                    return fInputStream.read(b, off, len);
2641                }
2642                int returnedVal = read();
2643                if(returnedVal == -1) {
2644                    fEndOffset = fOffset;
2645                    return -1;
2646                }
2647                b[off] = (byte)returnedVal;
2648                return 1;
2649            }
2650            if (len < bytesLeft) {
2651                if (len <= 0) {
2652                    return 0;
2653                }
2654            }
2655            else {
2656                len = bytesLeft;
2657            }
2658            if (b != null) {
2659                System.arraycopy(fData, fOffset, b, off, len);
2660            }
2661            fOffset += len;
2662            return len;
2663        }
2664
2665        public long skip(long n)
2666            throws IOException JavaDoc
2667        {
2668            int bytesLeft;
2669            if (n <= 0) {
2670                return 0;
2671            }
2672            bytesLeft = fLength - fOffset;
2673            if (bytesLeft == 0) {
2674                if (fOffset == fEndOffset) {
2675                    return 0;
2676                }
2677                return fInputStream.skip(n);
2678            }
2679            if (n <= bytesLeft) {
2680                fOffset += n;
2681                return n;
2682            }
2683            fOffset += bytesLeft;
2684            if (fOffset == fEndOffset) {
2685                return bytesLeft;
2686            }
2687            n -= bytesLeft;
2688           /*
2689            * In a manner of speaking, when this class isn't permitting more
2690            * than one byte at a time to be read, it is "blocking". The
2691            * available() method should indicate how much can be read without
2692            * blocking, so while we're in this mode, it should only indicate
2693            * that bytes in its buffer are available; otherwise, the result of
2694            * available() on the underlying InputStream is appropriate.
2695            */

2696            return fInputStream.skip(n) + bytesLeft;
2697        }
2698
2699        public int available() throws IOException JavaDoc {
2700            int bytesLeft = fLength - fOffset;
2701            if (bytesLeft == 0) {
2702                if (fOffset == fEndOffset) {
2703                    return -1;
2704                }
2705                return fCurrentEntity.mayReadChunks ? fInputStream.available()
2706                                                    : 0;
2707            }
2708            return bytesLeft;
2709        }
2710
2711        public void mark(int howMuch) {
2712            fMark = fOffset;
2713        }
2714
2715        public void reset() {
2716            fOffset = fMark;
2717        }
2718
2719        public boolean markSupported() {
2720            return true;
2721        }
2722
2723        public void close() throws IOException JavaDoc {
2724            if (fInputStream != null) {
2725                fInputStream.close();
2726                fInputStream = null;
2727            }
2728        }
2729    } // end of RewindableInputStream class
2730

2731} // class XMLEntityManager
2732
Popular Tags