/[classpath]/classpath/gnu/xml/aelfred2/XmlParser.java
ViewVC logotype

Diff of /classpath/gnu/xml/aelfred2/XmlParser.java

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.1.2.1 by gnu_andrew, Sun Jan 16 15:15:09 2005 UTC revision 1.1.2.2 by gnu_andrew, Sun Mar 13 14:38:33 2005 UTC
# Line 53  Partly derived from code which carried t Line 53  Partly derived from code which carried t
53    
54  package gnu.xml.aelfred2;  package gnu.xml.aelfred2;
55    
56    import gnu.java.security.action.GetPropertyAction;
57    
58  import java.io.BufferedInputStream;  import java.io.BufferedInputStream;
59  import java.io.CharConversionException;  import java.io.CharConversionException;
60  import java.io.EOFException;  import java.io.EOFException;
# Line 63  import java.io.Reader; Line 65  import java.io.Reader;
65  import java.io.UnsupportedEncodingException;  import java.io.UnsupportedEncodingException;
66  import java.net.URL;  import java.net.URL;
67  import java.net.URLConnection;  import java.net.URLConnection;
68    import java.security.AccessController;
69    
70  // maintaining 1.1 compatibility for now ...  import java.util.Iterator;
71  // Iterator and Hashmap ought to be faster  import java.util.HashMap;
72  import java.util.Enumeration;  import java.util.LinkedList;
 import java.util.Hashtable;  
 import java.util.Stack;  
73    
74  import org.xml.sax.InputSource;  import org.xml.sax.InputSource;
75  import org.xml.sax.SAXException;  import org.xml.sax.SAXException;
# Line 86  import org.xml.sax.SAXException; Line 87  import org.xml.sax.SAXException;
87   */   */
88  final class XmlParser  final class XmlParser
89  {  {
     // avoid slow per-character readCh()  
     private final static boolean USE_CHEATS = true;  
   
   
     //////////////////////////////////////////////////////////////////////  
     // Constructors.  
     ////////////////////////////////////////////////////////////////////////  
   
   
     /**  
      * Construct a new parser with no associated handler.  
      * @see #setHandler  
      * @see #parse  
      */  
     // package private  
     XmlParser ()  
     {  
     }  
   
   
     /**  
      * Set the handler that will receive parsing events.  
      * @param handler The handler to receive callback events.  
      * @see #parse  
      */  
     // package private  
     void setHandler (SAXDriver handler)  
     {  
         this.handler = handler;  
     }  
   
   
     /**  
      * Parse an XML document from the character stream, byte stream, or URI  
      * that you provide (in that order of preference).  Any URI that you  
      * supply will become the base URI for resolving relative URI, and may  
      * be used to acquire a reader or byte stream.  
      *  
      * <p> Only one thread at a time may use this parser; since it is  
      * private to this package, post-parse cleanup is done by the caller,  
      * which MUST NOT REUSE the parser (just null it).  
      *  
      * @param systemId Absolute URI of the document; should never be null,  
      *  but may be so iff a reader <em>or</em> a stream is provided.  
      * @param publicId The public identifier of the document, or null.  
      * @param reader A character stream; must be null if stream isn't.  
      * @param stream A byte input stream; must be null if reader isn't.  
      * @param encoding The suggested encoding, or null if unknown.  
      * @exception java.lang.Exception Basically SAXException or IOException  
      */  
     // package private  
     void doParse (  
         String          systemId,  
         String          publicId,  
         Reader          reader,  
         InputStream     stream,  
         String          encoding  
     ) throws Exception  
     {  
         if (handler == null)  
             throw new IllegalStateException ("no callback handler");  
   
         initializeVariables ();  
   
         // predeclare the built-in entities here (replacement texts)  
         // we don't need to intern(), since we're guaranteed literals  
         // are always (globally) interned.  
         setInternalEntity ("amp", "&#38;");  
         setInternalEntity ("lt", "&#60;");  
         setInternalEntity ("gt", "&#62;");  
         setInternalEntity ("apos", "&#39;");  
         setInternalEntity ("quot", "&#34;");  
   
         try {  
             // pushURL first to ensure locator is correct in startDocument  
             // ... it might report an IO or encoding exception.  
             handler.startDocument ();  
             pushURL (false, "[document]",  
                         // default baseURI: null  
                     new String [] { publicId, systemId, null},  
                     reader, stream, encoding, false);  
   
             parseDocument ();  
         } catch (EOFException e){  
             //empty input  
             error("empty document, with no root element.");  
         }finally {  
             if (reader != null)  
                 try { reader.close ();  
                 } catch (IOException e) { /* ignore */ }  
             if (stream != null)  
                 try { stream.close ();  
                 } catch (IOException e) { /* ignore */ }  
             if (is != null)  
                 try { is.close ();  
                 } catch (IOException e) { /* ignore */ }  
             if (reader != null)  
                 try {  
                     reader.close ();  
                 } catch (IOException e) { /* ignore */  
                 }  
             scratch = null;  
         }  
     }  
   
   
     ////////////////////////////////////////////////////////////////////////  
     // Constants.  
     ////////////////////////////////////////////////////////////////////////  
   
     //  
     // Constants for element content type.  
     //  
   
     /**  
      * Constant: an element has not been declared.  
      * @see #getElementContentType  
      */  
     public final static int CONTENT_UNDECLARED = 0;  
   
     /**  
      * Constant: the element has a content model of ANY.  
      * @see #getElementContentType  
      */  
     public final static int CONTENT_ANY = 1;  
   
     /**  
      * Constant: the element has declared content of EMPTY.  
      * @see #getElementContentType  
      */  
     public final static int CONTENT_EMPTY = 2;  
90    
91      /**    // avoid slow per-character readCh()
92       * Constant: the element has mixed content.    private final static boolean USE_CHEATS = true;
      * @see #getElementContentType  
      */  
     public final static int CONTENT_MIXED = 3;  
   
     /**  
      * Constant: the element has element content.  
      * @see #getElementContentType  
      */  
     public final static int CONTENT_ELEMENTS = 4;  
   
   
     //  
     // Constants for the entity type.  
     //  
   
     /**  
      * Constant: the entity has not been declared.  
      * @see #getEntityType  
      */  
     public final static int ENTITY_UNDECLARED = 0;  
   
     /**  
      * Constant: the entity is internal.  
      * @see #getEntityType  
      */  
     public final static int ENTITY_INTERNAL = 1;  
   
     /**  
      * Constant: the entity is external, non-parsable data.  
      * @see #getEntityType  
      */  
     public final static int ENTITY_NDATA = 2;  
   
     /**  
      * Constant: the entity is external XML data.  
      * @see #getEntityType  
      */  
     public final static int ENTITY_TEXT = 3;  
   
   
     //  
     // Attribute type constants are interned literal strings.  
     //  
   
     //  
     // Constants for supported encodings.  "external" is just a flag.  
     //  
     private final static int ENCODING_EXTERNAL = 0;  
     private final static int ENCODING_UTF_8 = 1;  
     private final static int ENCODING_ISO_8859_1 = 2;  
     private final static int ENCODING_UCS_2_12 = 3;  
     private final static int ENCODING_UCS_2_21 = 4;  
     private final static int ENCODING_UCS_4_1234 = 5;  
     private final static int ENCODING_UCS_4_4321 = 6;  
     private final static int ENCODING_UCS_4_2143 = 7;  
     private final static int ENCODING_UCS_4_3412 = 8;  
     private final static int ENCODING_ASCII = 9;  
   
   
     //  
     // Constants for attribute default value.  
     //  
   
     /**  
      * Constant: the attribute is not declared.  
      * @see #getAttributeDefaultValueType  
      */  
     public final static int ATTRIBUTE_DEFAULT_UNDECLARED = 30;  
   
     /**  
      * Constant: the attribute has a literal default value specified.  
      * @see #getAttributeDefaultValueType  
      * @see #getAttributeDefaultValue  
      */  
     public final static int ATTRIBUTE_DEFAULT_SPECIFIED = 31;  
   
     /**  
      * Constant: the attribute was declared #IMPLIED.  
      * @see #getAttributeDefaultValueType  
      */  
     public final static int ATTRIBUTE_DEFAULT_IMPLIED = 32;  
   
     /**  
      * Constant: the attribute was declared #REQUIRED.  
      * @see #getAttributeDefaultValueType  
      */  
     public final static int ATTRIBUTE_DEFAULT_REQUIRED = 33;  
   
     /**  
      * Constant: the attribute was declared #FIXED.  
      * @see #getAttributeDefaultValueType  
      * @see #getAttributeDefaultValue  
      */  
     public final static int ATTRIBUTE_DEFAULT_FIXED = 34;  
   
   
     //  
     // Constants for input.  
     //  
     private final static int INPUT_NONE = 0;  
     private final static int INPUT_INTERNAL = 1;  
     private final static int INPUT_STREAM = 3;  
     private final static int INPUT_READER = 5;  
   
   
     //  
     // Flags for reading literals.  
     //  
         // expand general entity refs (attribute values in dtd and content)  
     private final static int LIT_ENTITY_REF = 2;  
         // normalize this value (space chars) (attributes, public ids)  
     private final static int LIT_NORMALIZE = 4;  
         // literal is an attribute value  
     private final static int LIT_ATTRIBUTE = 8;  
         // don't expand parameter entities  
     private final static int LIT_DISABLE_PE = 16;  
         // don't expand [or parse] character refs  
     private final static int LIT_DISABLE_CREF = 32;  
         // don't parse general entity refs  
     private final static int LIT_DISABLE_EREF = 64;  
         // literal is a public ID value  
     private final static int LIT_PUBID = 256;  
93    
94      ////////////////////////////////////////////////////////////////////////
95      // Constants.
96      ////////////////////////////////////////////////////////////////////////
97      
98      //
99      // Constants for element content type.
100      //
101      
102      /**
103       * Constant: an element has not been declared.
104       * @see #getElementContentType
105       */
106      public final static int CONTENT_UNDECLARED = 0;
107      
108      /**
109       * Constant: the element has a content model of ANY.
110       * @see #getElementContentType
111       */
112      public final static int CONTENT_ANY = 1;
113      
114      /**
115       * Constant: the element has declared content of EMPTY.
116       * @see #getElementContentType
117       */
118      public final static int CONTENT_EMPTY = 2;
119      
120      /**
121       * Constant: the element has mixed content.
122       * @see #getElementContentType
123       */
124      public final static int CONTENT_MIXED = 3;
125      
126      /**
127       * Constant: the element has element content.
128       * @see #getElementContentType
129       */
130      public final static int CONTENT_ELEMENTS = 4;
131      
132      
133      //
134      // Constants for the entity type.
135      //
136      
137      /**
138       * Constant: the entity has not been declared.
139       * @see #getEntityType
140       */
141      public final static int ENTITY_UNDECLARED = 0;
142      
143      /**
144       * Constant: the entity is internal.
145       * @see #getEntityType
146       */
147      public final static int ENTITY_INTERNAL = 1;
148      
149      /**
150       * Constant: the entity is external, non-parsable data.
151       * @see #getEntityType
152       */
153      public final static int ENTITY_NDATA = 2;
154      
155      /**
156       * Constant: the entity is external XML data.
157       * @see #getEntityType
158       */
159      public final static int ENTITY_TEXT = 3;
160        
161      //
162      // Attribute type constants are interned literal strings.
163      //
164      
165      //
166      // Constants for supported encodings.  "external" is just a flag.
167      //
168      private final static int ENCODING_EXTERNAL = 0;
169      private final static int ENCODING_UTF_8 = 1;
170      private final static int ENCODING_ISO_8859_1 = 2;
171      private final static int ENCODING_UCS_2_12 = 3;
172      private final static int ENCODING_UCS_2_21 = 4;
173      private final static int ENCODING_UCS_4_1234 = 5;
174      private final static int ENCODING_UCS_4_4321 = 6;
175      private final static int ENCODING_UCS_4_2143 = 7;
176      private final static int ENCODING_UCS_4_3412 = 8;
177      private final static int ENCODING_ASCII = 9;
178      
179      //
180      // Constants for attribute default value.
181      //
182      
183      /**
184       * Constant: the attribute is not declared.
185       * @see #getAttributeDefaultValueType
186       */
187      public final static int ATTRIBUTE_DEFAULT_UNDECLARED = 30;
188      
189      /**
190       * Constant: the attribute has a literal default value specified.
191       * @see #getAttributeDefaultValueType
192       * @see #getAttributeDefaultValue
193       */
194      public final static int ATTRIBUTE_DEFAULT_SPECIFIED = 31;
195      
196      /**
197       * Constant: the attribute was declared #IMPLIED.
198       * @see #getAttributeDefaultValueType
199       */
200      public final static int ATTRIBUTE_DEFAULT_IMPLIED = 32;
201      
202      /**
203       * Constant: the attribute was declared #REQUIRED.
204       * @see #getAttributeDefaultValueType
205       */
206      public final static int ATTRIBUTE_DEFAULT_REQUIRED = 33;
207      
208      /**
209       * Constant: the attribute was declared #FIXED.
210       * @see #getAttributeDefaultValueType
211       * @see #getAttributeDefaultValue
212       */
213      public final static int ATTRIBUTE_DEFAULT_FIXED = 34;
214        
215      //
216      // Constants for input.
217      //
218      private final static int INPUT_NONE = 0;
219      private final static int INPUT_INTERNAL = 1;
220      private final static int INPUT_STREAM = 3;
221      private final static int INPUT_READER = 5;
222      
223      //
224      // Flags for reading literals.
225      //
226      // expand general entity refs (attribute values in dtd and content)
227      private final static int LIT_ENTITY_REF = 2;
228      // normalize this value (space chars) (attributes, public ids)
229      private final static int LIT_NORMALIZE = 4;
230      // literal is an attribute value
231      private final static int LIT_ATTRIBUTE = 8;
232      // don't expand parameter entities
233      private final static int LIT_DISABLE_PE = 16;
234      // don't expand [or parse] character refs
235      private final static int LIT_DISABLE_CREF = 32;
236      // don't parse general entity refs
237      private final static int LIT_DISABLE_EREF = 64;
238      // literal is a public ID value
239      private final static int LIT_PUBID = 256;
240        
241      //
242      // Flags affecting PE handling in DTDs (if expandPE is true).
243      // PEs expand with space padding, except inside literals.
244      //
245      private final static int CONTEXT_NORMAL = 0;
246      private final static int CONTEXT_LITERAL = 1;
247      
248      // Emit warnings for relative URIs with no base URI.
249      static boolean uriWarnings;
250      static
251      {
252        String key = "gnu.xml.aelfred2.XmlParser.uriWarnings";
253        GetPropertyAction a = new GetPropertyAction(key);
254        uriWarnings = "true".equals(AccessController.doPrivileged(a));      
255      }
256        
257      //
258      // The current XML handler interface.
259      //
260      private SAXDriver handler;
261      
262      //
263      // I/O information.
264      //
265      private Reader reader;   // current reader
266      private InputStream is;     // current input stream
267      private int line;     // current line number
268      private int column;   // current column number
269      private int sourceType;   // type of input source
270      private LinkedList inputStack;   // stack of input soruces
271      private URLConnection externalEntity; // current external entity
272      private int encoding;   // current character encoding
273      private int currentByteCount; // bytes read from current source
274      private InputSource scratch;  // temporary
275      
276      //
277      // Buffers for decoded but unparsed character input.
278      //
279      private char[] readBuffer;
280      private int readBufferPos;
281      private int readBufferLength;
282      private int readBufferOverflow;  // overflow from last data chunk.
283      
284      //
285      // Buffer for undecoded raw byte input.
286      //
287      private final static int READ_BUFFER_MAX = 16384;
288      private byte[] rawReadBuffer;
289      
290      
291      //
292      // Buffer for attribute values, char refs, DTD stuff.
293      //
294      private static int DATA_BUFFER_INITIAL = 4096;
295      private char[] dataBuffer;
296      private int dataBufferPos;
297      
298      //
299      // Buffer for parsed names.
300      //
301      private static int NAME_BUFFER_INITIAL = 1024;
302      private char[] nameBuffer;
303      private int nameBufferPos;
304      
305      //
306      // Save any standalone flag
307      //
308      private boolean docIsStandalone;
309      
310      //
311      // Hashtables for DTD information on elements, entities, and notations.
312      // Populated until we start ignoring decls (because of skipping a PE)
313      //
314      private HashMap elementInfo;
315      private HashMap entityInfo;
316      private HashMap notationInfo;
317      private boolean skippedPE;
318      
319      //
320      // Element type currently in force.
321      //
322      private String currentElement;
323      private int currentElementContent;
324      
325      //
326      // Stack of entity names, to detect recursion.
327      //
328      private LinkedList entityStack;
329      
330      //
331      // PE expansion is enabled in most chunks of the DTD, not all.
332      // When it's enabled, literals are treated differently.
333      //
334      private boolean inLiteral;
335      private boolean expandPE;
336      private boolean peIsError;
337      
338      //
339      // can't report entity expansion inside two constructs:
340      // - attribute expansions (internal entities only)
341      // - markup declarations (parameter entities only)
342      //
343      private boolean doReport;
344      
345      //
346      // Symbol table, for caching interned names.
347      //
348      // These show up wherever XML names or nmtokens are used:  naming elements,
349      // attributes, PIs, notations, entities, and enumerated attribute values.
350      //
351      // NOTE:  This hashtable doesn't grow.  The default size is intended to be
352      // rather large for most documents.  Example:  one snapshot of the DocBook
353      // XML 4.1 DTD used only about 350 such names.  As a rule, only pathological
354      // documents (ones that don't reuse names) should ever see much collision.
355      //
356      // Be sure that SYMBOL_TABLE_LENGTH always stays prime, for best hashing.
357      // "2039" keeps the hash table size at about two memory pages on typical
358      // 32 bit hardware.
359      //
360      private final static int SYMBOL_TABLE_LENGTH = 2039;
361      
362      private Object[][] symbolTable;
363      
364      //
365      // Hash table of attributes found in current start tag.
366      //
367      private String[] tagAttributes;
368      private int tagAttributePos;
369      
370      //
371      // Utility flag: have we noticed a CR while reading the last
372      // data chunk?  If so, we will have to go back and normalise
373      // CR or CR/LF line ends.
374      //
375      private boolean sawCR;
376      
377      //
378      // Utility flag: are we in CDATA?  If so, whitespace isn't ignorable.
379      //
380      private boolean inCDATA;
381      
382      //
383      // Xml version.
384      //  
385      private static final int XML_10 = 0;
386      private static final int XML_11 = 1;
387      private int xmlVersion = XML_10;
388    
389      //////////////////////////////////////////////////////////////////////
390      // Constructors.
391      ////////////////////////////////////////////////////////////////////////
392      
393      /**
394       * Construct a new parser with no associated handler.
395       * @see #setHandler
396       * @see #parse
397       */
398      // package private
399      XmlParser()
400      {
401      }
402    
403      //    /**
404      // Flags affecting PE handling in DTDs (if expandPE is true).     * Set the handler that will receive parsing events.
405      // PEs expand with space padding, except inside literals.     * @param handler The handler to receive callback events.
406      //     * @see #parse
407      private final static int CONTEXT_NORMAL = 0;     */
408      private final static int CONTEXT_LITERAL = 1;    // package private
409      void setHandler(SAXDriver handler)
410      {
411        this.handler = handler;
412      }
413    
414      /**
415       * Parse an XML document from the character stream, byte stream, or URI
416       * that you provide (in that order of preference).  Any URI that you
417       * supply will become the base URI for resolving relative URI, and may
418       * be used to acquire a reader or byte stream.
419       *
420       * <p> Only one thread at a time may use this parser; since it is
421       * private to this package, post-parse cleanup is done by the caller,
422       * which MUST NOT REUSE the parser (just null it).
423       *
424       * @param systemId Absolute URI of the document; should never be null,
425       *    but may be so iff a reader <em>or</em> a stream is provided.
426       * @param publicId The public identifier of the document, or null.
427       * @param reader A character stream; must be null if stream isn't.
428       * @param stream A byte input stream; must be null if reader isn't.
429       * @param encoding The suggested encoding, or null if unknown.
430       * @exception java.lang.Exception Basically SAXException or IOException
431       */
432      // package private
433      void doParse(String systemId, String publicId, Reader reader,
434                   InputStream stream, String encoding)
435        throws Exception
436      {
437        if (handler == null)
438          {
439            throw new IllegalStateException("no callback handler");
440          }
441    
442      //////////////////////////////////////////////////////////////////////      initializeVariables();
     // Error reporting.  
     //////////////////////////////////////////////////////////////////////  
443    
444        // predeclare the built-in entities here (replacement texts)
445        // we don't need to intern(), since we're guaranteed literals
446        // are always (globally) interned.
447        setInternalEntity("amp", "&#38;");
448        setInternalEntity("lt", "&#60;");
449        setInternalEntity("gt", "&#62;");
450        setInternalEntity("apos", "&#39;");
451        setInternalEntity("quot", "&#34;");
452    
453        try
454          {
455            // pushURL first to ensure locator is correct in startDocument
456            // ... it might report an IO or encoding exception.
457            handler.startDocument();
458            pushURL(false, "[document]",
459                    // default baseURI: null
460                    new ExternalIdentifiers(publicId, systemId, null),
461                    reader, stream, encoding, false);
462            
463            parseDocument();
464          }
465        catch (EOFException e)
466          {
467            //empty input
468            error("empty document, with no root element.");
469          }
470        finally
471          {
472            if (reader != null)
473              {
474                try
475                  {
476                    reader.close();
477                  }
478                catch (IOException e)
479                  {
480                    /* ignore */
481                  }
482              }
483            if (stream != null)
484              {
485                try
486                  {
487                    stream.close();
488                  }
489                catch (IOException e)
490                  {
491                    /* ignore */
492                  }
493              }
494            if (is != null)
495              {
496                try
497                  {
498                    is.close();
499                  }
500                catch (IOException e)
501                  {
502                    /* ignore */
503                  }
504              }
505            scratch = null;
506          }
507      }
508    
509      /**    //////////////////////////////////////////////////////////////////////
510       * Report an error.    // Error reporting.
511       * @param message The error message.    //////////////////////////////////////////////////////////////////////
512       * @param textFound The text that caused the error (or null).      
513       * @see SAXDriver#error    /**
514       * @see #line     * Report an error.
515       */     * @param message The error message.
516      private void error (String message, String textFound, String textExpected)     * @param textFound The text that caused the error (or null).
517       * @see SAXDriver#error
518       * @see #line
519       */
520      private void error(String message, String textFound, String textExpected)
521      throws SAXException      throws SAXException
522      {    {
523          if (textFound != null) {      if (textFound != null)
524              message = message + " (found \"" + textFound + "\")";        {
525          }          message = message + " (found \"" + textFound + "\")";
526          if (textExpected != null) {        }
527              message = message + " (expected \"" + textExpected + "\")";      if (textExpected != null)
528          }        {
529          handler.fatal (message);          message = message + " (expected \"" + textExpected + "\")";
530          }
531          // "can't happen"      handler.fatal(message);
532          throw new SAXException (message);      
533      }      // "can't happen"
534        throw new SAXException(message);
535      }
536    
537      /**    /**
538       * Report a serious error.     * Report a serious error.
539       * @param message The error message.     * @param message The error message.
540       * @param textFound The text that caused the error (or null).     * @param textFound The text that caused the error (or null).
541       */     */
542      private void error (String message, char textFound, String textExpected)    private void error(String message, char textFound, String textExpected)
543      throws SAXException      throws SAXException
544      {    {
545          error (message, new Character (textFound).toString (), textExpected);      error(message, new Character(textFound).toString(), textExpected);
546      }    }
547    
548      /** Report typical case fatal errors. */    /**
549      private void error (String message)     * Report typical case fatal errors.
550       */
551      private void error(String message)
552      throws SAXException      throws SAXException
553      {    {
554          handler.fatal (message);      handler.fatal(message);
555      }    }
   
   
     //////////////////////////////////////////////////////////////////////  
     // Major syntactic productions.  
     //////////////////////////////////////////////////////////////////////  
556    
557      //////////////////////////////////////////////////////////////////////
558      // Major syntactic productions.
559      //////////////////////////////////////////////////////////////////////
560    
561      /**    /**
562       * Parse an XML document.     * Parse an XML document.
563       * <pre>     * <pre>
564       * [1] document ::= prolog element Misc*     * [1] document ::= prolog element Misc*
565       * </pre>     * </pre>
566       * <p>This is the top-level parsing function for a single XML     * <p>This is the top-level parsing function for a single XML
567       * document.  As a minimum, a well-formed document must have     * document.  As a minimum, a well-formed document must have
568       * a document element, and a valid document must have a prolog     * a document element, and a valid document must have a prolog
569       * (one with doctype) as well.     * (one with doctype) as well.
570       */     */
571      private void parseDocument ()    private void parseDocument()
572      throws Exception      throws Exception
573      {    {
574          try {                                       // added by MHK      try
575              boolean sawDTD = parseProlog ();        {                                       // added by MHK
576              require ('<');          boolean sawDTD = parseProlog();
577              parseElement (!sawDTD);          require('<');
578          } catch (EOFException ee) {                 // added by MHK          parseElement(!sawDTD);
579              error("premature end of file", "[EOF]", null);        }
580          }      catch (EOFException ee)
581                  {                 // added by MHK
582          try {          error("premature end of file", "[EOF]", null);
583              parseMisc ();   //skip all white, PIs, and comments        }
584              char c = readCh ();    //if this doesn't throw an exception...      
585              error ("unexpected characters after document end", c, null);      try
586          } catch (EOFException e) {        {
587              return;          parseMisc();   //skip all white, PIs, and comments
588          }          char c = readCh();    //if this doesn't throw an exception...
589      }          error("unexpected characters after document end", c, null);
590          }
591      static final char   startDelimComment [] = { '<', '!', '-', '-' };      catch (EOFException e)
592      static final char   endDelimComment [] = { '-', '-' };        {
593            return;
594          }
595      }
596      
597      static final char[] startDelimComment = { '<', '!', '-', '-' };
598      static final char[] endDelimComment = { '-', '-' };
599    
600      /**    /**
601       * Skip a comment.     * Skip a comment.
602       * <pre>     * <pre>
603       * [15] Comment ::= '&lt;!--' ((Char - '-') | ('-' (Char - '-')))* "-->"     * [15] Comment ::= '&lt;!--' ((Char - '-') | ('-' (Char - '-')))* "-->"
604       * </pre>     * </pre>
605       * <p> (The <code>&lt;!--</code> has already been read.)     * <p> (The <code>&lt;!--</code> has already been read.)
606       */     */
607      private void parseComment ()    private void parseComment()
608      throws Exception      throws Exception
609      {    {
610          char c;      char c;
611          boolean saved = expandPE;      boolean saved = expandPE;
612        
613          expandPE = false;      expandPE = false;
614          parseUntil (endDelimComment);      parseUntil(endDelimComment);
615          require ('>');      require('>');
616          expandPE = saved;      expandPE = saved;
617          handler.comment (dataBuffer, 0, dataBufferPos);      handler.comment(dataBuffer, 0, dataBufferPos);
618          dataBufferPos = 0;      dataBufferPos = 0;
619      }    }
620      
621      static final char   startDelimPI [] = { '<', '?' };    static final char[] startDelimPI = { '<', '?' };
622      static final char   endDelimPI [] = { '?', '>' };    static final char[] endDelimPI = { '?', '>' };
623    
624      /**    /**
625       * Parse a processing instruction and do a call-back.     * Parse a processing instruction and do a call-back.
626       * <pre>     * <pre>
627       * [16] PI ::= '&lt;?' PITarget     * [16] PI ::= '&lt;?' PITarget
628       *          (S (Char* - (Char* '?&gt;' Char*)))?     *    (S (Char* - (Char* '?&gt;' Char*)))?
629       *          '?&gt;'     *    '?&gt;'
630       * [17] PITarget ::= Name - ( ('X'|'x') ('M'|m') ('L'|l') )     * [17] PITarget ::= Name - ( ('X'|'x') ('M'|m') ('L'|l') )
631       * </pre>     * </pre>
632       * <p> (The <code>&lt;?</code> has already been read.)     * <p> (The <code>&lt;?</code> has already been read.)
633       */     */
634      private void parsePI ()    private void parsePI()
635      throws SAXException, IOException      throws SAXException, IOException
636      {    {
637          String name;      String name;
638          boolean saved = expandPE;      boolean saved = expandPE;
639        
640          expandPE = false;      expandPE = false;
641          name = readNmtoken (true);      name = readNmtoken(true);
642          //NE08      //NE08
643          if (name.indexOf(':') >= 0)      if (name.indexOf(':') >= 0)
644             error ("Illegal character(':') in processing instruction name ", name, null);        {
645          if ("xml".equalsIgnoreCase (name))          error("Illegal character(':') in processing instruction name ",
646              error ("Illegal processing instruction target", name, null);                name, null);
647          if (!tryRead (endDelimPI)) {        }
648              requireWhitespace ();      if ("xml".equalsIgnoreCase(name))
649              parseUntil (endDelimPI);        {
650          }          error("Illegal processing instruction target", name, null);
651          expandPE = saved;        }
652          handler.processingInstruction (name, dataBufferToString ());      if (!tryRead(endDelimPI))
653      }        {
654            requireWhitespace();
655            parseUntil(endDelimPI);
656      static final char   endDelimCDATA [] = { ']', ']', '>' };        }
657        expandPE = saved;
658        handler.processingInstruction(name, dataBufferToString());
659      }
660      
661      static final char[] endDelimCDATA = { ']', ']', '>' };
662    
663          private boolean isDirtyCurrentElement;    private boolean isDirtyCurrentElement;
664    
665      /**    /**
666       * Parse a CDATA section.     * Parse a CDATA section.
667       * <pre>     * <pre>
668       * [18] CDSect ::= CDStart CData CDEnd     * [18] CDSect ::= CDStart CData CDEnd
669       * [19] CDStart ::= '&lt;![CDATA['     * [19] CDStart ::= '&lt;![CDATA['
670       * [20] CData ::= (Char* - (Char* ']]&gt;' Char*))     * [20] CData ::= (Char* - (Char* ']]&gt;' Char*))
671       * [21] CDEnd ::= ']]&gt;'     * [21] CDEnd ::= ']]&gt;'
672       * </pre>     * </pre>
673       * <p> (The '&lt;![CDATA[' has already been read.)     * <p> (The '&lt;![CDATA[' has already been read.)
674       */     */
675      private void parseCDSect ()    private void parseCDSect()
676      throws Exception      throws Exception
677      {    {
678          parseUntil (endDelimCDATA);      parseUntil(endDelimCDATA);
679          dataBufferFlush ();      dataBufferFlush();
680      }    }
   
681    
682      /**    /**
683       * Parse the prolog of an XML document.     * Parse the prolog of an XML document.
684       * <pre>     * <pre>
685       * [22] prolog ::= XMLDecl? Misc* (Doctypedecl Misc*)?     * [22] prolog ::= XMLDecl? Misc* (Doctypedecl Misc*)?
686       * </pre>     * </pre>
687       * <p>We do not look for the XML declaration here, because it was     * <p>We do not look for the XML declaration here, because it was
688       * handled by pushURL ().     * handled by pushURL ().
689       * @see pushURL     * @see pushURL
690       * @return true if a DTD was read.     * @return true if a DTD was read.
691       */     */
692      private boolean parseProlog ()    private boolean parseProlog()
693      throws Exception      throws Exception
694      {    {
695          parseMisc ();      parseMisc();
696    
697          if (tryRead ("<!DOCTYPE")) {      if (tryRead("<!DOCTYPE"))
698              parseDoctypedecl ();        {
699              parseMisc ();          parseDoctypedecl();
700              return true;          parseMisc();
701          }          return true;
702          return false;        }
703      }      return false;
704      }
705    
706      private void checkLegalVersion (String version)    private void checkLegalVersion(String version)
707      throws SAXException      throws SAXException
708      {    {
709          int len = version.length ();      int len = version.length();
710          for (int i = 0; i < len; i++) {      for (int i = 0; i < len; i++)
711              char c = version.charAt (i);        {
712              if ('0' <= c && c <= '9')          char c = version.charAt(i);
713                  continue;          if ('0' <= c && c <= '9')
714              if (c == '_' || c == '.' || c == ':' || c == '-')            {
715                  continue;              continue;
716              if ('a' <= c && c <= 'z')            }
717                  continue;          if (c == '_' || c == '.' || c == ':' || c == '-')
718              if ('A' <= c && c <= 'Z')            {
719                  continue;              continue;
720              error ("illegal character in version", version, "1.0");            }
721          }          if ('a' <= c && c <= 'z')
722      }            {
723                continue;
724              }
725            if ('A' <= c && c <= 'Z')
726              {
727                continue;
728              }
729            error ("illegal character in version", version, "1.0");
730          }
731      }
732    
733      /**    /**
734       * Parse the XML declaration.     * Parse the XML declaration.
735       * <pre>     * <pre>
736       * [23] XMLDecl ::= '&lt;?xml' VersionInfo EncodingDecl? SDDecl? S? '?&gt;'     * [23] XMLDecl ::= '&lt;?xml' VersionInfo EncodingDecl? SDDecl? S? '?&gt;'
737       * [24] VersionInfo ::= S 'version' Eq     * [24] VersionInfo ::= S 'version' Eq
738       *          ("'" VersionNum "'" | '"' VersionNum '"' )     *    ("'" VersionNum "'" | '"' VersionNum '"' )
739       * [26] VersionNum ::= ([a-zA-Z0-9_.:] | '-')*     * [26] VersionNum ::= ([a-zA-Z0-9_.:] | '-')*
740       * [32] SDDecl ::= S 'standalone' Eq     * [32] SDDecl ::= S 'standalone' Eq
741       *          ( "'"" ('yes' | 'no') "'"" | '"' ("yes" | "no") '"' )     *    ( "'"" ('yes' | 'no') "'"" | '"' ("yes" | "no") '"' )
742       * [80] EncodingDecl ::= S 'encoding' Eq     * [80] EncodingDecl ::= S 'encoding' Eq
743       *          ( "'" EncName "'" | "'" EncName "'" )     *    ( "'" EncName "'" | "'" EncName "'" )
744       * [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*     * [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*
745       * </pre>     * </pre>
746       * <p> (The <code>&lt;?xml</code> and whitespace have already been read.)     * <p> (The <code>&lt;?xml</code> and whitespace have already been read.)
747       * @return the encoding in the declaration, uppercased; or null     * @return the encoding in the declaration, uppercased; or null
748       * @see #parseTextDecl     * @see #parseTextDecl
749       * @see #setupDecoding     * @see #setupDecoding
750       */     */
751      private String parseXMLDecl (boolean ignoreEncoding)    private String parseXMLDecl(boolean ignoreEncoding)
752      throws SAXException, IOException      throws SAXException, IOException
753      {    {
754          String  version;      String version;
755          String  encodingName = null;      String encodingName = null;
756          String  standalone = null;      String standalone = null;
757          int     flags = LIT_DISABLE_CREF | LIT_DISABLE_PE | LIT_DISABLE_EREF;      int flags = LIT_DISABLE_CREF | LIT_DISABLE_PE | LIT_DISABLE_EREF;
758          String inputEncoding = null;      String inputEncoding = null;
759                    
760          switch (this.encoding)      switch (this.encoding)
761          {
762          case ENCODING_EXTERNAL:
763          case ENCODING_UTF_8:
764            inputEncoding = "UTF-8";
765            break;
766          case ENCODING_ISO_8859_1:
767            inputEncoding = "ISO-8859-1";
768            break;
769          case ENCODING_UCS_2_12:
770            inputEncoding = "UTF-16BE";
771            break;
772          case ENCODING_UCS_2_21:
773            inputEncoding = "UTF-16LE";
774            break;
775          }
776        
777        // Read the version.
778        require("version");
779        parseEq();
780        checkLegalVersion(version = readLiteral(flags));
781        if (!version.equals("1.0"))
782          {
783            if (version.equals("1.1"))
784            {            {
785            case ENCODING_EXTERNAL:              handler.warn("expected XML version 1.0, not: " + version);
786            case ENCODING_UTF_8:              xmlVersion = XML_11;
             inputEncoding = "UTF-8";  
             break;  
           case ENCODING_ISO_8859_1:  
             inputEncoding = "ISO-8859-1";  
             break;  
           case ENCODING_UCS_2_12:  
             inputEncoding = "UTF-16BE";  
             break;  
           case ENCODING_UCS_2_21:  
             inputEncoding = "UTF-16LE";  
             break;  
787            }            }
788            else
         // Read the version.  
         require ("version");  
         parseEq ();  
         checkLegalVersion (version = readLiteral (flags));  
         if (!version.equals ("1.0")){  
             if(version.equals ("1.1")){  
                 handler.warn ("expected XML version 1.0, not: " + version);  
                 xmlVersion = XML_11;  
             }else {  
                 error("illegal XML version", version, "1.0 or 1.1");  
             }  
         }  
         else  
             xmlVersion = XML_10;  
         // Try reading an encoding declaration.  
         boolean white = tryWhitespace ();  
   
         if (tryRead ("encoding")) {  
             if (!white)  
                 error ("whitespace required before 'encoding='");  
             parseEq ();  
             encodingName = readLiteral (flags);  
             if (!ignoreEncoding)  
                 setupDecoding (encodingName);  
         }  
   
         // Try reading a standalone declaration  
         if (encodingName != null)  
             white = tryWhitespace ();  
         if (tryRead ("standalone")) {  
             if (!white)  
                 error ("whitespace required before 'standalone='");  
             parseEq ();  
             standalone = readLiteral (flags);  
             if ("yes".equals (standalone))  
                 docIsStandalone = true;  
             else if (!"no".equals (standalone))  
                 error ("standalone flag must be 'yes' or 'no'");  
         }  
   
         skipWhitespace ();  
         require ("?>");  
   
         if (inputEncoding == null)  
789            {            {
790              inputEncoding = encodingName;              error("illegal XML version", version, "1.0 or 1.1");
791            }            }
792          handler.xmlDecl(version, encodingName, "yes".equals(standalone),        }
793                          inputEncoding);      else
794          {
795            xmlVersion = XML_10;
796          }
797        // Try reading an encoding declaration.
798        boolean white = tryWhitespace();
799        
800        if (tryRead("encoding"))
801          {
802            if (!white)
803              {
804                error("whitespace required before 'encoding='");
805              }
806            parseEq();
807            encodingName = readLiteral(flags);
808            if (!ignoreEncoding)
809              {
810                setupDecoding(encodingName);
811              }
812          }
813        
814        // Try reading a standalone declaration
815        if (encodingName != null)
816          {
817            white = tryWhitespace();
818          }
819        if (tryRead("standalone"))
820          {
821            if (!white)
822              {
823                error("whitespace required before 'standalone='");
824              }
825            parseEq();
826            standalone = readLiteral(flags);
827            if ("yes".equals(standalone))
828              {
829                docIsStandalone = true;
830              }
831            else if (!"no".equals(standalone))
832              {
833                error("standalone flag must be 'yes' or 'no'");
834              }
835          }
836    
837          return encodingName;      skipWhitespace();
838      }      require("?>");
839    
840        if (inputEncoding == null)
841          {
842            inputEncoding = encodingName;
843          }
844        handler.xmlDecl(version, encodingName, docIsStandalone,
845                        inputEncoding);
846        
847        return encodingName;
848      }
849    
850      /**    /**
851       * Parse a text declaration.     * Parse a text declaration.
852       * <pre>     * <pre>
853       * [79] TextDecl ::= '&lt;?xml' VersionInfo? EncodingDecl S? '?&gt;'     * [79] TextDecl ::= '&lt;?xml' VersionInfo? EncodingDecl S? '?&gt;'
854       * [80] EncodingDecl ::= S 'encoding' Eq     * [80] EncodingDecl ::= S 'encoding' Eq
855       *          ( '"' EncName '"' | "'" EncName "'" )     *    ( '"' EncName '"' | "'" EncName "'" )
856       * [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*     * [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*
857       * </pre>     * </pre>
858       * <p> (The <code>&lt;?xml</code>' and whitespace have already been read.)     * <p> (The <code>&lt;?xml</code>' and whitespace have already been read.)
859       * @return the encoding in the declaration, uppercased; or null     * @return the encoding in the declaration, uppercased; or null
860       * @see #parseXMLDecl     * @see #parseXMLDecl
861       * @see #setupDecoding     * @see #setupDecoding
862       */     */
863      private String parseTextDecl (boolean ignoreEncoding)    private String parseTextDecl(boolean ignoreEncoding)
864      throws SAXException, IOException      throws SAXException, IOException
865      {    {
866          String  encodingName = null;      String encodingName = null;
867          int     flags = LIT_DISABLE_CREF | LIT_DISABLE_PE | LIT_DISABLE_EREF;      int flags = LIT_DISABLE_CREF | LIT_DISABLE_PE | LIT_DISABLE_EREF;
   
         // Read an optional version.  
         if (tryRead ("version")) {  
             String version;  
             parseEq ();  
             checkLegalVersion (version = readLiteral (flags));  
               
             if (version.equals ("1.1")){  
                 if (xmlVersion == XML_10){  
                    error ("external subset has later version number.", "1.0", version);      
                 }  
                 handler.warn ("expected XML version 1.0, not: " + version);  
                 xmlVersion = XML_11;  
              }else if(!version.equals ("1.0")) {  
                  error("illegal XML version", version, "1.0 or 1.1");  
              }  
             requireWhitespace ();  
         }  
   
   
         // Read the encoding.  
         require ("encoding");  
         parseEq ();  
         encodingName = readLiteral (flags);  
         if (!ignoreEncoding)  
             setupDecoding (encodingName);  
   
         skipWhitespace ();  
         require ("?>");  
   
         return encodingName;  
     }  
868    
869        // Read an optional version.
870        if (tryRead ("version"))
871          {
872            String version;
873            parseEq();
874            checkLegalVersion(version = readLiteral(flags));
875            
876            if (version.equals("1.1"))
877              {
878                if (xmlVersion == XML_10)
879                  {
880                    error("external subset has later version number.", "1.0",
881                          version);    
882                  }
883                handler.warn("expected XML version 1.0, not: " + version);
884                xmlVersion = XML_11;
885              }
886            else if (!version.equals("1.0"))
887              {
888                error("illegal XML version", version, "1.0 or 1.1");
889              }
890            requireWhitespace();
891          }
892        
893        // Read the encoding.
894        require("encoding");
895        parseEq();
896        encodingName = readLiteral(flags);
897        if (!ignoreEncoding)
898          {
899            setupDecoding(encodingName);
900          }
901        skipWhitespace();
902        require("?>");
903        
904        return encodingName;
905      }
906    
907      /**    /**
908       * Sets up internal state so that we can decode an entity using the     * Sets up internal state so that we can decode an entity using the
909       * specified encoding.  This is used when we start to read an entity     * specified encoding.  This is used when we start to read an entity
910       * and we have been given knowledge of its encoding before we start to     * and we have been given knowledge of its encoding before we start to
911       * read any data (e.g. from a SAX input source or from a MIME type).     * read any data (e.g. from a SAX input source or from a MIME type).
912       *     *
913       * <p> It is also used after autodetection, at which point only very     * <p> It is also used after autodetection, at which point only very
914       * limited adjustments to the encoding may be used (switching between     * limited adjustments to the encoding may be used (switching between
915       * related builtin decoders).     * related builtin decoders).
916       *     *
917       * @param encodingName The name of the encoding specified by the user.     * @param encodingName The name of the encoding specified by the user.
918       * @exception IOException if the encoding isn't supported either     * @exception IOException if the encoding isn't supported either
919       *  internally to this parser, or by the hosting JVM.     *  internally to this parser, or by the hosting JVM.
920       * @see #parseXMLDecl     * @see #parseXMLDecl
921       * @see #parseTextDecl     * @see #parseTextDecl
922       */       */
923      private void setupDecoding (String encodingName)    private void setupDecoding(String encodingName)
924      throws SAXException, IOException      throws SAXException, IOException
925      {    {
926          encodingName = encodingName.toUpperCase ();      encodingName = encodingName.toUpperCase();
927        
928          // ENCODING_EXTERNAL indicates an encoding that wasn't      // ENCODING_EXTERNAL indicates an encoding that wasn't
929          // autodetected ... we can use builtin decoders, or      // autodetected ... we can use builtin decoders, or
930          // ones from the JVM (InputStreamReader).      // ones from the JVM (InputStreamReader).
931        
932          // Otherwise we can only tweak what was autodetected, and      // Otherwise we can only tweak what was autodetected, and
933          // only for single byte (ASCII derived) builtin encodings.      // only for single byte (ASCII derived) builtin encodings.
934        
935          // ASCII-derived encodings      // ASCII-derived encodings
936          if (encoding == ENCODING_UTF_8 || encoding == ENCODING_EXTERNAL) {      if (encoding == ENCODING_UTF_8 || encoding == ENCODING_EXTERNAL)
937              if (encodingName.equals ("ISO-8859-1")        {
938                      || encodingName.equals ("8859_1")          if (encodingName.equals("ISO-8859-1")
939                      || encodingName.equals ("ISO8859_1")              || encodingName.equals("8859_1")
940                ) {              || encodingName.equals("ISO8859_1"))
941                  encoding = ENCODING_ISO_8859_1;            {
942                  return;              encoding = ENCODING_ISO_8859_1;
943              } else if (encodingName.equals ("US-ASCII")              return;
944                          || encodingName.equals ("ASCII")) {            }
945                  encoding = ENCODING_ASCII;          else if (encodingName.equals("US-ASCII")
946                  return;                   || encodingName.equals("ASCII"))
947              } else if (encodingName.equals ("UTF-8")            {
948                          || encodingName.equals ("UTF8")) {              encoding = ENCODING_ASCII;
949                  encoding = ENCODING_UTF_8;              return;
950                  return;            }
951              } else if (encoding != ENCODING_EXTERNAL) {          else if (encodingName.equals("UTF-8")
952                  // used to start with a new reader ...                   || encodingName.equals("UTF8"))
953                  throw new UnsupportedEncodingException (encodingName);            {
954              }              encoding = ENCODING_UTF_8;
955              // else fallthrough ...              return;
956              // it's ASCII-ish and something other than a builtin            }
957          }          else if (encoding != ENCODING_EXTERNAL)
958              {
959          // Unicode and such              // used to start with a new reader ...
960          if (encoding == ENCODING_UCS_2_12 || encoding == ENCODING_UCS_2_21) {              throw new UnsupportedEncodingException(encodingName);
961              if (!(encodingName.equals ("ISO-10646-UCS-2")            }
962                      || encodingName.equals ("UTF-16")          // else fallthrough ...
963                      || encodingName.equals ("UTF-16BE")          // it's ASCII-ish and something other than a builtin
964                      || encodingName.equals ("UTF-16LE")))        }
965                  error ("unsupported Unicode encoding",      
966                         encodingName,      // Unicode and such
967                         "UTF-16");      if (encoding == ENCODING_UCS_2_12 || encoding == ENCODING_UCS_2_21)
968              return;        {
969          }          if (!(encodingName.equals("ISO-10646-UCS-2")
970                  || encodingName.equals("UTF-16")
971          // four byte encodings                || encodingName.equals("UTF-16BE")
972          if (encoding == ENCODING_UCS_4_1234                || encodingName.equals("UTF-16LE")))
973                  || encoding == ENCODING_UCS_4_4321            {
974                  || encoding == ENCODING_UCS_4_2143              error("unsupported Unicode encoding", encodingName, "UTF-16");
975                  || encoding == ENCODING_UCS_4_3412) {            }
976              // Strictly:  "UCS-4" == "UTF-32BE"; also, "UTF-32LE" exists          return;
977              if (!encodingName.equals ("ISO-10646-UCS-4"))        }
978                  error ("unsupported 32-bit encoding",      
979                         encodingName,      // four byte encodings
980                         "ISO-10646-UCS-4");      if (encoding == ENCODING_UCS_4_1234
981              return;          || encoding == ENCODING_UCS_4_4321
982          }          || encoding == ENCODING_UCS_4_2143
983            || encoding == ENCODING_UCS_4_3412)
984          // assert encoding == ENCODING_EXTERNAL        {
985          // if (encoding != ENCODING_EXTERNAL)          // Strictly:  "UCS-4" == "UTF-32BE"; also, "UTF-32LE" exists
986          //     throw new RuntimeException ("encoding = " + encoding);          if (!encodingName.equals("ISO-10646-UCS-4"))
987              {
988          if (encodingName.equals ("UTF-16BE")) {              error("unsupported 32-bit encoding", encodingName,
989              encoding = ENCODING_UCS_2_12;                    "ISO-10646-UCS-4");
990              return;            }
991          }          return;
992          if (encodingName.equals ("UTF-16LE")) {        }
993              encoding = ENCODING_UCS_2_21;      
994              return;      // assert encoding == ENCODING_EXTERNAL
995          }      // if (encoding != ENCODING_EXTERNAL)
996        //     throw new RuntimeException ("encoding = " + encoding);
997          // We couldn't use the builtin decoders at all.  But we can try to      
998          // create a reader, since we haven't messed up buffering.  Tweak      if (encodingName.equals("UTF-16BE"))
999          // the encoding name if necessary.        {
1000            encoding = ENCODING_UCS_2_12;
1001          if (encodingName.equals ("UTF-16")          return;
1002                  || encodingName.equals ("ISO-10646-UCS-2"))        }
1003              encodingName = "Unicode";      if (encodingName.equals("UTF-16LE"))
1004          // Ignoring all the EBCDIC aliases here        {
1005            encoding = ENCODING_UCS_2_21;
1006          reader = new InputStreamReader (is, encodingName);          return;
1007          sourceType = INPUT_READER;        }
1008      }      
1009        // We couldn't use the builtin decoders at all.  But we can try to
1010        // create a reader, since we haven't messed up buffering.  Tweak
1011      /**      // the encoding name if necessary.
1012       * Parse miscellaneous markup outside the document element and DOCTYPE      
1013       * declaration.      if (encodingName.equals("UTF-16")
1014       * <pre>          || encodingName.equals("ISO-10646-UCS-2"))
1015       * [27] Misc ::= Comment | PI | S        {
1016       * </pre>          encodingName = "Unicode";
1017       */        }
1018      private void parseMisc ()      // Ignoring all the EBCDIC aliases here
1019        
1020        reader = new InputStreamReader(is, encodingName);
1021        sourceType = INPUT_READER;
1022      }
1023      
1024      /**
1025       * Parse miscellaneous markup outside the document element and DOCTYPE
1026       * declaration.
1027       * <pre>
1028       * [27] Misc ::= Comment | PI | S
1029       * </pre>
1030       */
1031      private void parseMisc()
1032      throws Exception      throws Exception
1033      {    {
1034          while (true) {      while (true)
1035              skipWhitespace ();        {
1036              if (tryRead (startDelimPI)) {          skipWhitespace();
1037                  parsePI ();          if (tryRead(startDelimPI))
1038              } else if (tryRead (startDelimComment)) {            {
1039                  parseComment ();              parsePI();
1040              } else {            }
1041                  return;          else if (tryRead(startDelimComment))
1042              }            {
1043          }              parseComment();
1044      }            }
1045            else
1046              {
1047                return;
1048              }
1049          }
1050      }
1051    
1052      /**    /**
1053       * Parse a document type declaration.     * Parse a document type declaration.
1054       * <pre>     * <pre>
1055       * [28] doctypedecl ::= '&lt;!DOCTYPE' S Name (S ExternalID)? S?     * [28] doctypedecl ::= '&lt;!DOCTYPE' S Name (S ExternalID)? S?
1056       *          ('[' (markupdecl | PEReference | S)* ']' S?)? '&gt;'     *    ('[' (markupdecl | PEReference | S)* ']' S?)? '&gt;'
1057       * </pre>     * </pre>
1058       * <p> (The <code>&lt;!DOCTYPE</code> has already been read.)     * <p> (The <code>&lt;!DOCTYPE</code> has already been read.)
1059       */     */
1060      private void parseDoctypedecl ()    private void parseDoctypedecl()
1061      throws Exception      throws Exception
1062      {    {
1063          String rootName, ids[];      String rootName;
1064        ExternalIdentifiers ids;
         // Read the document type name.  
         requireWhitespace ();  
         rootName = readNmtoken (true);  
   
         // Read the External subset's IDs  
         skipWhitespace ();  
         ids = readExternalIds (false, true);  
   
         // report (a) declaration of name, (b) lexical info (ids)  
         handler.doctypeDecl (rootName, ids [0], ids [1]);  
   
         // Internal subset is parsed first, if present  
         skipWhitespace ();  
         if (tryRead ('[')) {  
   
             // loop until the subset ends  
             while (true) {  
                 doReport = expandPE = true;  
                 skipWhitespace ();  
                 doReport = expandPE = false;  
                 if (tryRead (']')) {  
                     break;              // end of subset  
                 } else {  
                     // WFC, PEs in internal subset (only between decls)  
                     peIsError = expandPE = true;  
                     parseMarkupdecl ();  
                     peIsError = expandPE = false;  
                 }  
             }  
         }  
         skipWhitespace ();  
         require ('>');  
   
         // Read the external subset, if any  
         InputSource     subset;  
   
         if (ids [1] == null)  
             subset = handler.getExternalSubset (rootName,  
                         handler.getSystemId ());  
         else  
             subset = null;  
         if (ids [1] != null || subset != null) {  
             pushString (null, ">");  
   
             // NOTE:  [dtd] is so we say what SAX2 expects,  
             // though it's misleading (subset, not entire dtd)  
             if (ids [1] != null)  
                 pushURL (true, "[dtd]", ids, null, null, null, true);  
             else {  
                 handler.warn ("modifying document by adding external subset");  
                 pushURL (true, "[dtd]",  
                     new String [] { subset.getPublicId (),  
                             subset.getSystemId (), null },  
                     subset.getCharacterStream (),  
                     subset.getByteStream (),  
                     subset.getEncoding (),  
                     false);  
             }  
   
             // Loop until we end up back at '>'  
             while (true) {  
                 doReport = expandPE = true;  
                 skipWhitespace ();  
                 doReport = expandPE = false;  
                 if (tryRead ('>')) {  
                     break;  
                 } else {  
                     expandPE = true;  
                     parseMarkupdecl ();  
                     expandPE = false;  
                 }  
             }  
   
             // the ">" string isn't popped yet  
             if (inputStack.size () != 1)  
                 error ("external subset has unmatched '>'");  
         }  
   
         // done dtd  
         handler.endDoctype ();  
         expandPE = false;  
         doReport = true;  
     }  
1065    
1066        // Read the document type name.
1067        requireWhitespace();
1068        rootName = readNmtoken(true);
1069    
1070        // Read the External subset's IDs
1071        skipWhitespace();
1072        ids = readExternalIds(false, true);
1073    
1074      /**      // report (a) declaration of name, (b) lexical info (ids)
1075       * Parse a markup declaration in the internal or external DTD subset.      handler.doctypeDecl(rootName, ids.publicId, ids.systemId);
1076       * <pre>      
1077       * [29] markupdecl ::= elementdecl | Attlistdecl | EntityDecl      // Internal subset is parsed first, if present
1078       *          | NotationDecl | PI | Comment      skipWhitespace();
1079       * [30] extSubsetDecl ::= (markupdecl | conditionalSect      if (tryRead('['))
1080       *          | PEReference | S) *        {
1081       * </pre>          
1082       * <p> Reading toplevel PE references is handled as a lexical issue          // loop until the subset ends
1083       * by the caller, as is whitespace.          while (true)
1084       */            {
1085      private void parseMarkupdecl ()              doReport = expandPE = true;
1086                skipWhitespace();
1087                doReport = expandPE = false;
1088                if (tryRead(']'))
1089                  {
1090                    break;     // end of subset
1091                  }
1092                else
1093                  {
1094                    // WFC, PEs in internal subset (only between decls)
1095                    peIsError = expandPE = true;
1096                    parseMarkupdecl();
1097                    peIsError = expandPE = false;
1098                  }
1099              }
1100          }
1101        skipWhitespace();
1102        require('>');
1103        
1104        // Read the external subset, if any
1105        InputSource subset;
1106        
1107        if (ids.systemId == null)
1108          {
1109            subset = handler.getExternalSubset(rootName,
1110                                               handler.getSystemId());
1111          }
1112        else
1113          {
1114            subset = null;
1115          }
1116        if (ids.systemId != null || subset != null)
1117          {
1118            pushString(null, ">");
1119          
1120            // NOTE:  [dtd] is so we say what SAX2 expects,
1121            // though it's misleading (subset, not entire dtd)
1122            if (ids.systemId != null)
1123              {
1124                pushURL(true, "[dtd]", ids, null, null, null, true);
1125              }
1126            else
1127              {
1128                handler.warn("modifying document by adding external subset");
1129                pushURL(true, "[dtd]",
1130                        new ExternalIdentifiers(subset.getPublicId(),
1131                                                subset.getSystemId(),
1132                                                null),
1133                        subset.getCharacterStream(),
1134                        subset.getByteStream(),
1135                        subset.getEncoding(),
1136                        false);
1137              }
1138            
1139            // Loop until we end up back at '>'
1140            while (true)
1141              {
1142                doReport = expandPE = true;
1143                skipWhitespace();
1144                doReport = expandPE = false;
1145                if (tryRead('>'))
1146                  {
1147                    break;
1148                  }
1149                else
1150                  {
1151                    expandPE = true;
1152                    parseMarkupdecl();
1153                    expandPE = false;
1154                  }
1155              }
1156            
1157            // the ">" string isn't popped yet
1158            if (inputStack.size() != 1)
1159              {
1160                error("external subset has unmatched '>'");
1161              }
1162          }
1163        
1164        // done dtd
1165        handler.endDoctype();
1166        expandPE = false;
1167        doReport = true;
1168      }
1169      
1170      /**
1171       * Parse a markup declaration in the internal or external DTD subset.
1172       * <pre>
1173       * [29] markupdecl ::= elementdecl | Attlistdecl | EntityDecl
1174       *    | NotationDecl | PI | Comment
1175       * [30] extSubsetDecl ::= (markupdecl | conditionalSect
1176       *    | PEReference | S) *
1177       * </pre>
1178       * <p> Reading toplevel PE references is handled as a lexical issue
1179       * by the caller, as is whitespace.
1180       */
1181      private void parseMarkupdecl()
1182      throws Exception      throws Exception
1183      {    {
1184          char    saved [] = null;      char[] saved = null;
1185          boolean savedPE = expandPE;      boolean savedPE = expandPE;
   
         // prevent "<%foo;" and ensures saved entity is right  
         require ('<');  
         unread ('<');  
         expandPE = false;  
   
         if (tryRead ("<!ELEMENT")) {  
             saved = readBuffer;  
             expandPE = savedPE;  
             parseElementDecl ();  
         } else if (tryRead ("<!ATTLIST")) {  
             saved = readBuffer;  
             expandPE = savedPE;  
             parseAttlistDecl ();  
         } else if (tryRead ("<!ENTITY")) {  
             saved = readBuffer;  
             expandPE = savedPE;  
             parseEntityDecl ();  
         } else if (tryRead ("<!NOTATION")) {  
             saved = readBuffer;  
             expandPE = savedPE;  
             parseNotationDecl ();  
         } else if (tryRead (startDelimPI)) {  
             saved = readBuffer;  
             expandPE = savedPE;  
             parsePI ();  
         } else if (tryRead (startDelimComment)) {  
             saved = readBuffer;  
             expandPE = savedPE;  
             parseComment ();  
         } else if (tryRead ("<![")) {  
             saved = readBuffer;  
             expandPE = savedPE;  
             if (inputStack.size () > 0)  
                 parseConditionalSect (saved);  
             else  
                 error ("conditional sections illegal in internal subset");  
         } else {  
             error ("expected markup declaration");  
         }  
   
         // VC: Proper Decl/PE Nesting  
         if (readBuffer != saved)  
             handler.verror ("Illegal Declaration/PE nesting");  
     }  
1186    
1187        // prevent "<%foo;" and ensures saved entity is right
1188        require('<');
1189        unread('<');
1190        expandPE = false;
1191        
1192        if (tryRead("<!ELEMENT"))
1193          {
1194            saved = readBuffer;
1195            expandPE = savedPE;
1196            parseElementDecl();
1197          }
1198        else if (tryRead("<!ATTLIST"))
1199          {
1200            saved = readBuffer;
1201            expandPE = savedPE;
1202            parseAttlistDecl();
1203          }
1204        else if (tryRead("<!ENTITY"))
1205          {
1206            saved = readBuffer;
1207            expandPE = savedPE;
1208            parseEntityDecl();
1209          }
1210        else if (tryRead("<!NOTATION"))
1211          {
1212            saved = readBuffer;
1213            expandPE = savedPE;
1214            parseNotationDecl();
1215          }
1216        else if (tryRead(startDelimPI))
1217          {
1218            saved = readBuffer;
1219            expandPE = savedPE;
1220            parsePI();
1221          }
1222        else if (tryRead(startDelimComment))
1223          {
1224            saved = readBuffer;
1225            expandPE = savedPE;
1226            parseComment();
1227          }
1228        else if (tryRead("<!["))
1229          {
1230            saved = readBuffer;
1231            expandPE = savedPE;
1232            if (inputStack.size() > 0)
1233              {
1234                parseConditionalSect(saved);
1235              }
1236            else
1237              {
1238                error("conditional sections illegal in internal subset");
1239              }
1240          }
1241        else
1242          {
1243            error("expected markup declaration");
1244          }
1245    
1246      /**      // VC: Proper Decl/PE Nesting
1247       * Parse an element, with its tags.      if (readBuffer != saved)
1248       * <pre>        {
1249       * [39] element ::= EmptyElementTag | STag content ETag          handler.verror("Illegal Declaration/PE nesting");
1250       * [40] STag ::= '&lt;' Name (S Attribute)* S? '&gt;'        }
1251       * [44] EmptyElementTag ::= '&lt;' Name (S Attribute)* S? '/&gt;'    }
1252       * </pre>    
1253       * <p> (The '&lt;' has already been read.)    /**
1254       * <p>NOTE: this method actually chains onto parseContent (), if necessary,     * Parse an element, with its tags.
1255       * and parseContent () will take care of calling parseETag ().     * <pre>
1256       */     * [39] element ::= EmptyElementTag | STag content ETag
1257      private void parseElement (boolean maybeGetSubset)     * [40] STag ::= '&lt;' Name (S Attribute)* S? '&gt;'
1258       * [44] EmptyElementTag ::= '&lt;' Name (S Attribute)* S? '/&gt;'
1259       * </pre>
1260       * <p> (The '&lt;' has already been read.)
1261       * <p>NOTE: this method actually chains onto parseContent (), if necessary,
1262       * and parseContent () will take care of calling parseETag ().
1263       */
1264      private void parseElement(boolean maybeGetSubset)
1265      throws Exception      throws Exception
1266      {    {
1267          String  gi;      String gi;
1268          char    c;      char c;
1269          int     oldElementContent = currentElementContent;      int oldElementContent = currentElementContent;
1270          String  oldElement = currentElement;      String oldElement = currentElement;
1271          Object  element [];      ElementDecl element;
1272    
1273          // This is the (global) counter for the      // This is the (global) counter for the
1274          // array of specified attributes.      // array of specified attributes.
1275          tagAttributePos = 0;      tagAttributePos = 0;
1276        
1277          // Read the element type name.      // Read the element type name.
1278          gi = readNmtoken (true);      gi = readNmtoken(true);
1279        
1280          // If we saw no DTD, and this is the document root element,      // If we saw no DTD, and this is the document root element,
1281          // let the application modify the input stream by providing one.      // let the application modify the input stream by providing one.
1282          if (maybeGetSubset) {      if (maybeGetSubset)
1283              InputSource subset = handler.getExternalSubset (gi,        {
1284                          handler.getSystemId ());          InputSource subset = handler.getExternalSubset(gi,
1285              if (subset != null) {                                                         handler.getSystemId());
1286                  String  publicId = subset.getPublicId ();          if (subset != null)
1287                  String  systemId = subset.getSystemId ();            {
1288                String publicId = subset.getPublicId();
1289                  handler.warn ("modifying document by adding DTD");              String systemId = subset.getSystemId();
1290                  handler.doctypeDecl (gi, publicId, systemId);              
1291                  pushString (null, ">");              handler.warn("modifying document by adding DTD");
1292                handler.doctypeDecl(gi, publicId, systemId);
1293                  // NOTE:  [dtd] is so we say what SAX2 expects,              pushString(null, ">");
1294                  // though it's misleading (subset, not entire dtd)              
1295                  pushURL (true, "[dtd]",              // NOTE:  [dtd] is so we say what SAX2 expects,
1296                      new String [] { publicId, systemId, null },              // though it's misleading (subset, not entire dtd)
1297                      subset.getCharacterStream (),              pushURL(true, "[dtd]",
1298                      subset.getByteStream (),                      new ExternalIdentifiers(publicId, systemId, null),
1299                      subset.getEncoding (),                      subset.getCharacterStream(),
1300                      false);                      subset.getByteStream(),
1301                        subset.getEncoding(),
1302                  // Loop until we end up back at '>'                      false);
1303                  while (true) {              
1304                      doReport = expandPE = true;              // Loop until we end up back at '>'
1305                      skipWhitespace ();              while (true)
1306                      doReport = expandPE = false;                {
1307                      if (tryRead ('>')) {                  doReport = expandPE = true;
1308                          break;                  skipWhitespace();
1309                      } else {                  doReport = expandPE = false;
1310                          expandPE = true;                  if (tryRead('>'))
1311                          parseMarkupdecl ();                    {
1312                          expandPE = false;                      break;
1313                      }                    }
1314                  }                  else
1315                      {
1316                  // the ">" string isn't popped yet                      expandPE = true;
1317                  if (inputStack.size () != 1)                      parseMarkupdecl();
1318                      error ("external subset has unmatched '>'");                      expandPE = false;
1319                      }
1320                  handler.endDoctype ();                }
1321              }              
1322          }              // the ">" string isn't popped yet
1323                if (inputStack.size() != 1)
1324          // Determine the current content type.                {
1325          currentElement = gi;                  error("external subset has unmatched '>'");
1326          element = (Object []) elementInfo.get (gi);                }
1327          currentElementContent = getContentType (element, CONTENT_ANY);              
1328                handler.endDoctype();
1329          // Read the attributes, if any.            }
1330          // After this loop, "c" is the closing delimiter.        }
1331          boolean white = tryWhitespace ();      
1332          c = readCh ();      // Determine the current content type.
1333          while (c != '/' && c != '>') {      currentElement = gi;
1334              unread (c);      element = (ElementDecl) elementInfo.get(gi);
1335              if (!white)      currentElementContent = getContentType(element, CONTENT_ANY);
1336                  error ("need whitespace between attributes");  
1337              parseAttribute (gi);      // Read the attributes, if any.
1338              white = tryWhitespace ();      // After this loop, "c" is the closing delimiter.
1339              c = readCh ();      boolean white = tryWhitespace();
1340          }      c = readCh();
1341        while (c != '/' && c != '>')
1342          // Supply any defaulted attributes.        {
1343          Enumeration atts = declaredAttributes (element);          unread(c);
1344          if (atts != null) {          if (!white)
1345              String aname;            {
1346                error("need whitespace between attributes");
1347              }
1348            parseAttribute(gi);
1349            white = tryWhitespace();
1350            c = readCh();
1351          }
1352        
1353        // Supply any defaulted attributes.
1354        Iterator atts = declaredAttributes(element);
1355        if (atts != null)
1356          {
1357            String aname;
1358  loop:  loop:
1359              while (atts.hasMoreElements ()) {          while (atts.hasNext())
1360                  aname = (String) atts.nextElement ();            {
1361                  // See if it was specified.              aname = (String) atts.next();
1362                  for (int i = 0; i < tagAttributePos; i++) {              // See if it was specified.
1363                      if (tagAttributes [i] == aname) {              for (int i = 0; i < tagAttributePos; i++)
1364                          continue loop;                {
1365                      }                  if (tagAttributes[i] == aname)
1366                  }                    {
1367                  // ... or has a default                      continue loop;
1368                  String value = getAttributeDefaultValue (gi, aname);                    }
1369                  }
1370                  if (value == null)              // ... or has a default
1371                      continue;              String value = getAttributeDefaultValue(gi, aname);
1372                  handler.attribute (aname, value, false);              
1373              }              if (value == null)
1374          }                {
1375                    continue;
1376          // Figure out if this is a start tag                }
1377          // or an empty element, and dispatch an              handler.attribute(aname, value, false);
1378          // event accordingly.            }
1379          switch (c) {        }
         case '>':  
             handler.startElement (gi);  
             parseContent ();  
             break;  
         case '/':  
             require ('>');  
             handler.startElement (gi);  
             handler.endElement (gi);  
             break;  
         }  
   
         // Restore the previous state.  
         currentElement = oldElement;  
         currentElementContent = oldElementContent;  
     }  
1380    
1381        // Figure out if this is a start tag
1382        // or an empty element, and dispatch an
1383        // event accordingly.
1384        switch (c)
1385          {
1386          case '>':
1387            handler.startElement(gi);
1388            parseContent();
1389            break;
1390          case '/':
1391            require('>');
1392            handler.startElement(gi);
1393            handler.endElement(gi);
1394            break;
1395          }
1396    
1397      /**      // Restore the previous state.
1398       * Parse an attribute assignment.      currentElement = oldElement;
1399       * <pre>      currentElementContent = oldElementContent;
      * [41] Attribute ::= Name Eq AttValue  
      * </pre>  
      * @param name The name of the attribute's element.  
      * @see SAXDriver#attribute  
      */  
     private void parseAttribute (String name)  
     throws Exception  
     {  
         String aname;  
         String type;  
         String value;  
         int flags = LIT_ATTRIBUTE |  LIT_ENTITY_REF;  
   
         // Read the attribute name.  
         aname = readNmtoken (true);  
         type = getAttributeType (name, aname);  
   
         // Parse '='  
         parseEq ();  
   
         // Read the value, normalizing whitespace  
         // unless it is CDATA.  
   if (handler.getFeature (SAXDriver.FEATURE + "string-interning")) {  
     if (type == "CDATA" || type == null) {  
             value = readLiteral (flags);  
     } else {  
             value = readLiteral (flags | LIT_NORMALIZE);  
     }  
   } else {  
     if (type.equals("CDATA") || type == null) {  
             value = readLiteral (flags);  
     } else {  
             value = readLiteral (flags | LIT_NORMALIZE);  
     }  
1400    }    }
1401        
1402      /**
1403       * Parse an attribute assignment.
1404       * <pre>
1405       * [41] Attribute ::= Name Eq AttValue
1406       * </pre>
1407       * @param name The name of the attribute's element.
1408       * @see SAXDriver#attribute
1409       */
1410      private void parseAttribute(String name)
1411        throws Exception
1412      {
1413        String aname;
1414        String type;
1415        String value;
1416        int flags = LIT_ATTRIBUTE |  LIT_ENTITY_REF;
1417        
1418        // Read the attribute name.
1419        aname = readNmtoken(true);
1420        type = getAttributeType(name, aname);
1421        
1422        // Parse '='
1423        parseEq();
1424    
1425          // WFC: no duplicate attributes      // Read the value, normalizing whitespace
1426          for (int i = 0; i < tagAttributePos; i++)      // unless it is CDATA.
1427              if (aname.equals (tagAttributes [i]))      if (handler.stringInterning)
1428                  error ("duplicate attribute", aname, null);        {
1429            if (type == "CDATA" || type == null)
1430          // Inform the handler about the            {
1431          // attribute.              value = readLiteral(flags);
1432          handler.attribute (aname, value, true);            }
1433          dataBufferPos = 0;          else
1434              {
1435          // Note that the attribute has been              value = readLiteral(flags | LIT_NORMALIZE);
1436          // specified.            }
1437          if (tagAttributePos == tagAttributes.length) {        }
1438              String newAttrib[] = new String [tagAttributes.length * 2];      else
1439              System.arraycopy (tagAttributes, 0, newAttrib, 0, tagAttributePos);        {
1440              tagAttributes = newAttrib;          if (type.equals("CDATA") || type == null)
1441          }            {
1442          tagAttributes [tagAttributePos++] = aname;              value = readLiteral(flags);
1443      }            }
1444            else
1445              {
1446                value = readLiteral(flags | LIT_NORMALIZE);
1447              }
1448          }
1449    
1450        // WFC: no duplicate attributes
1451        for (int i = 0; i < tagAttributePos; i++)
1452          {
1453            if (aname.equals(tagAttributes [i]))
1454              {
1455                error("duplicate attribute", aname, null);
1456              }
1457          }
1458    
1459      /**      // Inform the handler about the
1460       * Parse an equals sign surrounded by optional whitespace.      // attribute.
1461       * <pre>      handler.attribute(aname, value, true);
1462       * [25] Eq ::= S? '=' S?      dataBufferPos = 0;
1463       * </pre>      
1464       */      // Note that the attribute has been
1465      private void parseEq ()      // specified.
1466      throws SAXException, IOException      if (tagAttributePos == tagAttributes.length)
1467      {        {
1468          skipWhitespace ();          String newAttrib[] = new String[tagAttributes.length * 2];
1469          require ('=');          System.arraycopy(tagAttributes, 0, newAttrib, 0, tagAttributePos);
1470          skipWhitespace ();          tagAttributes = newAttrib;
1471      }        }
1472        tagAttributes[tagAttributePos++] = aname;
1473      }
1474    
1475      /**
1476       * Parse an equals sign surrounded by optional whitespace.
1477       * <pre>
1478       * [25] Eq ::= S? '=' S?
1479       * </pre>
1480       */
1481      private void parseEq()
1482        throws SAXException, IOException
1483      {
1484        skipWhitespace();
1485        require('=');
1486        skipWhitespace();
1487      }
1488    
1489      /**    /**
1490       * Parse an end tag.     * Parse an end tag.
1491       * <pre>     * <pre>
1492       * [42] ETag ::= '</' Name S? '>'     * [42] ETag ::= '</' Name S? '>'
1493       * </pre>     * </pre>
1494       * <p>NOTE: parseContent () chains to here, we already read the     * <p>NOTE: parseContent () chains to here, we already read the
1495       * "&lt;/".     * "&lt;/".
1496       */     */
1497      private void parseETag ()    private void parseETag()
1498      throws Exception      throws Exception
1499      {    {
1500          require (currentElement);      require(currentElement);
1501          skipWhitespace ();      skipWhitespace();
1502          require ('>');      require('>');
1503          handler.endElement (currentElement);      handler.endElement(currentElement);
1504          // not re-reporting any SAXException re bogus end tags,      // not re-reporting any SAXException re bogus end tags,
1505          // even though that diagnostic might be clearer ...      // even though that diagnostic might be clearer ...
1506      }    }
1507      
1508      /**
1509      /**     * Parse the content of an element.
1510       * Parse the content of an element.     * <pre>
1511       * <pre>     * [43] content ::= (element | CharData | Reference
1512       * [43] content ::= (element | CharData | Reference     *    | CDSect | PI | Comment)*
1513       *          | CDSect | PI | Comment)*     * [67] Reference ::= EntityRef | CharRef
1514       * [67] Reference ::= EntityRef | CharRef     * </pre>
1515       * </pre>     * <p> NOTE: consumes ETtag.
1516       * <p> NOTE: consumes ETtag.     */
1517       */    private void parseContent()
     private void parseContent ()  
1518      throws Exception      throws Exception
1519      {    {
1520          char c;      char c;
1521        
1522          while (true) {      while (true)
1523              // consume characters (or ignorable whitspace) until delimiter        {
1524              parseCharData ();          // consume characters (or ignorable whitspace) until delimiter
1525            parseCharData();
1526              // Handle delimiters  
1527              c = readCh ();          // Handle delimiters
1528              switch (c) {          c = readCh();
1529            switch (c)
1530              case '&':                   // Found "&"            {
1531                  c = readCh ();            case '&':       // Found "&"
1532                  if (c == '#') {              c = readCh();
1533                      parseCharRef ();              if (c == '#')
1534                  } else {                {
1535                      unread (c);                  parseCharRef();
1536                      parseEntityRef (true);                }
1537                  }              else
1538                  isDirtyCurrentElement = true;                {
1539                  break;                  unread(c);
1540                    parseEntityRef(true);
1541                case '<':                         // Found "<"                }
1542                  dataBufferFlush ();              isDirtyCurrentElement = true;
1543                  c = readCh ();              break;
1544                  switch (c) {              
1545                    case '!':                     // Found "<!"            case '<':       // Found "<"
1546                      c = readCh ();              dataBufferFlush();
1547                      switch (c) {              c = readCh();
1548                        case '-':                 // Found "<!-"              switch (c)
1549                          require ('-');                {
1550                          isDirtyCurrentElement = false;                case '!':       // Found "<!"
1551                          parseComment ();                  c = readCh();
1552                          break;                  switch (c)
1553                        case '[':                 // Found "<!["                    {
1554                          isDirtyCurrentElement = false;                    case '-':     // Found "<!-"
1555                          require ("CDATA[");                      require('-');
1556                          handler.startCDATA ();                      isDirtyCurrentElement = false;
1557                          inCDATA = true;                      parseComment();
1558                          parseCDSect ();                      break;
1559                          inCDATA = false;                    case '[':     // Found "<!["
1560                          handler.endCDATA ();                      isDirtyCurrentElement = false;
1561                          break;                      require("CDATA[");
1562                        default:                      handler.startCDATA();
1563                          error ("expected comment or CDATA section", c, null);                      inCDATA = true;
1564                          break;                      parseCDSect();
1565                      }                      inCDATA = false;
1566                      break;                      handler.endCDATA();
1567                        break;
1568                    case '?':             // Found "<?"                    default:
1569                      isDirtyCurrentElement = false;                      error("expected comment or CDATA section", c, null);
1570                      parsePI ();                      break;
1571                      break;                    }
1572                    break;
1573                    case '/':             // Found "</"                
1574                      isDirtyCurrentElement = false;                case '?':     // Found "<?"
1575                      parseETag ();                  isDirtyCurrentElement = false;
1576                      return;                  parsePI();
1577                    break;
1578                    default:              // Found "<" followed by something else                  
1579                      isDirtyCurrentElement = false;                case '/':     // Found "</"
1580                      unread (c);                  isDirtyCurrentElement = false;
1581                      parseElement (false);                  parseETag();
1582                      break;                  return;
1583                  }                  
1584              }                default:     // Found "<" followed by something else
1585          }                  isDirtyCurrentElement = false;
1586                            unread(c);
1587      }                  parseElement(false);
1588                    break;
1589                  }
1590      /**            }
1591       * Parse an element type declaration.        }
1592       * <pre>    }
1593       * [45] elementdecl ::= '&lt;!ELEMENT' S Name S contentspec S? '&gt;'    
1594       * </pre>    /**
1595       * <p> NOTE: the '&lt;!ELEMENT' has already been read.     * Parse an element type declaration.
1596       */     * <pre>
1597      private void parseElementDecl ()     * [45] elementdecl ::= '&lt;!ELEMENT' S Name S contentspec S? '&gt;'
1598       * </pre>
1599       * <p> NOTE: the '&lt;!ELEMENT' has already been read.
1600       */
1601      private void parseElementDecl()
1602      throws Exception      throws Exception
1603      {    {
1604          String name;      String name;
1605        
1606          requireWhitespace ();      requireWhitespace();
1607          // Read the element type name.      // Read the element type name.
1608          name = readNmtoken (true);      name = readNmtoken(true);
1609    
1610          requireWhitespace ();      requireWhitespace();
1611          // Read the content model.      // Read the content model.
1612          parseContentspec (name);      parseContentspec(name);
1613        
1614          skipWhitespace ();      skipWhitespace();
1615          require ('>');      require('>');
1616      }    }
   
1617    
1618      /**    /**
1619       * Content specification.     * Content specification.
1620       * <pre>     * <pre>
1621       * [46] contentspec ::= 'EMPTY' | 'ANY' | Mixed | elements     * [46] contentspec ::= 'EMPTY' | 'ANY' | Mixed | elements
1622       * </pre>     * </pre>
1623       */     */
1624      private void parseContentspec (String name)    private void parseContentspec(String name)
1625      throws Exception      throws Exception
1626      {    {
1627  // FIXME: move elementDecl() into setElement(), pass EMTPY/ANY ...      // FIXME: move elementDecl() into setElement(), pass EMTPY/ANY ...
1628          if (tryRead ("EMPTY")) {      if (tryRead("EMPTY"))
1629              setElement (name, CONTENT_EMPTY, null, null);        {
1630              if (!skippedPE)          setElement(name, CONTENT_EMPTY, null, null);
1631                  handler.getDeclHandler ().elementDecl (name, "EMPTY");          if (!skippedPE)
1632              return;            {
1633          } else if (tryRead ("ANY")) {              handler.getDeclHandler().elementDecl(name, "EMPTY");
1634              setElement (name, CONTENT_ANY, null, null);            }
1635              if (!skippedPE)          return;
1636                  handler.getDeclHandler ().elementDecl (name, "ANY");        }
1637              return;      else if (tryRead("ANY"))
1638          } else {        {
1639              String      model;          setElement(name, CONTENT_ANY, null, null);
1640              char        saved [];          if (!skippedPE)
1641              {
1642              require ('(');              handler.getDeclHandler().elementDecl(name, "ANY");
1643              saved = readBuffer;            }
1644              dataBufferAppend ('(');          return;
1645              skipWhitespace ();        }
1646              if (tryRead ("#PCDATA")) {      else
1647                  dataBufferAppend ("#PCDATA");        {
1648                  parseMixed (saved);          String model;
1649                  model = dataBufferToString ();          char[] saved;
1650                  setElement (name, CONTENT_MIXED, model, null);          
1651              } else {          require('(');
1652                  parseElements (saved);          saved = readBuffer;
1653                  model = dataBufferToString ();          dataBufferAppend('(');
1654                  setElement (name, CONTENT_ELEMENTS, model, null);          skipWhitespace();
1655              }          if (tryRead("#PCDATA"))
1656              if (!skippedPE)            {
1657                  handler.getDeclHandler ().elementDecl (name, model);              dataBufferAppend("#PCDATA");
1658          }              parseMixed(saved);
1659      }              model = dataBufferToString();
1660                setElement(name, CONTENT_MIXED, model, null);
1661      /**            }
1662       * Parse an element-content model.          else
1663       * <pre>            {
1664       * [47] elements ::= (choice | seq) ('?' | '*' | '+')?              parseElements(saved);
1665       * [49] choice ::= '(' S? cp (S? '|' S? cp)+ S? ')'              model = dataBufferToString();
1666       * [50] seq ::= '(' S? cp (S? ',' S? cp)* S? ')'              setElement(name, CONTENT_ELEMENTS, model, null);
1667       * </pre>            }
1668       *          if (!skippedPE)
1669       * <p> NOTE: the opening '(' and S have already been read.            {
1670       *              handler.getDeclHandler().elementDecl(name, model);
1671       * @param saved Buffer for entity that should have the terminal ')'            }
1672       */        }
1673      private void parseElements (char saved [])    }
1674      
1675      /**
1676       * Parse an element-content model.
1677       * <pre>
1678       * [47] elements ::= (choice | seq) ('?' | '*' | '+')?
1679       * [49] choice ::= '(' S? cp (S? '|' S? cp)+ S? ')'
1680       * [50] seq ::= '(' S? cp (S? ',' S? cp)* S? ')'
1681       * </pre>
1682       *
1683       * <p> NOTE: the opening '(' and S have already been read.
1684       *
1685       * @param saved Buffer for entity that should have the terminal ')'
1686       */
1687      private void parseElements(char[] saved)
1688      throws Exception      throws Exception
1689      {    {
1690          char c;      char c;
1691          char sep;      char sep;
1692        
1693          // Parse the first content particle      // Parse the first content particle
1694          skipWhitespace ();      skipWhitespace();
1695          parseCp ();      parseCp();
1696        
1697          // Check for end or for a separator.      // Check for end or for a separator.
1698          skipWhitespace ();      skipWhitespace();
1699          c = readCh ();      c = readCh();
1700          switch (c) {      switch (c)
1701          case ')':        {
1702              // VC: Proper Group/PE Nesting        case ')':
1703              if (readBuffer != saved)          // VC: Proper Group/PE Nesting
1704                  handler.verror ("Illegal Group/PE nesting");          if (readBuffer != saved)
1705              {
1706              dataBufferAppend (')');              handler.verror("Illegal Group/PE nesting");
1707              c = readCh ();            }
1708              switch (c) {          
1709              case '*':          dataBufferAppend(')');
1710              case '+':          c = readCh();
1711              case '?':          switch (c)
1712                  dataBufferAppend (c);            {
1713                  break;            case '*':
1714              default:            case '+':
1715                  unread (c);            case '?':
1716              }              dataBufferAppend(c);
1717              return;              break;
1718          case ',':                       // Register the separator.            default:
1719          case '|':              unread(c);
1720              sep = c;            }
1721              dataBufferAppend (c);          return;
1722              break;        case ',':       // Register the separator.
1723          default:        case '|':
1724              error ("bad separator in content model", c, null);          sep = c;
1725              return;          dataBufferAppend(c);
1726          }          break;
1727          default:
1728          // Parse the rest of the content model.          error("bad separator in content model", c, null);
1729          while (true) {          return;
1730              skipWhitespace ();        }
1731              parseCp ();      
1732              skipWhitespace ();      // Parse the rest of the content model.
1733              c = readCh ();      while (true)
1734              if (c == ')') {        {
1735                  // VC: Proper Group/PE Nesting          skipWhitespace();
1736                  if (readBuffer != saved)          parseCp();
1737                      handler.verror ("Illegal Group/PE nesting");          skipWhitespace();
1738            c = readCh();
1739                  dataBufferAppend (')');          if (c == ')')
1740                  break;            {
1741              } else if (c != sep) {              // VC: Proper Group/PE Nesting
1742                  error ("bad separator in content model", c, null);              if (readBuffer != saved)
1743                  return;                {
1744              } else {                  handler.verror("Illegal Group/PE nesting");
1745                  dataBufferAppend (c);                }
1746              }              
1747          }              dataBufferAppend(')');
1748                break;
1749          // Check for the occurrence indicator.            }
1750          c = readCh ();          else if (c != sep)
1751          switch (c) {            {
1752          case '?':              error("bad separator in content model", c, null);
1753          case '*':              return;
1754          case '+':            }
1755              dataBufferAppend (c);          else
1756              return;            {
1757          default:              dataBufferAppend(c);
1758              unread (c);            }
1759              return;        }
1760          }      
1761      }      // Check for the occurrence indicator.
1762        c = readCh();
1763        switch (c)
1764      /**        {
1765       * Parse a content particle.        case '?':
1766       * <pre>        case '*':
1767       * [48] cp ::= (Name | choice | seq) ('?' | '*' | '+')?        case '+':
1768       * </pre>          dataBufferAppend(c);
1769       */          return;
1770      private void parseCp ()        default:
1771            unread(c);
1772            return;
1773          }
1774      }
1775      
1776      /**
1777       * Parse a content particle.
1778       * <pre>
1779       * [48] cp ::= (Name | choice | seq) ('?' | '*' | '+')?
1780       * </pre>
1781       */
1782      private void parseCp()
1783      throws Exception      throws Exception
1784      {    {
1785          if (tryRead ('(')) {      if (tryRead('('))
1786              dataBufferAppend ('(');        {
1787              parseElements (readBuffer);          dataBufferAppend('(');
1788          } else {          parseElements(readBuffer);
1789              dataBufferAppend (readNmtoken (true));        }
1790              char c = readCh ();      else
1791              switch (c) {        {
1792              case '?':          dataBufferAppend(readNmtoken(true));
1793              case '*':          char c = readCh();
1794              case '+':          switch (c)
1795                  dataBufferAppend (c);            {
1796                  break;            case '?':
1797              default:            case '*':
1798                  unread (c);            case '+':
1799                  break;              dataBufferAppend(c);
1800              }              break;
1801          }            default:
1802      }              unread(c);
1803                break;
1804              }
1805          }
1806      }
1807    
1808      /**    /**
1809       * Parse mixed content.     * Parse mixed content.
1810       * <pre>     * <pre>
1811       * [51] Mixed ::= '(' S? ( '#PCDATA' (S? '|' S? Name)*) S? ')*'     * [51] Mixed ::= '(' S? ( '#PCDATA' (S? '|' S? Name)*) S? ')*'
1812       *        | '(' S? ('#PCDATA') S? ')'     *        | '(' S? ('#PCDATA') S? ')'
1813       * </pre>     * </pre>
1814       *     *
1815       * @param saved Buffer for entity that should have the terminal ')'     * @param saved Buffer for entity that should have the terminal ')'
1816       */     */
1817      private void parseMixed (char saved [])    private void parseMixed(char[] saved)
1818      throws Exception      throws Exception
1819      {    {
1820          // Check for PCDATA alone.      // Check for PCDATA alone.
1821          skipWhitespace ();      skipWhitespace();
1822          if (tryRead (')')) {      if (tryRead(')'))
1823              // VC: Proper Group/PE Nesting        {
1824              if (readBuffer != saved)          // VC: Proper Group/PE Nesting
1825                  handler.verror ("Illegal Group/PE nesting");          if (readBuffer != saved)
1826              {
1827              dataBufferAppend (")*");              handler.verror("Illegal Group/PE nesting");
1828              tryRead ('*');            }
1829              return;          
1830          }          dataBufferAppend(")*");
1831            tryRead('*');
1832          // Parse mixed content.          return;
1833          skipWhitespace ();        }
1834          while (!tryRead (")")) {      
1835              require ('|');      // Parse mixed content.
1836              dataBufferAppend ('|');      skipWhitespace();
1837              skipWhitespace ();      while (!tryRead(")"))
1838              dataBufferAppend (readNmtoken (true));        {
1839              skipWhitespace ();          require('|');
1840          }          dataBufferAppend('|');
1841            skipWhitespace();
1842          // VC: Proper Group/PE Nesting          dataBufferAppend(readNmtoken(true));
1843          if (readBuffer != saved)          skipWhitespace();
1844              handler.verror ("Illegal Group/PE nesting");        }
1845        
1846          require ('*');      // VC: Proper Group/PE Nesting
1847          dataBufferAppend (")*");      if (readBuffer != saved)
1848      }        {
1849            handler.verror("Illegal Group/PE nesting");
1850          }
1851      /**      
1852       * Parse an attribute list declaration.      require('*');
1853       * <pre>      dataBufferAppend(")*");
1854       * [52] AttlistDecl ::= '&lt;!ATTLIST' S Name AttDef* S? '&gt;'    }
1855       * </pre>    
1856       * <p>NOTE: the '&lt;!ATTLIST' has already been read.    /**
1857       */     * Parse an attribute list declaration.
1858      private void parseAttlistDecl ()     * <pre>
1859       * [52] AttlistDecl ::= '&lt;!ATTLIST' S Name AttDef* S? '&gt;'
1860       * </pre>
1861       * <p>NOTE: the '&lt;!ATTLIST' has already been read.
1862       */
1863      private void parseAttlistDecl()
1864      throws Exception      throws Exception
1865      {    {
1866          String elementName;      String elementName;
1867        
1868          requireWhitespace ();      requireWhitespace();
1869          elementName = readNmtoken (true);      elementName = readNmtoken(true);
1870          boolean white = tryWhitespace ();      boolean white = tryWhitespace();
1871          while (!tryRead ('>')) {      while (!tryRead('>'))
1872              if (!white)        {
1873                  error ("whitespace required before attribute definition");          if (!white)
1874              parseAttDef (elementName);            {
1875              white = tryWhitespace ();              error("whitespace required before attribute definition");
1876          }            }
1877      }          parseAttDef(elementName);
1878            white = tryWhitespace();
1879          }
1880      /**    }
1881       * Parse a single attribute definition.    
1882       * <pre>    /**
1883       * [53] AttDef ::= S Name S AttType S DefaultDecl     * Parse a single attribute definition.
1884       * </pre>     * <pre>
1885       */     * [53] AttDef ::= S Name S AttType S DefaultDecl
1886      private void parseAttDef (String elementName)     * </pre>
1887       */
1888      private void parseAttDef(String elementName)
1889      throws Exception      throws Exception
1890      {    {
1891          String name;      String name;
1892          String type;      String type;
1893          String enumer = null;      String enumer = null;
1894        
1895          // Read the attribute name.      // Read the attribute name.
1896          name = readNmtoken (true);      name = readNmtoken(true);
   
         // Read the attribute type.  
         requireWhitespace ();  
         type = readAttType ();  
   
         // Get the string of enumerated values if necessary.  
   if (handler.getFeature (SAXDriver.FEATURE + "string-interning")) {  
     if ("ENUMERATION" == type || "NOTATION" == type)  
             enumer = dataBufferToString ();  
   } else {  
     if ("ENUMERATION".equals(type) || "NOTATION".equals(type))  
             enumer = dataBufferToString ();  
   }  
   
         // Read the default value.  
         requireWhitespace ();  
         parseDefault (elementName, name, type, enumer);  
     }  
1897    
1898        // Read the attribute type.
1899        requireWhitespace();
1900        type = readAttType();
1901    
1902        // Get the string of enumerated values if necessary.
1903        if (handler.stringInterning)
1904          {
1905            if ("ENUMERATION" == type || "NOTATION" == type)
1906              {
1907                enumer = dataBufferToString();
1908              }
1909          }
1910        else
1911          {
1912            if ("ENUMERATION".equals(type) || "NOTATION".equals(type))
1913              {
1914                enumer = dataBufferToString();
1915              }
1916          }
1917        
1918        // Read the default value.
1919        requireWhitespace();
1920        parseDefault(elementName, name, type, enumer);
1921      }
1922    
1923    /**    /**
1924     * Parse the attribute type.     * Parse the attribute type.
# Line 1598  loop: Line 1926  loop:
1926     * [54] AttType ::= StringType | TokenizedType | EnumeratedType     * [54] AttType ::= StringType | TokenizedType | EnumeratedType
1927     * [55] StringType ::= 'CDATA'     * [55] StringType ::= 'CDATA'
1928     * [56] TokenizedType ::= 'ID' | 'IDREF' | 'IDREFS' | 'ENTITY'     * [56] TokenizedType ::= 'ID' | 'IDREF' | 'IDREFS' | 'ENTITY'
1929     *            | 'ENTITIES' | 'NMTOKEN' | 'NMTOKENS'     *    | 'ENTITIES' | 'NMTOKEN' | 'NMTOKENS'
1930     * [57] EnumeratedType ::= NotationType | Enumeration     * [57] EnumeratedType ::= NotationType | Enumeration
1931     * </pre>     * </pre>
1932     */     */
1933    private String readAttType ()    private String readAttType()
1934      throws Exception      throws Exception
1935    {    {
1936      if (tryRead ('(')) {      if (tryRead('('))
1937              parseEnumeration (false);        {
1938              return "ENUMERATION";          parseEnumeration(false);
1939      } else {          return "ENUMERATION";
1940              String typeString = readNmtoken (true);        }
1941        if (handler.getFeature (SAXDriver.FEATURE + "string-interning")) {      else
1942          if ("NOTATION" == typeString) {        {
1943            parseNotationType ();          String typeString = readNmtoken(true);
1944            return typeString;          if (handler.stringInterning)
1945          } else if ("CDATA" == typeString            {
1946                     || "ID" == typeString              if ("NOTATION" == typeString)
1947                     || "IDREF" == typeString                {
1948                     || "IDREFS" == typeString                  parseNotationType();
1949                     || "ENTITY" == typeString                  return typeString;
1950                     || "ENTITIES" == typeString                }
1951                     || "NMTOKEN" == typeString              else if ("CDATA" == typeString
1952                     || "NMTOKENS" == typeString)                       || "ID" == typeString
1953            return typeString;                       || "IDREF" == typeString
1954        } else {                       || "IDREFS" == typeString
1955          if ("NOTATION".equals(typeString)) {                       || "ENTITY" == typeString
1956            parseNotationType ();                       || "ENTITIES" == typeString
1957            return typeString;                       || "NMTOKEN" == typeString
1958          } else if ("CDATA".equals(typeString)                       || "NMTOKENS" == typeString)
1959                     || "ID".equals(typeString)                {
1960                     || "IDREF".equals(typeString)                  return typeString;
1961                     || "IDREFS".equals(typeString)                }
1962                     || "ENTITY".equals(typeString)            }
1963                     || "ENTITIES".equals(typeString)          else
1964                     || "NMTOKEN".equals(typeString)            {
1965                     || "NMTOKENS".equals(typeString))              if ("NOTATION".equals(typeString))
1966            return typeString;                {
1967                    parseNotationType();
1968                    return typeString;
1969                  }
1970                else if ("CDATA".equals(typeString)
1971                         || "ID".equals(typeString)
1972                         || "IDREF".equals(typeString)
1973                         || "IDREFS".equals(typeString)
1974                         || "ENTITY".equals(typeString)
1975                         || "ENTITIES".equals(typeString)
1976                         || "NMTOKEN".equals(typeString)
1977                         || "NMTOKENS".equals(typeString))
1978                  {
1979                    return typeString;
1980                  }
1981              }
1982            error("illegal attribute type", typeString, null);
1983            return null;
1984        }        }
             error ("illegal attribute type", typeString, null);  
             return null;  
     }  
1985    }    }
1986        
1987      /**
1988      /**     * Parse an enumeration.
1989       * Parse an enumeration.     * <pre>
1990       * <pre>     * [59] Enumeration ::= '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')'
1991       * [59] Enumeration ::= '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')'     * </pre>
1992       * </pre>     * <p>NOTE: the '(' has already been read.
1993       * <p>NOTE: the '(' has already been read.     */
1994       */    private void parseEnumeration(boolean isNames)
     private void parseEnumeration (boolean isNames)  
1995      throws Exception      throws Exception
1996      {    {
1997          dataBufferAppend ('(');      dataBufferAppend('(');
   
         // Read the first token.  
         skipWhitespace ();  
         dataBufferAppend (readNmtoken (isNames));  
         // Read the remaining tokens.  
         skipWhitespace ();  
         while (!tryRead (')')) {  
             require ('|');  
             dataBufferAppend ('|');  
             skipWhitespace ();  
             dataBufferAppend (readNmtoken (isNames));  
             skipWhitespace ();  
         }  
         dataBufferAppend (')');  
     }  
1998    
1999        // Read the first token.
2000        skipWhitespace();
2001        dataBufferAppend(readNmtoken(isNames));
2002        // Read the remaining tokens.
2003        skipWhitespace();
2004        while (!tryRead(')'))
2005          {
2006            require('|');
2007            dataBufferAppend('|');
2008            skipWhitespace();
2009            dataBufferAppend(readNmtoken (isNames));
2010            skipWhitespace();
2011          }
2012        dataBufferAppend(')');
2013      }
2014    
2015      /**    /**
2016       * Parse a notation type for an attribute.     * Parse a notation type for an attribute.
2017       * <pre>     * <pre>
2018       * [58] NotationType ::= 'NOTATION' S '(' S? NameNtoks     * [58] NotationType ::= 'NOTATION' S '(' S? NameNtoks
2019       *          (S? '|' S? name)* S? ')'     *    (S? '|' S? name)* S? ')'
2020       * </pre>     * </pre>
2021       * <p>NOTE: the 'NOTATION' has already been read     * <p>NOTE: the 'NOTATION' has already been read
2022       */     */
2023      private void parseNotationType ()    private void parseNotationType()
2024      throws Exception      throws Exception
2025      {    {
2026          requireWhitespace ();      requireWhitespace();
2027          require ('(');      require('(');
2028        
2029          parseEnumeration (true);      parseEnumeration(true);
     }  
   
   
     /**  
      * Parse the default value for an attribute.  
      * <pre>  
      * [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED'  
      *          | (('#FIXED' S)? AttValue)  
      * </pre>  
      */  
     private void parseDefault (  
         String elementName,  
         String name,  
         String type,  
         String enumer  
     ) throws Exception  
     {  
         int     valueType = ATTRIBUTE_DEFAULT_SPECIFIED;  
         String  value = null;  
         int     flags = LIT_ATTRIBUTE;  
         boolean saved = expandPE;  
         String  defaultType = null;  
   
         // LIT_ATTRIBUTE forces '<' checks now (ASAP) and turns whitespace  
         // chars to spaces (doesn't matter when that's done if it doesn't  
         // interfere with char refs expanding to whitespace).  
   
         if (!skippedPE) {  
     flags |= LIT_ENTITY_REF;  
     if (handler.getFeature (SAXDriver.FEATURE + "string-interning")) {  
             if ("CDATA" != type)  
         flags |= LIT_NORMALIZE;  
     } else {  
             if (!"CDATA".equals(type))  
         flags |= LIT_NORMALIZE;  
     }  
         }  
   
         expandPE = false;  
         if (tryRead ('#')) {  
             if (tryRead ("FIXED")) {  
                 defaultType = "#FIXED";  
                 valueType = ATTRIBUTE_DEFAULT_FIXED;  
                 requireWhitespace ();  
                 value = readLiteral (flags);  
             } else if (tryRead ("REQUIRED")) {  
                 defaultType = "#REQUIRED";  
                 valueType = ATTRIBUTE_DEFAULT_REQUIRED;  
             } else if (tryRead ("IMPLIED")) {  
                 defaultType = "#IMPLIED";  
                 valueType = ATTRIBUTE_DEFAULT_IMPLIED;  
             } else {  
                 error ("illegal keyword for attribute default value");  
             }  
         } else  
             value = readLiteral (flags);  
         expandPE = saved;  
         setAttribute (elementName, name, type, enumer, value, valueType);  
   if (handler.getFeature (SAXDriver.FEATURE + "string-interning")) {  
     if ("ENUMERATION" == type)  
             type = enumer;  
     else if ("NOTATION" == type)  
             type = "NOTATION " + enumer;  
   } else {  
     if ("ENUMERATION".equals(type))  
             type = enumer;  
     else if ("NOTATION".equals(type))  
             type = "NOTATION " + enumer;  
2030    }    }
         if (!skippedPE) handler.getDeclHandler ()  
             .attributeDecl (elementName, name, type, defaultType, value);  
     }  
   
2031    
2032      /**    /**
2033       * Parse a conditional section.     * Parse the default value for an attribute.
2034       * <pre>     * <pre>
2035       * [61] conditionalSect ::= includeSect || ignoreSect     * [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED'
2036       * [62] includeSect ::= '&lt;![' S? 'INCLUDE' S? '['     *    | (('#FIXED' S)? AttValue)
2037       *          extSubsetDecl ']]&gt;'     * </pre>
2038       * [63] ignoreSect ::= '&lt;![' S? 'IGNORE' S? '['     */
2039       *          ignoreSectContents* ']]&gt;'    private void parseDefault(String elementName, String name,
2040       * [64] ignoreSectContents ::= Ignore                              String type, String enumer)
      *          ('&lt;![' ignoreSectContents* ']]&gt;' Ignore )*  
      * [65] Ignore ::= Char* - (Char* ( '&lt;![' | ']]&gt;') Char* )  
      * </pre>  
      * <p> NOTE: the '&gt;![' has already been read.  
      */  
     private void parseConditionalSect (char saved [])  
2041      throws Exception      throws Exception
2042      {    {
2043          skipWhitespace ();      int valueType = ATTRIBUTE_DEFAULT_SPECIFIED;
2044          if (tryRead ("INCLUDE")) {      String value = null;
2045              skipWhitespace ();      int flags = LIT_ATTRIBUTE;
2046              require ('[');      boolean saved = expandPE;
2047              // VC: Proper Conditional Section/PE Nesting      String defaultType = null;
2048              if (readBuffer != saved)      
2049                  handler.verror ("Illegal Conditional Section/PE nesting");      // LIT_ATTRIBUTE forces '<' checks now (ASAP) and turns whitespace
2050              skipWhitespace ();      // chars to spaces (doesn't matter when that's done if it doesn't
2051              while (!tryRead ("]]>")) {      // interfere with char refs expanding to whitespace).
2052                  parseMarkupdecl ();      
2053                  skipWhitespace ();      if (!skippedPE)
2054              }        {
2055          } else if (tryRead ("IGNORE")) {          flags |= LIT_ENTITY_REF;
2056              skipWhitespace ();          if (handler.stringInterning)
2057              require ('[');            {
2058              // VC: Proper Conditional Section/PE Nesting              if ("CDATA" != type)
2059              if (readBuffer != saved)                {
2060                  handler.verror ("Illegal Conditional Section/PE nesting");                  flags |= LIT_NORMALIZE;
2061              int nesting = 1;                }
2062              char c;            }
2063              expandPE = false;          else
2064              for (int nest = 1; nest > 0;) {            {
2065                  c = readCh ();              if (!"CDATA".equals(type))
2066                  switch (c) {                {
2067                  case '<':                  flags |= LIT_NORMALIZE;
2068                      if (tryRead ("![")) {                }
2069                          nest++;            }
2070                      }        }
2071                  case ']':      
2072                      if (tryRead ("]>")) {      expandPE = false;
2073                          nest--;      if (tryRead('#'))
2074                      }        {
2075                  }          if (tryRead("FIXED"))
2076              }            {
2077              expandPE = true;              defaultType = "#FIXED";
2078          } else {              valueType = ATTRIBUTE_DEFAULT_FIXED;
2079              error ("conditional section must begin with INCLUDE or IGNORE");              requireWhitespace();
2080          }              value = readLiteral(flags);
2081      }            }
2082            else if (tryRead("REQUIRED"))
2083    private void parseCharRef ()            {
2084                defaultType = "#REQUIRED";
2085                valueType = ATTRIBUTE_DEFAULT_REQUIRED;
2086              }
2087            else if (tryRead("IMPLIED"))
2088              {
2089                defaultType = "#IMPLIED";
2090                valueType = ATTRIBUTE_DEFAULT_IMPLIED;
2091              }
2092            else
2093              {
2094                error("illegal keyword for attribute default value");
2095              }
2096          }
2097        else
2098          {
2099            value = readLiteral(flags);
2100          }
2101        expandPE = saved;
2102        setAttribute(elementName, name, type, enumer, value, valueType);
2103        if (handler.stringInterning)
2104          {
2105            if ("ENUMERATION" == type)
2106              {
2107                type = enumer;
2108              }
2109            else if ("NOTATION" == type)
2110              {
2111                type = "NOTATION " + enumer;
2112              }
2113          }
2114        else
2115          {
2116            if ("ENUMERATION".equals(type))
2117              {
2118                type = enumer;
2119              }
2120            else if ("NOTATION".equals(type))
2121              {
2122                type = "NOTATION " + enumer;
2123              }
2124          }
2125        if (!skippedPE)
2126          {
2127            handler.getDeclHandler().attributeDecl(elementName, name, type,
2128                                                   defaultType, value);
2129          }
2130      }
2131      
2132      /**
2133       * Parse a conditional section.
2134       * <pre>
2135       * [61] conditionalSect ::= includeSect || ignoreSect
2136       * [62] includeSect ::= '&lt;![' S? 'INCLUDE' S? '['
2137       *    extSubsetDecl ']]&gt;'
2138       * [63] ignoreSect ::= '&lt;![' S? 'IGNORE' S? '['
2139       *    ignoreSectContents* ']]&gt;'
2140       * [64] ignoreSectContents ::= Ignore
2141       *    ('&lt;![' ignoreSectContents* ']]&gt;' Ignore )*
2142       * [65] Ignore ::= Char* - (Char* ( '&lt;![' | ']]&gt;') Char* )
2143       * </pre>
2144       * <p> NOTE: the '&gt;![' has already been read.
2145       */
2146      private void parseConditionalSect(char[] saved)
2147        throws Exception
2148      {
2149        skipWhitespace();
2150        if (tryRead("INCLUDE"))
2151          {
2152            skipWhitespace();
2153            require('[');
2154            // VC: Proper Conditional Section/PE Nesting
2155            if (readBuffer != saved)
2156              {
2157                handler.verror("Illegal Conditional Section/PE nesting");
2158              }
2159            skipWhitespace();
2160            while (!tryRead("]]>"))
2161              {
2162                parseMarkupdecl();
2163                skipWhitespace();
2164              }
2165          }
2166        else if (tryRead("IGNORE"))
2167          {
2168            skipWhitespace();
2169            require('[');
2170            // VC: Proper Conditional Section/PE Nesting
2171            if (readBuffer != saved)
2172              {
2173                handler.verror("Illegal Conditional Section/PE nesting");
2174              }
2175            int nesting = 1;
2176            char c;
2177            expandPE = false;
2178            for (int nest = 1; nest > 0; )
2179              {
2180                c = readCh();
2181                switch (c)
2182                  {
2183                  case '<':
2184                    if (tryRead("!["))
2185                      {
2186                        nest++;
2187                      }
2188                  case ']':
2189                    if (tryRead("]>"))
2190                      {
2191                        nest--;
2192                      }
2193                  }
2194              }
2195            expandPE = true;
2196          }
2197        else
2198          {
2199            error("conditional section must begin with INCLUDE or IGNORE");
2200          }
2201      }
2202      
2203      private void parseCharRef()
2204      throws SAXException, IOException      throws SAXException, IOException
2205    {    {
2206      parseCharRef (true /* do flushDataBuffer by default */);      parseCharRef(true /* do flushDataBuffer by default */);
2207    }    }
2208    
2209    /**    /**
# Line 1830  loop: Line 2213  loop:
2213     * </pre>     * </pre>
2214     * <p>NOTE: the '&#' has already been read.     * <p>NOTE: the '&#' has already been read.
2215     */     */
2216    private void tryReadCharRef ()    private void tryReadCharRef()
2217    throws SAXException, IOException      throws SAXException, IOException
2218    {    {
2219          int value = 0;      int value = 0;
2220          char c;      char c;
2221        
2222          if (tryRead ('x')) {      if (tryRead('x'))
2223          {
2224  loop1:  loop1:
2225              while (true) {          while (true)
2226                  c = readCh ();            {
2227                  int n;              c = readCh();
2228                  switch (c) {              if (c == ';')
2229                  case '0': case '1': case '2': case '3': case '4':                {
2230                  case '5': case '6': case '7': case '8': case '9':                  break loop1;
2231                      n = c - '0';                }
2232                      break;              else
2233                  case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':                {
2234                      n = (c - 'a') + 10;                  int n = Character.digit(c, 16);
2235                      break;                  if (n == -1)
2236                  case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':                    {
2237                      n = (c - 'A') + 10;                      error("illegal character in character reference", c, null);
2238                      break;                      break loop1;
2239                  case ';':                    }
2240                      break loop1;                  value *= 16;
2241                  default:                  value += n;
2242                      error ("illegal character in character reference", c, null);                }
2243                      break loop1;            }
2244                  }        }
2245                  value *= 16;      else
2246                  value += n;        {
             }  
         } else {  
2247  loop2:  loop2:
2248              while (true) {          while (true)
2249                  c = readCh ();            {
2250                  switch (c) {              c = readCh();
2251                  case '0': case '1': case '2': case '3': case '4':              if (c == ';')
2252                  case '5': case '6': case '7': case '8': case '9':                {
2253                      value *= 10;                  break loop2;
2254                      value += c - '0';                }
2255                      break;              else
2256                  case ';':                {
2257                      break loop2;                  int n = Character.digit(c, 10);
2258                  default:                  if (n == -1)
2259                      error ("illegal character in character reference", c, null);                    {
2260                      break loop2;                      error("illegal character in character reference", c, null);
2261                  }                      break loop2;
2262              }                    }
2263          }                  value *= 10;
2264                    value += n;
2265          // check for character refs being legal XML                }
2266          if ((value < 0x0020            }
2267                  && ! (value == '\n' || value == '\t' || value == '\r'))        }
2268                  || (value >= 0xD800 && value <= 0xDFFF)      
2269                  || value == 0xFFFE || value == 0xFFFF      // check for character refs being legal XML
2270                  || value > 0x0010ffff)      if ((value < 0x0020
2271              error ("illegal XML character reference U+"           && ! (value == '\n' || value == '\t' || value == '\r'))
2272                      + Integer.toHexString (value));          || (value >= 0xD800 && value <= 0xDFFF)
2273            || value == 0xFFFE || value == 0xFFFF
2274          // Check for surrogates: 00000000 0000xxxx yyyyyyyy zzzzzzzz          || value > 0x0010ffff)
2275          //  (1101|10xx|xxyy|yyyy + 1101|11yy|zzzz|zzzz:        {
2276          if (value > 0x0010ffff) {          error("illegal XML character reference U+"
2277              // too big for surrogate                + Integer.toHexString(value));
2278              error ("character reference " + value + " is too large for UTF-16",        }
2279                     new Integer (value).toString (), null);      
2280          }      // Check for surrogates: 00000000 0000xxxx yyyyyyyy zzzzzzzz
2281        //  (1101|10xx|xxyy|yyyy + 1101|11yy|zzzz|zzzz:
2282        if (value > 0x0010ffff)
2283          {
2284            // too big for surrogate
2285            error("character reference " + value + " is too large for UTF-16",
2286                  new Integer(value).toString(), null);
2287          }
2288        
2289    }    }
2290        
2291      /**    /**
2292       * Read and interpret a character reference.     * Read and interpret a character reference.
2293       * <pre>     * <pre>
2294       * [66] CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';'     * [66] CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';'
2295       * </pre>     * </pre>
2296       * <p>NOTE: the '&#' has already been read.     * <p>NOTE: the '&#' has already been read.
2297       */     */
2298      private void parseCharRef (boolean doFlush)    private void parseCharRef(boolean doFlush)
2299      throws SAXException, IOException      throws SAXException, IOException
2300      {    {
2301          int value = 0;      int value = 0;
2302          char c;      char c;
2303        
2304          if (tryRead ('x')) {      if (tryRead('x'))
2305          {
2306  loop1:  loop1:
2307              while (true) {          while (true)
2308                  c = readCh ();            {
2309                  int n;              c = readCh();
2310                  switch (c) {              if (c == ';')
2311                  case '0': case '1': case '2': case '3': case '4':                {
2312                  case '5': case '6': case '7': case '8': case '9':                  break loop1;
2313                      n = c - '0';                }
2314                      break;              else
2315                  case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':                {
2316                      n = (c - 'a') + 10;                  int n = Character.digit(c, 16);
2317                      break;                  if (n == -1)
2318                  case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':                    {
2319                      n = (c - 'A') + 10;                      error("illegal character in character reference", c, null);
2320                      break;                      break loop1;
2321                  case ';':                    }
2322                      break loop1;                  value *= 16;
2323                  default:                  value += n;
2324                      error ("illegal character in character reference", c, null);                }
2325                      break loop1;            }
2326                  }        }
2327                  value *= 16;      else
2328                  value += n;        {
             }  
         } else {  
2329  loop2:  loop2:
2330              while (true) {          while (true)
2331                  c = readCh ();            {
2332                  switch (c) {              c = readCh();
2333                  case '0': case '1': case '2': case '3': case '4':              if (c == ';')
2334                  case '5': case '6': case '7': case '8': case '9':                {
2335                      value *= 10;                  break loop2;
2336                      value += c - '0';                }
2337                      break;              else
2338                  case ';':                {
2339                      break loop2;                  int n = Character.digit(c, 10);
2340                  default:                  if (n == -1)
2341                      error ("illegal character in character reference", c, null);                    {
2342                      break loop2;                      error("illegal character in character reference", c, null);
2343                  }                      break loop2;
2344              }                    }
2345          }                  value *= 10;
2346                    value += c - '0';
2347          // check for character refs being legal XML                }
2348          if ((value < 0x0020            }
2349                  && ! (value == '\n' || value == '\t' || value == '\r'))        }
2350                  || (value >= 0xD800 && value <= 0xDFFF)      
2351                  || value == 0xFFFE || value == 0xFFFF      // check for character refs being legal XML
2352                  || value > 0x0010ffff)      if ((value < 0x0020
2353              error ("illegal XML character reference U+"           && ! (value == '\n' || value == '\t' || value == '\r'))
2354                      + Integer.toHexString (value));          || (value >= 0xD800 && value <= 0xDFFF)
2355            || value == 0xFFFE || value == 0xFFFF
2356          // Check for surrogates: 00000000 0000xxxx yyyyyyyy zzzzzzzz          || value > 0x0010ffff)
2357          //  (1101|10xx|xxyy|yyyy + 1101|11yy|zzzz|zzzz:        {
2358          if (value <= 0x0000ffff) {          error("illegal XML character reference U+"
2359              // no surrogates needed                + Integer.toHexString(value));
2360              dataBufferAppend ((char) value);        }
2361          } else if (value <= 0x0010ffff) {      
2362              value -= 0x10000;      // Check for surrogates: 00000000 0000xxxx yyyyyyyy zzzzzzzz
2363              // > 16 bits, surrogate needed      //  (1101|10xx|xxyy|yyyy + 1101|11yy|zzzz|zzzz:
2364              dataBufferAppend ((char) (0xd800 | (value >> 10)));      if (value <= 0x0000ffff)
2365              dataBufferAppend ((char) (0xdc00 | (value & 0x0003ff)));        {
2366          } else {          // no surrogates needed
2367              // too big for surrogate          dataBufferAppend((char) value);
2368              error ("character reference " + value + " is too large for UTF-16",        }
2369                     new Integer (value).toString (), null);      else if (value <= 0x0010ffff)
2370          }        {
2371    if (doFlush) dataBufferFlush ();          value -= 0x10000;
2372      }          // > 16 bits, surrogate needed
2373            dataBufferAppend((char) (0xd800 | (value >> 10)));
2374            dataBufferAppend((char) (0xdc00 | (value & 0x0003ff)));
2375      /**        }
2376       * Parse and expand an entity reference.      else
2377       * <pre>        {
2378       * [68] EntityRef ::= '&' Name ';'          // too big for surrogate
2379       * </pre>          error("character reference " + value + " is too large for UTF-16",
2380       * <p>NOTE: the '&amp;' has already been read.                new Integer(value).toString(), null);
2381       * @param externalAllowed External entities are allowed here.        }
2382       */      if (doFlush)
2383      private void parseEntityRef (boolean externalAllowed)        {
2384            dataBufferFlush();
2385          }
2386      }
2387      
2388      /**
2389       * Parse and expand an entity reference.
2390       * <pre>
2391       * [68] EntityRef ::= '&' Name ';'
2392       * </pre>
2393       * <p>NOTE: the '&amp;' has already been read.
2394       * @param externalAllowed External entities are allowed here.
2395       */
2396      private void parseEntityRef(boolean externalAllowed)
2397      throws SAXException, IOException      throws SAXException, IOException
2398      {    {
2399          String name;      String name;
2400        
2401          name = readNmtoken (true);      name = readNmtoken(true);
2402          require (';');      require(';');
2403          switch (getEntityType (name)) {      switch (getEntityType(name))
2404          case ENTITY_UNDECLARED:        {
2405              // NOTE:  XML REC describes amazingly convoluted handling for        case ENTITY_UNDECLARED:
2406              // this case.  Nothing as meaningful as being a WFness error          // NOTE:  XML REC describes amazingly convoluted handling for
2407              // unless the processor might _legitimately_ not have seen a          // this case.  Nothing as meaningful as being a WFness error
2408              // declaration ... which is what this implements.          // unless the processor might _legitimately_ not have seen a
2409              String      message;          // declaration ... which is what this implements.
2410                        String message;
2411              message = "reference to undeclared general entity " + name;          
2412              if (skippedPE && !docIsStandalone) {          message = "reference to undeclared general entity " + name;
2413                  handler.verror (message);          if (skippedPE && !docIsStandalone)
2414                  // we don't know this entity, and it might be external...            {
2415                  if (externalAllowed)              handler.verror(message);
2416                      handler.skippedEntity (name);              // we don't know this entity, and it might be external...
2417              } else              if (externalAllowed)
2418                  error (message);                {
2419              break;                  handler.skippedEntity(name);
2420          case ENTITY_INTERNAL:                }
2421              pushString (name, getEntityValue (name));            }
2422                        else
2423              //workaround for possible input pop before marking            {
2424              //the buffer reading position                    error(message);
2425              char t = readCh ();            }
2426              unread (t);          break;
2427              int bufferPosMark = readBufferPos;        case ENTITY_INTERNAL:
2428                        pushString(name, getEntityValue(name));
2429              int end = readBufferPos + getEntityValue (name).length();            
2430              for(int k = readBufferPos; k < end; k++){            //workaround for possible input pop before marking
2431                      t = readCh ();            //the buffer reading position  
2432                      if (t == '&'){            char t = readCh();
2433                          t = readCh ();              unread(t);
2434                          if (t  == '#'){            int bufferPosMark = readBufferPos;
2435                             //try to match a character ref            
2436                             tryReadCharRef ();            int end = readBufferPos + getEntityValue(name).length();
2437                                        for (int k = readBufferPos; k < end; k++)
2438                             //everything has been read              {
2439                             if (readBufferPos >= end)                t = readCh();
2440                                break;                if (t == '&')
2441                             k = readBufferPos;                  {
2442                             continue;                    t = readCh();  
2443                          }                    if (t  == '#')
2444                          else if (Character.isLetter(t)){                      {
2445                             //looks like an entity ref                        //try to match a character ref
2446                             unread (t);                        tryReadCharRef();
2447                             readNmtoken (true);                  
2448                             require (';');                        //everything has been read
2449                                                  if (readBufferPos >= end)
2450                             //everything has been read                          {
2451                             if (readBufferPos >= end)                            break;
2452                                break;                          }
2453                             k = readBufferPos;                        k = readBufferPos;
2454                             continue;                        continue;
2455                          }                      }
2456                          error(" malformed entity reference");                    else if (Character.isLetter(t))
2457                      }                      {
2458                                            //looks like an entity ref
2459                          unread(t);
2460                          readNmtoken(true);
2461                          require(';');
2462                          
2463                          //everything has been read
2464                          if (readBufferPos >= end)
2465                            {
2466                              break;
2467                            }
2468                          k = readBufferPos;
2469                          continue;
2470                        }
2471                      error(" malformed entity reference");
2472                    }
2473                  
2474              }              }
2475              readBufferPos = bufferPosMark;            readBufferPos = bufferPosMark;
2476              break;            break;
2477          case ENTITY_TEXT:        case ENTITY_TEXT:
2478              if (externalAllowed) {            if (externalAllowed)
2479                  pushURL (false, name, getEntityIds (name),              {
2480                          null, null, null, true);                pushURL(false, name, getEntityIds(name),
2481              } else {                        null, null, null, true);
2482                  error ("reference to external entity in attribute value.",              }
2483                          name, null);            else
2484              }              {
2485              break;                error("reference to external entity in attribute value.",
2486          case ENTITY_NDATA:                      name, null);
2487              if (externalAllowed) {              }
2488                  error ("unparsed entity reference in content", name, null);            break;
2489              } else {        case ENTITY_NDATA:
2490                  error ("reference to external entity in attribute value.",            if (externalAllowed)
2491                          name, null);              {
2492              }                error("unparsed entity reference in content", name, null);
2493              break;              }
2494          default:            else
2495              throw new RuntimeException ();              {
2496          }                error("reference to external entity in attribute value.",
2497      }                      name, null);
2498                }
2499              break;
2500      /**        default:
2501       * Parse and expand a parameter entity reference.            throw new RuntimeException();
2502       * <pre>        }
2503       * [69] PEReference ::= '%' Name ';'    }
2504       * </pre>      
2505       * <p>NOTE: the '%' has already been read.    /**
2506       */     * Parse and expand a parameter entity reference.
2507      private void parsePEReference ()     * <pre>
2508       * [69] PEReference ::= '%' Name ';'
2509       * </pre>
2510       * <p>NOTE: the '%' has already been read.
2511       */
2512      private void parsePEReference()
2513      throws SAXException, IOException      throws SAXException, IOException
2514      {    {
2515          String name;      String name;
2516        
2517          name = "%" + readNmtoken (true);      name = "%" + readNmtoken(true);
2518          require (';');      require(';');
2519          switch (getEntityType (name)) {      switch (getEntityType(name))
2520          case ENTITY_UNDECLARED:        {
2521              // VC: Entity Declared        case ENTITY_UNDECLARED:
2522              handler.verror ("reference to undeclared parameter entity " + name);          // VC: Entity Declared
2523            handler.verror("reference to undeclared parameter entity " + name);
2524              // we should disable handling of all subsequent declarations          
2525              // unless this is a standalone document (info discarded)          // we should disable handling of all subsequent declarations
2526              break;          // unless this is a standalone document (info discarded)
2527          case ENTITY_INTERNAL:          break;
2528              if (inLiteral)        case ENTITY_INTERNAL:
2529                  pushString (name, getEntityValue (name));          if (inLiteral)
2530              else            {
2531                  pushString (name, ' ' + getEntityValue (name) + ' ');              pushString(name, getEntityValue(name));
2532              break;            }
2533          case ENTITY_TEXT:          else
2534              if (!inLiteral)            {
2535                  pushString (null, " ");              pushString(name, ' ' + getEntityValue(name) + ' ');
2536              pushURL (true, name, getEntityIds (name), null, null, null, true);            }
2537              if (!inLiteral)          break;
2538                  pushString (null, " ");        case ENTITY_TEXT:
2539              break;          if (!inLiteral)
2540          }            {
2541      }              pushString(null, " ");
2542              }
2543      /**          pushURL(true, name, getEntityIds(name), null, null, null, true);
2544       * Parse an entity declaration.          if (!inLiteral)
2545       * <pre>            {
2546       * [70] EntityDecl ::= GEDecl | PEDecl              pushString(null, " ");
2547       * [71] GEDecl ::= '&lt;!ENTITY' S Name S EntityDef S? '&gt;'            }
2548       * [72] PEDecl ::= '&lt;!ENTITY' S '%' S Name S PEDef S? '&gt;'          break;
2549       * [73] EntityDef ::= EntityValue | (ExternalID NDataDecl?)        }
2550       * [74] PEDef ::= EntityValue | ExternalID    }
2551       * [75] ExternalID ::= 'SYSTEM' S SystemLiteral    
2552       *             | 'PUBLIC' S PubidLiteral S SystemLiteral    /**
2553       * [76] NDataDecl ::= S 'NDATA' S Name     * Parse an entity declaration.
2554       * </pre>     * <pre>
2555       * <p>NOTE: the '&lt;!ENTITY' has already been read.     * [70] EntityDecl ::= GEDecl | PEDecl
2556       */     * [71] GEDecl ::= '&lt;!ENTITY' S Name S EntityDef S? '&gt;'
2557      private void parseEntityDecl ()     * [72] PEDecl ::= '&lt;!ENTITY' S '%' S Name S PEDef S? '&gt;'
2558       * [73] EntityDef ::= EntityValue | (ExternalID NDataDecl?)
2559       * [74] PEDef ::= EntityValue | ExternalID
2560       * [75] ExternalID ::= 'SYSTEM' S SystemLiteral
2561       *       | 'PUBLIC' S PubidLiteral S SystemLiteral
2562       * [76] NDataDecl ::= S 'NDATA' S Name
2563       * </pre>
2564       * <p>NOTE: the '&lt;!ENTITY' has already been read.
2565       */
2566      private void parseEntityDecl()
2567      throws Exception      throws Exception
2568      {    {
2569          boolean peFlag = false;      boolean peFlag = false;
2570          int flags = 0;      int flags = 0;
2571        
2572          // Check for a parameter entity.      // Check for a parameter entity.
2573          expandPE = false;      expandPE = false;
2574          requireWhitespace ();      requireWhitespace();
2575          if (tryRead ('%')) {      if (tryRead('%'))
2576              peFlag = true;        {
2577              requireWhitespace ();          peFlag = true;
2578          }          requireWhitespace();
2579          expandPE = true;        }
2580        expandPE = true;
2581          // Read the entity name, and prepend      
2582          // '%' if necessary.      // Read the entity name, and prepend
2583          String name = readNmtoken (true);      // '%' if necessary.
2584          //NE08      String name = readNmtoken(true);
2585          if (name.indexOf(':') >= 0)      //NE08
2586             error ("Illegal character(':') in entity name ", name, null);      if (name.indexOf(':') >= 0)
2587          if (peFlag) {        {
2588              name = "%" + name;          error("Illegal character(':') in entity name ", name, null);
2589          }        }
2590        if (peFlag)
2591          // Read the entity value.        {
2592          requireWhitespace ();          name = "%" + name;
2593          char c = readCh ();        }
         unread (c);  
         if (c == '"' || c == '\'') {  
             // Internal entity ... replacement text has expanded refs  
             // to characters and PEs, but not to general entities  
             String value = readLiteral (flags);  
             setInternalEntity (name, value);  
         } else {  
             // Read the external IDs  
             String ids [] = readExternalIds (false, false);  
   
             // Check for NDATA declaration.  
             boolean white = tryWhitespace ();  
             if (!peFlag && tryRead ("NDATA")) {  
                 if (!white)  
                     error ("whitespace required before NDATA");  
                 requireWhitespace ();  
                 String notationName = readNmtoken (true);  
                 if (!skippedPE) {  
                     setExternalEntity (name, ENTITY_NDATA, ids, notationName);  
                     handler.unparsedEntityDecl (name, ids, notationName);  
                 }  
             } else if (!skippedPE) {  
                 setExternalEntity (name, ENTITY_TEXT, ids, null);  
                 handler.getDeclHandler ()  
                     .externalEntityDecl (name, ids [0],  
                             handler.resolveURIs ()  
                                         // FIXME: ASSUMES not skipped  
                                         // "false" forces error on bad URI  
                                 ? handler.absolutize (ids [2], ids [1], false)  
                                 : ids [1]);  
             }  
         }  
   
         // Finish the declaration.  
         skipWhitespace ();  
         require ('>');  
     }  
2594    
2595        // Read the entity value.
2596        requireWhitespace();
2597        char c = readCh();
2598        unread (c);
2599        if (c == '"' || c == '\'')
2600          {
2601            // Internal entity ... replacement text has expanded refs
2602            // to characters and PEs, but not to general entities
2603            String value = readLiteral(flags);
2604            setInternalEntity(name, value);
2605          }
2606        else
2607          {
2608            // Read the external IDs
2609            ExternalIdentifiers ids = readExternalIds(false, false);
2610            
2611            // Check for NDATA declaration.
2612            boolean white = tryWhitespace();
2613            if (!peFlag && tryRead("NDATA"))
2614              {
2615                if (!white)
2616                  {
2617                    error("whitespace required before NDATA");
2618                  }
2619                requireWhitespace();
2620                String notationName = readNmtoken(true);
2621                if (!skippedPE)
2622                  {
2623                    setExternalEntity(name, ENTITY_NDATA, ids, notationName);
2624                    handler.unparsedEntityDecl(name, ids.publicId, ids.systemId,
2625                                               ids.baseUri, notationName);
2626                  }
2627              }
2628            else if (!skippedPE)
2629              {
2630                setExternalEntity(name, ENTITY_TEXT, ids, null);
2631                handler.getDeclHandler()
2632                  .externalEntityDecl(name, ids.publicId,
2633                                       handler.resolveURIs()
2634                                       // FIXME: ASSUMES not skipped
2635                                       // "false" forces error on bad URI
2636                                       ? handler.absolutize(ids.baseUri,
2637                                                            ids.systemId,
2638                                                            false)
2639                                       : ids.systemId);
2640              }
2641          }
2642        
2643        // Finish the declaration.
2644        skipWhitespace();
2645        require('>');
2646      }
2647    
2648      /**    /**
2649       * Parse a notation declaration.     * Parse a notation declaration.
2650       * <pre>     * <pre>
2651       * [82] NotationDecl ::= '&lt;!NOTATION' S Name S     * [82] NotationDecl ::= '&lt;!NOTATION' S Name S
2652       *          (ExternalID | PublicID) S? '&gt;'     *    (ExternalID | PublicID) S? '&gt;'
2653       * [83] PublicID ::= 'PUBLIC' S PubidLiteral     * [83] PublicID ::= 'PUBLIC' S PubidLiteral
2654       * </pre>     * </pre>
2655       * <P>NOTE: the '&lt;!NOTATION' has already been read.     * <P>NOTE: the '&lt;!NOTATION' has already been read.
2656       */     */
2657      private void parseNotationDecl ()    private void parseNotationDecl()
2658      throws Exception      throws Exception
2659      {    {
2660          String nname, ids[];      String nname;
2661        ExternalIdentifiers ids;
   
         requireWhitespace ();  
         nname = readNmtoken (true);  
         //NE08  
         if (nname.indexOf(':') >= 0)  
            error ("Illegal character(':') in notation name ", nname, null);  
         requireWhitespace ();  
   
         // Read the external identifiers.  
         ids = readExternalIds (true, false);  
   
         // Register the notation.  
         setNotation (nname, ids);  
2662    
2663          skipWhitespace ();      requireWhitespace();
2664          require ('>');      nname = readNmtoken(true);
2665      }      //NE08
2666        if (nname.indexOf(':') >= 0)
2667          {
2668            error("Illegal character(':') in notation name ", nname, null);
2669          }
2670        requireWhitespace();
2671    
2672        // Read the external identifiers.
2673        ids = readExternalIds(true, false);
2674    
2675      /**      // Register the notation.
2676       * Parse character data.      setNotation(nname, ids);
2677       * <pre>      
2678       * [14] CharData ::= [^&lt;&amp;]* - ([^&lt;&amp;]* ']]&gt;' [^&lt;&amp;]*)      skipWhitespace();
2679       * </pre>      require('>');
2680       */    }
2681      private void parseCharData ()    
2682      /**
2683       * Parse character data.
2684       * <pre>
2685       * [14] CharData ::= [^&lt;&amp;]* - ([^&lt;&amp;]* ']]&gt;' [^&lt;&amp;]*)
2686       * </pre>
2687       */
2688      private void parseCharData()
2689      throws Exception      throws Exception
2690      {    {
2691          char    c;      char c;
2692          int     state = 0;      int state = 0;
2693          boolean pureWhite = false;      boolean pureWhite = false;
   
         // assert (dataBufferPos == 0);  
   
         // are we expecting pure whitespace?  it might be dirty...  
         if ((currentElementContent == CONTENT_ELEMENTS) && !isDirtyCurrentElement)  
             pureWhite = true;  
   
         // always report right out of readBuffer  
         // to minimize (pointless) buffer copies  
         while (true) {  
             int lineAugment = 0;  
             int columnAugment = 0;  
             int i;  
   
 loop:  
             for (i = readBufferPos; i < readBufferLength; i++) {  
                 switch (c = readBuffer [i]) {  
                 case '\n':  
                     lineAugment++;  
                     columnAugment = 0;  
                     // pureWhite unmodified  
                     break;  
                 case '\r':      // should not happen!!  
                 case '\t':  
                 case ' ':  
                     // pureWhite unmodified  
                     columnAugment++;  
                     break;  
                 case '&':  
                 case '<':  
                     columnAugment++;  
                     // pureWhite unmodified  
                     // CLEAN end of text sequence  
                     state = 1;  
                     break loop;  
                 case ']':  
                     // that's not a whitespace char, and  
                     // can not terminate pure whitespace either  
                     pureWhite = false;  
                     if ((i + 2) < readBufferLength) {  
                         if (readBuffer [i + 1] == ']'  
                                 && readBuffer [i + 2] == '>') {  
                             // ERROR end of text sequence  
                             state = 2;  
                             break loop;  
                         }  
                     } else {  
                         // FIXME missing two end-of-buffer cases  
                     }  
                     columnAugment++;  
                     break;  
                 default:  
                         if ((c < 0x0020 || c > 0xFFFD)  
                            || ((c >= 0x007f) && (c <= 0x009f) && (c != 0x0085)  
                                && xmlVersion == XML_11))  
                                 error ("illegal XML character U+"  
                                         + Integer.toHexString (c));  
                     // that's not a whitespace char  
                     pureWhite = false;  
                     columnAugment++;  
                 }  
             }  
   
             // report text thus far  
             if (lineAugment > 0) {  
                 line += lineAugment;  
                 column = columnAugment;  
             } else {  
                 column += columnAugment;  
             }  
   
             // report characters/whitspace  
             int         length = i - readBufferPos;  
   
             if (length != 0) {  
                 if (pureWhite)  
                     handler.ignorableWhitespace (readBuffer,  
                                 readBufferPos, length);  
                 else  
                     handler.charData (readBuffer, readBufferPos, length);  
                 readBufferPos = i;  
             }  
               
             if (state != 0)  
                 break;  
   
             // fill next buffer from this entity, or  
             // pop stack and continue with previous entity  
             unread (readCh ());  
         }  
         if (!pureWhite)  
            isDirtyCurrentElement = true;  
         // finish, maybe with error  
         if (state != 1) // finish, no error  
             error ("character data may not contain ']]>'");  
     }  
   
2694    
2695      //////////////////////////////////////////////////////////////////////      // assert (dataBufferPos == 0);
2696      // High-level reading and scanning methods.      
2697      //////////////////////////////////////////////////////////////////////      // are we expecting pure whitespace?  it might be dirty...
2698        if ((currentElementContent == CONTENT_ELEMENTS) && !isDirtyCurrentElement)
2699          {
2700            pureWhite = true;
2701          }
2702    
2703      /**      // always report right out of readBuffer
2704       * Require whitespace characters.      // to minimize (pointless) buffer copies
2705       */      while (true)
2706      private void requireWhitespace ()        {
2707            int lineAugment = 0;
2708            int columnAugment = 0;
2709            int i;
2710            
2711    loop:
2712            for (i = readBufferPos; i < readBufferLength; i++)
2713              {
2714                switch (c = readBuffer[i])
2715                  {
2716                  case '\n':
2717                    lineAugment++;
2718                    columnAugment = 0;
2719                    // pureWhite unmodified
2720                    break;
2721                  case '\r':  // should not happen!!
2722                  case '\t':
2723                  case ' ':
2724                    // pureWhite unmodified
2725                    columnAugment++;
2726                    break;
2727                  case '&':
2728                  case '<':
2729                    columnAugment++;
2730                    // pureWhite unmodified
2731                    // CLEAN end of text sequence
2732                    state = 1;
2733                    break loop;
2734                  case ']':
2735                    // that's not a whitespace char, and
2736                    // can not terminate pure whitespace either
2737                    pureWhite = false;
2738                    if ((i + 2) < readBufferLength)
2739                      {
2740                        if (readBuffer [i + 1] == ']'
2741                            && readBuffer [i + 2] == '>')
2742                          {
2743                            // ERROR end of text sequence
2744                            state = 2;
2745                            break loop;
2746                          }
2747                      }
2748                    else
2749                      {
2750                        // FIXME missing two end-of-buffer cases
2751                      }
2752                    columnAugment++;
2753                    break;
2754                  default:
2755                    if ((c < 0x0020 || c > 0xFFFD)
2756                        || ((c >= 0x007f) && (c <= 0x009f) && (c != 0x0085)
2757                            && xmlVersion == XML_11))
2758                      {
2759                        error("illegal XML character U+"
2760                              + Integer.toHexString(c));
2761                      }
2762                    // that's not a whitespace char
2763                    pureWhite = false;
2764                    columnAugment++;
2765                  }
2766              }
2767            
2768            // report text thus far
2769            if (lineAugment > 0)
2770              {
2771                line += lineAugment;
2772                column = columnAugment;
2773              }
2774            else
2775              {
2776                column += columnAugment;
2777              }
2778            
2779            // report characters/whitspace
2780            int length = i - readBufferPos;
2781            
2782            if (length != 0)
2783              {
2784                if (pureWhite)
2785                  {
2786                    handler.ignorableWhitespace(readBuffer,
2787                                                readBufferPos, length);
2788                  }
2789                else
2790                  {
2791                    handler.charData(readBuffer, readBufferPos, length);
2792                  }
2793                readBufferPos = i;
2794              }
2795            
2796            if (state != 0)
2797              {
2798                break;
2799              }
2800            
2801            // fill next buffer from this entity, or
2802            // pop stack and continue with previous entity
2803            unread(readCh());
2804          }
2805        if (!pureWhite)
2806          {
2807            isDirtyCurrentElement = true;
2808          }
2809        // finish, maybe with error
2810        if (state != 1)  // finish, no error
2811          {
2812            error("character data may not contain ']]>'");
2813          }
2814      }
2815      
2816      //////////////////////////////////////////////////////////////////////
2817      // High-level reading and scanning methods.
2818      //////////////////////////////////////////////////////////////////////
2819      
2820      /**
2821       * Require whitespace characters.
2822       */
2823      private void requireWhitespace()
2824      throws SAXException, IOException      throws SAXException, IOException
2825      {    {
2826          char c = readCh ();      char c = readCh();
2827          if (isWhitespace (c)) {      if (isWhitespace(c))
2828              skipWhitespace ();        {
2829          } else {          skipWhitespace();
2830              error ("whitespace required", c, null);        }
2831          }      else
2832      }        {
2833            error("whitespace required", c, null);
2834          }
2835      }
2836    
2837      /**    /**
2838       * Skip whitespace characters.     * Skip whitespace characters.
2839       * <pre>     * <pre>
2840       * [3] S ::= (#x20 | #x9 | #xd | #xa)+     * [3] S ::= (#x20 | #x9 | #xd | #xa)+
2841       * </pre>     * </pre>
2842       */     */
2843      private void skipWhitespace ()    private void skipWhitespace()
2844      throws SAXException, IOException      throws SAXException, IOException
2845      {    {
2846          // Start with a little cheat.  Most of      // Start with a little cheat.  Most of
2847          // the time, the white space will fall      // the time, the white space will fall
2848          // within the current read buffer; if      // within the current read buffer; if
2849          // not, then fall through.      // not, then fall through.
2850          if (USE_CHEATS) {      if (USE_CHEATS)
2851              int lineAugment = 0;        {
2852              int columnAugment = 0;          int lineAugment = 0;
2853            int columnAugment = 0;
2854            
2855  loop:  loop:
2856              for (int i = readBufferPos; i < readBufferLength; i++) {          for (int i = readBufferPos; i < readBufferLength; i++)
2857                  switch (readBuffer [i]) {            {
2858                  case ' ':              switch (readBuffer[i])
2859                  case '\t':                {
2860                  case '\r':                case ' ':
2861                      columnAugment++;                case '\t':
2862                      break;                case '\r':
2863                  case '\n':                  columnAugment++;
2864                      lineAugment++;                  break;
2865                      columnAugment = 0;                case '\n':
2866                      break;                  lineAugment++;
2867                  case '%':                  columnAugment = 0;
2868                      if (expandPE)                  break;
2869                          break loop;                case '%':
2870                      // else fall through...                  if (expandPE)
2871                  default:                    {
2872                      readBufferPos = i;                      break loop;
2873                      if (lineAugment > 0) {                    }
2874                          line += lineAugment;                  // else fall through...
2875                          column = columnAugment;                default:
2876                      } else {                  readBufferPos = i;
2877                          column += columnAugment;                  if (lineAugment > 0)
2878                      }                    {
2879                      return;                      line += lineAugment;
2880                  }                      column = columnAugment;
2881              }                    }
2882          }                  else
2883                      {
2884          // OK, do it the slow way.                      column += columnAugment;
2885          char c = readCh ();                    }
2886          while (isWhitespace (c)) {                  return;
2887              c = readCh ();                }
2888          }            }
2889          unread (c);        }
2890      }      
2891        // OK, do it the slow way.
2892        char c = readCh ();
2893      /**      while (isWhitespace(c))
2894       * Read a name or (when parsing an enumeration) name token.        {
2895       * <pre>          c = readCh();
2896       * [5] Name ::= (Letter | '_' | ':') (NameChar)*        }
2897       * [7] Nmtoken ::= (NameChar)+      unread(c);
2898       * </pre>    }
2899       */    
2900      private String readNmtoken (boolean isName)    /**
2901       * Read a name or (when parsing an enumeration) name token.
2902       * <pre>
2903       * [5] Name ::= (Letter | '_' | ':') (NameChar)*
2904       * [7] Nmtoken ::= (NameChar)+
2905       * </pre>
2906       */
2907      private String readNmtoken(boolean isName)
2908      throws SAXException, IOException      throws SAXException, IOException
2909      {    {
2910          char c;      char c;
2911        
2912          if (USE_CHEATS) {      if (USE_CHEATS)
2913          {
2914  loop:  loop:
2915              for (int i = readBufferPos; i < readBufferLength; i++) {          for (int i = readBufferPos; i < readBufferLength; i++)
2916                  c = readBuffer [i];            {
2917                  switch (c) {              c = readBuffer[i];
2918                    case '%':              switch (c)
2919                      if (expandPE)                {
2920                          break loop;                case '%':
2921                      // else fall through...                  if (expandPE)
2922                      {
2923                      // What may legitimately come AFTER a name/nmtoken?                      break loop;
2924                    case '<': case '>': case '&':                    }
2925                    case ',': case '|': case '*': case '+': case '?':                  // else fall through...
2926                    case ')':                  
2927                    case '=':                  // What may legitimately come AFTER a name/nmtoken?
2928                    case '\'': case '"':                case '<': case '>': case '&':
2929                    case '[':                case ',': case '|': case '*': case '+': case '?':
2930                    case ' ': case '\t': case '\r': case '\n':                case ')':
2931                    case ';':                case '=':
2932                    case '/':                case '\'': case '"':
2933                      int start = readBufferPos;                case '[':
2934                      if (i == start)                case ' ': case '\t': case '\r': case '\n':
2935                          error ("name expected", readBuffer [i], null);                case ';':
2936                      readBufferPos = i;                case '/':
2937                      return intern (readBuffer, start, i - start);                  int start = readBufferPos;
2938                    if (i == start)
2939                    default:                    {
2940  // FIXME ... per IBM's OASIS test submission, these:                      error("name expected", readBuffer[i], null);
2941  //   ?          U+06dd                    }
2942  //   Combining  U+309B                  readBufferPos = i;
2943                      //these switches are kind of ugly but at least we won't                  return intern(readBuffer, start, i - start);
2944                      //have to go over the whole lits for each char                  
2945                      if (isName && i == readBufferPos){                default:
2946                              char c2 = (char) (c & 0x00f0);                  // FIXME ... per IBM's OASIS test submission, these:
2947                              switch (c & 0xff00){                  //   ?    U+06dd
2948                                  //starting with 01                  //   Combining  U+309B
2949                                  case 0x0100:                  //these switches are kind of ugly but at least we won't
2950                                      switch (c2){                  //have to go over the whole lits for each char
2951                                          case 0x0030:                  if (isName && i == readBufferPos)
2952                                              if (c == 0x0132 || c == 0x0133 || c == 0x013f)                    {
2953                                                  error ("Not a name start character, U+"                      char c2 = (char) (c & 0x00f0);
2954                                                         + Integer.toHexString (c));                      switch (c & 0xff00)
2955                                          break;                        {
2956                                          case 0x0040:                          //starting with 01
2957                                              if (c == 0x0140 || c == 0x0149)                        case 0x0100:
2958                                                  error ("Not a name start character, U+"                          switch (c2)
2959                                                         + Integer.toHexString (c));                            {
2960                                          break;                            case 0x0030:
2961                                          case 0x00c0:                              if (c == 0x0132 || c == 0x0133 || c == 0x013f)
2962                                              if (c == 0x01c4 || c == 0x01cc)                                {
2963                                                  error ("Not a name start character, U+"                                  error("Not a name start character, U+"
2964                                                         + Integer.toHexString (c));                                        + Integer.toHexString(c));
2965                                          break;                                }
2966                                          case 0x00f0:                              break;
2967                                              if (c == 0x01f1 || c == 0x01f3)                            case 0x0040:
2968                                                  error ("Not a name start character, U+"                              if (c == 0x0140 || c == 0x0149)
2969                                                         + Integer.toHexString (c));                                {
2970                                          break;                                  error("Not a name start character, U+"
2971                                          case 0x00b0:                                        + Integer.toHexString(c));
2972                                              if (c == 0x01f1 || c == 0x01f3)                                }
2973                                                  error ("Not a name start character, U+"                              break;
2974                                                         + Integer.toHexString (c));                            case 0x00c0:
2975                                          break;                              if (c == 0x01c4 || c == 0x01cc)
2976                                          default:                                {
2977                                              if (c == 0x017f)                                  error("Not a name start character, U+"
2978                                                  error ("Not a name start character, U+"                                        + Integer.toHexString(c));
2979                                                          + Integer.toHexString (c));                                    }
2980                                      }                              break;
2981                                                                  case 0x00f0:
2982                                  break;                              if (c == 0x01f1 || c == 0x01f3)
2983                                  //starting with 11                                {
2984                                  case 0x1100:                                  error("Not a name start character, U+"
2985                                      switch (c2){                                        + Integer.toHexString(c));
2986                                          case 0x0000:                                }
2987                                              if (c == 0x1104 || c == 0x1108 ||                              break;
2988                                                  c == 0x110a || c == 0x110d)                            case 0x00b0:
2989                                                  error ("Not a name start character, U+"                              if (c == 0x01f1 || c == 0x01f3)
2990                                                       + Integer.toHexString (c));                                {
2991                                          break;                                  error("Not a name start character, U+"
2992                                          case 0x0030:                                        + Integer.toHexString(c));
2993                                              if (c == 0x113b || c == 0x113f)                                }
2994                                                  error ("Not a name start character, U+"                              break;
2995                                                         + Integer.toHexString (c));                            default:
2996                                          break;                              if (c == 0x017f)
2997                                          case 0x0040:                                {
2998                                              if (c == 0x1141 || c == 0x114d                                  error("Not a name start character, U+"
2999                                                  || c == 0x114f )                                        + Integer.toHexString(c));
3000                                                  error ("Not a name start character, U+"                                }
3001                                                         + Integer.toHexString (c));                            }
3002                                          break;                          
3003                                          case 0x0050:                          break;
3004                                               if (c == 0x1151 || c == 0x1156)                          //starting with 11
3005                                                   error ("Not a name start character, U+"                        case 0x1100:
3006                                                          + Integer.toHexString (c));                          switch (c2)
3007                                          break;                            {
3008                                          case 0x0060:                            case 0x0000:
3009                                               if (c == 0x1162 || c == 0x1164                              if (c == 0x1104 || c == 0x1108 ||
3010                                                   || c == 0x1166 || c == 0x116b                                  c == 0x110a || c == 0x110d)
3011                                                   || c == 0x116f)                                {
3012                                                   error ("Not a name start character, U+"                                  error("Not a name start character, U+"
3013                                                           + Integer.toHexString (c));                                        + Integer.toHexString(c));
3014                                                  break;                                }
3015                                          case 0x00b0:                              break;
3016                                               if (c == 0x11b6 || c == 0x11b9                            case 0x0030:
3017                                                   || c == 0x11bb || c == 0x116f)                              if (c == 0x113b || c == 0x113f)
3018                                                   error ("Not a name start character, U+"                                {
3019                                                          + Integer.toHexString (c));                                  error("Not a name start character, U+"
3020                                          break;                                        + Integer.toHexString(c));
3021                                          default:                                }
3022                                              if (c == 0x1174 || c == 0x119f                              break;
3023                                                  || c == 0x11ac || c == 0x11c3                            case 0x0040:
3024                                                  || c == 0x11f1)                              if (c == 0x1141 || c == 0x114d
3025                                                  error ("Not a name start character, U+"                                  || c == 0x114f )
3026                                                          + Integer.toHexString (c));                                {
3027                                      }                                  error("Not a name start character, U+"
3028                                  break;                                        + Integer.toHexString(c));
3029                                  default:                                }
3030                                     if (c == 0x0e46 || c == 0x1011                              break;
3031                                         || c == 0x212f || c == 0x0587                            case 0x0050:
3032                                         || c == 0x0230 )                              if (c == 0x1151 || c == 0x1156)
3033                                         error ("Not a name start character, U+"                                {
3034                                                + Integer.toHexString (c));                                  error("Not a name start character, U+"
3035                              }                                        + Integer.toHexString(c));
3036                      }                                }
3037                      // punt on exact tests from Appendix A; approximate                              break;
3038                      // them using the Unicode ID start/part rules                            case 0x0060:
3039                      if (i == readBufferPos && isName) {                              if (c == 0x1162 || c == 0x1164
3040                          if (!Character.isUnicodeIdentifierStart (c)                                  || c == 0x1166 || c == 0x116b
3041                                  && c != ':' && c != '_')                                  || c == 0x116f)
3042                              error ("Not a name start character, U+"                                {
3043                                    + Integer.toHexString (c));                                  error("Not a name start character, U+"
3044                      } else if (!Character.isUnicodeIdentifierPart (c)                                        + Integer.toHexString(c));
3045                              && c != '-' && c != ':' && c != '_' && c != '.'                                }
3046                              && !isExtender (c))                              break;
3047                          error ("Not a name character, U+"                            case 0x00b0:
3048                                  + Integer.toHexString (c));                              if (c == 0x11b6 || c == 0x11b9
3049                  }                                  || c == 0x11bb || c == 0x116f)
3050              }                                {
3051          }                                  error("Not a name start character, U+"
3052                                          + Integer.toHexString(c));
3053          nameBufferPos = 0;                                }
3054                                break;
3055                              default:
3056                                if (c == 0x1174 || c == 0x119f
3057                                    || c == 0x11ac || c == 0x11c3
3058                                    || c == 0x11f1)
3059                                  {
3060                                    error("Not a name start character, U+"
3061                                          + Integer.toHexString(c));
3062                                  }
3063                              }
3064                            break;
3065                          default:
3066                            if (c == 0x0e46 || c == 0x1011
3067                                || c == 0x212f || c == 0x0587
3068                                || c == 0x0230 )
3069                              {
3070                                error("Not a name start character, U+"
3071                                      + Integer.toHexString(c));
3072                              }
3073                          }
3074                      }
3075                    // punt on exact tests from Appendix A; approximate
3076                    // them using the Unicode ID start/part rules
3077                    if (i == readBufferPos && isName)
3078                      {
3079                        if (!Character.isUnicodeIdentifierStart(c)
3080                            && c != ':' && c != '_')
3081                          {
3082                            error("Not a name start character, U+"
3083                                  + Integer.toHexString(c));
3084                          }
3085                      }
3086                    else if (!Character.isUnicodeIdentifierPart(c)
3087                             && c != '-' && c != ':' && c != '_' && c != '.'
3088                             && !isExtender(c))
3089                      {
3090                        error("Not a name character, U+"
3091                              + Integer.toHexString(c));
3092                      }
3093                  }
3094              }
3095          }
3096        
3097        nameBufferPos = 0;
3098    
3099          // Read the first character.      // Read the first character.
3100  loop:  loop:
3101          while (true) {      while (true)
3102              c = readCh ();        {
3103              switch (c) {          c = readCh();
3104              case '%':          switch (c)
3105              case '<': case '>': case '&':            {
3106              case ',': case '|': case '*': case '+': case '?':            case '%':
3107              case ')':            case '<': case '>': case '&':
3108              case '=':            case ',': case '|': case '*': case '+': case '?':
3109              case '\'': case '"':            case ')':
3110              case '[':            case '=':
3111              case ' ': case '\t': case '\n': case '\r':            case '\'': case '"':
3112              case ';':            case '[':
3113              case '/':            case ' ': case '\t': case '\n': case '\r':
3114                  unread (c);            case ';':
3115                  if (nameBufferPos == 0) {            case '/':
3116                      error ("name expected");              unread(c);
3117                  }              if (nameBufferPos == 0)
3118                  // punt on exact tests from Appendix A, but approximate them                {
3119                  if (isName                  error ("name expected");
3120                          && !Character.isUnicodeIdentifierStart (                }
3121                                  nameBuffer [0])              // punt on exact tests from Appendix A, but approximate them
3122                          && ":_".indexOf (nameBuffer [0]) == -1)              if (isName
3123                      error ("Not a name start character, U+"                  && !Character.isUnicodeIdentifierStart(nameBuffer[0])
3124                                + Integer.toHexString (nameBuffer [0]));                  && ":_".indexOf(nameBuffer[0]) == -1)
3125                  String s = intern (nameBuffer, 0, nameBufferPos);                {
3126                  nameBufferPos = 0;                  error("Not a name start character, U+"
3127                  return s;                        + Integer.toHexString(nameBuffer[0]));
3128              default:                }
3129                  // punt on exact tests from Appendix A, but approximate them              String s = intern(nameBuffer, 0, nameBufferPos);
3130                nameBufferPos = 0;
3131                  if ((nameBufferPos != 0 || !isName)              return s;
3132                          && !Character.isUnicodeIdentifierPart (c)            default:
3133                          && ":-_.".indexOf (c) == -1              // punt on exact tests from Appendix A, but approximate them
3134                          && !isExtender (c))              
3135                      error ("Not a name character, U+"              if ((nameBufferPos != 0 || !isName)
3136                              + Integer.toHexString (c));                  && !Character.isUnicodeIdentifierPart(c)
3137                  if (nameBufferPos >= nameBuffer.length)                  && ":-_.".indexOf(c) == -1
3138                      nameBuffer =                  && !isExtender(c))
3139                          (char[]) extendArray (nameBuffer,                {
3140                                      nameBuffer.length, nameBufferPos);                  error("Not a name character, U+"
3141                  nameBuffer [nameBufferPos++] = c;                        + Integer.toHexString(c));
3142              }                }
3143          }              if (nameBufferPos >= nameBuffer.length)
3144      }                {
3145                    nameBuffer =
3146      private static boolean isExtender (char c)                    (char[]) extendArray(nameBuffer,
3147      {                                         nameBuffer.length, nameBufferPos);
3148          // [88] Extender ::= ...                }
3149          return c == 0x00b7 || c == 0x02d0 || c == 0x02d1 || c == 0x0387              nameBuffer[nameBufferPos++] = c;
3150                 || c == 0x0640 || c == 0x0e46 || c == 0x0ec6 || c == 0x3005            }
3151                 || (c >= 0x3031 && c <= 0x3035)        }
3152                 || (c >= 0x309d && c <= 0x309e)    }
3153                 || (c >= 0x30fc && c <= 0x30fe);    
3154      }    private static boolean isExtender(char c)
3155      {
3156        // [88] Extender ::= ...
3157        return c == 0x00b7 || c == 0x02d0 || c == 0x02d1 || c == 0x0387
3158          || c == 0x0640 || c == 0x0e46 || c == 0x0ec6 || c == 0x3005
3159          || (c >= 0x3031 && c <= 0x3035)
3160          || (c >= 0x309d && c <= 0x309e)
3161          || (c >= 0x30fc && c <= 0x30fe);
3162      }
3163    
3164      /**    /**
3165       * Read a literal.  With matching single or double quotes as     * Read a literal.  With matching single or double quotes as
3166       * delimiters (and not embedded!) this is used to parse:     * delimiters (and not embedded!) this is used to parse:
3167       * <pre>     * <pre>
3168       *  [9] EntityValue ::= ... ([^%&amp;] | PEReference | Reference)* ...     *  [9] EntityValue ::= ... ([^%&amp;] | PEReference | Reference)* ...
3169       *  [10] AttValue ::= ... ([^<&] | Reference)* ...     *  [10] AttValue ::= ... ([^<&] | Reference)* ...
3170       *  [11] SystemLiteral ::= ... (URLchar - "'")* ...     *  [11] SystemLiteral ::= ... (URLchar - "'")* ...
3171       *  [12] PubidLiteral ::= ... (PubidChar - "'")* ...     *  [12] PubidLiteral ::= ... (PubidChar - "'")* ...
3172       * </pre>     * </pre>
3173       * as well as the quoted strings in XML and text declarations     * as well as the quoted strings in XML and text declarations
3174       * (for version, encoding, and standalone) which have their     * (for version, encoding, and standalone) which have their
3175       * own constraints.     * own constraints.
3176       */     */
3177      private String readLiteral (int flags)    private String readLiteral(int flags)
3178      throws SAXException, IOException      throws SAXException, IOException
3179      {    {
3180          char    delim, c;      char delim, c;
3181          int     startLine = line;      int startLine = line;
3182          boolean saved = expandPE;      boolean saved = expandPE;
3183          boolean savedReport = doReport;      boolean savedReport = doReport;
3184        
3185          // Find the first delimiter.      // Find the first delimiter.
3186          delim = readCh ();      delim = readCh();
3187          if (delim != '"' && delim != '\'') {      if (delim != '"' && delim != '\'')
3188              error ("expected '\"' or \"'\"", delim, null);        {
3189              return null;          error("expected '\"' or \"'\"", delim, null);
3190          }          return null;
3191          inLiteral = true;        }
3192          if ((flags & LIT_DISABLE_PE) != 0)      inLiteral = true;
3193              expandPE = false;      if ((flags & LIT_DISABLE_PE) != 0)
3194          doReport = false;        {
3195            expandPE = false;
3196          // Each level of input source has its own buffer; remember        }
3197          // ours, so we won't read the ending delimiter from any      doReport = false;
3198          // other input source, regardless of entity processing.      
3199          char ourBuf [] = readBuffer;      // Each level of input source has its own buffer; remember
3200        // ours, so we won't read the ending delimiter from any
3201          // Read the literal.      // other input source, regardless of entity processing.
3202          try {      char[] ourBuf = readBuffer;
3203              c = readCh ();  
3204              boolean ampRead = false;      // Read the literal.
3205        try
3206          {
3207            c = readCh();
3208            boolean ampRead = false;
3209  loop:  loop:
3210              while (! (c == delim && readBuffer == ourBuf)) {          while (! (c == delim && readBuffer == ourBuf))
3211                  switch (c) {            {
3212                      // attributes and public ids are normalized              switch (c)
3213                      // in almost the same ways                {
3214                  case '\n':                  // attributes and public ids are normalized
3215                  case '\r':                  // in almost the same ways
3216                      if ((flags & (LIT_ATTRIBUTE | LIT_PUBID)) != 0)                case '\n':
3217                          c = ' ';                case '\r':
3218                      break;                  if ((flags & (LIT_ATTRIBUTE | LIT_PUBID)) != 0)
3219                  case '\t':                    {
3220                      if ((flags & LIT_ATTRIBUTE) != 0)                      c = ' ';
3221                          c = ' ';                    }
3222                      break;                  break;
3223                  case '&':                case '\t':
3224                      c = readCh ();                  if ((flags & LIT_ATTRIBUTE) != 0)
3225                      // Char refs are expanded immediately, except for                    {
3226                      // all the cases where it's deferred.                      c = ' ';
3227                      if (c == '#') {                    }
3228                          if ((flags & LIT_DISABLE_CREF) != 0) {                  break;
3229                              dataBufferAppend ('&');                case '&':
3230                              break;                  c = readCh();
3231                          }                  // Char refs are expanded immediately, except for
3232                          parseCharRef (false /* Do not do flushDataBuffer */);                  // all the cases where it's deferred.
3233                    if (c == '#')
3234                          // exotic WFness risk: this is an entity literal,                    {
3235                          // dataBuffer [dataBufferPos - 1] == '&', and                      if ((flags & LIT_DISABLE_CREF) != 0)
3236                          // following chars are a _partial_ entity/char ref                        {
3237                                              dataBufferAppend('&');
3238                      // It looks like an entity ref ...                          break;
3239                      } else {                        }
3240                          unread (c);                      parseCharRef(false /* Do not do flushDataBuffer */);
3241                          // Expand it?                      
3242                          if ((flags & LIT_ENTITY_REF) > 0) {                      // exotic WFness risk: this is an entity literal,
3243                              parseEntityRef (false);                      // dataBuffer [dataBufferPos - 1] == '&', and
3244                              if (String.valueOf (readBuffer).equals("&#38;"))                      // following chars are a _partial_ entity/char ref
3245                                  ampRead = true;                      
3246                        // It looks like an entity ref ...
3247                      }
3248                    else
3249                      {
3250                        unread(c);
3251                        // Expand it?
3252                        if ((flags & LIT_ENTITY_REF) > 0)
3253                          {
3254                            parseEntityRef(false);
3255                            if (String.valueOf(readBuffer).equals("&#38;"))
3256                              {
3257                                ampRead = true;
3258                              }
3259                          //Is it just data?                          //Is it just data?
3260                          } else if ((flags & LIT_DISABLE_EREF) != 0) {                        }
3261                              dataBufferAppend ('&');                      else if ((flags & LIT_DISABLE_EREF) != 0)
3262                          {
3263                          // OK, it will be an entity ref -- expanded later.                          dataBufferAppend('&');
3264                          } else {                          
3265                              String name = readNmtoken (true);                          // OK, it will be an entity ref -- expanded later.
3266                              require (';');                        }
3267                              dataBufferAppend ('&');                      else
3268                              dataBufferAppend (name);                        {
3269                              dataBufferAppend (';');                          String name = readNmtoken(true);
3270                          }                          require(';');
3271                      }                          dataBufferAppend('&');
3272                      c = readCh ();                          dataBufferAppend(name);
3273                      continue loop;                          dataBufferAppend(';');
3274                          }
3275                  case '<':                    }
3276                      // and why?  Perhaps so "&foo;" expands the same                  c = readCh();
3277                      // inside and outside an attribute?                  continue loop;
3278                      if ((flags & LIT_ATTRIBUTE) != 0)                  
3279                          error ("attribute values may not contain '<'");                case '<':
3280                      break;                  // and why?  Perhaps so "&foo;" expands the same
3281                    // inside and outside an attribute?
3282                  // We don't worry about case '%' and PE refs, readCh does.                  if ((flags & LIT_ATTRIBUTE) != 0)
3283                      {
3284                  default:                      error("attribute values may not contain '<'");
3285                      break;                    }
3286                  }                  break;
3287                  dataBufferAppend (c);  
3288                  c = readCh ();                  // We don't worry about case '%' and PE refs, readCh does.
3289              }                  
3290          } catch (EOFException e) {                default:
3291              error ("end of input while looking for delimiter (started on line "                  break;
3292                     + startLine + ')', null, new Character (delim).toString ());                }
3293          }              dataBufferAppend(c);
3294          inLiteral = false;              c = readCh();
3295          expandPE = saved;            }
3296          doReport = savedReport;        }
3297        catch (EOFException e)
3298          // Normalise whitespace if necessary.        {
3299          if ((flags & LIT_NORMALIZE) > 0) {          error("end of input while looking for delimiter (started on line "
3300              dataBufferNormalize ();                + startLine + ')', null, new Character(delim).toString());
3301          }        }
3302        inLiteral = false;
3303          // Return the value.      expandPE = saved;
3304          return dataBufferToString ();      doReport = savedReport;
3305      }      
3306        // Normalise whitespace if necessary.
3307        if ((flags & LIT_NORMALIZE) > 0)
3308      /**        {
3309       * Try reading external identifiers.          dataBufferNormalize();
3310       * A system identifier is not required for notations.        }
3311       * @param inNotation Are we parsing a notation decl?      
3312       * @param isSubset Parsing external subset decl (may be omitted)?      // Return the value.
3313       * @return A three-member String array containing the identifiers,      return dataBufferToString();
3314       *  or nulls. Order: public, system, baseURI.    }
3315       */    
3316      private String[] readExternalIds (boolean inNotation, boolean isSubset)    /**
3317       * Try reading external identifiers.
3318       * A system identifier is not required for notations.
3319       * @param inNotation Are we parsing a notation decl?
3320       * @param isSubset Parsing external subset decl (may be omitted)?
3321       * @return A three-member String array containing the identifiers,
3322       *  or nulls. Order: public, system, baseURI.
3323       */
3324      private ExternalIdentifiers readExternalIds(boolean inNotation,
3325                                                  boolean isSubset)
3326      throws Exception      throws Exception
3327      {    {
3328          char    c;      char c;
3329          String  ids[] = new String [3];      ExternalIdentifiers ids = new ExternalIdentifiers();
3330          int     flags = LIT_DISABLE_CREF | LIT_DISABLE_PE | LIT_DISABLE_EREF;      int flags = LIT_DISABLE_CREF | LIT_DISABLE_PE | LIT_DISABLE_EREF;
3331        
3332          if (tryRead ("PUBLIC")) {      if (tryRead("PUBLIC"))
3333              requireWhitespace ();        {
3334              ids [0] = readLiteral (LIT_NORMALIZE | LIT_PUBID | flags);          requireWhitespace();
3335              if (inNotation) {          ids.publicId = readLiteral(LIT_NORMALIZE | LIT_PUBID | flags);
3336                  skipWhitespace ();          if (inNotation)
3337                  c = readCh ();            {
3338                  unread (c);              skipWhitespace();
3339                  if (c == '"' || c == '\'') {              c = readCh();
3340                      ids [1] = readLiteral (flags);              unread(c);
3341                  }              if (c == '"' || c == '\'')
3342              } else {                {
3343                  requireWhitespace ();                  ids.systemId = readLiteral(flags);
3344                  ids [1] = readLiteral (flags);                }
3345              }            }
3346            else
3347              for (int i = 0; i < ids [0].length (); i++) {            {
3348                  c = ids [0].charAt (i);              requireWhitespace();
3349                  if (c >= 'a' && c <= 'z')              ids.systemId = readLiteral(flags);
3350                      continue;            }
3351                  if (c >= 'A' && c <= 'Z')          
3352                      continue;          for (int i = 0; i < ids.publicId.length(); i++)
3353                  if (" \r\n0123456789-' ()+,./:=?;!*#@$_%".indexOf (c) != -1)            {
3354                      continue;              c = ids.publicId.charAt(i);
3355                  error ("illegal PUBLIC id character U+"              if (c >= 'a' && c <= 'z')
3356                          + Integer.toHexString (c));                {
3357              }                  continue;
3358          } else if (tryRead ("SYSTEM")) {                }
3359              requireWhitespace ();              if (c >= 'A' && c <= 'Z')
3360              ids [1] = readLiteral (flags);                {
3361          } else if (!isSubset)                  continue;
3362                  error ("missing SYSTEM or PUBLIC keyword");                }
3363                if (" \r\n0123456789-' ()+,./:=?;!*#@$_%".indexOf(c) != -1)
3364          if (ids [1] != null) {                {
3365              if (ids [1].indexOf ('#') != -1)                  continue;
3366                  handler.verror ("SYSTEM id has a URI fragment: " + ids [1]);                }
3367              ids [2] = handler.getSystemId ();              error("illegal PUBLIC id character U+"
3368              if (ids [2] == null)                    + Integer.toHexString(c));
3369                  handler.warn ("No base URI; hope URI is absolute: "            }
3370                          + ids [1]);        }
3371          }      else if (tryRead("SYSTEM"))
3372          {
3373          return ids;          requireWhitespace();
3374      }          ids.systemId = readLiteral(flags);
3375          }
3376        else if (!isSubset)
3377      /**        {
3378       * Test if a character is whitespace.          error("missing SYSTEM or PUBLIC keyword");
3379       * <pre>        }
3380       * [3] S ::= (#x20 | #x9 | #xd | #xa)+        
3381       * </pre>      if (ids.systemId != null)
3382       * @param c The character to test.        {
3383       * @return true if the character is whitespace.          if (ids.systemId.indexOf('#') != -1)
3384       */            {
3385      private final boolean isWhitespace (char c)              handler.verror("SYSTEM id has a URI fragment: " + ids.systemId);
3386      {            }
3387          if (c > 0x20)          ids.baseUri = handler.getSystemId();
3388              return false;          if (ids.baseUri == null && uriWarnings)
3389          if (c == 0x20 || c == 0x0a || c == 0x09 || c == 0x0d)            {
3390              return true;              handler.warn("No base URI; hope URI is absolute: "
3391          return false;   // illegal ...                           + ids.systemId);
3392      }            }
3393          }
3394        
3395      //////////////////////////////////////////////////////////////////////      return ids;
3396      // Utility routines.    }
     //////////////////////////////////////////////////////////////////////  
   
   
     /**  
      * Add a character to the data buffer.  
      */  
     private void dataBufferAppend (char c)  
     {  
         // Expand buffer if necessary.  
         if (dataBufferPos >= dataBuffer.length)  
             dataBuffer =  
                 (char[]) extendArray (dataBuffer,  
                         dataBuffer.length, dataBufferPos);  
         dataBuffer [dataBufferPos++] = c;  
     }  
   
   
     /**  
      * Add a string to the data buffer.  
      */  
     private void dataBufferAppend (String s)  
     {  
         dataBufferAppend (s.toCharArray (), 0, s.length ());  
     }  
   
   
     /**  
      * Append (part of) a character array to the data buffer.  
      */  
     private void dataBufferAppend (char ch[], int start, int length)  
     {  
         dataBuffer = (char[])  
                 extendArray (dataBuffer, dataBuffer.length,  
                                     dataBufferPos + length);  
3397    
3398          System.arraycopy (ch, start, dataBuffer, dataBufferPos, length);    /**
3399          dataBufferPos += length;     * Test if a character is whitespace.
3400      }     * <pre>
3401       * [3] S ::= (#x20 | #x9 | #xd | #xa)+
3402       * </pre>
3403       * @param c The character to test.
3404       * @return true if the character is whitespace.
3405       */
3406      private final boolean isWhitespace(char c)
3407      {
3408        if (c > 0x20)
3409          {
3410            return false;
3411          }
3412        if (c == 0x20 || c == 0x0a || c == 0x09 || c == 0x0d)
3413          {
3414            return true;
3415          }
3416        return false;  // illegal ...
3417      }
3418    
3419      //////////////////////////////////////////////////////////////////////
3420      // Utility routines.
3421      //////////////////////////////////////////////////////////////////////
3422        
3423      /**
3424       * Add a character to the data buffer.
3425       */
3426      private void dataBufferAppend(char c)
3427      {
3428        // Expand buffer if necessary.
3429        if (dataBufferPos >= dataBuffer.length)
3430          {
3431            dataBuffer = (char[]) extendArray(dataBuffer,
3432                                              dataBuffer.length, dataBufferPos);
3433          }
3434        dataBuffer[dataBufferPos++] = c;
3435      }
3436    
3437      /**    /**
3438       * Normalise space characters in the data buffer.     * Add a string to the data buffer.
3439       */     */
3440      private void dataBufferNormalize ()    private void dataBufferAppend(String s)
3441      {    {
3442          int i = 0;      dataBufferAppend(s.toCharArray(), 0, s.length());
3443          int j = 0;    }
         int end = dataBufferPos;  
   
         // Skip spaces at the start.  
         while (j < end && dataBuffer [j] == ' ') {  
             j++;  
         }  
   
         // Skip whitespace at the end.  
         while (end > j && dataBuffer [end - 1] == ' ') {  
             end --;  
         }  
   
         // Start copying to the left.  
         while (j < end) {  
   
             char c = dataBuffer [j++];  
   
             // Normalise all other spaces to  
             // a single space.  
             if (c == ' ') {  
                 while (j < end && dataBuffer [j++] == ' ')  
                     continue;  
                 dataBuffer [i++] = ' ';  
                 dataBuffer [i++] = dataBuffer [j - 1];  
             } else {  
                 dataBuffer [i++] = c;  
             }  
         }  
3444    
3445          // The new length is <= the old one.    /**
3446          dataBufferPos = i;     * Append (part of) a character array to the data buffer.
3447      }     */
3448      private void dataBufferAppend(char[] ch, int start, int length)
3449      {
3450        dataBuffer = (char[]) extendArray(dataBuffer, dataBuffer.length,
3451                                          dataBufferPos + length);
3452        
3453        System.arraycopy(ch, start, dataBuffer, dataBufferPos, length);
3454        dataBufferPos += length;
3455      }
3456    
3457      /**
3458       * Normalise space characters in the data buffer.
3459       */
3460      private void dataBufferNormalize()
3461      {
3462        int i = 0;
3463        int j = 0;
3464        int end = dataBufferPos;
3465        
3466        // Skip spaces at the start.
3467        while (j < end && dataBuffer[j] == ' ')
3468          {
3469            j++;
3470          }
3471        
3472        // Skip whitespace at the end.
3473        while (end > j && dataBuffer[end - 1] == ' ')
3474          {
3475            end --;
3476          }
3477    
3478      /**      // Start copying to the left.
3479       * Convert the data buffer to a string.      while (j < end)
3480       */        {
3481      private String dataBufferToString ()          
3482      {          char c = dataBuffer[j++];
3483          String s = new String (dataBuffer, 0, dataBufferPos);          
3484          dataBufferPos = 0;          // Normalise all other spaces to
3485          return s;          // a single space.
3486      }          if (c == ' ')
3487              {
3488                while (j < end && dataBuffer[j++] == ' ')
3489                  {
3490                    continue;
3491                  }
3492                dataBuffer[i++] = ' ';
3493                dataBuffer[i++] = dataBuffer[j - 1];
3494              }
3495            else
3496              {
3497                dataBuffer[i++] = c;
3498              }
3499          }
3500        
3501        // The new length is <= the old one.
3502        dataBufferPos = i;
3503      }
3504    
3505      /**
3506       * Convert the data buffer to a string.
3507       */
3508      private String dataBufferToString()
3509      {
3510        String s = new String(dataBuffer, 0, dataBufferPos);
3511        dataBufferPos = 0;
3512        return s;
3513      }
3514    
3515      /**    /**
3516       * Flush the contents of the data buffer to the handler, as     * Flush the contents of the data buffer to the handler, as
3517       * appropriate, and reset the buffer for new input.     * appropriate, and reset the buffer for new input.
3518       */     */
3519      private void dataBufferFlush ()    private void dataBufferFlush()
3520      throws SAXException      throws SAXException
3521      {    {
3522          if (currentElementContent == CONTENT_ELEMENTS      if (currentElementContent == CONTENT_ELEMENTS
3523                  && dataBufferPos > 0          && dataBufferPos > 0
3524                  && !inCDATA          && !inCDATA)
3525                  ) {        {
3526              // We can't just trust the buffer to be whitespace, there          // We can't just trust the buffer to be whitespace, there
3527              // are (error) cases when it isn't          // are (error) cases when it isn't
3528              for (int i = 0; i < dataBufferPos; i++) {          for (int i = 0; i < dataBufferPos; i++)
3529                  if (!isWhitespace (dataBuffer [i])) {            {
3530                      handler.charData (dataBuffer, 0, dataBufferPos);              if (!isWhitespace(dataBuffer[i]))
3531                      dataBufferPos = 0;                {
3532                  }                  handler.charData(dataBuffer, 0, dataBufferPos);
3533              }                  dataBufferPos = 0;
3534              if (dataBufferPos > 0) {                }
3535                  handler.ignorableWhitespace (dataBuffer, 0, dataBufferPos);            }
3536                  dataBufferPos = 0;          if (dataBufferPos > 0)
3537              }            {
3538          } else if (dataBufferPos > 0) {              handler.ignorableWhitespace(dataBuffer, 0, dataBufferPos);
3539              handler.charData (dataBuffer, 0, dataBufferPos);              dataBufferPos = 0;
3540              dataBufferPos = 0;            }
3541          }        }
3542      }      else if (dataBufferPos > 0)
3543          {
3544            handler.charData(dataBuffer, 0, dataBufferPos);
3545            dataBufferPos = 0;
3546          }
3547      }
3548    
3549      /**    /**
3550       * Require a string to appear, or throw an exception.     * Require a string to appear, or throw an exception.
3551       * <p><em>Precondition:</em> Entity expansion is not required.     * <p><em>Precondition:</em> Entity expansion is not required.
3552       * <p><em>Precondition:</em> data buffer has no characters that     * <p><em>Precondition:</em> data buffer has no characters that
3553       * will get sent to the application.     * will get sent to the application.
3554       */     */
3555      private void require (String delim)    private void require(String delim)
3556      throws SAXException, IOException      throws SAXException, IOException
3557      {    {
3558          int     length = delim.length ();      int length = delim.length();
3559          char    ch [];      char[] ch;
3560                        
3561          if (length < dataBuffer.length) {      if (length < dataBuffer.length)
3562              ch = dataBuffer;        {
3563              delim.getChars (0, length, ch, 0);          ch = dataBuffer;
3564          } else          delim.getChars(0, length, ch, 0);
3565              ch = delim.toCharArray ();        }
3566        else
3567          if (USE_CHEATS        {
3568                  && length <= (readBufferLength - readBufferPos)) {          ch = delim.toCharArray();
3569              int offset = readBufferPos;        }
3570          
3571              for (int i = 0; i < length; i++, offset++)      if (USE_CHEATS && length <= (readBufferLength - readBufferPos))
3572                  if (ch [i] != readBuffer [offset])        {
3573                      error ("required string", null, delim);          int offset = readBufferPos;
3574              readBufferPos = offset;          
3575                        for (int i = 0; i < length; i++, offset++)
3576          } else {            {
3577              for (int i = 0; i < length; i++)              if (ch[i] != readBuffer[offset])
3578                  require (ch [i]);                {
3579          }                  error ("required string", null, delim);
3580      }                }
3581              }
3582            readBufferPos = offset;
3583            
3584          }
3585        else
3586          {
3587            for (int i = 0; i < length; i++)
3588              {
3589                require(ch[i]);
3590              }
3591          }
3592      }
3593    
3594      /**    /**
3595       * Require a character to appear, or throw an exception.     * Require a character to appear, or throw an exception.
3596       */     */
3597      private void require (char delim)    private void require(char delim)
3598      throws SAXException, IOException      throws SAXException, IOException
3599      {    {
3600          char c = readCh ();      char c = readCh();
3601        
3602          if (c != delim) {      if (c != delim)
3603              error ("required character", c, new Character (delim).toString ());        {
3604          }          error("required character", c, new Character(delim).toString());
3605      }        }
3606      }
3607      
3608      /**    /**
3609       * Create an interned string from a character array.     * Create an interned string from a character array.
3610       * &AElig;lfred uses this method to create an interned version     * &AElig;lfred uses this method to create an interned version
3611       * of all names and name tokens, so that it can test equality     * of all names and name tokens, so that it can test equality
3612       * with <code>==</code> instead of <code>String.equals ()</code>.     * with <code>==</code> instead of <code>String.equals ()</code>.
3613       *     *
3614       * <p>This is much more efficient than constructing a non-interned     * <p>This is much more efficient than constructing a non-interned
3615       * string first, and then interning it.     * string first, and then interning it.
3616       *     *
3617       * @param ch an array of characters for building the string.     * @param ch an array of characters for building the string.
3618       * @param start the starting position in the array.     * @param start the starting position in the array.
3619       * @param length the number of characters to place in the string.     * @param length the number of characters to place in the string.
3620       * @return an interned string.     * @return an interned string.
3621       * @see #intern (String)     * @see #intern (String)
3622       * @see java.lang.String#intern     * @see java.lang.String#intern
3623       */     */
3624      public String intern (char ch[], int start, int length)    public String intern(char[] ch, int start, int length)
3625      {    {
3626          int     index = 0;      int index = 0;
3627          int     hash = 0;      int hash = 0;
3628          Object  bucket [];      Object[] bucket;
3629    
3630          // Generate a hash code.  This is a widely used string hash,      // Generate a hash code.  This is a widely used string hash,
3631          // often attributed to Brian Kernighan.      // often attributed to Brian Kernighan.
3632          for (int i = start; i < start + length; i++)      for (int i = start; i < start + length; i++)
3633              hash = 31 * hash + ch [i];        {
3634          hash = (hash & 0x7fffffff) % SYMBOL_TABLE_LENGTH;          hash = 31 * hash + ch[i];
3635          }
3636          // Get the bucket -- consists of {array,String} pairs      hash = (hash & 0x7fffffff) % SYMBOL_TABLE_LENGTH;
3637          if ((bucket = symbolTable [hash]) == null) {      
3638              // first string in this bucket      // Get the bucket -- consists of {array,String} pairs
3639              bucket = new Object [8];      if ((bucket = symbolTable[hash]) == null)
3640          {
3641          // Search for a matching tuple, and          // first string in this bucket
3642          // return the string if we find one.          bucket = new Object[8];
3643          } else {          
3644              while (index < bucket.length) {          // Search for a matching tuple, and
3645                  char chFound [] = (char []) bucket [index];          // return the string if we find one.
3646          }
3647                  // Stop when we hit an empty entry.      else
3648                  if (chFound == null)        {
3649                      break;          while (index < bucket.length)
3650              {
3651                  // If they're the same length, check for a match.              char[] chFound = (char[]) bucket[index];
3652                  if (chFound.length == length) {          
3653                      for (int i = 0; i < chFound.length; i++) {              // Stop when we hit an empty entry.
3654                          // continue search on failure              if (chFound == null)
3655                          if (ch [start + i] != chFound [i]) {                {
3656                              break;                  break;
3657                          } else if (i == length - 1) {                }
3658                              // That's it, we have a match!              
3659                              return (String) bucket [index + 1];              // If they're the same length, check for a match.
3660                          }              if (chFound.length == length)
3661                      }                {
3662                  }                  for (int i = 0; i < chFound.length; i++)
3663                  index += 2;                    {
3664              }                      // continue search on failure
3665              // Not found -- we'll have to add it.                      if (ch[start + i] != chFound[i])
3666                          {
3667              // Do we have to grow the bucket?                          break;
3668              bucket = (Object []) extendArray (bucket, bucket.length, index);                        }
3669          }                      else if (i == length - 1)
3670          symbolTable [hash] = bucket;                        {
3671                            // That's it, we have a match!
3672          // OK, add it to the end of the bucket -- "local" interning.                          return (String) bucket[index + 1];
3673          // Intern "globally" to let applications share interning benefits.                        }
3674          // That is, "!=" and "==" work on our strings, not just equals().                    }
3675          String s = new String (ch, start, length).intern ();                }
3676          bucket [index] = s.toCharArray ();              index += 2;
3677          bucket [index + 1] = s;            }
3678          return s;          // Not found -- we'll have to add it.
3679      }          
3680            // Do we have to grow the bucket?
3681      /**          bucket = (Object[]) extendArray(bucket, bucket.length, index);
3682       * Ensure the capacity of an array, allocating a new one if        }
3683       * necessary.  Usually extends only for name hash collisions.      symbolTable[hash] = bucket;
3684       */      
3685      private Object extendArray (Object array, int currentSize, int requiredSize)      // OK, add it to the end of the bucket -- "local" interning.
3686      {      // Intern "globally" to let applications share interning benefits.
3687          if (requiredSize < currentSize) {      // That is, "!=" and "==" work on our strings, not just equals().
3688              return array;      String s = new String(ch, start, length).intern();
3689          } else {      bucket[index] = s.toCharArray();
3690              Object newArray = null;      bucket[index + 1] = s;
3691              int newSize = currentSize * 2;      return s;
3692      }
             if (newSize <= requiredSize)  
                 newSize = requiredSize + 1;  
   
             if (array instanceof char[])  
                 newArray = new char [newSize];  
             else if (array instanceof Object[])  
                 newArray = new Object [newSize];  
             else  
                 throw new RuntimeException ();  
   
             System.arraycopy (array, 0, newArray, 0, currentSize);  
             return newArray;  
         }  
     }  
   
   
     //////////////////////////////////////////////////////////////////////  
     // XML query routines.  
     //////////////////////////////////////////////////////////////////////  
   
   
     boolean isStandalone () { return docIsStandalone; }  
   
   
     //  
     // Elements  
     //  
   
     private int getContentType (Object element [], int defaultType)  
     {  
         int retval;  
   
         if (element == null)  
             return defaultType;  
         retval = ((Integer) element [0]).intValue ();  
         if (retval == CONTENT_UNDECLARED)  
             retval = defaultType;  
         return retval;  
     }  
   
   
     /**  
      * Look up the content type of an element.  
      * @param name The element type name.  
      * @return An integer constant representing the content type.  
      * @see #CONTENT_UNDECLARED  
      * @see #CONTENT_ANY  
      * @see #CONTENT_EMPTY  
      * @see #CONTENT_MIXED  
      * @see #CONTENT_ELEMENTS  
      */  
     public int getElementContentType (String name)  
     {  
         Object element [] = (Object []) elementInfo.get (name);  
         return getContentType (element, CONTENT_UNDECLARED);  
     }  
   
   
     /**  
      * Register an element.  
      * Array format:  
      *  [0] element type name  
      *  [1] content model (mixed, elements only)  
      *  [2] attribute hash table  
      */  
     private void setElement (  
         String          name,  
         int             contentType,  
         String          contentModel,  
         Hashtable       attributes  
     ) throws SAXException  
     {  
         if (skippedPE)  
             return;  
   
         Object element [] = (Object []) elementInfo.get (name);  
   
         // first <!ELEMENT ...> or <!ATTLIST ...> for this type?  
         if (element == null) {  
             element = new Object [3];  
             element [0] = new Integer (contentType);  
             element [1] = contentModel;  
             element [2] = attributes;  
             elementInfo.put (name, element);  
             return;  
         }  
   
         // <!ELEMENT ...> declaration?  
         if (contentType != CONTENT_UNDECLARED) {  
             // ... following an associated <!ATTLIST ...>  
             if (((Integer) element [0]).intValue () == CONTENT_UNDECLARED) {  
                 element [0] = new Integer (contentType);  
                 element [1] = contentModel;  
             } else  
                 // VC: Unique Element Type Declaration  
                 handler.verror ("multiple declarations for element type: "  
                         + name);  
         }  
   
         // first <!ATTLIST ...>, before <!ELEMENT ...> ?  
         else if (attributes != null)  
             element [2] = attributes;  
     }  
   
   
     /**  
      * Look up the attribute hash table for an element.  
      * The hash table is the second item in the element array.  
      */  
     private Hashtable getElementAttributes (String name)  
     {  
         Object element[] = (Object[]) elementInfo.get (name);  
         if (element == null)  
             return null;  
         else  
             return (Hashtable) element [2];  
     }  
   
   
   
     //  
     // Attributes  
     //  
3693    
3694      /**    /**
3695       * Get the declared attributes for an element type.     * Ensure the capacity of an array, allocating a new one if
3696       * @param elname The name of the element type.     * necessary.  Usually extends only for name hash collisions.
3697       * @return An Enumeration of all the attributes declared for     */
3698       *   a specific element type.  The results will be valid only    private Object extendArray(Object array, int currentSize, int requiredSize)
3699       *   after the DTD (if any) has been parsed.    {
3700       * @see #getAttributeType      if (requiredSize < currentSize)
3701       * @see #getAttributeEnumeration        {
3702       * @see #getAttributeDefaultValueType          return array;
3703       * @see #getAttributeDefaultValue        }
3704       * @see #getAttributeExpandedValue      else
3705       */        {
3706      private Enumeration declaredAttributes (Object element [])          Object newArray = null;
3707      {          int newSize = currentSize * 2;
3708          Hashtable attlist;          
3709            if (newSize <= requiredSize)
3710              {
3711                newSize = requiredSize + 1;
3712              }
3713            
3714            if (array instanceof char[])
3715              {
3716                newArray = new char[newSize];
3717              }
3718            else if (array instanceof Object[])
3719              {
3720                newArray = new Object[newSize];
3721              }
3722            else
3723              {
3724                throw new RuntimeException();
3725              }
3726            
3727            System.arraycopy(array, 0, newArray, 0, currentSize);
3728            return newArray;
3729          }
3730      }
3731    
3732          if (element == null)    //////////////////////////////////////////////////////////////////////
3733              return null;    // XML query routines.
3734          if ((attlist = (Hashtable) element [2]) == null)    //////////////////////////////////////////////////////////////////////
3735              return null;    
3736          return attlist.keys ();    boolean isStandalone()
3737      }    {
3738        return docIsStandalone;
3739      }
3740        
3741      //
3742      // Elements
3743      //
3744      
3745      private int getContentType(ElementDecl element, int defaultType)
3746      {
3747        int retval;
3748        
3749        if (element == null)
3750          {
3751            return defaultType;
3752          }
3753        retval = element.contentType;
3754        if (retval == CONTENT_UNDECLARED)
3755          {
3756            retval = defaultType;
3757          }
3758        return retval;
3759      }
3760    
3761      /**    /**
3762       * Get the declared attributes for an element type.     * Look up the content type of an element.
3763       * @param elname The name of the element type.     * @param name The element type name.
3764       * @return An Enumeration of all the attributes declared for     * @return An integer constant representing the content type.
3765       *   a specific element type.  The results will be valid only     * @see #CONTENT_UNDECLARED
3766       *   after the DTD (if any) has been parsed.     * @see #CONTENT_ANY
3767       * @see #getAttributeType     * @see #CONTENT_EMPTY
3768       * @see #getAttributeEnumeration     * @see #CONTENT_MIXED
3769       * @see #getAttributeDefaultValueType     * @see #CONTENT_ELEMENTS
3770       * @see #getAttributeDefaultValue     */
3771       * @see #getAttributeExpandedValue    public int getElementContentType(String name)
3772       */    {
3773      public Enumeration declaredAttributes (String elname)      ElementDecl element = (ElementDecl) elementInfo.get(name);
3774      {      return getContentType(element, CONTENT_UNDECLARED);
3775          return declaredAttributes ((Object []) elementInfo.get (elname));    }
3776      }    
3777      /**
3778       * Register an element.
3779       * Array format:
3780       *  [0] element type name
3781       *  [1] content model (mixed, elements only)
3782       *  [2] attribute hash table
3783       */
3784      private void setElement(String name, int contentType,
3785                              String contentModel, HashMap attributes)
3786        throws SAXException
3787      {
3788        if (skippedPE)
3789          {
3790            return;
3791          }
3792    
3793        ElementDecl element = (ElementDecl) elementInfo.get(name);
3794        
3795        // first <!ELEMENT ...> or <!ATTLIST ...> for this type?
3796        if (element == null)
3797          {
3798            element = new ElementDecl();
3799            element.contentType = contentType;
3800            element.contentModel = contentModel;
3801            element.attributes = attributes;
3802            elementInfo.put(name, element);
3803            return;
3804          }
3805        
3806        // <!ELEMENT ...> declaration?
3807        if (contentType != CONTENT_UNDECLARED)
3808          {
3809            // ... following an associated <!ATTLIST ...>
3810            if (element.contentType == CONTENT_UNDECLARED)
3811              {
3812                element.contentType = contentType;
3813                element.contentModel = contentModel;
3814              }
3815            else
3816              {
3817                // VC: Unique Element Type Declaration
3818                handler.verror("multiple declarations for element type: "
3819                               + name);
3820              }
3821          }
3822        
3823        // first <!ATTLIST ...>, before <!ELEMENT ...> ?
3824        else if (attributes != null)
3825          {
3826            element.attributes = attributes;
3827          }
3828      }
3829      
3830      /**
3831       * Look up the attribute hash table for an element.
3832       * The hash table is the second item in the element array.
3833       */
3834      private HashMap getElementAttributes(String name)
3835      {
3836        ElementDecl element = (ElementDecl) elementInfo.get(name);
3837        return (element == null) ? null : element.attributes;
3838      }
3839    
3840      /**    //
3841       * Retrieve the declared type of an attribute.    // Attributes
3842       * @param name The name of the associated element.    //
3843       * @param aname The name of the attribute.    
3844       * @return An interend string denoting the type, or null    /**
3845       *  indicating an undeclared attribute.     * Get the declared attributes for an element type.
3846       */     * @param elname The name of the element type.
3847      public String getAttributeType (String name, String aname)     * @return An iterator over all the attributes declared for
3848      {     *   a specific element type.  The results will be valid only
3849          Object attribute[] = getAttribute (name, aname);     *   after the DTD (if any) has been parsed.
3850          if (attribute == null) {     * @see #getAttributeType
3851              return null;     * @see #getAttributeEnumeration
3852          } else {     * @see #getAttributeDefaultValueType
3853              return (String) attribute [0];     * @see #getAttributeDefaultValue
3854          }     * @see #getAttributeExpandedValue
3855      }     */
3856      private Iterator declaredAttributes(ElementDecl element)
3857      {
3858        HashMap attlist;
3859        
3860        if (element == null)
3861          {
3862            return null;
3863          }
3864        if ((attlist = element.attributes) == null)
3865          {
3866            return null;
3867          }
3868        return attlist.keySet().iterator();
3869      }
3870    
3871      /**
3872       * Get the declared attributes for an element type.
3873       * @param elname The name of the element type.
3874       * @return An iterator over all the attributes declared for
3875       *   a specific element type.  The results will be valid only
3876       *   after the DTD (if any) has been parsed.
3877       * @see #getAttributeType
3878       * @see #getAttributeEnumeration
3879       * @see #getAttributeDefaultValueType
3880       * @see #getAttributeDefaultValue
3881       * @see #getAttributeExpandedValue
3882       */
3883      public Iterator declaredAttributes(String elname)
3884      {
3885        return declaredAttributes((ElementDecl) elementInfo.get(elname));
3886      }
3887    
3888      /**    /**
3889       * Retrieve the allowed values for an enumerated attribute type.     * Retrieve the declared type of an attribute.
3890       * @param name The name of the associated element.     * @param name The name of the associated element.
3891       * @param aname The name of the attribute.     * @param aname The name of the attribute.
3892       * @return A string containing the token list.     * @return An interend string denoting the type, or null
3893       */     *  indicating an undeclared attribute.
3894      public String getAttributeEnumeration (String name, String aname)     */
3895      {    public String getAttributeType(String name, String aname)
3896          Object attribute[] = getAttribute (name, aname);    {
3897          if (attribute == null) {      AttributeDecl attribute = getAttribute(name, aname);
3898              return null;      return (attribute == null) ? null : attribute.type;
3899          } else {    }
             // assert:  attribute [0] is "ENUMERATION" or "NOTATION"  
             return (String) attribute [3];  
         }  
     }  
3900    
3901      /**
3902       * Retrieve the allowed values for an enumerated attribute type.
3903       * @param name The name of the associated element.
3904       * @param aname The name of the attribute.
3905       * @return A string containing the token list.
3906       */
3907      public String getAttributeEnumeration(String name, String aname)
3908      {
3909        AttributeDecl attribute = getAttribute(name, aname);
3910        // assert:  attribute.enumeration is "ENUMERATION" or "NOTATION"
3911        return (attribute == null) ? null : attribute.enumeration;
3912      }
3913    
3914      /**    /**
3915       * Retrieve the default value of a declared attribute.     * Retrieve the default value of a declared attribute.
3916       * @param name The name of the associated element.     * @param name The name of the associated element.
3917       * @param aname The name of the attribute.     * @param aname The name of the attribute.
3918       * @return The default value, or null if the attribute was     * @return The default value, or null if the attribute was
3919       *   #IMPLIED or simply undeclared and unspecified.     *   #IMPLIED or simply undeclared and unspecified.
3920       * @see #getAttributeExpandedValue     * @see #getAttributeExpandedValue
3921       */     */
3922      public String getAttributeDefaultValue (String name, String aname)    public String getAttributeDefaultValue(String name, String aname)
3923      {    {
3924          Object attribute[] = getAttribute (name, aname);      AttributeDecl attribute = getAttribute(name, aname);
3925          if (attribute == null) {      return (attribute == null) ? null : attribute.value;
3926              return null;    }
         } else {  
             return (String) attribute [1];  
         }  
     }  
3927    
3928      /*      /*
3929    
# Line 3325  loop: Line 3940  loop:
3940       * @param name The name of the associated element.       * @param name The name of the associated element.
3941       * @param aname The name of the attribute.       * @param aname The name of the attribute.
3942       * @return The expanded default value, or null if the attribute was       * @return The expanded default value, or null if the attribute was
3943       *   #IMPLIED or simply undeclared       *   #IMPLIED or simply undeclared
3944       * @see #getAttributeDefaultValue       * @see #getAttributeDefaultValue
3945      public String getAttributeExpandedValue (String name, String aname)      public String getAttributeExpandedValue (String name, String aname)
3946      throws Exception      throws Exception
3947      {      {
3948          Object attribute[] = getAttribute (name, aname);    AttributeDecl attribute = getAttribute (name, aname);
3949    
3950          if (attribute == null) {    if (attribute == null) {
3951              return null;        return null;
3952          } else if (attribute [4] == null && attribute [1] != null) {    } else if (attribute.defaultValue == null && attribute.value != null) {
3953              // we MUST use the same buf for both quotes else the literal        // we MUST use the same buf for both quotes else the literal
3954              // can't be properly terminated        // can't be properly terminated
3955              char buf [] = new char [1];        char buf [] = new char [1];
3956              int flags = LIT_ENTITY_REF | LIT_ATTRIBUTE;        int  flags = LIT_ENTITY_REF | LIT_ATTRIBUTE;
3957              String type = getAttributeType (name, aname);        String type = getAttributeType (name, aname);
3958    
3959              if (type != "CDATA" && type != null)        if (type != "CDATA" && type != null)
3960                  flags |= LIT_NORMALIZE;      flags |= LIT_NORMALIZE;
3961              buf [0] = '"';        buf [0] = '"';
3962              pushCharArray (null, buf, 0, 1);        pushCharArray (null, buf, 0, 1);
3963              pushString (null, (String) attribute [1]);        pushString (null, attribute.value);
3964              pushCharArray (null, buf, 0, 1);        pushCharArray (null, buf, 0, 1);
3965              attribute [4] = readLiteral (flags);        attribute.defaultValue = readLiteral (flags);
3966          }    }
3967          return (String) attribute [4];    return attribute.defaultValue;
3968      }      }
3969       */       */
3970    
3971      /**    /**
3972       * Retrieve the default value mode of a declared attribute.     * Retrieve the default value mode of a declared attribute.
3973       * @see #ATTRIBUTE_DEFAULT_SPECIFIED     * @see #ATTRIBUTE_DEFAULT_SPECIFIED
3974       * @see #ATTRIBUTE_DEFAULT_IMPLIED     * @see #ATTRIBUTE_DEFAULT_IMPLIED
3975       * @see #ATTRIBUTE_DEFAULT_REQUIRED     * @see #ATTRIBUTE_DEFAULT_REQUIRED
3976       * @see #ATTRIBUTE_DEFAULT_FIXED     * @see #ATTRIBUTE_DEFAULT_FIXED
3977       */     */
3978      public int getAttributeDefaultValueType (String name, String aname)    public int getAttributeDefaultValueType(String name, String aname)
3979      {    {
3980          Object attribute[] = getAttribute (name, aname);      AttributeDecl attribute = getAttribute(name, aname);
3981          if (attribute == null) {      return (attribute == null) ? ATTRIBUTE_DEFAULT_UNDECLARED :
3982              return ATTRIBUTE_DEFAULT_UNDECLARED;        attribute.valueType;
3983          } else {    }  
3984              return ((Integer) attribute [2]).intValue ();    
3985          }    /**
3986      }     * Register an attribute declaration for later retrieval.
3987       * Format:
3988       * - String type
3989      /**     * - String default value
3990       * Register an attribute declaration for later retrieval.     * - int value type
3991       * Format:     * - enumeration
3992       * - String type     * - processed default value
3993       * - String default value     */
3994       * - int value type    private void setAttribute(String elName, String name, String type,
3995       * - enumeration                              String enumeration, String value, int valueType)
      * - processed default value  
      */  
     private void setAttribute (String elName, String name, String type,  
                         String enumeration,  
                         String value, int valueType)  
3996      throws Exception      throws Exception
3997      {    {
3998          Hashtable attlist;      HashMap attlist;
3999        
4000          if (skippedPE)      if (skippedPE)
4001              return;        {
4002            return;
4003          // Create a new hashtable if necessary.        }
4004          attlist = getElementAttributes (elName);      
4005          if (attlist == null)      // Create a new hashtable if necessary.
4006              attlist = new Hashtable ();      attlist = getElementAttributes(elName);
4007        if (attlist == null)
4008          // ignore multiple attribute declarations!        {
4009          if (attlist.get (name) != null) {          attlist = new HashMap();
4010              // warn ...        }
4011              return;      
4012          } else {      // ignore multiple attribute declarations!
4013              Object attribute [] = new Object [5];      if (attlist.get(name) != null)
4014              attribute [0] = type;        {
4015              attribute [1] = value;          // warn ...
4016              attribute [2] = new Integer (valueType);          return;
4017              attribute [3] = enumeration;        }
4018              attribute [4] = null;      else
4019              attlist.put (name, attribute);        {
4020            AttributeDecl attribute = new AttributeDecl();
4021              // save; but don't overwrite any existing <!ELEMENT ...>          attribute.type = type;
4022              setElement (elName, CONTENT_UNDECLARED, null, attlist);          attribute.value = value;
4023          }          attribute.valueType = valueType;
4024      }          attribute.enumeration = enumeration;
4025            attlist.put(name, attribute);
4026          
4027      /**          // save; but don't overwrite any existing <!ELEMENT ...>
4028       * Retrieve the array representing an attribute declaration.          setElement(elName, CONTENT_UNDECLARED, null, attlist);
4029       */        }
4030      private Object[] getAttribute (String elName, String name)    }
     {  
         Hashtable attlist;  
   
         attlist = getElementAttributes (elName);  
         if (attlist == null)  
             return null;  
         return (Object[]) attlist.get (name);  
     }  
   
   
     //  
     // Entities  
     //  
   
     /**  
      * Find the type of an entity.  
      * @returns An integer constant representing the entity type.  
      * @see #ENTITY_UNDECLARED  
      * @see #ENTITY_INTERNAL  
      * @see #ENTITY_NDATA  
      * @see #ENTITY_TEXT  
      */  
     public int getEntityType (String ename)  
     {  
         Object entity[] = (Object[]) entityInfo.get (ename);  
         if (entity == null) {  
             return ENTITY_UNDECLARED;  
         } else {  
             return ((Integer) entity [0]).intValue ();  
         }  
     }  
   
4031    
4032      /**    /**
4033       * Return an external entity's identifier array.     * Retrieve the attribute declaration for the given element name and name.
4034       * @param ename The name of the external entity.     */
4035       * @return Three element array containing (in order) the entity's    private AttributeDecl getAttribute(String elName, String name)
4036       *  public identifier, system identifier, and base URI.  Null if    {
4037       *   the entity was not declared as an external entity.      HashMap attlist = getElementAttributes(elName);
4038       * @see #getEntityType      return (attlist == null) ? null : (AttributeDecl) attlist.get(name);
4039       */    }
     public String [] getEntityIds (String ename)  
     {  
         Object entity[] = (Object[]) entityInfo.get (ename);  
         if (entity == null) {  
             return null;  
         } else {  
             return (String []) entity [1];  
         }  
     }  
4040    
4041      //
4042      // Entities
4043      //
4044      
4045      /**
4046       * Find the type of an entity.
4047       * @returns An integer constant representing the entity type.
4048       * @see #ENTITY_UNDECLARED
4049       * @see #ENTITY_INTERNAL
4050       * @see #ENTITY_NDATA
4051       * @see #ENTITY_TEXT
4052       */
4053      public int getEntityType(String ename)
4054      {
4055        EntityInfo entity = (EntityInfo) entityInfo.get(ename);
4056        return (entity == null) ?  ENTITY_UNDECLARED : entity.type;
4057      }
4058    
4059      /**    /**
4060       * Return an internal entity's replacement text.     * Return an external entity's identifiers.
4061       * @param ename The name of the internal entity.     * @param ename The name of the external entity.
4062       * @return The entity's replacement text, or null if     * @return The entity's public identifier, system identifier, and base URI.
4063       *   the entity was not declared as an internal entity.     *  Null if the entity was not declared as an external entity.
4064       * @see #getEntityType     * @see #getEntityType
4065       */     */
4066      public String getEntityValue (String ename)    public ExternalIdentifiers getEntityIds(String ename)
4067      {    {
4068          Object entity[] = (Object[]) entityInfo.get (ename);      EntityInfo entity = (EntityInfo) entityInfo.get(ename);
4069          if (entity == null) {      return (entity == null) ? null : entity.ids;
4070              return null;    }
         } else {  
             return (String) entity [3];  
         }  
     }  
4071    
4072      /**
4073       * Return an internal entity's replacement text.
4074       * @param ename The name of the internal entity.
4075       * @return The entity's replacement text, or null if
4076       *   the entity was not declared as an internal entity.
4077       * @see #getEntityType
4078       */
4079      public String getEntityValue(String ename)
4080      {
4081        EntityInfo entity = (EntityInfo) entityInfo.get(ename);
4082        return (entity == null) ? null : entity.value;
4083      }
4084    
4085      /**    /**
4086       * Register an entity declaration for later retrieval.     * Register an entity declaration for later retrieval.
4087       */     */
4088      private void setInternalEntity (String eName, String value)    private void setInternalEntity(String eName, String value)
4089      throws SAXException      throws SAXException
4090      {    {
4091          if (skippedPE)      if (skippedPE)
4092              return;        {
4093            return;
4094          }
4095    
4096          if (entityInfo.get (eName) == null) {      if (entityInfo.get(eName) == null)
4097              Object entity[] = new Object [5];        {
4098              entity [0] = new Integer (ENTITY_INTERNAL);          EntityInfo entity = new EntityInfo();
4099  // FIXME: shrink!!  [2] useless          entity.type = ENTITY_INTERNAL;
4100              entity [3] = value;          entity.value = value;
4101              entityInfo.put (eName, entity);          entityInfo.put(eName, entity);
4102          }        }
4103    if (handler.getFeature (SAXDriver.FEATURE + "string-interning")) {      if (handler.stringInterning)
4104      if ("lt" == eName || "gt" == eName || "quot" == eName        {
4105          || "apos" == eName || "amp" == eName)          if ("lt" == eName || "gt" == eName || "quot" == eName
4106              return;              || "apos" == eName || "amp" == eName)
4107    } else {            {
4108      if ("lt".equals(eName) || "gt".equals(eName) || "quot".equals(eName)              return;
4109          || "apos".equals(eName) || "amp".equals(eName))            }
4110              return;        }
4111        else
4112          {
4113            if ("lt".equals(eName) || "gt".equals(eName) || "quot".equals(eName)
4114                || "apos".equals(eName) || "amp".equals(eName))
4115              {
4116                return;
4117              }
4118          }
4119        handler.getDeclHandler().internalEntityDecl(eName, value);
4120    }    }
         handler.getDeclHandler ()  
             .internalEntityDecl (eName, value);  
     }  
   
   
     /**  
      * Register an external entity declaration for later retrieval.  
      */  
     private void setExternalEntity (String eName, int eClass,  
                      String ids [], String nName)  
     {  
         if (entityInfo.get (eName) == null) {  
             Object entity[] = new Object [5];  
             entity [0] = new Integer (eClass);  
             entity [1] = ids;  
 // FIXME: shrink!!  [2] no longer used, [4] irrelevant given [0]  
             entity [4] = nName;  
             entityInfo.put (eName, entity);  
         }  
     }  
   
4121    
4122      //    /**
4123      // Notations.     * Register an external entity declaration for later retrieval.
4124      //     */
4125      private void setExternalEntity(String eName, int eClass,
4126                                     ExternalIdentifiers ids, String nName)
4127      {
4128        if (entityInfo.get(eName) == null)
4129          {
4130            EntityInfo entity = new EntityInfo();
4131            entity.type = eClass;
4132            entity.ids = ids;
4133            entity.notationName = nName;
4134            entityInfo.put(eName, entity);
4135          }
4136      }
4137    
4138      /**    //
4139       * Report a notation declaration, checking for duplicates.    // Notations.
4140       */    //
4141      private void setNotation (String nname, String ids [])    
4142      /**
4143       * Report a notation declaration, checking for duplicates.
4144       */
4145      private void setNotation(String nname, ExternalIdentifiers ids)
4146      throws SAXException      throws SAXException
4147      {    {
4148          if (skippedPE)      if (skippedPE)
4149              return;        {
4150            return;
4151          handler.notationDecl (nname, ids);        }
4152          if (notationInfo.get (nname) == null)      
4153              notationInfo.put (nname, nname);      handler.notationDecl(nname, ids.publicId, ids.systemId, ids.baseUri);
4154          else      if (notationInfo.get(nname) == null)
4155              // VC: Unique Notation Name        {
4156              handler.verror ("Duplicate notation name decl: " + nname);          notationInfo.put(nname, nname);
4157      }        }
4158        else
4159          {
4160            // VC: Unique Notation Name
4161            handler.verror("Duplicate notation name decl: " + nname);
4162          }
4163      }
4164      
4165      //
4166      // Location.
4167      //
4168      
4169      /**
4170       * Return the current line number.
4171       */
4172      public int getLineNumber()
4173      {
4174        return line;
4175      }
4176    
4177      //    /**
4178      // Location.     * Return the current column number.
4179      //     */
4180      public int getColumnNumber()
4181      {
4182        return column;
4183      }
4184    
4185      //////////////////////////////////////////////////////////////////////
4186      // High-level I/O.
4187      //////////////////////////////////////////////////////////////////////
4188      
4189      /**
4190       * Read a single character from the readBuffer.
4191       * <p>The readDataChunk () method maintains the buffer.
4192       * <p>If we hit the end of an entity, try to pop the stack and
4193       * keep going.
4194       * <p> (This approach doesn't really enforce XML's rules about
4195       * entity boundaries, but this is not currently a validating
4196       * parser).
4197       * <p>This routine also attempts to keep track of the current
4198       * position in external entities, but it's not entirely accurate.
4199       * @return The next available input character.
4200       * @see #unread (char)
4201       * @see #readDataChunk
4202       * @see #readBuffer
4203       * @see #line
4204       * @return The next character from the current input source.
4205       */
4206      private char readCh()
4207        throws SAXException, IOException
4208      {
4209        // As long as there's nothing in the
4210        // read buffer, try reading more data
4211        // (for an external entity) or popping
4212        // the entity stack (for either).
4213        while (readBufferPos >= readBufferLength)
4214          {
4215            switch (sourceType)
4216              {
4217              case INPUT_READER:
4218              case INPUT_STREAM:
4219                readDataChunk();
4220                while (readBufferLength < 1)
4221                  {
4222                    popInput();
4223                    if (readBufferLength < 1)
4224                      {
4225                        readDataChunk();
4226                      }
4227                  }
4228                break;
4229                
4230              default:
4231                
4232                popInput();
4233                break;
4234              }
4235          }
4236        
4237        char c = readBuffer[readBufferPos++];
4238        
4239        if (c == '\n')
4240          {
4241            line++;
4242            column = 0;
4243          }
4244        else
4245          {
4246            if (c == '<')
4247              {
4248                /* the most common return to parseContent () ... NOP */
4249              }
4250            else if (((c < 0x0020 && (c != '\t') && (c != '\r')) || c > 0xFFFD)
4251                     || ((c >= 0x007f) && (c <= 0x009f) && (c != 0x0085)
4252                         && xmlVersion == XML_11))
4253              {
4254                error("illegal XML character U+" + Integer.toHexString(c));
4255              }
4256    
4257      /**          // If we're in the DTD and in a context where PEs get expanded,
4258       * Return the current line number.          // do so ... 1/14/2000 errata identify those contexts.  There
4259       */          // are also spots in the internal subset where PE refs are fatal
4260      public int getLineNumber ()          // errors, hence yet another flag.
4261      {          else if (c == '%' && expandPE)
4262          return line;            {
4263      }              if (peIsError)
4264                  {
4265                    error("PE reference within decl in internal subset.");
4266                  }
4267                parsePEReference();
4268                return readCh();
4269              }
4270            column++;
4271          }
4272    
4273        return c;
4274      }
4275    
4276      /**    /**
4277       * Return the current column number.     * Push a single character back onto the current input stream.
4278       */     * <p>This method usually pushes the character back onto
4279      public int getColumnNumber ()     * the readBuffer.
4280      {     * <p>I don't think that this would ever be called with
4281          return column;     * readBufferPos = 0, because the methods always reads a character
4282      }     * before unreading it, but just in case, I've added a boundary
4283       * condition.
4284       * @param c The character to push back.
4285       * @see #readCh
4286       * @see #unread (char[])
4287       * @see #readBuffer
4288       */
4289      private void unread(char c)
4290        throws SAXException
4291      {
4292        // Normal condition.
4293        if (c == '\n')
4294          {
4295            line--;
4296            column = -1;
4297          }
4298        if (readBufferPos > 0)
4299          {
4300            readBuffer[--readBufferPos] = c;
4301          }
4302        else
4303          {
4304            pushString(null, new Character(c).toString());
4305          }
4306      }
4307    
4308      /**
4309       * Push a char array back onto the current input stream.
4310       * <p>NOTE: you must <em>never</em> push back characters that you
4311       * haven't actually read: use pushString () instead.
4312       * @see #readCh
4313       * @see #unread (char)
4314       * @see #readBuffer
4315       * @see #pushString
4316       */
4317      private void unread(char[] ch, int length)
4318        throws SAXException
4319      {
4320        for (int i = 0; i < length; i++)
4321          {
4322            if (ch[i] == '\n')
4323              {
4324                line--;
4325                column = -1;
4326              }
4327          }
4328        if (length < readBufferPos)
4329          {
4330            readBufferPos -= length;
4331          }
4332        else
4333          {
4334            pushCharArray(null, ch, 0, length);
4335          }
4336      }
4337    
4338      //////////////////////////////////////////////////////////////////////    /**
4339      // High-level I/O.     * Push, or skip, a new external input source.
4340      //////////////////////////////////////////////////////////////////////     * The source will be some kind of parsed entity, such as a PE
4341       * (including the external DTD subset) or content for the body.
4342       *
4343      /**     * @param url The java.net.URL object for the entity.
4344       * Read a single character from the readBuffer.     * @see SAXDriver#resolveEntity
4345       * <p>The readDataChunk () method maintains the buffer.     * @see #pushString
4346       * <p>If we hit the end of an entity, try to pop the stack and     * @see #sourceType
4347       * keep going.     * @see #pushInput
4348       * <p> (This approach doesn't really enforce XML's rules about     * @see #detectEncoding
4349       * entity boundaries, but this is not currently a validating     * @see #sourceType
4350       * parser).     * @see #readBuffer
4351       * <p>This routine also attempts to keep track of the current     */
4352       * position in external entities, but it's not entirely accurate.    private void pushURL(boolean isPE,
4353       * @return The next available input character.                         String ename,
4354       * @see #unread (char)                         ExternalIdentifiers ids,
4355       * @see #readDataChunk                         Reader reader,
4356       * @see #readBuffer                         InputStream stream,
4357       * @see #line                         String encoding,
4358       * @return The next character from the current input source.                         boolean doResolve)
      */  
     private char readCh ()  
4359      throws SAXException, IOException      throws SAXException, IOException
4360      {    {
4361          // As long as there's nothing in the      boolean ignoreEncoding;
4362          // read buffer, try reading more data      String systemId;
4363          // (for an external entity) or popping      InputSource source;
4364          // the entity stack (for either).  
4365          while (readBufferPos >= readBufferLength) {      if (!isPE)
4366              switch (sourceType) {        {
4367              case INPUT_READER:          dataBufferFlush();
4368              case INPUT_STREAM:        }
                 readDataChunk ();  
                 while (readBufferLength < 1) {  
                     popInput ();  
                     if (readBufferLength < 1) {  
                         readDataChunk ();  
                     }  
                 }  
                 break;  
   
             default:  
   
                 popInput ();  
                 break;  
             }  
         }  
   
         char c = readBuffer [readBufferPos++];  
         
         if (c == '\n') {  
             line++;  
             column = 0;  
         } else {  
             if (c == '<') {  
                 /* the most common return to parseContent () ... NOP */  
             } else if (((c < 0x0020 && (c != '\t') && (c != '\r')) || c > 0xFFFD)  
                         || ((c >= 0x007f) && (c <= 0x009f) && (c != 0x0085)  
                            && xmlVersion == XML_11))  
                 error ("illegal XML character U+"  
                         + Integer.toHexString (c));  
   
             // If we're in the DTD and in a context where PEs get expanded,  
             // do so ... 1/14/2000 errata identify those contexts.  There  
             // are also spots in the internal subset where PE refs are fatal  
             // errors, hence yet another flag.  
             else if (c == '%' && expandPE) {  
                 if (peIsError)  
                     error ("PE reference within decl in internal subset.");  
                 parsePEReference ();  
                 return readCh ();  
             }  
             column++;  
         }  
   
         return c;  
     }  
4369    
4370        scratch.setPublicId(ids.publicId);
4371        scratch.setSystemId(ids.systemId);
4372    
4373      /**      // See if we should skip or substitute the entity.
4374       * Push a single character back onto the current input stream.      // If we're not skipping, resolving reports startEntity()
4375       * <p>This method usually pushes the character back onto      // and updates the (handler's) stack of URIs.
4376       * the readBuffer.      if (doResolve)
4377       * <p>I don't think that this would ever be called with        {
4378       * readBufferPos = 0, because the methods always reads a character          // assert (stream == null && reader == null && encoding == null)
4379       * before unreading it, but just in case, I've added a boundary          source = handler.resolveEntity(isPE, ename, scratch, ids.baseUri);
4380       * condition.          if (source == null)
4381       * @param c The character to push back.            {
4382       * @see #readCh              handler.warn("skipping entity: " + ename);
4383       * @see #unread (char[])              handler.skippedEntity(ename);
4384       * @see #readBuffer              if (isPE)
4385       */                {
4386      private void unread (char c)                  skippedPE = true;
4387      throws SAXException                }
4388      {              return;
4389          // Normal condition.            }
         if (c == '\n') {  
             line--;  
             column = -1;  
         }  
         if (readBufferPos > 0) {  
             readBuffer [--readBufferPos] = c;  
         } else {  
             pushString (null, new Character (c).toString ());  
         }  
     }  
4390    
4391            // we might be using alternate IDs/encoding
4392            systemId = source.getSystemId();
4393            // The following warning and setting systemId was deleted bcause
4394            // the application has the option of not setting systemId
4395            // provided that it has set the characte/byte stream.
4396            /*
4397               if (systemId == null) {
4398               handler.warn ("missing system ID, using " + ids.systemId);
4399               systemId = ids.systemId;
4400               }
4401             */
4402          }
4403        else
4404          {
4405            // "[document]", or "[dtd]" via getExternalSubset()
4406            scratch.setCharacterStream(reader);
4407            scratch.setByteStream(stream);
4408            scratch.setEncoding(encoding);
4409            source = scratch;
4410            systemId = ids.systemId;
4411            if (handler.stringInterning)
4412              {
4413                handler.startExternalEntity(ename, systemId,
4414                                            "[document]" == ename);
4415              }
4416            else
4417              {
4418                handler.startExternalEntity(ename, systemId,
4419                                            "[document]".equals(ename));
4420              }
4421          }
4422    
4423      /**      // we may have been given I/O streams directly
4424       * Push a char array back onto the current input stream.      if (source.getCharacterStream() != null)
4425       * <p>NOTE: you must <em>never</em> push back characters that you        {
4426       * haven't actually read: use pushString () instead.          if (source.getByteStream() != null)
4427       * @see #readCh            error("InputSource has two streams!");
4428       * @see #unread (char)          reader = source.getCharacterStream();
4429       * @see #readBuffer        }
4430       * @see #pushString      else if (source.getByteStream() != null)
4431       */        {
4432      private void unread (char ch[], int length)          encoding = source.getEncoding();
4433      throws SAXException          if (encoding == null)
4434      {            {
4435          for (int i = 0; i < length; i++) {              stream = source.getByteStream();
4436              if (ch [i] == '\n') {            }
4437                  line--;          else
4438                  column = -1;            {
4439              }              try
4440          }                {
4441          if (length < readBufferPos) {                  reader = new InputStreamReader(source.getByteStream(),
4442              readBufferPos -= length;                                                 encoding);
4443          } else {                }
4444              pushCharArray (null, ch, 0, length);              catch (IOException e)
4445          }                {
4446      }                  stream = source.getByteStream();
4447                  }
4448              }
4449          }
4450        else if (systemId == null)
4451          {
4452            error("InputSource has no URI!");
4453          }
4454        scratch.setCharacterStream(null);
4455        scratch.setByteStream(null);
4456        scratch.setEncoding(null);
4457        
4458        // Push the existing status.
4459        pushInput(ename);
4460    
4461        // Create a new read buffer.
4462        // (Note the four-character margin)
4463        readBuffer = new char[READ_BUFFER_MAX + 4];
4464        readBufferPos = 0;
4465        readBufferLength = 0;
4466        readBufferOverflow = -1;
4467        is = null;
4468        line = 1;
4469        column = 0;
4470        currentByteCount = 0;
4471    
4472        // If there's an explicit character stream, just
4473        // ignore encoding declarations.
4474        if (reader != null)
4475          {
4476            sourceType = INPUT_READER;
4477            this.reader = reader;
4478            tryEncodingDecl(true);
4479            return;
4480          }
4481      
4482        // Else we handle the conversion, and need to ensure
4483        // it's done right.
4484        sourceType = INPUT_STREAM;
4485        if (stream != null)
4486          {
4487            is = stream;
4488          }
4489        else
4490          {
4491            // We have to open our own stream to the URL.
4492            URL url = new URL(systemId);
4493            
4494            externalEntity = url.openConnection();
4495            externalEntity.connect();
4496            is = externalEntity.getInputStream();
4497          }
4498        
4499        // If we get to here, there must be
4500        // an InputStream available.
4501        if (!is.markSupported())
4502          {
4503            is = new BufferedInputStream(is);
4504          }
4505    
4506      /**      // Get any external encoding label.
4507       * Push, or skip, a new external input source.      if (encoding == null && externalEntity != null)
4508       * The source will be some kind of parsed entity, such as a PE        {
4509       * (including the external DTD subset) or content for the body.          // External labels can be untrustworthy; filesystems in
4510       *          // particular often have the wrong default for content
4511       * @param url The java.net.URL object for the entity.          // that wasn't locally originated.  Those we autodetect.
4512       * @see SAXDriver#resolveEntity          if (!"file".equals(externalEntity.getURL().getProtocol()))
4513       * @see #pushString            {
4514       * @see #sourceType              int temp;
4515       * @see #pushInput            
4516       * @see #detectEncoding              // application/xml;charset=something;otherAttr=...
4517       * @see #sourceType              // ... with many variants on 'something'
4518       * @see #readBuffer              encoding = externalEntity.getContentType();
4519       */            
4520      private void pushURL (              // MHK code (fix for Saxon 5.5.1/007):
4521          boolean         isPE,              // protect against encoding==null
4522          String          ename,              if (encoding == null)
4523          String          ids [],         // public, system, baseURI                {
4524          Reader          reader,                  temp = -1;
4525          InputStream     stream,                }
4526          String          encoding,              else
4527          boolean         doResolve                {
4528      ) throws SAXException, IOException                  temp = encoding.indexOf("charset");
4529      {                }
4530          boolean         ignoreEncoding;            
4531          String          systemId;              // RFC 2376 sez MIME text defaults to ASCII, but since the
4532          InputSource     source;              // JDK will create a MIME type out of thin air, we always
4533                // autodetect when there's no explicit charset attribute.
4534          if (!isPE)              if (temp < 0)
4535              dataBufferFlush ();                {
4536                    encoding = null;  // autodetect
4537          scratch.setPublicId (ids [0]);                }
4538          scratch.setSystemId (ids [1]);              else
4539                  {
4540          // See if we should skip or substitute the entity.                  // only this one attribute
4541          // If we're not skipping, resolving reports startEntity()                  if ((temp = encoding.indexOf(';')) > 0)
4542          // and updates the (handler's) stack of URIs.                    {
4543          if (doResolve) {                      encoding = encoding.substring(0, temp);
4544              // assert (stream == null && reader == null && encoding == null)                    }
4545              source = handler.resolveEntity (isPE, ename, scratch, ids [2]);                  
4546              if (source == null) {                  if ((temp = encoding.indexOf('=', temp + 7)) > 0)
4547                  handler.warn ("skipping entity: " + ename);                    {
4548                  handler.skippedEntity (ename);                      encoding = encoding.substring(temp + 1);
4549                  if (isPE)                      
4550                      skippedPE = true;                      // attributes can have comment fields (RFC 822)
4551                  return;                      if ((temp = encoding.indexOf('(')) > 0)
4552              }                        {
4553                            encoding = encoding.substring(0, temp);
4554              // we might be using alternate IDs/encoding                        }
4555              systemId = source.getSystemId ();                      // ... and values may be quoted
4556              // The following warning and setting systemId was deleted bcause                      if ((temp = encoding.indexOf('"')) > 0)
4557              // the application has the option of not setting systemId                        {
4558              // provided that it has set the characte/byte stream.                          encoding =
4559              /*                            encoding.substring(temp + 1,
4560              if (systemId == null) {                                               encoding.indexOf('"', temp + 2));
4561                  handler.warn ("missing system ID, using " + ids [1]);                        }
4562                  systemId = ids [1];                      encoding.trim();
4563              }                    }
4564              */                  else
4565          } else {                    {
4566              // "[document]", or "[dtd]" via getExternalSubset()                      handler.warn("ignoring illegal MIME attribute: "
4567              scratch.setCharacterStream (reader);                                   + encoding);
4568              scratch.setByteStream (stream);                      encoding = null;
4569              scratch.setEncoding (encoding);                    }
4570              source = scratch;                }
4571              systemId = ids [1];            }
4572        if (handler.getFeature (SAXDriver.FEATURE + "string-interning")) {        }
4573          handler.startExternalEntity (ename, systemId,      
4574                                       "[document]" == ename);      // if we got an external encoding label, use it ...
4575        } else {      if (encoding != null)
4576          handler.startExternalEntity (ename, systemId,        {
4577                                       "[document]".equals(ename));          this.encoding = ENCODING_EXTERNAL;
4578        }          setupDecoding(encoding);
4579          }          ignoreEncoding = true;
4580            
4581          // we may have been given I/O streams directly          // ... else autodetect from first bytes.
4582          if (source.getCharacterStream () != null) {        }
4583              if (source.getByteStream () != null)      else
4584                  error ("InputSource has two streams!");        {
4585              reader = source.getCharacterStream ();          detectEncoding();
4586          } else if (source.getByteStream () != null) {          ignoreEncoding = false;
4587              encoding = source.getEncoding ();        }
             if (encoding == null)  
                 stream = source.getByteStream ();  
             else try {  
                 reader = new InputStreamReader (  
                     source.getByteStream (),  
                     encoding);  
             } catch (IOException e) {  
                 stream = source.getByteStream ();  
             }  
         } else if (systemId == null)  
             error ("InputSource has no URI!");  
         scratch.setCharacterStream (null);  
         scratch.setByteStream (null);  
         scratch.setEncoding (null);  
   
         // Push the existing status.  
         pushInput (ename);  
   
         // Create a new read buffer.  
         // (Note the four-character margin)  
         readBuffer = new char [READ_BUFFER_MAX + 4];  
         readBufferPos = 0;  
         readBufferLength = 0;  
         readBufferOverflow = -1;  
         is = null;  
         line = 1;  
         column = 0;  
         currentByteCount = 0;  
   
         // If there's an explicit character stream, just  
         // ignore encoding declarations.  
         if (reader != null) {  
             sourceType = INPUT_READER;  
             this.reader = reader;  
             tryEncodingDecl (true);  
             return;  
         }  
           
         // Else we handle the conversion, and need to ensure  
         // it's done right.  
         sourceType = INPUT_STREAM;  
         if (stream != null) {  
             is = stream;  
         } else {  
             // We have to open our own stream to the URL.  
             URL url = new URL (systemId);  
   
             externalEntity = url.openConnection ();  
             externalEntity.connect ();  
             is = externalEntity.getInputStream ();  
         }  
   
         // If we get to here, there must be  
         // an InputStream available.  
         if (!is.markSupported ()) {  
             is = new BufferedInputStream (is);  
         }  
   
         // Get any external encoding label.  
         if (encoding == null && externalEntity != null) {  
             // External labels can be untrustworthy; filesystems in  
             // particular often have the wrong default for content  
             // that wasn't locally originated.  Those we autodetect.  
             if (!"file".equals (externalEntity.getURL ().getProtocol ())) {  
                 int temp;  
   
                 // application/xml;charset=something;otherAttr=...  
                 // ... with many variants on 'something'  
                 encoding = externalEntity.getContentType ();  
   
                 // MHK code (fix for Saxon 5.5.1/007):  
                 // protect against encoding==null  
                 if (encoding==null) {  
                     temp = -1;  
                 } else {  
                     temp = encoding.indexOf ("charset");  
                 }  
   
                 // RFC 2376 sez MIME text defaults to ASCII, but since the  
                 // JDK will create a MIME type out of thin air, we always  
                 // autodetect when there's no explicit charset attribute.  
                 if (temp < 0)  
                     encoding = null;    // autodetect  
                 else {  
                     // only this one attribute  
                     if ((temp = encoding.indexOf (';')) > 0)  
                         encoding = encoding.substring (0, temp);  
   
                     if ((temp = encoding.indexOf ('=', temp + 7)) > 0) {  
                         encoding = encoding.substring (temp + 1);  
   
                         // attributes can have comment fields (RFC 822)  
                         if ((temp = encoding.indexOf ('(')) > 0)  
                             encoding = encoding.substring (0, temp);  
                         // ... and values may be quoted  
                         if ((temp = encoding.indexOf ('"')) > 0)  
                             encoding = encoding.substring (temp + 1,  
                                     encoding.indexOf ('"', temp + 2));  
                         encoding.trim ();  
                     } else {  
                         handler.warn ("ignoring illegal MIME attribute: "  
                                 + encoding);  
                         encoding = null;  
                     }  
                 }  
             }  
         }  
   
         // if we got an external encoding label, use it ...  
         if (encoding != null) {  
             this.encoding = ENCODING_EXTERNAL;  
             setupDecoding (encoding);  
             ignoreEncoding = true;  
           
         // ... else autodetect from first bytes.  
         } else {  
             detectEncoding ();  
             ignoreEncoding = false;  
         }  
   
         // Read any XML or text declaration.  
         // If we autodetected, it may tell us the "real" encoding.  
         try {  
             tryEncodingDecl (ignoreEncoding);  
         } catch (UnsupportedEncodingException x) {  
             encoding = x.getMessage ();  
   
             // if we don't handle the declared encoding,  
             // try letting a JVM InputStreamReader do it  
             try {  
                 if (sourceType != INPUT_STREAM)  
                     throw x;  
   
                 is.reset ();  
                 readBufferPos = 0;  
                 readBufferLength = 0;  
                 readBufferOverflow = -1;  
                 line = 1;  
                 currentByteCount = column = 0;  
   
                 sourceType = INPUT_READER;  
                 this.reader = new InputStreamReader (is, encoding);  
                 is = null;  
   
                 tryEncodingDecl (true);  
   
             } catch (IOException e) {  
                 error ("unsupported text encoding",  
                        encoding,  
                        null);  
             }  
         }  
     }  
4588    
4589        // Read any XML or text declaration.
4590        // If we autodetected, it may tell us the "real" encoding.
4591        try
4592          {
4593            tryEncodingDecl(ignoreEncoding);
4594          }
4595        catch (UnsupportedEncodingException x)
4596          {
4597            encoding = x.getMessage();
4598    
4599            // if we don't handle the declared encoding,
4600            // try letting a JVM InputStreamReader do it
4601            try
4602              {
4603                if (sourceType != INPUT_STREAM)
4604                  {
4605                    throw x;
4606                  }
4607    
4608                is.reset();
4609                readBufferPos = 0;
4610                readBufferLength = 0;
4611                readBufferOverflow = -1;
4612                line = 1;
4613                currentByteCount = column = 0;
4614                
4615                sourceType = INPUT_READER;
4616                this.reader = new InputStreamReader(is, encoding);
4617                is = null;
4618                
4619                tryEncodingDecl(true);
4620                
4621              }
4622            catch (IOException e)
4623              {
4624                error("unsupported text encoding",
4625                      encoding,
4626                      null);
4627              }
4628          }
4629      }
4630    
4631      /**    /**
4632       * Check for an encoding declaration.  This is the second part of the     * Check for an encoding declaration.  This is the second part of the
4633       * XML encoding autodetection algorithm, relying on detectEncoding to     * XML encoding autodetection algorithm, relying on detectEncoding to
4634       * get to the point that this part can read any encoding declaration     * get to the point that this part can read any encoding declaration
4635       * in the document (using only US-ASCII characters).     * in the document (using only US-ASCII characters).
4636       *     *
4637       * <p> Because this part starts to fill parser buffers with this data,     * <p> Because this part starts to fill parser buffers with this data,
4638       * it's tricky to setup a reader so that Java's built-in decoders can be     * it's tricky to setup a reader so that Java's built-in decoders can be
4639       * used for the character encodings that aren't built in to this parser     * used for the character encodings that aren't built in to this parser
4640       * (such as EUC-JP, KOI8-R, Big5, etc).     * (such as EUC-JP, KOI8-R, Big5, etc).
4641       *     *
4642       * @return any encoding in the declaration, uppercased; or null     * @return any encoding in the declaration, uppercased; or null
4643       * @see detectEncoding     * @see detectEncoding
4644       */     */
4645      private String tryEncodingDecl (boolean ignoreEncoding)    private String tryEncodingDecl(boolean ignoreEncoding)
4646      throws SAXException, IOException      throws SAXException, IOException
4647      {    {
4648          // Read the XML/text declaration.      // Read the XML/text declaration.
4649          if (tryRead ("<?xml")) {      if (tryRead("<?xml"))
4650              if (tryWhitespace ()) {        {
4651                  if (inputStack.size () > 0) {          if (tryWhitespace())
4652                      return parseTextDecl (ignoreEncoding);            {
4653                  } else {              if (inputStack.size() > 0)
4654                      return parseXMLDecl (ignoreEncoding);                {
4655                  }                  return parseTextDecl(ignoreEncoding);
4656              } else {                }
4657                  // <?xml-stylesheet ...?> or similar              else
4658                  unread ('l');                {
4659                  unread ('m');                  return parseXMLDecl(ignoreEncoding);
4660                  unread ('x');                }
4661                  unread ('?');            }
4662                  unread ('<');          else
4663              }            {
4664          }              // <?xml-stylesheet ...?> or similar
4665          return null;              unread('l');
4666      }              unread('m');
4667                unread('x');
4668                unread('?');
4669                unread('<');
4670              }
4671          }
4672        return null;
4673      }
4674    
4675      /**    /**
4676       * Attempt to detect the encoding of an entity.     * Attempt to detect the encoding of an entity.
4677       * <p>The trick here (as suggested in the XML standard) is that     * <p>The trick here (as suggested in the XML standard) is that
4678       * any entity not in UTF-8, or in UCS-2 with a byte-order mark,     * any entity not in UTF-8, or in UCS-2 with a byte-order mark,
4679       * <b>must</b> begin with an XML declaration or an encoding     * <b>must</b> begin with an XML declaration or an encoding
4680       * declaration; we simply have to look for "&lt;?xml" in various     * declaration; we simply have to look for "&lt;?xml" in various
4681       * encodings.     * encodings.
4682       * <p>This method has no way to distinguish among 8-bit encodings.     * <p>This method has no way to distinguish among 8-bit encodings.
4683       * Instead, it sets up for UTF-8, then (possibly) revises its assumption     * Instead, it sets up for UTF-8, then (possibly) revises its assumption
4684       * later in setupDecoding ().  Any ASCII-derived 8-bit encoding     * later in setupDecoding ().  Any ASCII-derived 8-bit encoding
4685       * should work, but most will be rejected later by setupDecoding ().     * should work, but most will be rejected later by setupDecoding ().
4686       * @see #tryEncoding (byte[], byte, byte, byte, byte)     * @see #tryEncoding (byte[], byte, byte, byte, byte)
4687       * @see #tryEncoding (byte[], byte, byte)     * @see #tryEncoding (byte[], byte, byte)
4688       * @see #setupDecoding     * @see #setupDecoding
4689       */     */
4690      private void detectEncoding ()    private void detectEncoding()
4691      throws SAXException, IOException      throws SAXException, IOException
4692      {    {
4693          byte signature[] = new byte [4];      byte[] signature = new byte[4];
4694    
4695          // Read the first four bytes for      // Read the first four bytes for
4696          // autodetection.      // autodetection.
4697          is.mark (4);      is.mark(4);
4698          is.read (signature);      is.read(signature);
4699          is.reset ();      is.reset();
4700    
4701          //      //
4702          // FIRST:  four byte encodings (who uses these?)      // FIRST:  four byte encodings (who uses these?)
4703          //      //
4704          if (tryEncoding (signature, (byte) 0x00, (byte) 0x00,      if (tryEncoding(signature, (byte) 0x00, (byte) 0x00,
4705                            (byte) 0x00, (byte) 0x3c)) {                      (byte) 0x00, (byte) 0x3c))
4706              // UCS-4 must begin with "<?xml"        {
4707              // 0x00 0x00 0x00 0x3c: UCS-4, big-endian (1234)          // UCS-4 must begin with "<?xml"
4708              // "UTF-32BE"          // 0x00 0x00 0x00 0x3c: UCS-4, big-endian (1234)
4709              encoding = ENCODING_UCS_4_1234;          // "UTF-32BE"
4710            encoding = ENCODING_UCS_4_1234;
4711          } else if (tryEncoding (signature, (byte) 0x3c, (byte) 0x00,        }
4712                                   (byte) 0x00, (byte) 0x00)) {      else if (tryEncoding(signature, (byte) 0x3c, (byte) 0x00,
4713              // 0x3c 0x00 0x00 0x00: UCS-4, little-endian (4321)                           (byte) 0x00, (byte) 0x00))
4714              // "UTF-32LE"        {
4715              encoding = ENCODING_UCS_4_4321;          // 0x3c 0x00 0x00 0x00: UCS-4, little-endian (4321)
4716            // "UTF-32LE"
4717          } else if (tryEncoding (signature, (byte) 0x00, (byte) 0x00,          encoding = ENCODING_UCS_4_4321;
4718                                   (byte) 0x3c, (byte) 0x00)) {        }
4719              // 0x00 0x00 0x3c 0x00: UCS-4, unusual (2143)      else if (tryEncoding(signature, (byte) 0x00, (byte) 0x00,
4720              encoding = ENCODING_UCS_4_2143;                           (byte) 0x3c, (byte) 0x00))
4721          {
4722          } else if (tryEncoding (signature, (byte) 0x00, (byte) 0x3c,          // 0x00 0x00 0x3c 0x00: UCS-4, unusual (2143)
4723                                   (byte) 0x00, (byte) 0x00)) {          encoding = ENCODING_UCS_4_2143;
4724              // 0x00 0x3c 0x00 0x00: UCS-4, unusual (3421)        }
4725              encoding = ENCODING_UCS_4_3412;      else if (tryEncoding(signature, (byte) 0x00, (byte) 0x3c,
4726                             (byte) 0x00, (byte) 0x00))
4727              // 00 00 fe ff UCS_4_1234 (with BOM)        {
4728              // ff fe 00 00 UCS_4_4321 (with BOM)          // 0x00 0x3c 0x00 0x00: UCS-4, unusual (3421)
4729          }          encoding = ENCODING_UCS_4_3412;
   
         //  
         // SECOND:  two byte encodings  
         // note ... with 1/14/2000 errata the XML spec identifies some  
         // more "broken UTF-16" autodetection cases, with no XML decl,  
         // which we don't handle here (that's legal too).  
         //  
         else if (tryEncoding (signature, (byte) 0xfe, (byte) 0xff)) {  
             // UCS-2 with a byte-order marker. (UTF-16)  
             // 0xfe 0xff: UCS-2, big-endian (12)  
             encoding = ENCODING_UCS_2_12;  
             is.read (); is.read ();  
   
         } else if (tryEncoding (signature, (byte) 0xff, (byte) 0xfe)) {  
             // UCS-2 with a byte-order marker. (UTF-16)  
             // 0xff 0xfe: UCS-2, little-endian (21)  
             encoding = ENCODING_UCS_2_21;  
             is.read (); is.read ();  
   
         } else if (tryEncoding (signature, (byte) 0x00, (byte) 0x3c,  
                                  (byte) 0x00, (byte) 0x3f)) {  
             // UTF-16BE (otherwise, malformed UTF-16)  
             // 0x00 0x3c 0x00 0x3f: UCS-2, big-endian, no byte-order mark  
             encoding = ENCODING_UCS_2_12;  
             error ("no byte-order mark for UCS-2 entity");  
   
         } else if (tryEncoding (signature, (byte) 0x3c, (byte) 0x00,  
                                  (byte) 0x3f, (byte) 0x00)) {  
             // UTF-16LE (otherwise, malformed UTF-16)  
             // 0x3c 0x00 0x3f 0x00: UCS-2, little-endian, no byte-order mark  
             encoding = ENCODING_UCS_2_21;  
             error ("no byte-order mark for UCS-2 entity");  
         }  
   
         //  
         // THIRD:  ASCII-derived encodings, fixed and variable lengths  
         //  
         else if (tryEncoding (signature, (byte) 0x3c, (byte) 0x3f,  
                                (byte) 0x78, (byte) 0x6d)) {  
             // ASCII derived  
             // 0x3c 0x3f 0x78 0x6d: UTF-8 or other 8-bit markup (read ENCODING)  
             encoding = ENCODING_UTF_8;  
             prefetchASCIIEncodingDecl ();  
   
         } else if (signature [0] == (byte) 0xef  
                 && signature [1] == (byte) 0xbb  
                 && signature [2] == (byte) 0xbf) {  
             // 0xef 0xbb 0xbf: UTF-8 BOM (not part of document text)  
             // this un-needed notion slipped into XML 2nd ed through a  
             // "non-normative" erratum; now required by MSFT and UDDI,  
             // and E22 made it normative.  
             encoding = ENCODING_UTF_8;  
             is.read (); is.read (); is.read ();  
   
         } else {  
             // 4c 6f a7 94 ... we don't understand EBCDIC flavors  
             // ... but we COULD at least kick in some fixed code page  
   
             // (default) UTF-8 without encoding/XML declaration  
             encoding = ENCODING_UTF_8;  
         }  
     }  
4730    
4731            // 00 00 fe ff UCS_4_1234 (with BOM)
4732            // ff fe 00 00 UCS_4_4321 (with BOM)
4733          }
4734    
4735      /**      //
4736       * Check for a four-byte signature.      // SECOND:  two byte encodings
4737       * <p>Utility routine for detectEncoding ().      // note ... with 1/14/2000 errata the XML spec identifies some
4738       * <p>Always looks for some part of "<?XML" in a specific encoding.      // more "broken UTF-16" autodetection cases, with no XML decl,
4739       * @param sig The first four bytes read.      // which we don't handle here (that's legal too).
4740       * @param b1 The first byte of the signature      //
4741       * @param b2 The second byte of the signature      else if (tryEncoding(signature, (byte) 0xfe, (byte) 0xff))
4742       * @param b3 The third byte of the signature        {
4743       * @param b4 The fourth byte of the signature          // UCS-2 with a byte-order marker. (UTF-16)
4744       * @see #detectEncoding          // 0xfe 0xff: UCS-2, big-endian (12)
4745       */          encoding = ENCODING_UCS_2_12;
4746      private static boolean tryEncoding (          is.read(); is.read();
4747          byte sig[], byte b1, byte b2, byte b3, byte b4)        }
4748      {      else if (tryEncoding(signature, (byte) 0xff, (byte) 0xfe))
4749          return (sig [0] == b1 && sig [1] == b2        {
4750                  && sig [2] == b3 && sig [3] == b4);          // UCS-2 with a byte-order marker. (UTF-16)
4751      }          // 0xff 0xfe: UCS-2, little-endian (21)
4752            encoding = ENCODING_UCS_2_21;
4753            is.read(); is.read();
4754          }
4755        else if (tryEncoding(signature, (byte) 0x00, (byte) 0x3c,
4756                             (byte) 0x00, (byte) 0x3f))
4757          {
4758            // UTF-16BE (otherwise, malformed UTF-16)
4759            // 0x00 0x3c 0x00 0x3f: UCS-2, big-endian, no byte-order mark
4760            encoding = ENCODING_UCS_2_12;
4761            error("no byte-order mark for UCS-2 entity");
4762          }
4763        else if (tryEncoding(signature, (byte) 0x3c, (byte) 0x00,
4764                             (byte) 0x3f, (byte) 0x00))
4765          {
4766            // UTF-16LE (otherwise, malformed UTF-16)
4767            // 0x3c 0x00 0x3f 0x00: UCS-2, little-endian, no byte-order mark
4768            encoding = ENCODING_UCS_2_21;
4769            error("no byte-order mark for UCS-2 entity");
4770          }
4771    
4772        //
4773        // THIRD:  ASCII-derived encodings, fixed and variable lengths
4774        //
4775        else if (tryEncoding(signature, (byte) 0x3c, (byte) 0x3f,
4776                             (byte) 0x78, (byte) 0x6d))
4777          {
4778            // ASCII derived
4779            // 0x3c 0x3f 0x78 0x6d: UTF-8 or other 8-bit markup (read ENCODING)
4780            encoding = ENCODING_UTF_8;
4781            prefetchASCIIEncodingDecl();
4782          }
4783        else if (signature[0] == (byte) 0xef
4784                 && signature[1] == (byte) 0xbb
4785                 && signature[2] == (byte) 0xbf)
4786          {
4787            // 0xef 0xbb 0xbf: UTF-8 BOM (not part of document text)
4788            // this un-needed notion slipped into XML 2nd ed through a
4789            // "non-normative" erratum; now required by MSFT and UDDI,
4790            // and E22 made it normative.
4791            encoding = ENCODING_UTF_8;
4792            is.read(); is.read(); is.read();
4793          }
4794        else
4795          {
4796            // 4c 6f a7 94 ... we don't understand EBCDIC flavors
4797            // ... but we COULD at least kick in some fixed code page
4798            
4799            // (default) UTF-8 without encoding/XML declaration
4800            encoding = ENCODING_UTF_8;
4801          }
4802      }
4803    
4804      /**    /**
4805       * Check for a two-byte signature.     * Check for a four-byte signature.
4806       * <p>Looks for a UCS-2 byte-order mark.     * <p>Utility routine for detectEncoding ().
4807       * <p>Utility routine for detectEncoding ().     * <p>Always looks for some part of "<?XML" in a specific encoding.
4808       * @param sig The first four bytes read.     * @param sig The first four bytes read.
4809       * @param b1 The first byte of the signature     * @param b1 The first byte of the signature
4810       * @param b2 The second byte of the signature     * @param b2 The second byte of the signature
4811       * @see #detectEncoding     * @param b3 The third byte of the signature
4812       */     * @param b4 The fourth byte of the signature
4813      private static boolean tryEncoding (byte sig[], byte b1, byte b2)     * @see #detectEncoding
4814      {     */
4815          return ((sig [0] == b1) && (sig [1] == b2));    private static boolean tryEncoding(byte[] sig, byte b1, byte b2,
4816      }                                       byte b3, byte b4)
4817      {
4818        return (sig[0] == b1 && sig[1] == b2
4819                && sig[2] == b3 && sig[3] == b4);
4820      }
4821    
4822      /**
4823       * Check for a two-byte signature.
4824       * <p>Looks for a UCS-2 byte-order mark.
4825       * <p>Utility routine for detectEncoding ().
4826       * @param sig The first four bytes read.
4827       * @param b1 The first byte of the signature
4828       * @param b2 The second byte of the signature
4829       * @see #detectEncoding
4830       */
4831      private static boolean tryEncoding(byte[] sig, byte b1, byte b2)
4832      {
4833        return ((sig[0] == b1) && (sig[1] == b2));
4834      }
4835    
4836      /**    /**
4837       * This method pushes a string back onto input.     * This method pushes a string back onto input.
4838       * <p>It is useful either as the expansion of an internal entity,     * <p>It is useful either as the expansion of an internal entity,
4839       * or for backtracking during the parse.     * or for backtracking during the parse.
4840       * <p>Call pushCharArray () to do the actual work.     * <p>Call pushCharArray () to do the actual work.
4841       * @param s The string to push back onto input.     * @param s The string to push back onto input.
4842       * @see #pushCharArray     * @see #pushCharArray
4843       */     */
4844      private void pushString (String ename, String s)    private void pushString(String ename, String s)
4845      throws SAXException      throws SAXException
4846      {    {
4847          char ch[] = s.toCharArray ();      char[] ch = s.toCharArray();
4848          pushCharArray (ename, ch, 0, ch.length);      pushCharArray(ename, ch, 0, ch.length);
4849      }    }
   
4850    
4851      /**    /**
4852       * Push a new internal input source.     * Push a new internal input source.
4853       * <p>This method is useful for expanding an internal entity,     * <p>This method is useful for expanding an internal entity,
4854       * or for unreading a string of characters.  It creates a new     * or for unreading a string of characters.  It creates a new
4855       * readBuffer containing the characters in the array, instead     * readBuffer containing the characters in the array, instead
4856       * of characters converted from an input byte stream.     * of characters converted from an input byte stream.
4857       * @param ch The char array to push.     * @param ch The char array to push.
4858       * @see #pushString     * @see #pushString
4859       * @see #pushURL     * @see #pushURL
4860       * @see #readBuffer     * @see #readBuffer
4861       * @see #sourceType     * @see #sourceType
4862       * @see #pushInput     * @see #pushInput
4863       */     */
4864      private void pushCharArray (String ename, char ch[], int start, int length)    private void pushCharArray(String ename, char[] ch, int start, int length)
4865      throws SAXException      throws SAXException
4866      {    {
4867          // Push the existing status      // Push the existing status
4868          pushInput (ename);      pushInput(ename);
4869          if (ename != null && doReport) {      if (ename != null && doReport)
4870              dataBufferFlush ();        {
4871              handler.startInternalEntity (ename);          dataBufferFlush();
4872          }          handler.startInternalEntity(ename);
4873          sourceType = INPUT_INTERNAL;        }
4874          readBuffer = ch;      sourceType = INPUT_INTERNAL;
4875          readBufferPos = start;      readBuffer = ch;
4876          readBufferLength = length;      readBufferPos = start;
4877          readBufferOverflow = -1;      readBufferLength = length;
4878      }      readBufferOverflow = -1;
4879      }
4880    
4881      /**    /**
4882       * Save the current input source onto the stack.     * Save the current input source onto the stack.
4883       * <p>This method saves all of the global variables associated with     * <p>This method saves all of the global variables associated with
4884       * the current input source, so that they can be restored when a new     * the current input source, so that they can be restored when a new
4885       * input source has finished.  It also tests for entity recursion.     * input source has finished.  It also tests for entity recursion.
4886       * <p>The method saves the following global variables onto a stack     * <p>The method saves the following global variables onto a stack
4887       * using a fixed-length array:     * using a fixed-length array:
4888       * <ol>     * <ol>
4889       * <li>sourceType     * <li>sourceType
4890       * <li>externalEntity     * <li>externalEntity
4891       * <li>readBuffer     * <li>readBuffer
4892       * <li>readBufferPos     * <li>readBufferPos
4893       * <li>readBufferLength     * <li>readBufferLength
4894       * <li>line     * <li>line
4895       * <li>encoding     * <li>encoding
4896       * </ol>     * </ol>
4897       * @param ename The name of the entity (if any) causing the new input.     * @param ename The name of the entity (if any) causing the new input.
4898       * @see #popInput     * @see #popInput
4899       * @see #sourceType     * @see #sourceType
4900       * @see #externalEntity     * @see #externalEntity
4901       * @see #readBuffer     * @see #readBuffer
4902       * @see #readBufferPos     * @see #readBufferPos
4903       * @see #readBufferLength     * @see #readBufferLength
4904       * @see #line     * @see #line
4905       * @see #encoding     * @see #encoding
4906       */     */
4907      private void pushInput (String ename)    private void pushInput(String ename)
4908      throws SAXException      throws SAXException
4909      {    {
4910          // Check for entity recursion.      // Check for entity recursion.
4911          if (ename != null) {      if (ename != null)
4912              Enumeration entities = entityStack.elements ();        {
4913              while (entities.hasMoreElements ()) {          Iterator entities = entityStack.iterator();
4914                  String e = (String) entities.nextElement ();          while (entities.hasNext())
4915                  if (e != null && e == ename) {            {
4916                      error ("recursive reference to entity", ename, null);              String e = (String) entities.next();
4917                  }              if (e != null && e == ename)
4918              }                {
4919          }                  error("recursive reference to entity", ename, null);
4920          entityStack.push (ename);                }
4921              }
4922          // Don't bother if there is no current input.        }
4923          if (sourceType == INPUT_NONE) {      entityStack.addLast(ename);
4924              return;      
4925          }      // Don't bother if there is no current input.
4926        if (sourceType == INPUT_NONE)
4927          // Set up a snapshot of the current        {
4928          // input source.          return;
4929          Object input[] = new Object [12];        }
4930        
4931          input [0] = new Integer (sourceType);      // Set up a snapshot of the current
4932          input [1] = externalEntity;      // input source.
4933          input [2] = readBuffer;      Input input = new Input();
4934          input [3] = new Integer (readBufferPos);  
4935          input [4] = new Integer (readBufferLength);      input.sourceType = sourceType;
4936          input [5] = new Integer (line);      input.externalEntity = externalEntity;
4937          input [6] = new Integer (encoding);      input.readBuffer = readBuffer;
4938          input [7] = new Integer (readBufferOverflow);      input.readBufferPos = readBufferPos;
4939          input [8] = is;      input.readBufferLength = readBufferLength;
4940          input [9] = new Integer (currentByteCount);      input.line = line;
4941          input [10] = new Integer (column);      input.encoding = encoding;
4942          input [11] = reader;      input.readBufferOverflow = readBufferOverflow;
4943        input.is = is;
4944          // Push it onto the stack.      input.currentByteCount = currentByteCount;
4945          inputStack.push (input);      input.column = column;
4946      }      input.reader = reader;
4947        
4948        // Push it onto the stack.
4949        inputStack.addLast(input);
4950      }
4951    
4952      /**    /**
4953       * Restore a previous input source.     * Restore a previous input source.
4954       * <p>This method restores all of the global variables associated with     * <p>This method restores all of the global variables associated with
4955       * the current input source.     * the current input source.
4956       * @exception java.io.EOFException     * @exception java.io.EOFException
4957       *    If there are no more entries on the input stack.     *    If there are no more entries on the input stack.
4958       * @see #pushInput     * @see #pushInput
4959       * @see #sourceType     * @see #sourceType
4960       * @see #externalEntity     * @see #externalEntity
4961       * @see #readBuffer     * @see #readBuffer
4962       * @see #readBufferPos     * @see #readBufferPos
4963       * @see #readBufferLength     * @see #readBufferLength
4964       * @see #line     * @see #line
4965       * @see #encoding     * @see #encoding
4966       */     */
4967      private void popInput ()    private void popInput()
4968      throws SAXException, IOException      throws SAXException, IOException
4969      {    {
4970          String ename = (String) entityStack.pop ();      String ename = (String) entityStack.removeLast();
   
         if (ename != null && doReport)  
             dataBufferFlush ();  
         switch (sourceType) {  
         case INPUT_STREAM:  
             handler.endExternalEntity (ename);  
             is.close ();  
             break;  
         case INPUT_READER:  
             handler.endExternalEntity (ename);  
             reader.close ();  
             break;  
         case INPUT_INTERNAL:  
             if (ename != null && doReport)  
                 handler.endInternalEntity (ename);  
             break;  
         }  
   
         // Throw an EOFException if there  
         // is nothing else to pop.  
         if (inputStack.isEmpty ()) {  
             throw new EOFException ("no more input");  
         }  
   
         Object input [] = (Object[]) inputStack.pop ();  
   
         sourceType = ((Integer) input [0]).intValue ();  
         externalEntity = (URLConnection) input [1];  
         readBuffer = (char[]) input [2];  
         readBufferPos = ((Integer) input [3]).intValue ();  
         readBufferLength = ((Integer) input [4]).intValue ();  
         line = ((Integer) input [5]).intValue ();  
         encoding = ((Integer) input [6]).intValue ();  
         readBufferOverflow = ((Integer) input [7]).intValue ();  
         is = (InputStream) input [8];  
         currentByteCount = ((Integer) input [9]).intValue ();  
         column = ((Integer) input [10]).intValue ();  
         reader = (Reader) input [11];  
     }  
4971    
4972        if (ename != null && doReport)
4973          {
4974            dataBufferFlush();
4975          }
4976        switch (sourceType)
4977          {
4978          case INPUT_STREAM:
4979            handler.endExternalEntity(ename);
4980            is.close();
4981            break;
4982          case INPUT_READER:
4983            handler.endExternalEntity(ename);
4984            reader.close();
4985            break;
4986          case INPUT_INTERNAL:
4987            if (ename != null && doReport)
4988              {
4989                handler.endInternalEntity(ename);
4990              }
4991            break;
4992          }
4993    
4994      /**      // Throw an EOFException if there
4995       * Return true if we can read the expected character.      // is nothing else to pop.
4996       * <p>Note that the character will be removed from the input stream      if (inputStack.isEmpty())
4997       * on success, but will be put back on failure.  Do not attempt to        {
4998       * read the character again if the method succeeds.          throw new EOFException("no more input");
4999       * @param delim The character that should appear next.  For a        }
      *        insensitive match, you must supply this in upper-case.  
      * @return true if the character was successfully read, or false if  
      *   it was not.  
      * @see #tryRead (String)  
      */  
     private boolean tryRead (char delim)  
     throws SAXException, IOException  
     {  
         char c;  
5000    
5001          // Read the character      Input input = (Input) inputStack.removeLast();
         c = readCh ();  
5002    
5003          // Test for a match, and push the character      sourceType = input.sourceType;
5004          // back if the match fails.      externalEntity = input.externalEntity;
5005          if (c == delim) {      readBuffer = input.readBuffer;
5006              return true;      readBufferPos = input.readBufferPos;
5007          } else {      readBufferLength = input.readBufferLength;
5008              unread (c);      line = input.line;
5009              return false;      encoding = input.encoding;
5010          }      readBufferOverflow = input.readBufferOverflow;
5011      }      is = input.is;
5012        currentByteCount = input.currentByteCount;
5013        column = input.column;
5014        reader = input.reader;
5015      }
5016      
5017      /**
5018       * Return true if we can read the expected character.
5019       * <p>Note that the character will be removed from the input stream
5020       * on success, but will be put back on failure.  Do not attempt to
5021       * read the character again if the method succeeds.
5022       * @param delim The character that should appear next.  For a
5023       *        insensitive match, you must supply this in upper-case.
5024       * @return true if the character was successfully read, or false if
5025       *   it was not.
5026       * @see #tryRead (String)
5027       */
5028      private boolean tryRead(char delim)
5029        throws SAXException, IOException
5030      {
5031        char c;
5032        
5033        // Read the character
5034        c = readCh();
5035    
5036        // Test for a match, and push the character
5037        // back if the match fails.
5038        if (c == delim)
5039          {
5040            return true;
5041          }
5042        else
5043          {
5044            unread(c);
5045            return false;
5046          }
5047      }
5048    
5049      /**    /**
5050       * Return true if we can read the expected string.     * Return true if we can read the expected string.
5051       * <p>This is simply a convenience method.     * <p>This is simply a convenience method.
5052       * <p>Note that the string will be removed from the input stream     * <p>Note that the string will be removed from the input stream
5053       * on success, but will be put back on failure.  Do not attempt to     * on success, but will be put back on failure.  Do not attempt to
5054       * read the string again if the method succeeds.     * read the string again if the method succeeds.
5055       * <p>This method will push back a character rather than an     * <p>This method will push back a character rather than an
5056       * array whenever possible (probably the majority of cases).     * array whenever possible (probably the majority of cases).
5057       * @param delim The string that should appear next.     * @param delim The string that should appear next.
5058       * @return true if the string was successfully read, or false if     * @return true if the string was successfully read, or false if
5059       *   it was not.     *   it was not.
5060       * @see #tryRead (char)     * @see #tryRead (char)
5061       */     */
5062      private boolean tryRead (String delim)    private boolean tryRead(String delim)
5063      throws SAXException, IOException      throws SAXException, IOException
5064      {    {
5065          return tryRead (delim.toCharArray ());      return tryRead(delim.toCharArray());
5066      }    }
5067    
5068      private boolean tryRead (char ch [])    private boolean tryRead(char[] ch)
5069      throws SAXException, IOException      throws SAXException, IOException
5070      {    {
5071          char c;      char c;
   
         // Compare the input, character-  
         // by character.  
   
         for (int i = 0; i < ch.length; i++) {  
             c = readCh ();  
             if (c != ch [i]) {  
                 unread (c);  
                 if (i != 0) {  
                     unread (ch, i);  
                 }  
                 return false;  
             }  
         }  
         return true;  
     }  
   
5072    
5073        // Compare the input, character-
5074        // by character.
5075        
5076        for (int i = 0; i < ch.length; i++)
5077          {
5078            c = readCh();
5079            if (c != ch[i])
5080              {
5081                unread(c);
5082                if (i != 0)
5083                  {
5084                    unread(ch, i);
5085                  }
5086                return false;
5087              }
5088          }
5089        return true;
5090      }
5091    
5092      /**    /**
5093       * Return true if we can read some whitespace.     * Return true if we can read some whitespace.
5094       * <p>This is simply a convenience method.     * <p>This is simply a convenience method.
5095       * <p>This method will push back a character rather than an     * <p>This method will push back a character rather than an
5096       * array whenever possible (probably the majority of cases).     * array whenever possible (probably the majority of cases).
5097       * @return true if whitespace was found.     * @return true if whitespace was found.
5098       */     */
5099      private boolean tryWhitespace ()    private boolean tryWhitespace()
5100      throws SAXException, IOException      throws SAXException, IOException
5101      {    {
5102          char c;      char c;
5103          c = readCh ();      c = readCh();
5104          if (isWhitespace (c)) {      if (isWhitespace(c))
5105              skipWhitespace ();        {
5106              return true;          skipWhitespace();
5107          } else {          return true;
5108              unread (c);        }
5109              return false;      else
5110          }        {
5111      }          unread(c);
5112            return false;
5113          }
5114      /**    }
5115       * Read all data until we find the specified string.    
5116       * This is useful for scanning CDATA sections and PIs.    /**
5117       * <p>This is inefficient right now, since it calls tryRead ()     * Read all data until we find the specified string.
5118       * for every character.     * This is useful for scanning CDATA sections and PIs.
5119       * @param delim The string delimiter     * <p>This is inefficient right now, since it calls tryRead ()
5120       * @see #tryRead (String, boolean)     * for every character.
5121       * @see #readCh     * @param delim The string delimiter
5122       */     * @see #tryRead (String, boolean)
5123      private void parseUntil (String delim)     * @see #readCh
5124       */
5125      private void parseUntil(String delim)
5126      throws SAXException, IOException      throws SAXException, IOException
5127      {    {
5128          parseUntil (delim.toCharArray ());      parseUntil(delim.toCharArray());
5129      }    }
5130    
5131      private void parseUntil (char delim [])    private void parseUntil(char[] delim)
5132      throws SAXException, IOException      throws SAXException, IOException
5133      {    {
5134          char c;      char c;
5135          int startLine = line;      int startLine = line;
5136        
5137          try {      try
5138              while (!tryRead (delim)) {        {
5139                  c = readCh ();          while (!tryRead(delim))
5140                  dataBufferAppend (c);            {
5141              }              c = readCh();
5142          } catch (EOFException e) {              dataBufferAppend(c);
5143              error ("end of input while looking for delimiter "            }
5144                  + "(started on line " + startLine        }
5145                  + ')', null, new String (delim));      catch (EOFException e)
5146          }        {
5147      }          error("end of input while looking for delimiter "
5148                  + "(started on line " + startLine
5149                  + ')', null, new String(delim));
5150      //////////////////////////////////////////////////////////////////////        }
5151      // Low-level I/O.    }
     //////////////////////////////////////////////////////////////////////  
   
5152    
5153      /**    //////////////////////////////////////////////////////////////////////
5154       * Prefetch US-ASCII XML/text decl from input stream into read buffer.    // Low-level I/O.
5155       * Doesn't buffer more than absolutely needed, so that when an encoding    //////////////////////////////////////////////////////////////////////
5156       * decl says we need to create an InputStreamReader, we can discard our    
5157       * buffer and reset().  Caller knows the first chars of the decl exist    /**
5158       * in the input stream.     * Prefetch US-ASCII XML/text decl from input stream into read buffer.
5159       */     * Doesn't buffer more than absolutely needed, so that when an encoding
5160      private void prefetchASCIIEncodingDecl ()     * decl says we need to create an InputStreamReader, we can discard our
5161       * buffer and reset().  Caller knows the first chars of the decl exist
5162       * in the input stream.
5163       */
5164      private void prefetchASCIIEncodingDecl()
5165      throws SAXException, IOException      throws SAXException, IOException
5166      {    {
5167          int ch;      int ch;
5168          readBufferPos = readBufferLength = 0;      readBufferPos = readBufferLength = 0;
5169        
5170          is.mark (readBuffer.length);      is.mark(readBuffer.length);
5171          while (true) {      while (true)
5172              ch = is.read ();        {
5173              readBuffer [readBufferLength++] = (char) ch;          ch = is.read();
5174              switch (ch) {          readBuffer[readBufferLength++] = (char) ch;
5175                case (int) '>':          switch (ch)
5176                  return;            {
5177                case -1:            case (int) '>':
5178                  error ("file ends before end of XML or encoding declaration.",              return;
5179                         null, "?>");            case -1:
5180              }              error("file ends before end of XML or encoding declaration.",
5181              if (readBuffer.length == readBufferLength)                    null, "?>");
5182                  error ("unfinished XML or encoding declaration");            }
5183          }          if (readBuffer.length == readBufferLength)
5184      }            {
5185                error("unfinished XML or encoding declaration");
5186              }
5187          }
5188      }
5189    
5190      /**    /**
5191       * Read a chunk of data from an external input source.     * Read a chunk of data from an external input source.
5192       * <p>This is simply a front-end that fills the rawReadBuffer     * <p>This is simply a front-end that fills the rawReadBuffer
5193       * with bytes, then calls the appropriate encoding handler.     * with bytes, then calls the appropriate encoding handler.
5194       * @see #encoding     * @see #encoding
5195       * @see #rawReadBuffer     * @see #rawReadBuffer
5196       * @see #readBuffer     * @see #readBuffer
5197       * @see #filterCR     * @see #filterCR
5198       * @see #copyUtf8ReadBuffer     * @see #copyUtf8ReadBuffer
5199       * @see #copyIso8859_1ReadBuffer     * @see #copyIso8859_1ReadBuffer
5200       * @see #copyUcs_2ReadBuffer     * @see #copyUcs_2ReadBuffer
5201       * @see #copyUcs_4ReadBuffer     * @see #copyUcs_4ReadBuffer
5202       */     */
5203      private void readDataChunk ()    private void readDataChunk()
5204      throws SAXException, IOException      throws SAXException, IOException
5205      {    {
5206          int count;      int count;
5207        
5208          // See if we have any overflow (filterCR sets for CR at end)      // See if we have any overflow (filterCR sets for CR at end)
5209          if (readBufferOverflow > -1) {      if (readBufferOverflow > -1)
5210              readBuffer [0] = (char) readBufferOverflow;        {
5211              readBufferOverflow = -1;          readBuffer[0] = (char) readBufferOverflow;
5212              readBufferPos = 1;          readBufferOverflow = -1;
5213              sawCR = true;          readBufferPos = 1;
5214          } else {          sawCR = true;
5215              readBufferPos = 0;        }
5216              sawCR = false;      else
5217          }        {
5218            readBufferPos = 0;
5219          // input from a character stream.          sawCR = false;
5220          if (sourceType == INPUT_READER) {        }
             count = reader.read (readBuffer,  
                             readBufferPos, READ_BUFFER_MAX - readBufferPos);  
             if (count < 0)  
                 readBufferLength = readBufferPos;  
             else  
                 readBufferLength = readBufferPos + count;  
             if (readBufferLength > 0)  
                 filterCR (count >= 0);  
             sawCR = false;  
             return;  
         }  
   
         // Read as many bytes as possible into the raw buffer.  
         count = is.read (rawReadBuffer, 0, READ_BUFFER_MAX);  
   
         // Dispatch to an encoding-specific reader method to populate  
         // the readBuffer.  In most parser speed profiles, these routines  
         // show up at the top of the CPU usage chart.  
         if (count > 0) {  
             switch (encoding) {  
               // one byte builtins  
               case ENCODING_ASCII:  
                 copyIso8859_1ReadBuffer (count, (char) 0x0080);  
                 break;  
               case ENCODING_UTF_8:  
                 copyUtf8ReadBuffer (count);  
                 break;  
               case ENCODING_ISO_8859_1:  
                 copyIso8859_1ReadBuffer (count, (char) 0);  
                 break;  
   
               // two byte builtins  
               case ENCODING_UCS_2_12:  
                 copyUcs2ReadBuffer (count, 8, 0);  
                 break;  
               case ENCODING_UCS_2_21:  
                 copyUcs2ReadBuffer (count, 0, 8);  
                 break;  
   
               // four byte builtins  
               case ENCODING_UCS_4_1234:  
                 copyUcs4ReadBuffer (count, 24, 16, 8, 0);  
                 break;  
               case ENCODING_UCS_4_4321:  
                 copyUcs4ReadBuffer (count, 0, 8, 16, 24);  
                 break;  
               case ENCODING_UCS_4_2143:  
                 copyUcs4ReadBuffer (count, 16, 24, 0, 8);  
                 break;  
               case ENCODING_UCS_4_3412:  
                 copyUcs4ReadBuffer (count, 8, 0, 24, 16);  
                 break;  
             }  
         } else  
             readBufferLength = readBufferPos;  
   
         readBufferPos = 0;  
   
         // Filter out all carriage returns if we've seen any  
         // (including any saved from a previous read)  
         if (sawCR) {  
             filterCR (count >= 0);  
             sawCR = false;  
   
             // must actively report EOF, lest some CRs get lost.  
             if (readBufferLength == 0 && count >= 0)  
                 readDataChunk ();  
         }  
5221    
5222          if (count > 0)      // input from a character stream.
5223              currentByteCount += count;      if (sourceType == INPUT_READER)
5224      }        {
5225            count = reader.read(readBuffer,
5226                                readBufferPos, READ_BUFFER_MAX - readBufferPos);
5227            if (count < 0)
5228              {
5229                readBufferLength = readBufferPos;
5230              }
5231            else
5232              {
5233                readBufferLength = readBufferPos + count;
5234              }
5235            if (readBufferLength > 0)
5236              {
5237                filterCR(count >= 0);
5238              }
5239            sawCR = false;
5240            return;
5241          }
5242        
5243        // Read as many bytes as possible into the raw buffer.
5244        count = is.read(rawReadBuffer, 0, READ_BUFFER_MAX);
5245    
5246        // Dispatch to an encoding-specific reader method to populate
5247        // the readBuffer.  In most parser speed profiles, these routines
5248        // show up at the top of the CPU usage chart.
5249        if (count > 0)
5250          {
5251            switch (encoding)
5252              {
5253                // one byte builtins
5254              case ENCODING_ASCII:
5255                copyIso8859_1ReadBuffer(count, (char) 0x0080);
5256                break;
5257              case ENCODING_UTF_8:
5258                copyUtf8ReadBuffer(count);
5259                break;
5260              case ENCODING_ISO_8859_1:
5261                copyIso8859_1ReadBuffer(count, (char) 0);
5262                break;
5263    
5264      /**              // two byte builtins
5265       * Filter carriage returns in the read buffer.            case ENCODING_UCS_2_12:
5266       * CRLF becomes LF; CR becomes LF.              copyUcs2ReadBuffer(count, 8, 0);
5267       * @param moreData true iff more data might come from the same source              break;
5268       * @see #readDataChunk            case ENCODING_UCS_2_21:
5269       * @see #readBuffer              copyUcs2ReadBuffer(count, 0, 8);
5270       * @see #readBufferOverflow              break;
5271       */              
5272      private void filterCR (boolean moreData)              // four byte builtins
5273      {            case ENCODING_UCS_4_1234:
5274          int i, j;              copyUcs4ReadBuffer(count, 24, 16, 8, 0);
5275                break;
5276              case ENCODING_UCS_4_4321:
5277                copyUcs4ReadBuffer(count, 0, 8, 16, 24);
5278                break;
5279              case ENCODING_UCS_4_2143:
5280                copyUcs4ReadBuffer(count, 16, 24, 0, 8);
5281                break;
5282              case ENCODING_UCS_4_3412:
5283                copyUcs4ReadBuffer(count, 8, 0, 24, 16);
5284                break;
5285              }
5286          }
5287        else
5288          {
5289            readBufferLength = readBufferPos;
5290          }
5291    
5292          readBufferOverflow = -1;      readBufferPos = 0;
5293        
5294        // Filter out all carriage returns if we've seen any
5295        // (including any saved from a previous read)
5296        if (sawCR)
5297          {
5298            filterCR(count >= 0);
5299            sawCR = false;
5300            
5301            // must actively report EOF, lest some CRs get lost.
5302            if (readBufferLength == 0 && count >= 0)
5303              {
5304                readDataChunk();
5305              }
5306          }
5307        
5308        if (count > 0)
5309          {
5310            currentByteCount += count;
5311          }
5312      }
5313      
5314      /**
5315       * Filter carriage returns in the read buffer.
5316       * CRLF becomes LF; CR becomes LF.
5317       * @param moreData true iff more data might come from the same source
5318       * @see #readDataChunk
5319       * @see #readBuffer
5320       * @see #readBufferOverflow
5321       */
5322      private void filterCR(boolean moreData)
5323      {
5324        int i, j;
5325    
5326        readBufferOverflow = -1;
5327        
5328  loop:  loop:
5329          for (i = j = readBufferPos; j < readBufferLength; i++, j++) {      for (i = j = readBufferPos; j < readBufferLength; i++, j++)
5330              switch (readBuffer [j]) {        {
5331              case '\r':          switch (readBuffer[j])
5332                  if (j == readBufferLength - 1) {            {
5333                      if (moreData) {            case '\r':
5334                          readBufferOverflow = '\r';              if (j == readBufferLength - 1)
5335                          readBufferLength--;                {
5336                      } else      // CR at end of buffer                  if (moreData)
5337                          readBuffer [i++] = '\n';                    {
5338                      break loop;                      readBufferOverflow = '\r';
5339                  } else if (readBuffer [j + 1] == '\n') {                      readBufferLength--;
5340                      j++;                    }
5341                  }                  else   // CR at end of buffer
5342                  readBuffer [i] = '\n';                    {
5343                  break;                      readBuffer[i++] = '\n';
5344                      }
5345              case '\n':                  break loop;
5346              default:                }
5347                  readBuffer [i] = readBuffer [j];              else if (readBuffer[j + 1] == '\n')
5348                  break;                {
5349              }                  j++;
5350          }                }
5351          readBufferLength = i;              readBuffer[i] = '\n';
5352      }              break;
   
     /**  
      * Convert a buffer of UTF-8-encoded bytes into UTF-16 characters.  
      * <p>When readDataChunk () calls this method, the raw bytes are in  
      * rawReadBuffer, and the final characters will appear in  
      * readBuffer.  
      * <p>Note that as of Unicode 3.1, good practice became a requirement,  
      * so that each Unicode character has exactly one UTF-8 representation.  
      * @param count The number of bytes to convert.  
      * @see #readDataChunk  
      * @see #rawReadBuffer  
      * @see #readBuffer  
      * @see #getNextUtf8Byte  
      */  
     private void copyUtf8ReadBuffer (int count)  
     throws SAXException, IOException  
     {  
         int     i = 0;  
         int     j = readBufferPos;  
         int     b1;  
         char    c = 0;  
   
         /*  
         // check once, so the runtime won't (if it's smart enough)  
         if (count < 0 || count > rawReadBuffer.length)  
             throw new ArrayIndexOutOfBoundsException (Integer.toString (count));  
         */  
   
         while (i < count) {  
             b1 = rawReadBuffer [i++];  
   
             // Determine whether we are dealing  
             // with a one-, two-, three-, or four-  
             // byte sequence.  
             if (b1 < 0) {  
                 if ((b1 & 0xe0) == 0xc0) {  
                     // 2-byte sequence: 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx  
                     c = (char) (((b1 & 0x1f) << 6)  
                                 | getNextUtf8Byte (i++, count));  
                     if (c < 0x0080)  
                         encodingError ("Illegal two byte UTF-8 sequence",  
                                 c, 0);  
                     //Sec 2.11  
                     // [1] the two-character sequence #xD #xA  
                     // [2] the two-character sequence #xD #x85  
                     if ((c == 0x0085 || c == 0x000a) && sawCR)  
                         continue;  
                       
                     // Sec 2.11  
                     // [3] the single character #x85  
                       
                     if(c == 0x0085  && xmlVersion == XML_11)  
                         readBuffer[j++] = '\r';  
                 } else if ((b1 & 0xf0) == 0xe0) {  
                     // 3-byte sequence:  
                     // zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx  
                     // most CJKV characters  
                     c = (char) (((b1 & 0x0f) << 12) |  
                                    (getNextUtf8Byte (i++, count) << 6) |  
                                    getNextUtf8Byte (i++, count));  
                     //sec 2.11  
                     //[4] the single character #x2028  
                     if(c == 0x2028 && xmlVersion == XML_11){  
                         readBuffer[j++] = '\r';  
                         sawCR = true;  
                         continue;  
                     }  
                     if (c < 0x0800 || (c >= 0xd800 && c <= 0xdfff))  
                         encodingError ("Illegal three byte UTF-8 sequence",  
                                 c, 0);  
                 } else if ((b1 & 0xf8) == 0xf0) {  
                     // 4-byte sequence: 11101110wwwwzzzzyy + 110111yyyyxxxxxx  
                     //     = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx  
                     // (uuuuu = wwww + 1)  
                     // "Surrogate Pairs" ... from the "Astral Planes"  
                     // Unicode 3.1 assigned the first characters there  
                     int iso646 = b1 & 07;  
                     iso646 = (iso646 << 6) + getNextUtf8Byte (i++, count);  
                     iso646 = (iso646 << 6) + getNextUtf8Byte (i++, count);  
                     iso646 = (iso646 << 6) + getNextUtf8Byte (i++, count);  
   
                     if (iso646 <= 0xffff) {  
                         encodingError ("Illegal four byte UTF-8 sequence",  
                                 iso646, 0);  
                     } else {  
                         if (iso646 > 0x0010ffff)  
                             encodingError (  
                                 "UTF-8 value out of range for Unicode",  
                                 iso646, 0);  
                         iso646 -= 0x010000;  
                         readBuffer [j++] = (char) (0xd800 | (iso646 >> 10));  
                         readBuffer [j++] = (char) (0xdc00 | (iso646 & 0x03ff));  
                         continue;  
                     }  
                 } else {  
                     // The five and six byte encodings aren't supported;  
                     // they exceed the Unicode (and XML) range.  
                     encodingError (  
                             "unsupported five or six byte UTF-8 sequence",  
                             0xff & b1, i);  
                     // NOTREACHED  
                     c = 0;  
                 }  
             } else {  
                 // 1-byte sequence: 000000000xxxxxxx = 0xxxxxxx  
                 // (US-ASCII character, "common" case, one branch to here)  
                 c = (char) b1;  
             }  
             readBuffer [j++] = c;  
             if (c == '\r')  
                 sawCR = true;  
         }  
         // How many characters have we read?  
         readBufferLength = j;  
     }  
5353    
5354              case '\n':
5355              default:
5356                readBuffer[i] = readBuffer[j];
5357                break;
5358              }
5359          }
5360        readBufferLength = i;
5361      }
5362    
5363      /**    /**
5364       * Return the next byte value in a UTF-8 sequence.     * Convert a buffer of UTF-8-encoded bytes into UTF-16 characters.
5365       * If it is not possible to get a byte from the current     * <p>When readDataChunk () calls this method, the raw bytes are in
5366       * entity, throw an exception.     * rawReadBuffer, and the final characters will appear in
5367       * @param pos The current position in the rawReadBuffer.     * readBuffer.
5368       * @param count The number of bytes in the rawReadBuffer     * <p>Note that as of Unicode 3.1, good practice became a requirement,
5369       * @return The significant six bits of a non-initial byte in     * so that each Unicode character has exactly one UTF-8 representation.
5370       *   a UTF-8 sequence.     * @param count The number of bytes to convert.
5371       * @exception EOFException If the sequence is incomplete.     * @see #readDataChunk
5372       */     * @see #rawReadBuffer
5373      private int getNextUtf8Byte (int pos, int count)     * @see #readBuffer
5374       * @see #getNextUtf8Byte
5375       */
5376      private void copyUtf8ReadBuffer(int count)
5377      throws SAXException, IOException      throws SAXException, IOException
5378      {    {
5379          int val;      int i = 0;
5380        int j = readBufferPos;
5381        int b1;
5382        char c = 0;
5383        
5384        /*
5385        // check once, so the runtime won't (if it's smart enough)
5386        if (count < 0 || count > rawReadBuffer.length)
5387        throw new ArrayIndexOutOfBoundsException (Integer.toString (count));
5388         */
5389    
5390          // Take a character from the buffer      while (i < count)
5391          // or from the actual input stream.        {
5392          if (pos < count) {          b1 = rawReadBuffer[i++];
5393              val = rawReadBuffer [pos];  
5394          } else {          // Determine whether we are dealing
5395              val = is.read ();          // with a one-, two-, three-, or four-
5396              if (val == -1) {          // byte sequence.
5397                  encodingError ("unfinished multi-byte UTF-8 sequence at EOF",          if (b1 < 0)
5398                          -1, pos);            {
5399              }              if ((b1 & 0xe0) == 0xc0)
5400          }                {
5401                    // 2-byte sequence: 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
5402          // Check for the correct bits at the start.                  c = (char) (((b1 & 0x1f) << 6)
5403          if ((val & 0xc0) != 0x80) {                              | getNextUtf8Byte(i++, count));
5404              encodingError ("bad continuation of multi-byte UTF-8 sequence",                  if (c < 0x0080)
5405                      val, pos + 1);                    {
5406          }                      encodingError("Illegal two byte UTF-8 sequence",
5407                                      c, 0);
5408                      }
5409                    
5410                    //Sec 2.11
5411                    // [1] the two-character sequence #xD #xA
5412                    // [2] the two-character sequence #xD #x85
5413                    if ((c == 0x0085 || c == 0x000a) && sawCR)
5414                      {
5415                        continue;
5416                      }
5417                    
5418                    // Sec 2.11
5419                    // [3] the single character #x85
5420                    
5421                    if (c == 0x0085 && xmlVersion == XML_11)
5422                      {
5423                        readBuffer[j++] = '\r';
5424                      }
5425                  }
5426                else if ((b1 & 0xf0) == 0xe0)
5427                  {
5428                    // 3-byte sequence:
5429                    // zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
5430                    // most CJKV characters
5431                    c = (char) (((b1 & 0x0f) << 12) |
5432                                (getNextUtf8Byte(i++, count) << 6) |
5433                                getNextUtf8Byte(i++, count));
5434                    //sec 2.11
5435                    //[4] the single character #x2028
5436                    if (c == 0x2028 && xmlVersion == XML_11)
5437                      {
5438                        readBuffer[j++] = '\r';
5439                        sawCR = true;
5440                        continue;
5441                      }
5442                    if (c < 0x0800 || (c >= 0xd800 && c <= 0xdfff))
5443                      {
5444                        encodingError("Illegal three byte UTF-8 sequence",
5445                                      c, 0);
5446                      }
5447                  }
5448                else if ((b1 & 0xf8) == 0xf0)
5449                  {
5450                    // 4-byte sequence: 11101110wwwwzzzzyy + 110111yyyyxxxxxx
5451                    //     = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
5452                    // (uuuuu = wwww + 1)
5453                    // "Surrogate Pairs" ... from the "Astral Planes"
5454                    // Unicode 3.1 assigned the first characters there
5455                    int iso646 = b1 & 07;
5456                    iso646 = (iso646 << 6) + getNextUtf8Byte(i++, count);
5457                    iso646 = (iso646 << 6) + getNextUtf8Byte(i++, count);
5458                    iso646 = (iso646 << 6) + getNextUtf8Byte(i++, count);
5459                    
5460                    if (iso646 <= 0xffff)
5461                      {
5462                        encodingError("Illegal four byte UTF-8 sequence",
5463                                      iso646, 0);
5464                      }
5465                    else
5466                      {
5467                        if (iso646 > 0x0010ffff)
5468                          {
5469                            encodingError("UTF-8 value out of range for Unicode",
5470                                          iso646, 0);
5471                          }
5472                        iso646 -= 0x010000;
5473                        readBuffer[j++] = (char) (0xd800 | (iso646 >> 10));
5474                        readBuffer[j++] = (char) (0xdc00 | (iso646 & 0x03ff));
5475                        continue;
5476                      }
5477                  }
5478                else
5479                  {
5480                    // The five and six byte encodings aren't supported;
5481                    // they exceed the Unicode (and XML) range.
5482                    encodingError("unsupported five or six byte UTF-8 sequence",
5483                                  0xff & b1, i);
5484                    // NOTREACHED
5485                    c = 0;
5486                  }
5487              }
5488            else
5489              {
5490                // 1-byte sequence: 000000000xxxxxxx = 0xxxxxxx
5491                // (US-ASCII character, "common" case, one branch to here)
5492                c = (char) b1;
5493              }
5494            readBuffer[j++] = c;
5495            if (c == '\r')
5496              {
5497                sawCR = true;
5498              }
5499          }
5500        // How many characters have we read?
5501        readBufferLength = j;
5502      }
5503      
5504      /**
5505       * Return the next byte value in a UTF-8 sequence.
5506       * If it is not possible to get a byte from the current
5507       * entity, throw an exception.
5508       * @param pos The current position in the rawReadBuffer.
5509       * @param count The number of bytes in the rawReadBuffer
5510       * @return The significant six bits of a non-initial byte in
5511       *   a UTF-8 sequence.
5512       * @exception EOFException If the sequence is incomplete.
5513       */
5514      private int getNextUtf8Byte(int pos, int count)
5515        throws SAXException, IOException
5516      {
5517        int val;
5518        
5519        // Take a character from the buffer
5520        // or from the actual input stream.
5521        if (pos < count)
5522          {
5523            val = rawReadBuffer[pos];
5524          }
5525        else
5526          {
5527            val = is.read();
5528            if (val == -1)
5529              {
5530                encodingError("unfinished multi-byte UTF-8 sequence at EOF",
5531                              -1, pos);
5532              }
5533          }
5534    
5535          // Return the significant bits.      // Check for the correct bits at the start.
5536          return (val & 0x3f);      if ((val & 0xc0) != 0x80)
5537      }        {
5538            encodingError("bad continuation of multi-byte UTF-8 sequence",
5539                          val, pos + 1);
5540          }
5541    
5542        // Return the significant bits.
5543        return (val & 0x3f);
5544      }
5545    
5546      /**    /**
5547       * Convert a buffer of US-ASCII or ISO-8859-1-encoded bytes into     * Convert a buffer of US-ASCII or ISO-8859-1-encoded bytes into
5548       * UTF-16 characters.     * UTF-16 characters.
5549       *     *
5550       * <p>When readDataChunk () calls this method, the raw bytes are in     * <p>When readDataChunk () calls this method, the raw bytes are in
5551       * rawReadBuffer, and the final characters will appear in     * rawReadBuffer, and the final characters will appear in
5552       * readBuffer.     * readBuffer.
5553       *     *
5554       * @param count The number of bytes to convert.     * @param count The number of bytes to convert.
5555       * @param mask For ASCII conversion, 0x7f; else, 0xff.     * @param mask For ASCII conversion, 0x7f; else, 0xff.
5556       * @see #readDataChunk     * @see #readDataChunk
5557       * @see #rawReadBuffer     * @see #rawReadBuffer
5558       * @see #readBuffer     * @see #readBuffer
5559       */     */
5560      private void copyIso8859_1ReadBuffer (int count, char mask)    private void copyIso8859_1ReadBuffer(int count, char mask)
5561      throws IOException      throws IOException
5562      {    {
5563          int i, j;      int i, j;
5564          for (i = 0, j = readBufferPos; i < count; i++, j++) {      for (i = 0, j = readBufferPos; i < count; i++, j++)
5565              char c = (char) (rawReadBuffer [i] & 0xff);        {
5566              if ((c & mask) != 0)          char c = (char) (rawReadBuffer[i] & 0xff);
5567                  throw new CharConversionException ("non-ASCII character U+"          if ((c & mask) != 0)
5568                                                      + Integer.toHexString (c));            {
5569              if (c == 0x0085 && xmlVersion == XML_11)              throw new CharConversionException("non-ASCII character U+"
5570                 c = '\r';                                                        + Integer.toHexString(c));
5571              readBuffer [j] = c;            }
5572              if (c == '\r') {          if (c == 0x0085 && xmlVersion == XML_11)
5573                  sawCR = true;            {
5574              }              c = '\r';
5575          }            }
5576          readBufferLength = j;          readBuffer[j] = c;
5577      }          if (c == '\r')
5578              {
5579                sawCR = true;
5580              }
5581          }
5582        readBufferLength = j;
5583      }
5584    
5585      /**    /**
5586       * Convert a buffer of UCS-2-encoded bytes into UTF-16 characters     * Convert a buffer of UCS-2-encoded bytes into UTF-16 characters
5587       * (as used in Java string manipulation).     * (as used in Java string manipulation).
5588       *     *
5589       * <p>When readDataChunk () calls this method, the raw bytes are in     * <p>When readDataChunk () calls this method, the raw bytes are in
5590       * rawReadBuffer, and the final characters will appear in     * rawReadBuffer, and the final characters will appear in
5591       * readBuffer.     * readBuffer.
5592       * @param count The number of bytes to convert.     * @param count The number of bytes to convert.
5593       * @param shift1 The number of bits to shift byte 1.     * @param shift1 The number of bits to shift byte 1.
5594       * @param shift2 The number of bits to shift byte 2     * @param shift2 The number of bits to shift byte 2
5595       * @see #readDataChunk     * @see #readDataChunk
5596       * @see #rawReadBuffer     * @see #rawReadBuffer
5597       * @see #readBuffer     * @see #readBuffer
5598       */     */
5599      private void copyUcs2ReadBuffer (int count, int shift1, int shift2)    private void copyUcs2ReadBuffer(int count, int shift1, int shift2)
5600      throws SAXException      throws SAXException
5601      {    {
5602          int j = readBufferPos;      int j = readBufferPos;
5603        
5604          if (count > 0 && (count % 2) != 0) {      if (count > 0 && (count % 2) != 0)
5605              encodingError ("odd number of bytes in UCS-2 encoding", -1, count);        {
5606          }          encodingError("odd number of bytes in UCS-2 encoding", -1, count);
5607          // The loops are faster with less internal brancing; hence two        }
5608          if (shift1 == 0) {      // "UTF-16-LE"      // The loops are faster with less internal brancing; hence two
5609              for (int i = 0; i < count; i += 2) {      if (shift1 == 0)
5610                  char c = (char) (rawReadBuffer [i + 1] << 8);        {  // "UTF-16-LE"
5611                  c |= 0xff & rawReadBuffer [i];          for (int i = 0; i < count; i += 2)
5612                  readBuffer [j++] = c;            {
5613                  if (c == '\r')              char c = (char) (rawReadBuffer[i + 1] << 8);
5614                      sawCR = true;              c |= 0xff & rawReadBuffer[i];
5615              }              readBuffer[j++] = c;
5616          } else {        // "UTF-16-BE"              if (c == '\r')
5617              for (int i = 0; i < count; i += 2) {                {
5618                  char c = (char) (rawReadBuffer [i] << 8);                  sawCR = true;
5619                  c |= 0xff & rawReadBuffer [i + 1];                }
5620                  readBuffer [j++] = c;            }
5621                  if (c == '\r')        }
5622                      sawCR = true;      else
5623              }        {  // "UTF-16-BE"
5624          }          for (int i = 0; i < count; i += 2)
5625          readBufferLength = j;            {
5626      }              char c = (char) (rawReadBuffer[i] << 8);
5627                c |= 0xff & rawReadBuffer[i + 1];
5628                readBuffer[j++] = c;
5629                if (c == '\r')
5630                  {
5631                    sawCR = true;
5632                  }
5633              }
5634          }
5635        readBufferLength = j;
5636      }
5637    
5638      /**
5639       * Convert a buffer of UCS-4-encoded bytes into UTF-16 characters.
5640       *
5641       * <p>When readDataChunk () calls this method, the raw bytes are in
5642       * rawReadBuffer, and the final characters will appear in
5643       * readBuffer.
5644       * <p>Java has Unicode chars, and this routine uses surrogate pairs
5645       * for ISO-10646 values between 0x00010000 and 0x000fffff.  An
5646       * exception is thrown if the ISO-10646 character has no Unicode
5647       * representation.
5648       *
5649       * @param count The number of bytes to convert.
5650       * @param shift1 The number of bits to shift byte 1.
5651       * @param shift2 The number of bits to shift byte 2
5652       * @param shift3 The number of bits to shift byte 2
5653       * @param shift4 The number of bits to shift byte 2
5654       * @see #readDataChunk
5655       * @see #rawReadBuffer
5656       * @see #readBuffer
5657       */
5658      private void copyUcs4ReadBuffer(int count, int shift1, int shift2,
5659                                      int shift3, int shift4)
5660        throws SAXException
5661      {
5662        int j = readBufferPos;
5663        
5664        if (count > 0 && (count % 4) != 0)
5665          {
5666            encodingError("number of bytes in UCS-4 encoding " +
5667                          "not divisible by 4",
5668                          -1, count);
5669          }
5670        for (int i = 0; i < count; i += 4)
5671          {
5672            int value = (((rawReadBuffer [i] & 0xff) << shift1) |
5673                         ((rawReadBuffer [i + 1] & 0xff) << shift2) |
5674                         ((rawReadBuffer [i + 2] & 0xff) << shift3) |
5675                         ((rawReadBuffer [i + 3] & 0xff) << shift4));
5676            if (value < 0x0000ffff)
5677              {
5678                readBuffer [j++] = (char) value;
5679                if (value == (int) '\r')
5680                  {
5681                    sawCR = true;
5682                  }
5683              }
5684            else if (value < 0x0010ffff)
5685              {
5686                value -= 0x010000;
5687                readBuffer[j++] = (char) (0xd8 | ((value >> 10) & 0x03ff));
5688                readBuffer[j++] = (char) (0xdc | (value & 0x03ff));
5689              }
5690            else
5691              {
5692                encodingError("UCS-4 value out of range for Unicode",
5693                              value, i);
5694              }
5695          }
5696        readBufferLength = j;
5697      }
5698    
5699      /**    /**
5700       * Convert a buffer of UCS-4-encoded bytes into UTF-16 characters.     * Report a character encoding error.
5701       *     */
5702       * <p>When readDataChunk () calls this method, the raw bytes are in    private void encodingError(String message, int value, int offset)
      * rawReadBuffer, and the final characters will appear in  
      * readBuffer.  
      * <p>Java has Unicode chars, and this routine uses surrogate pairs  
      * for ISO-10646 values between 0x00010000 and 0x000fffff.  An  
      * exception is thrown if the ISO-10646 character has no Unicode  
      * representation.  
      *  
      * @param count The number of bytes to convert.  
      * @param shift1 The number of bits to shift byte 1.  
      * @param shift2 The number of bits to shift byte 2  
      * @param shift3 The number of bits to shift byte 2  
      * @param shift4 The number of bits to shift byte 2  
      * @see #readDataChunk  
      * @see #rawReadBuffer  
      * @see #readBuffer  
      */  
     private void copyUcs4ReadBuffer (int count, int shift1, int shift2,  
                               int shift3, int shift4)  
5703      throws SAXException      throws SAXException
5704      {    {
5705          int j = readBufferPos;      if (value != -1)
5706          {
5707            message = message + " (character code: 0x" +
5708              Integer.toHexString(value) + ')';
5709            error(message);
5710          }
5711      }
5712      
5713      //////////////////////////////////////////////////////////////////////
5714      // Local Variables.
5715      //////////////////////////////////////////////////////////////////////
5716      
5717      /**
5718       * Re-initialize the variables for each parse.
5719       */
5720      private void initializeVariables()
5721      {
5722        // First line
5723        line = 1;
5724        column = 0;
5725        
5726        // Set up the buffers for data and names
5727        dataBufferPos = 0;
5728        dataBuffer = new char[DATA_BUFFER_INITIAL];
5729        nameBufferPos = 0;
5730        nameBuffer = new char[NAME_BUFFER_INITIAL];
5731    
5732        // Set up the DTD hash tables
5733        elementInfo = new HashMap();
5734        entityInfo = new HashMap();
5735        notationInfo = new HashMap();
5736        skippedPE = false;
5737    
5738        // Set up the variables for the current
5739        // element context.
5740        currentElement = null;
5741        currentElementContent = CONTENT_UNDECLARED;
5742        
5743        // Set up the input variables
5744        sourceType = INPUT_NONE;
5745        inputStack = new LinkedList();
5746        entityStack = new LinkedList();
5747        externalEntity = null;
5748        tagAttributePos = 0;
5749        tagAttributes = new String[100];
5750        rawReadBuffer = new byte[READ_BUFFER_MAX];
5751        readBufferOverflow = -1;
5752    
5753        scratch = new InputSource();
5754    
5755        inLiteral = false;
5756        expandPE = false;
5757        peIsError = false;
5758        
5759        doReport = false;
5760        
5761        inCDATA = false;
5762        
5763        symbolTable = new Object[SYMBOL_TABLE_LENGTH][];
5764      }
5765    
5766          if (count > 0 && (count % 4) != 0) {    static class ExternalIdentifiers
5767              encodingError (    {
                     "number of bytes in UCS-4 encoding not divisible by 4",  
                     -1, count);  
         }  
         for (int i = 0; i < count; i += 4) {  
             int value = (((rawReadBuffer [i] & 0xff) << shift1) |  
                       ((rawReadBuffer [i + 1] & 0xff) << shift2) |  
                       ((rawReadBuffer [i + 2] & 0xff) << shift3) |  
                       ((rawReadBuffer [i + 3] & 0xff) << shift4));  
             if (value < 0x0000ffff) {  
                 readBuffer [j++] = (char) value;  
                 if (value == (int) '\r') {  
                     sawCR = true;  
                 }  
             } else if (value < 0x0010ffff) {  
                 value -= 0x010000;  
                 readBuffer [j++] = (char) (0xd8 | ((value >> 10) & 0x03ff));  
                 readBuffer [j++] = (char) (0xdc | (value & 0x03ff));  
             } else {  
                 encodingError ("UCS-4 value out of range for Unicode",  
                                value, i);  
             }  
         }  
         readBufferLength = j;  
     }  
5768    
5769        String publicId;
5770        String systemId;
5771        String baseUri;
5772    
5773      /**      ExternalIdentifiers()
      * Report a character encoding error.  
      */  
     private void encodingError (String message, int value, int offset)  
     throws SAXException  
5774      {      {
         if (value != -1)  
             message = message + " (character code: 0x" +  
                       Integer.toHexString (value) + ')';  
         error (message);  
5775      }      }
5776    
5777        ExternalIdentifiers(String publicId, String systemId, String baseUri)
     //////////////////////////////////////////////////////////////////////  
     // Local Variables.  
     //////////////////////////////////////////////////////////////////////  
   
     /**  
      * Re-initialize the variables for each parse.  
      */  
     private void initializeVariables ()  
5778      {      {
5779          // First line        this.publicId = publicId;
5780          line = 1;        this.systemId = systemId;
5781          column = 0;        this.baseUri = baseUri;
   
         // Set up the buffers for data and names  
         dataBufferPos = 0;  
         dataBuffer = new char [DATA_BUFFER_INITIAL];  
         nameBufferPos = 0;  
         nameBuffer = new char [NAME_BUFFER_INITIAL];  
   
         // Set up the DTD hash tables  
         elementInfo = new Hashtable ();  
         entityInfo = new Hashtable ();  
         notationInfo = new Hashtable ();  
         skippedPE = false;  
   
         // Set up the variables for the current  
         // element context.  
         currentElement = null;  
         currentElementContent = CONTENT_UNDECLARED;  
   
         // Set up the input variables  
         sourceType = INPUT_NONE;  
         inputStack = new Stack ();  
         entityStack = new Stack ();  
         externalEntity = null;  
         tagAttributePos = 0;  
         tagAttributes = new String [100];  
         rawReadBuffer = new byte [READ_BUFFER_MAX];  
         readBufferOverflow = -1;  
   
         scratch = new InputSource ();  
   
         inLiteral = false;  
         expandPE = false;  
         peIsError = false;  
   
         doReport = false;  
   
         inCDATA = false;  
   
         symbolTable = new Object [SYMBOL_TABLE_LENGTH][];  
5782      }      }
5783        
5784      }
5785    
5786      static class EntityInfo
5787      {
5788    
5789      //      int type;
5790      // The current XML handler interface.      ExternalIdentifiers ids;
5791      //      String value;
5792      private SAXDriver   handler;      String notationName;
5793        
5794      //    }
     // I/O information.  
     //  
     private Reader      reader;         // current reader  
     private InputStream is;             // current input stream  
     private int         line;           // current line number  
     private int         column;         // current column number  
     private int         sourceType;     // type of input source  
     private Stack       inputStack;     // stack of input soruces  
     private URLConnection externalEntity; // current external entity  
     private int         encoding;       // current character encoding  
     private int         currentByteCount; // bytes read from current source  
     private InputSource scratch;        // temporary  
   
     //  
     // Buffers for decoded but unparsed character input.  
     //  
     private char        readBuffer [];  
     private int         readBufferPos;  
     private int         readBufferLength;  
     private int         readBufferOverflow;  // overflow from last data chunk.  
   
   
     //  
     // Buffer for undecoded raw byte input.  
     //  
     private final static int READ_BUFFER_MAX = 16384;  
     private byte        rawReadBuffer [];  
   
   
     //  
     // Buffer for attribute values, char refs, DTD stuff.  
     //  
     private static int DATA_BUFFER_INITIAL = 4096;  
     private char        dataBuffer [];  
     private int         dataBufferPos;  
   
     //  
     // Buffer for parsed names.  
     //  
     private static int NAME_BUFFER_INITIAL = 1024;  
     private char        nameBuffer [];  
     private int         nameBufferPos;  
   
     //  
     // Save any standalone flag  
     //  
     private boolean     docIsStandalone;  
   
     //  
     // Hashtables for DTD information on elements, entities, and notations.  
     // Populated until we start ignoring decls (because of skipping a PE)  
     //  
     private Hashtable   elementInfo;  
     private Hashtable   entityInfo;  
     private Hashtable   notationInfo;  
     private boolean     skippedPE;  
   
   
     //  
     // Element type currently in force.  
     //  
     private String      currentElement;  
     private int         currentElementContent;  
   
     //  
     // Stack of entity names, to detect recursion.  
     //  
     private Stack       entityStack;  
   
     //  
     // PE expansion is enabled in most chunks of the DTD, not all.  
     // When it's enabled, literals are treated differently.  
     //  
     private boolean     inLiteral;  
     private boolean     expandPE;  
     private boolean     peIsError;  
   
     //  
     // can't report entity expansion inside two constructs:  
     // - attribute expansions (internal entities only)  
     // - markup declarations (parameter entities only)  
     //  
     private boolean     doReport;  
   
     //  
     // Symbol table, for caching interned names.  
     //  
     // These show up wherever XML names or nmtokens are used:  naming elements,  
     // attributes, PIs, notations, entities, and enumerated attribute values.  
     //  
     // NOTE:  This hashtable doesn't grow.  The default size is intended to be  
     // rather large for most documents.  Example:  one snapshot of the DocBook  
     // XML 4.1 DTD used only about 350 such names.  As a rule, only pathological  
     // documents (ones that don't reuse names) should ever see much collision.  
     //  
     // Be sure that SYMBOL_TABLE_LENGTH always stays prime, for best hashing.  
     // "2039" keeps the hash table size at about two memory pages on typical  
     // 32 bit hardware.  
     //  
     private final static int SYMBOL_TABLE_LENGTH = 2039;  
   
     private Object      symbolTable [][];  
5795    
5796      //    static class AttributeDecl
5797      // Hash table of attributes found in current start tag.    {
5798      //      
5799      private String      tagAttributes [];      String type;
5800      private int         tagAttributePos;      String value;
5801        int valueType;
5802        String enumeration;
5803        String defaultValue;
5804    
5805      //    }
     // Utility flag: have we noticed a CR while reading the last  
     // data chunk?  If so, we will have to go back and normalise  
     // CR or CR/LF line ends.  
     //  
     private boolean     sawCR;  
5806    
5807      //    static class ElementDecl
5808      // Utility flag: are we in CDATA?  If so, whitespace isn't ignorable.    {
     //  
     private boolean     inCDATA;  
5809            
5810      //      int contentType;
5811      // Xml version.      String contentModel;
5812      //        HashMap attributes;
5813      private static final int XML_10 = 0;    
5814      private static final int XML_11 = 1;    }
5815      private int         xmlVersion = XML_10;  
5816      static class Input
5817      {
5818        
5819        int sourceType;
5820        URLConnection externalEntity;
5821        char[] readBuffer;
5822        int readBufferPos;
5823        int readBufferLength;
5824        int line;
5825        int encoding;
5826        int readBufferOverflow;
5827        InputStream is;
5828        int currentByteCount;
5829        int column;
5830        Reader reader;
5831        
5832      }
5833      
5834  }  }
5835    

Legend:
Removed from v.1.1.2.1  
changed lines
  Added in v.1.1.2.2

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26