home *** CD-ROM | disk | FTP | other *** search
/ Dynamic HTML in Action / Dynamicke-HTML-v-akci-covermount.bin / XML / PARSER / XMLINST.EXE / classes / com / ms / xml / parser / Parser.java < prev    next >
Encoding:
Java Source  |  1998-05-26  |  96.2 KB  |  3,007 lines

  1. /*
  2.  * @(#)Parser.java 1.0 6/3/97
  3.  * 
  4.  * Copyright (c) 1997 Microsoft, Corp. All Rights Reserved.
  5.  * 
  6.  */
  7.  
  8. package com.ms.xml.parser;
  9.  
  10. import com.ms.xml.om.Element;
  11. import com.ms.xml.om.ElementImpl;
  12. import com.ms.xml.om.ElementFactory;
  13. import com.ms.xml.om.ElementFactoryImpl;
  14. import com.ms.xml.util.EnumWrapper;
  15. import com.ms.xml.util.Name;
  16. import com.ms.xml.util.Atom;
  17. import com.ms.xml.util.XMLInputStream;
  18. import com.ms.xml.util.XMLOutputStream;
  19.  
  20. import java.lang.String;
  21. import java.util.Hashtable;
  22. import java.util.Stack;
  23. import java.util.Enumeration;
  24. import java.util.Vector;
  25. import java.io.*;
  26. import java.net.*;
  27.  
  28. /**
  29.  * This class implements an eXtensible Markup Language (XML) parser according to the 
  30.  * latest World Wide Web Consortium (W3C) working draft of the XML specification. 
  31.  * This parser class is used internally by the XML document
  32.  * load method, so you shouldn't need to use it directly.
  33.  * @version 1.0, 6/3/97
  34.  */
  35. public class Parser
  36. {    
  37.     static final int TAGSTART       = '<';
  38.     static final int TAGEND         = '>';
  39.     static final int SLASH          = '/';
  40.     static final int EQ             = '=';
  41.     static final int LPAREN         = '(';
  42.     static final int RPAREN         = ')';
  43.     static final int BANG           = '!';
  44.     static final int QMARK          = '?';
  45.     static final int DASH           = '-';
  46.     static final int PERCENT        = '%';
  47.     static final int AMP            = '&';
  48.     static final int LEFTSQB        = '[';
  49.     static final int RIGHTSQB       = ']';
  50.     static final int QUOTE          = '\'';
  51.     static final int OR             = '|';
  52.     static final int ASTERISK       = '*';
  53.     static final int PLUS           = '+';
  54.     static final int HASH           = '#';
  55.     static final int COMMA          = ',';
  56.     static final int INVALIDTOKEN   = 0;
  57.     static final int EOF            = -1;
  58.     static final int WHITESPACE     = -2;
  59.     static final int WORDCHAR       = -3;   
  60.     static final int NAME           = -4;
  61.     static final int TEXT           = -5;
  62.     static final int PITAGSTART     = -6;
  63.     static final int PITAGEND       = -7;
  64.     static final int DECLTAGSTART   = -8;
  65.     static final int CLOSETAGSTART  = -9;
  66.     static final int EMPTYTAGEND    = -10;
  67.     static final int COMMENT        = -11;
  68.     static final int DOCTYPE        = -12;
  69.     static final int SYSTEM         = -13;
  70.     static final int CDATATAGSTART  = -14;
  71.     static final int ELEMENT        = -15;
  72.     static final int EMPTY          = -16;
  73.     static final int ANY            = -17;
  74.     static final int PCDATA         = -18;
  75.     static final int ATTLIST        = -19;
  76.     static final int CDATA          = -20;
  77.     static final int ID             = -21;
  78.     static final int IDREF          = -22;
  79.     static final int IDREFS         = -23;
  80.     static final int ENTITY         = -24;
  81.     static final int ENTITIES       = -25;
  82.     static final int NMTOKEN        = -26;
  83.     static final int NMTOKENS       = -27;
  84.     static final int NOTATION       = -28;
  85.     static final int ENUMERATION    = -29;
  86.     static final int FIXED          = -30;
  87.     static final int REQUIRED       = -31;
  88.     static final int IMPLIED        = -32;
  89.     static final int NDATA          = -33;
  90.     static final int INCLUDETAGSTART= -34;
  91.     static final int IGNORETAGSTART = -35;
  92.     static final int NAMESPACE      = -36;
  93.     static final int EXTENDS        = -37;
  94.     static final int IMPLEMENTS     = -38;
  95.     static final int XML            = -39;
  96.     static final int VERSION        = -40;
  97.     static final int ENCODING       = -41;
  98.     static final int STANDALONE     = -42;
  99.     static final int CDEND          = -43;
  100.     static final int PUBLIC         = -100;
  101.  
  102.  
  103.     /**
  104.      * Creates a new parser object.
  105.      */ 
  106.     public Parser() 
  107.     {
  108.         String version = System.getProperty("java.version");
  109.         Float fver = new Float(version);
  110.         jdk11 = (fver.doubleValue() >= 1.1) ? true : false;
  111.         caseInsensitive = false;
  112.     }
  113.         
  114.     /**
  115.      * Parses the XML document pointed to by the given URL and
  116.      * creates the corresponding XML document hierarchy.
  117.      * @param url the url points to the XML document to parse.
  118.      * @param factory used to create XML Elements during parsing.
  119.      * @param dtd the object that the parser stores DTD information in.
  120.      * @param root the root node to start with and add children to
  121.      * during parsing.
  122.      * @param loadext whether to load external DTD's and/or entities
  123.      * @exception ParseException if syntax or other error encountered.
  124.      */    
  125.     public final void parse(URL url, ElementFactory factory, DTD dtd, Element root, boolean caseInsensitive, boolean loadExt) throws ParseException
  126.     {
  127.         this.dtd = dtd;
  128.         this.root = root;
  129.         this.loadexternal = loadExt;
  130.         setURL(url);
  131.         setFactory(factory);
  132.         this.caseInsensitive = caseInsensitive;
  133.         safeParse();
  134.     }
  135.  
  136.     final void safeParse() throws ParseException
  137.     {
  138.         try {
  139.             parseDocument();
  140.         } catch (ParseException e)
  141.         {
  142.             if (xmlIn != null)
  143.             {
  144.                 try {
  145.                     xmlIn.close();
  146.                 } catch (Exception f)
  147.                 {
  148.                 }
  149.             }
  150.             throw e;
  151.         }
  152.         try {
  153.             xmlIn.close();
  154.         } catch (Exception f)
  155.         {
  156.         }
  157.     }
  158.  
  159.     /**
  160.      * Parses the XML from given input stream.
  161.      * @param in the input stream containing XML data to parse.
  162.      * @param factory used to create XML Elements during parsing.
  163.      * @param dtd the object that the parser stores DTD information in.
  164.      * @param root the root node to start with and add children to
  165.      * during parsing.
  166.      * @exception ParseException if syntax or other error encountered.
  167.      */
  168.     final public void parse(InputStream in, ElementFactory factory, DTD dtd, Element root, boolean caseInsensitive, boolean loadExt) throws ParseException
  169.     {
  170.         this.dtd = dtd;
  171.         url = null;
  172.         this.root = root;
  173.         this.loadexternal = loadExt;
  174.         setInputStream(in);
  175.         setFactory(factory);
  176.         this.caseInsensitive = caseInsensitive;
  177.         safeParse();
  178.     }
  179.  
  180.     /**
  181.      * Reports errors to the specified output stream including parsing
  182.      * context and stack trace where the error occurred.
  183.      * @param e The exception to report.
  184.      * @param out The output stream to write the report to.
  185.      * @return No return value.
  186.      */
  187.     final public void report(ParseException e, OutputStream out)
  188.     {
  189.         PrintStream o = new PrintStream(out);
  190.         String s = null;
  191.  
  192.         o.println(e.getMessage());
  193.  
  194.         if (e.owner instanceof Parser)
  195.         {
  196.             URL u = ((Parser)e.owner).url;
  197.             if (u != null)
  198.                 s = u.toString();
  199.         }
  200.         else if (e.owner instanceof Entity)
  201.         {  
  202.             s = "Parsing <" + ((Entity)e.owner).name + ">";
  203.         }
  204.         else
  205.         {
  206.             s = "Parsing";
  207.         }
  208.         o.println("Location: " + s + "(" + e.line + "," + e.column + ")");
  209.         o.print("Context: ");
  210.         for (int i = 0; i < contextAt; i++)
  211.         {
  212.             Name name = ((Context)contexts.elementAt(i)).e.getTagName();
  213.             if (name != null)
  214.                 o.print("<" + name + ">");
  215.         }
  216.         o.print("<");
  217.         if (current != null) o.print(current.e.getTagName());
  218.         o.println(">");
  219.     }
  220.  
  221.     /**
  222.      * Creates an output stream that best matches the XML data format
  223.      * found during parsing. 
  224.      * For example, this will match big endian or little endian
  225.      * XML data formats.
  226.      * @param out  The output stream.
  227.      * @return an <code>XMLOutputStream</code> object that uses the newline 
  228.      * separator
  229.      * defined by the system property "line.separator". 
  230.      */
  231.     public final XMLOutputStream createOutputStream(OutputStream out)
  232.     {
  233.         if (xmlIn != null)
  234.             return xmlIn.createOutputStream(out);
  235.         return null;
  236.     }
  237.  
  238.     /**
  239.      * throw error
  240.      */                                 
  241.     final void error(String s) throws ParseException
  242.     {
  243.         int i = 1;
  244.         // BUGBUG: the position may still be incorrect
  245.         if (token == NAME)
  246.             i = name.toString().length(); 
  247.         throw new ParseException(s, reader.line, reader.column - 1 - i, reader.owner);
  248.     }
  249.         
  250.         
  251.     /**
  252.      * get next char and update line number / char position
  253.      */    
  254.     final void advance() throws ParseException
  255.     {
  256.         lookahead = reader.read();
  257.         // if EOF and reading 'included' entity pop it...
  258.         while (lookahead == -1 && reader.owner != this)
  259.         {   
  260.             // For external text entities there may be some PCDATA
  261.             // left over that needs to be added also.
  262.             if (charAt != 0) 
  263.             {
  264.                 addPCDATA();
  265.             }
  266.             reader = reader.prev;            
  267.             pop(); // pop the entity element
  268.             if (! inTag)
  269.                 charAt = 0;
  270.             lookahead = reader.read();
  271.         }
  272.     }
  273.             
  274.     /**
  275.      * return next token
  276.      * @exception ParseException when syntax or other error is encountered.
  277.      */    
  278.     final int nextToken() throws ParseException
  279.     {
  280.         bufAt = 0;
  281.         int bufStart = bufAt;
  282.         if (inTag || ! current.preserveWS) 
  283.         {
  284.             while (isWhiteSpaceChar((char)lookahead))
  285.             {
  286.                 if (! inTag) 
  287.                 {
  288.                     buf[bufAt++] = (char)lookahead;
  289.                     seenWS = true;
  290.                 }
  291.                 advance();
  292.             }
  293.         }
  294.         if (inTag)
  295.         {
  296.             switch (lookahead)
  297.             {
  298.                 case -1:
  299.                     token = EOF;
  300.                     break;
  301.                 case '>':
  302.                     token = TAGEND;
  303.                     inTag = false;
  304.                     advance();
  305.                     break;
  306.                 case '/':
  307.                     advance();
  308.                     if (lookahead == '>')
  309.                     {
  310.                         token = EMPTYTAGEND;
  311.                         inTag = false;
  312.                         advance();
  313.                     }
  314.                     break;
  315.                 case '?':
  316.                     advance();
  317.                     if (current.type == Element.ELEMENTDECL)
  318.                     {
  319.                         token = QMARK;
  320.                     }
  321.                     else {
  322.                         if (lookahead == '>')
  323.                         {
  324.                             token = PITAGEND;
  325.                             inTag = false;
  326.                             advance();
  327.                         }
  328.                         else
  329.                         {
  330.                             token = QMARK;
  331.                         }
  332.                     }
  333.                     break;
  334.                 case '=':
  335.                 case '(':
  336.                 case ')':
  337.                 case ',':
  338.                 case '|':
  339.                 case '[':
  340.                 case ']':
  341.                 case '*':
  342.                 case '+':
  343.                 case '#':
  344.                     token = lookahead;
  345.                     advance();
  346.                     break;
  347.                 case '%':
  348.                     advance();
  349.                     if (substitution > 0 && isNameChar((char)lookahead))
  350.                     {
  351.                         scanEntityRef(true);
  352.                         return nextToken();
  353.                     }
  354.                     token = PERCENT;
  355.                     break;
  356.                 case '\"':
  357.                 case '\'':
  358.                     quote = (char)lookahead;
  359.                     token = QUOTE;
  360.                     advance();
  361.                     break;
  362.                 default:
  363.                     if (isNameChar((char)lookahead) || nameSpaceSeparator == (char)lookahead)
  364.                     {
  365.                         scanName("name");
  366.  
  367.                         if (keyword > 0)
  368.                         {
  369.                             token = lookup(name.getName());
  370.                         }
  371.                     }
  372.                     else
  373.                     {
  374.                         error("Unexpected token '" + (char)lookahead + "' inside tag <" + current.e.getTagName() + ">");
  375.                     }
  376.             }
  377.         }
  378.         else
  379.         {
  380.             if (seenWS && ! current.lastWasWS && 
  381.                 (lookahead == -1 || lookahead == '<') ) {
  382.                 addNewElement( Element.WHITESPACE , null, false, 
  383.                     new String(buf, bufStart, bufAt - bufStart));
  384.             }
  385.             switch (lookahead)
  386.             {
  387.                 case -1:
  388.                     token = EOF;
  389.                     break;
  390.                 case '<':
  391.                     inTag = true;
  392.                     seenWS = false; // reset
  393.                     advance();
  394.                     switch (lookahead)
  395.                     {
  396.                         case '?':
  397.                             token = PITAGSTART;
  398.                             advance();
  399.                             break;
  400.                         case '!':
  401.                             token = DECLTAGSTART;
  402.                             advance();
  403.                             if (lookahead == '-')
  404.                             {
  405.                                 advance();
  406.                                 if (lookahead == '-')
  407.                                 {
  408.                                     token = COMMENT;
  409.                                     advance();
  410.                                 }
  411.                                 else
  412.                                 {
  413.                                     error("Bad comment start syntax.  Expected '<!--'");
  414.                                 }
  415.                             }
  416.                             else if (lookahead == '[')
  417.                             {
  418.                                 advance();
  419.                                 boolean ref = false;   // whether the keyword is a parameter entity reference
  420.                                 if (lookahead == '%')
  421.                                 {
  422.                                     advance();
  423.                                     Entity n = scanEntityRef(true);
  424.                                     conditionRef = n.getName();
  425.                                     ref = true;       // entity reference encountered
  426.                                 }
  427.                                 else 
  428.                                 {
  429.                                     conditionRef = null;
  430.                                 }
  431.  
  432.                                 parseKeyword(0, "CDATA or Conditional section start tag");
  433.  
  434.                                 if (token == INCLUDETAGSTART || token == IGNORETAGSTART 
  435.                                     || token == CDATA && !ref)
  436.                                 {
  437.                                     // scanned <![CDATA
  438.                                     if (token == CDATA)
  439.                                         token = CDATATAGSTART;
  440.                                     else
  441.                                         inTag = false;
  442.  
  443.                                     if (lookahead == '[')
  444.                                     {
  445.                                         advance();
  446.                                     }
  447.                                     else 
  448.                                     {
  449.                                         if (token == CDATATAGSTART)
  450.                                             error("Bad CDATA start syntax. Expected '['");
  451.                                         else
  452.                                             error("Bad conditional section start syntax. Expected '['");
  453.                                     }
  454.                                 }
  455.                                 else
  456.                                 {
  457.                                     error("Bad start tag: '<!['" + tokenString(token) + token);
  458.                                 }
  459.                             }
  460.                             break;
  461.                         case '/':
  462.                             token = CLOSETAGSTART;
  463.                             advance();
  464.                             break;
  465.                         default:
  466.                             token = TAGSTART;
  467.                     }
  468.                     break;
  469.                 case ']':
  470.                     // check CDEND "]]>' first
  471.                     advance();
  472.                     if (lookahead == ']')
  473.                     {
  474.                         advance();
  475.                         if (lookahead == TAGEND)
  476.                         {
  477.                             advance();
  478.                             token = CDEND;
  479.                             break;
  480.                         }
  481.                         else 
  482.                         {
  483.                             reader.push((char)lookahead); 
  484.                             reader.push(']');
  485.                             lookahead = ']';
  486.                         }
  487.                     }
  488.                     else
  489.                     {
  490.                         reader.push((char)lookahead);
  491.                         lookahead = ']';
  492.                     }
  493.  
  494.                     // check end of internal set of DTD ']'
  495.                     if (breakText == lookahead)
  496.                     {
  497.                         advance();
  498.                         token = RIGHTSQB;
  499.                         break;
  500.                     }
  501.  
  502.                     // fall thru to text otherwise
  503.                 default:
  504.                     token = TEXT;
  505.                     break;
  506.             }
  507.         }
  508.         return token;
  509.     }
  510.  
  511.     final String tokenString(int token)
  512.     {
  513.         return tokenString(token,null);
  514.     }
  515.  
  516.     final String tokenString(int token, String s)
  517.     {
  518.         switch (token) 
  519.         {            
  520.             case TAGSTART       : return "start tag(<)";
  521.             case TAGEND         : return "tag end(>)";
  522.             case SLASH          : return "/";
  523.             case EQ             : return "=";
  524.             case LPAREN         : return "(";
  525.             case RPAREN         : return ")";
  526.             case BANG           : return "!";
  527.             case QMARK          : return "question mark(?)";
  528.             case DASH           : return "-";
  529.             case PERCENT        : return "percent(%)";
  530.             case AMP            : return "&";
  531.             case LEFTSQB        : return "[";
  532.             case RIGHTSQB       : return "]";
  533.             case QUOTE          : return "quote(' or \")"; 
  534.             case OR             : return "|";
  535.             case ASTERISK       : return "*";
  536.             case PLUS           : return "+";
  537.             case HASH           : return "#";
  538.             case COMMA          : return ",";
  539.             case INVALIDTOKEN   : return "invalidtoken";
  540.             case EOF            : return "EOF";
  541.             case WHITESPACE     : return "whitespace";
  542.             case WORDCHAR       : return "word character";   
  543.             case NAME           : if (s != null) return s;
  544.                                   else return "NAME '" + name + "'";
  545.             case TEXT           : return "TEXT '" + text + "'";
  546.             case PITAGSTART     : return "<?";
  547.             case PITAGEND       : return "?>";
  548.             case CDEND          : return "]]>";
  549.             case DECLTAGSTART   : return "<!";
  550.             case CLOSETAGSTART  : return "</";
  551.             case EMPTYTAGEND    : return "/>";
  552.             case COMMENT        : return "<!--";
  553.             case DOCTYPE        : return "DOCTYPE";
  554.             case SYSTEM         : return "SYSTEM";
  555.             case CDATATAGSTART  : return "<![CDATA";
  556.             case ELEMENT        : return "ELEMENT";
  557.             case EMPTY          : return "EMPTY";
  558.             case ANY            : return "ANY";
  559.             case PCDATA         : return "PCDATA";
  560.             case ATTLIST        : return "ATTLIST";
  561.             case CDATA          : return "CDATA";
  562.             case ID             : return "ID";
  563.             case IDREF          : return "IDREF";
  564.             case IDREFS         : return "IDREFS";
  565.             case ENTITY         : return "ENTITY";
  566.             case ENTITIES       : return "ENTITIES";
  567.             case NMTOKEN        : return "NMTOKEN";
  568.             case NMTOKENS       : return "NMTOKENS";
  569.             case NOTATION       : return "NOTATION";
  570.             case ENUMERATION    : return "ENUMERATION";
  571.             case FIXED          : return "FIXED";
  572.             case REQUIRED       : return "REQUIRED";
  573.             case IMPLIED        : return "IMPLIED";
  574.             case NDATA          : return "NDATA";
  575.             case INCLUDETAGSTART: return "INCLUDETAGSTART";
  576.             case IGNORETAGSTART : return "IGNORETAGSTART";
  577.             case NAMESPACE      : return "NAMESPACE";
  578.             case PUBLIC         : return "PUBLIC";
  579.     
  580.             default:            return s;
  581.         }
  582.     }
  583.     
  584.     final int lookup(String n)
  585.     {
  586.         Object o = tokens.get(n);
  587.         if (o != null)
  588.         {
  589.             token = ((Integer)o).intValue();
  590.         }
  591.         else
  592.         {
  593.             token = NAME;
  594.         }
  595.  
  596.         return token;
  597.     }
  598.     
  599.     /**
  600.      * return true if character is whitespace
  601.      */    
  602.     static final boolean isWhiteSpaceChar(char c)
  603.     {
  604.         if (c < 256)
  605.         {
  606.             return (chartype[c] & FWHITESPACE) != 0;
  607.         }
  608.         else
  609.         {
  610.             if (jdk11)
  611.                 return Character.isWhitespace(c);
  612.  
  613.             return Character.isSpace(c);    
  614.         }
  615.     }
  616.  
  617.     /**
  618.      * return true if character can be part of a name
  619.      */    
  620.     static final boolean isNameChar(char c)
  621.     {
  622.         if (c < 256)
  623.         {
  624.             return (chartype[c] & (FLETTER | FDIGIT | FMISCNAME)) != 0;
  625.         }
  626.         else
  627.         {
  628.             return  Character.isLetter(c) || 
  629.                     Character.isDigit(c) ||
  630.                     c == '-' ||  
  631.                     c == '_' ||
  632.                     c == '.';
  633.         }
  634.         // BUGBUG:: according to spec, should allow combiningChar, Ignorable, and Extender chars as well
  635.     }
  636.  
  637.     /**
  638.      * Scan a name
  639.      */    
  640.     final void scanName(String s) throws ParseException
  641.     {
  642.         String n = null;
  643.         Atom ns = null;
  644.         boolean scanned = false;
  645.         
  646.         if (nameappend == 0)
  647.         {
  648.             bufAt = 0;
  649.         }
  650.         int bufStart = bufAt;
  651.         if (nameSpaceSeparator != (char)lookahead)
  652.         {
  653.             bufAt = scanSimpleName(bufAt, s);
  654.             scanned = true;
  655.         }
  656.  
  657.         if (nametoken == 0 && simplename == 0 && nameSpaceSeparator == (char)lookahead) 
  658.         {
  659.             int nsgap = 1;
  660.             bufAt++;
  661.             advance();
  662.             // Make second colon optional.
  663.             if (nameSpaceSeparator == (char)lookahead) 
  664.             { 
  665.                 nsgap++;
  666.                 bufAt++;
  667.                 advance();
  668.             }
  669.             if (scanned) // name space found
  670.             {
  671.                 if (caseInsensitive)
  672.                      n = new String(buf, bufStart, bufAt - bufStart - nsgap).toUpperCase();
  673.                 else n = new String(buf, bufStart, bufAt - bufStart - nsgap);
  674.                 Atom atom = Atom.create(n);
  675.                 if (DTD.isReservedNameSpace(atom)) 
  676.                 {
  677.                     ns = atom;
  678.                 } else {
  679.                     // check the current context first 
  680.                     ns = current.findNameSpace(atom);
  681.                     if (ns == null)
  682.                     {
  683.                         // check the global name space when the name space is not defined in current context
  684.                         ns = dtd.findLongNameSpace(atom);
  685.                         if (ns == null) 
  686.                         {
  687. //                          error("Name space not defined '" + n + "'");
  688.                             ns = atom;
  689.                             addNameSpace(ns,ns,false);
  690.                         }
  691.                     }
  692.                 }
  693.             }
  694.             bufStart = bufAt;
  695.             bufAt = scanSimpleName(bufAt, s);
  696. //            else 
  697. //            {// report error and throw
  698. //                error("Expecting namespace separator ':' instead of '" + (char)lookahead + "'");
  699. //            }
  700.         }    
  701.         else 
  702.         {
  703.             if ((nametoken == 0 && simplename == 0) || inEntityRef > 0)
  704.                 ns = current.defaultNameSpace;
  705.         }
  706.  
  707.         if ((nametoken == 0 && simplename == 0) || inEntityRef > 0)
  708.             current.nameSpace = ns;
  709.         if ((keyword > 0 && inEntityRef == 0) || ns == null) 
  710.             name = Name.create(buf, bufStart, bufAt - bufStart);
  711.         else 
  712.             name = Name.create(new String(buf, bufStart, bufAt - bufStart), ns);        
  713.         token = NAME; 
  714.     }
  715.  
  716.     final char toUpperCase(char c)
  717.     {
  718.         if (nouppercase != 0)
  719.             return c;
  720.         else
  721.         {
  722.             if (c < 256)
  723.             {
  724.                 return charupper[c];
  725.             }
  726.             else
  727.             {
  728.                 return Character.toUpperCase(c);
  729.             }
  730.         }
  731.     }
  732.  
  733.     final int scanSimpleName(int bufAt, String s) throws ParseException
  734.     {
  735.         boolean startname;
  736.  
  737.         if (lookahead < 256)
  738.         {
  739.             startname = (chartype[lookahead] & (FLETTER | FSTARTNAME)) != 0;
  740.         }
  741.         else
  742.         {
  743.             startname = Character.isLetter((char)lookahead) || lookahead == '_';
  744.         }
  745.         if (!startname)
  746.         {
  747.             if (!(nametoken > 0 && Character.isDigit((char)lookahead)))
  748.             {
  749.                 // report error and throw
  750.                 error("Expecting " + s + " instead of '" + (char)lookahead + "'");
  751.             }
  752.         }
  753.         // This is a flattened loop, to optimize loop speed.
  754.         // Rather than doing a complex condition on each loop
  755.         // iteration, the loop is duplicated 4 times for each
  756.         // different condition.  This is a deliberate code-size
  757.         // versus speed trade off since this loop is very critical
  758.         if (nametoken == 0 && simplename == 0)
  759.         {
  760.             if (caseInsensitive)
  761.             {
  762.                 buf[bufAt++]=toUpperCase((char)lookahead);
  763.                 advance();
  764.                 while (isNameChar((char)lookahead))
  765.                 {
  766.                     buf[bufAt++] = toUpperCase((char)lookahead);
  767.                     advance();
  768.                 }
  769.             }
  770.             else
  771.             {
  772.                 buf[bufAt++]=(char)lookahead;
  773.                 advance();
  774.                 while (isNameChar((char)lookahead))
  775.                 {
  776.                     buf[bufAt++] = (char)lookahead;
  777.                     advance();
  778.                 }
  779.             }
  780.         } else {
  781.             if (caseInsensitive)
  782.             {
  783.                 buf[bufAt++]=toUpperCase((char)lookahead);
  784.                 advance();
  785.                 while (isNameChar((char)lookahead) ||
  786.                     lookahead == nameSpaceSeparator )
  787.                 {
  788.                     buf[bufAt++] = toUpperCase((char)lookahead);
  789.                     advance();
  790.                 }
  791.             }
  792.             else
  793.             {
  794.                 buf[bufAt++]=(char)lookahead;
  795.                 advance();
  796.                 while (isNameChar((char)lookahead) ||
  797.                     lookahead == nameSpaceSeparator )
  798.                 {
  799.                     buf[bufAt++] = (char)lookahead;
  800.                     advance();
  801.                 }
  802.             }
  803.         }
  804.         return bufAt;
  805.     }
  806.  
  807.     /**
  808.      * Scan text into a string
  809.      */    
  810.     final void scanText(int eref, int cref, boolean stripws) throws ParseException
  811.     {
  812.         charAt = 0;
  813.  
  814.         while (lookahead != -1 && lookahead != '<' && lookahead != breakText && (charAt + 1) < chars.length)
  815.         {
  816.             if (lookahead == cref)
  817.             {
  818.                 if (seenWS) { chars[charAt++] = ' '; seenWS = false; }
  819.                 advance();
  820.                 if (lookahead == '#')
  821.                 {
  822.                     scanCharRef();
  823.                 } 
  824.                 else if (isNameChar((char)lookahead)) 
  825.                 {
  826.                     scanEntityRef(false);
  827.                 }
  828.                 else
  829.                 {
  830.                     chars[charAt++] = (char)cref;
  831.                 }
  832.             }
  833.             else if (lookahead == eref)
  834.             {
  835.                 if (seenWS) 
  836.                 { 
  837.                     chars[charAt++] = ' '; seenWS = false; 
  838.                 }
  839.                 advance();
  840.                 if (isNameChar((char)lookahead))
  841.                 {
  842.                     scanEntityRef(eref == '%');
  843.                 }
  844.                 else
  845.                 {
  846.                     chars[charAt++] = (char)cref;
  847.                 }
  848.             }
  849.             else
  850.             {
  851.                 if (stripws && isWhiteSpaceChar((char)lookahead))
  852.                 {
  853.                     seenWS = true;
  854.                 }
  855.                 else
  856.                 {
  857.                     if (seenWS) 
  858.                     { 
  859.                         chars[charAt++] = ' '; seenWS = false; 
  860.                     }
  861.                     chars[charAt++] = (char)lookahead;
  862.                 }
  863.                 advance();
  864.             }
  865.         }
  866.         token = TEXT;
  867.     }
  868.  
  869.     /** 
  870.      * Process entity reference
  871.      * If successful lookahead contains the next character after the entity.
  872.      * @param par if this is a parametrized entity.
  873.      */
  874.     final Entity scanEntityRef(boolean par) throws ParseException
  875.     {
  876.         nouppercase++;
  877.         inEntityRef++;
  878.         scanName("entity ref");
  879.         inEntityRef--;
  880.         nouppercase--;
  881.         if (lookahead != ';')
  882.         {
  883.             error("Entity reference syntax error " + name);
  884.         }
  885.         Entity en = dtd.findEntity(name);
  886.         if (en == null)
  887.         {
  888.             String msg = "Missing entity '" + name + "'.";
  889.             if (! loadexternal) 
  890.             {
  891.                 msg += "\nPerhaps you need to change the loadexternal flag to 'true'.";
  892.             }
  893.             error(msg);
  894.         }
  895.  
  896.         if (par != en.par)
  897.         {
  898.             if (par)
  899.                 error("Entity '" + name + "' is not a parameter entity.");
  900.             else error("Entity '" + name + "' is a parameter entity.");
  901.         }
  902.  
  903.         if (par) // in DTD
  904.         {
  905.             // BUG: We still haven't finished this yet.  
  906.             // In the case where a parameter entity is used
  907.             // inside an element declaration, for example,
  908.             // we will forget this fact in the saved document. 
  909.  
  910.             if (! inTag) // add an entity reference node
  911.             {
  912.                 addNewElement(Element.ENTITYREF, name, false, null);
  913.             }
  914.             if (en.getURL() == null) // parse entity text
  915.             {
  916.                 push(en,name,Element.ENTITY);
  917.                 reader = en.getReader(reader);              
  918.             }
  919.             else // get external DTD
  920.             {
  921.                 if (en.ndata != null)
  922.                     error("Binary parameter entity " + name.toString() + "cannot be used in DTD");
  923.  
  924.                 if (loadexternal)
  925.                     loadDTD(en.getURL(), current.defaultNameSpace);
  926.             }   
  927.         }
  928.         else  // in document 
  929.         {
  930.             // NOTE: in the following code, we pass the ElementDecl from
  931.             // the current context to the new context, since we want to
  932.             // validate the contents of the ENTITY relative to the parent
  933.             // of the ENTITYREF.
  934.  
  935.             if (! inTag)
  936.             {
  937.                 // Put current substring in new PCDATA node.
  938.                 addPCDATA();
  939.                 charAt = 0;
  940.                 // Now we need to store a special ENTITYREF
  941.                 // node in the tree to remember where the entities
  942.                 // are so we can save them back out.
  943.                 addNewElement(Element.ENTITYREF, name,true, null);
  944.             }
  945.             if (en.getLength() == -1) // special characters
  946.             {
  947.                 // skip it since the special built in entities
  948.                     // do not require parsing.
  949.             }
  950.             else {
  951.                 if (! en.parsed) 
  952.                 {
  953.                     Context c = current;
  954.                     if (c.parent != null)
  955.                         c = c.parent;
  956.                     if (en.getURL() == null) // parse entity text
  957.                     {
  958.                         en.parsed = true;
  959.                         push(en,name,Element.ENTITY);
  960.                         // here we have to link the entity context to the parent
  961.                         // element context so we can do the validation correctly.
  962.                         // See comments in addNewElement.
  963.                         current.parent = c;
  964.                         reader = en.getReader(reader);
  965.                     }
  966.                     else if (en.ndata == null && loadexternal)
  967.                     {
  968.                         en.parsed = true;
  969.                         push(en, name,Element.ENTITY);
  970.                         // here we have to link the entity context to the parent
  971.                         // element context so we can do the validation correctly.
  972.                         // See comments in addNewElement.
  973.                         current.parent = c;
  974.                         try 
  975.                         {
  976.                             URL u = new URL(url,en.getURL());
  977.                             reader = new EntityReader(
  978.                                 u.openStream(),
  979.                                 reader.line, reader.column, reader, en);
  980.                         } 
  981.                         catch (Exception e) 
  982.                         {
  983.                             error("Cannot load external text entity: " + name.toString());
  984.                         }
  985.                     }
  986.                 }
  987.             }
  988.         }
  989.         advance();
  990.         return en;
  991.     }
  992.  
  993.     /**
  994.      *  Hax ::= [0-9a-fA-F]
  995.      *  CharRef ::=  '&#' [0-9]+ ';' | '&#x' Hex+ ';'
  996.      */
  997.     final void scanCharRef() throws ParseException
  998.     {
  999.         int n = 0;
  1000.  
  1001.         if (lookahead == '#')
  1002.         {
  1003.             advance();
  1004.             if (lookahead == 'x' || lookahead == 'X') {
  1005.                 advance();
  1006.                 while (true)
  1007.                 {
  1008.                     if (lookahead >= '0' && lookahead <= '9')
  1009.                     {
  1010.                         n = n * 16 + lookahead - '0';
  1011.                     }
  1012.                     else if (lookahead >= 'a' && lookahead <= 'f')
  1013.                     {
  1014.                         n = n * 16 + lookahead - 'a' + 10;
  1015.                     }
  1016.                     else if (lookahead >= 'A' && lookahead <= 'F')
  1017.                     {
  1018.                         n = n * 16 + lookahead - 'A' + 10;
  1019.                     }
  1020.                     else
  1021.                     {
  1022.                         break;
  1023.                     }
  1024.                     advance();
  1025.                 }
  1026.             } else while (lookahead >= '0' && lookahead <= '9') {
  1027.                 n = n * 10 + lookahead - '0';
  1028.                 advance();
  1029.             }
  1030.         }
  1031.  
  1032.         if (lookahead != ';')
  1033.         {
  1034.             error("Bad character reference syntax. Expecting &#xx;");
  1035.         }
  1036.         else
  1037.         {
  1038.             chars[charAt++] = (char)n;
  1039.         }
  1040.         advance();
  1041.     }
  1042.     
  1043.     /**
  1044.      * Scan a quoted string
  1045.      *
  1046.      * @param q match the end
  1047.      * @param eref match entity reference start char (& or %) if not 0xffff
  1048.      * @param cref match character refence char (&# or &u) if not 0xffff
  1049.      */    
  1050.     final void scanString(int endChar, int eref, int cref, int breakChar) throws ParseException
  1051.     {
  1052.         charAt = 0;
  1053.         while (lookahead != -1 && lookahead != endChar)
  1054.         {
  1055.             if (lookahead == breakChar)
  1056.             {
  1057.                 error("Illegal character in string " + (char)lookahead);
  1058.             }
  1059.             else if (lookahead == cref)
  1060.             {
  1061.                 advance();
  1062.                 if (lookahead == '#')
  1063.                 {
  1064.                     scanCharRef();
  1065.                 } 
  1066.                 else if (isNameChar((char)lookahead)) 
  1067.                 { 
  1068.                     if (expandNamedEntities) 
  1069.                     {
  1070.                         scanEntityRef(false);
  1071.                     } else {
  1072.                         chars[charAt++] = (char)cref;
  1073.                     }
  1074.                 } 
  1075.                 else 
  1076.                 {
  1077.                     chars[charAt++] = (char)lookahead;
  1078.                 }
  1079.             }
  1080.             else if (lookahead == eref)
  1081.             {
  1082.                 advance();
  1083.                 if (isNameChar((char)lookahead)) 
  1084.                 {
  1085.                     boolean par = (eref == '%');
  1086.                     if (expandNamedEntities)  // par || 
  1087.                     {
  1088.                         scanEntityRef(par);
  1089.                     } else {
  1090.                         chars[charAt++] = (char)eref;
  1091.                     }
  1092.                 } 
  1093.                 else 
  1094.                 {
  1095.                     chars[charAt++] = (char)lookahead;
  1096.                 }
  1097.             }
  1098.             else
  1099.             {
  1100.                 chars[charAt++] = (char)lookahead;
  1101.                 advance();
  1102.             }
  1103.         }
  1104.         if (lookahead == endChar)
  1105.         {
  1106.             advance();
  1107.         }
  1108.         else
  1109.         {
  1110.             error("Unterminated string");
  1111.         }
  1112.         text = new String(chars, 0, charAt);
  1113.     }
  1114.     
  1115.     /**
  1116.      * scan URL string
  1117.      */    
  1118.     final String scanUrl() throws ParseException
  1119.     {
  1120.         parseToken(QUOTE, "Url");
  1121.         scanString(quote, 0xFFFF, 0xFFFF, 0xFFFF);
  1122.         return text;
  1123.     }
  1124.         
  1125.     /**
  1126.      * expect token type t
  1127.      */    
  1128.     final void parseToken(int t, String s) throws ParseException
  1129.     {
  1130.         if (nextToken() != t)
  1131.         {
  1132.             error("Expected " + tokenString(t,s) + " instead of " + tokenString(token));
  1133.         }
  1134.     }
  1135.  
  1136.     /**
  1137.      * expect token type t
  1138.      */    
  1139.     final void checkToken(int t, String s) throws ParseException
  1140.     {
  1141.         if (token != t)
  1142.         {
  1143.             error("Expected " + tokenString(t,s) + " instead of " + tokenString(token));
  1144.         }
  1145.     }
  1146.     
  1147.     
  1148.     /**
  1149.      * expect keyword k
  1150.      */    
  1151.     final void parseKeyword(int k, String s) throws ParseException
  1152.     {
  1153.         keyword++;
  1154.         if (k == 0)
  1155.         {
  1156.             nextToken();
  1157.         }
  1158.         else
  1159.         {
  1160.             parseToken(k, s);
  1161.         }
  1162.         keyword--;        
  1163.     }
  1164.  
  1165.     /**
  1166.      * Parse names separated by given token and return the number of names
  1167.      * found.  Also, if the value argument is not null it returns the names
  1168.      * concatenated with a ' ' separator in this string object.
  1169.      */
  1170.     final int parseNames(Vector names, int separator, StringBuffer value) throws ParseException
  1171.     {
  1172.         int i = 0;
  1173.         bufAt = 0;
  1174.         if (value != null) nameappend++;
  1175.         while (nextToken() == NAME)
  1176.         {
  1177.             // Insert space to separate names for the returned
  1178.             // concatenated String value.
  1179.             if (i > 0)
  1180.             {
  1181.                 buf[bufAt++] = ' ';
  1182.             }
  1183.             
  1184.             names.addElement(name);
  1185.             i++;
  1186.             if (separator != INVALIDTOKEN && nextToken() != OR)
  1187.                 break;
  1188.         }
  1189.         if (value != null) {
  1190.             value.append(buf, 0, bufAt);
  1191.             nameappend--;
  1192.         }
  1193.         return i;
  1194.     }
  1195.     
  1196.     /**
  1197.      * expect XML document
  1198.      *
  1199.      * Document ::= Prolog Element Misc*
  1200.      */    
  1201.     final void parseDocument() throws ParseException
  1202.     {
  1203.         expandNamedEntities = true;
  1204.         internalSubset = false;
  1205.         seenWS = false;
  1206.         contextAt = 0;
  1207.         standAlone = false;
  1208.         validating = false;
  1209.  
  1210.  
  1211.         // The default is to strip white space.  
  1212.         // This default is also assumed in Document.save().
  1213.         // So changing the default would also require some changes
  1214.         // to the implementation of Document.save().
  1215.         newContext(root, null, Element.ELEMENT, false, null, null);
  1216.         parseProlog();
  1217.         parseRootElement();
  1218.         
  1219.         if (lookahead != -1)
  1220.         {
  1221.             nextToken();
  1222.             tryMisc();
  1223.             if (lookahead != -1)
  1224.                 error("Expected comments, PI, or EOF instead of " + tokenString(token));
  1225.         }
  1226.         dtd.checkIDs();
  1227.     }
  1228.     
  1229.     void newContext(Element e, Name n, int type, boolean preserveWS, Atom nameSpace, Hashtable spaceTable)
  1230.     {
  1231.         if (contextAt == contexts.size())
  1232.         {
  1233.             current = new Context(e, n, type, preserveWS, nameSpace, spaceTable);
  1234.             contexts.addElement(current);
  1235.         }
  1236.         else
  1237.         {
  1238.             current = (Context)contexts.elementAt(contextAt);
  1239.             current.reset(e, n, type, preserveWS, nameSpace, spaceTable);
  1240.         }
  1241.     }
  1242.  
  1243.     final void push(Element e, Name n, int type)
  1244.     {
  1245.         contextAt++;
  1246.         // White space handling is inherited.
  1247.         newContext(e, n, type, current.preserveWS, current.nameSpace, current.spaceTable);
  1248.         return;
  1249.     }
  1250.     
  1251.     final void pop()
  1252.     {
  1253.         current = (Context)contexts.elementAt(--contextAt);
  1254.     }
  1255.     
  1256.     /**
  1257.      * expect prolog
  1258.      *
  1259.      * Prolog ::= XMLDecl Misc* DocTypeDecl? Misc*
  1260.      */    
  1261.     final void parseProlog() throws ParseException
  1262.     {
  1263.         if (lookahead != -1)
  1264.         {
  1265.             nextToken();
  1266.             if (token == PITAGSTART)
  1267.             {
  1268.                 parseToken(NAME, "PI tag");
  1269.                 if (name == nameXML)
  1270.                 {
  1271. // 10/21/97 CJL - Disabled this check for CDF compatibility.
  1272. //                    if (current.lastWasWS)
  1273. //                        error("An XML declaration can only appear in the very beginning of the document.");
  1274.                     parseXMLDecl();
  1275.                 }
  1276.                 else  
  1277.                 {
  1278.                     if (name == nameXMLNameSpace)
  1279.                         parseNameSpaceDecl(true, true);
  1280.                     else finishPI();
  1281.                 }
  1282.                 nextToken();
  1283.                 firstLine = false;
  1284.             }           
  1285.         }
  1286.         tryMisc();
  1287.         tryDocTypeDecl();
  1288.         tryMisc();
  1289.     }
  1290.  
  1291.  
  1292.     /**
  1293.      * expect XML declaration
  1294.      *
  1295.      * XMLDecl = '<?XML' VersionInfo EncodingDecl? SDDecl? S? '?>'
  1296.      */
  1297.     final void parseXMLDecl() throws ParseException
  1298.     {
  1299.         Element xml = addNewElement(Element.PI, name,false, null);
  1300.         push(xml, name, Element.PI); // so that error reporting is correct.
  1301.         ElementDecl ed = current.ed;
  1302.         current.ed = XMLDecl;
  1303.  
  1304.         // check version information
  1305.         parseKeyword(VERSION, nameVERSION.toString());
  1306.         parseToken(EQ,"=");
  1307.         parseToken(QUOTE, "string");
  1308.         scanString(quote, 0xFFFF, 0xFFFF, 0xFFFF);
  1309.         if (!text.equals("1.0"))
  1310.             error("Expected version 1.0 instead of " + text);
  1311.         factory.parsedAttribute(xml,nameVERSION, text);
  1312.  
  1313.         // check encoding information
  1314.         parseKeyword(0, "encoding or standalone");
  1315.         String encoding = null;
  1316.         if (token == ENCODING) {
  1317.             parseToken(EQ,"=");
  1318.             parseToken(QUOTE, "string");
  1319.             scanString(quote, 0xFFFF, 0xFFFF, 0xFFFF);
  1320.             factory.parsedAttribute(xml,nameENCODING, text);
  1321.             encoding = text;
  1322.             parseKeyword(0, nameStandalone.toString());
  1323.         }
  1324.  
  1325.         // check Standalone
  1326.         if (token == STANDALONE) {
  1327.             parseToken(EQ,"=");
  1328.             parseToken(QUOTE, "string");
  1329.             scanString(quote, 0xFFFF, 0xFFFF, 0xFFFF);
  1330.             if (caseInsensitive)
  1331.                 text = text.toUpperCase();
  1332.             Name value = Name.create(text);
  1333.             if (value == nameYes)
  1334.             {
  1335.                 standAlone = true;
  1336.             } 
  1337.             else if (value == nameNo)
  1338.             {
  1339.                 standAlone = false;
  1340.             }
  1341.             else
  1342.             {
  1343.                 error("Expected 'yes' or 'no' instead of " + value);
  1344.             }
  1345.             factory.parsedAttribute(xml, nameStandalone, value.toString());
  1346.             nextToken();
  1347.         }
  1348.         if (encoding != null)
  1349.         {
  1350.             try {
  1351.                 xmlIn.setEncoding(encoding);
  1352.             } catch (IOException e) {
  1353.                 error("Unsupported XML encoding: \"" + encoding + "\"" +
  1354.                        "\nLow level error: " + e.getMessage());
  1355.             }
  1356.         }
  1357.         if (token != PITAGEND) 
  1358.             error("Expected " + tokenString(PITAGEND) + " instead of " + tokenString(token));
  1359.         current.ed = ed;
  1360.         pop();
  1361.     }
  1362.  
  1363.     /**
  1364.      * check for misc elements
  1365.      *
  1366.      * Misc ::= Comment | PI | S
  1367.      */
  1368.     final void tryMisc() throws ParseException
  1369.     {
  1370.         for (;;)
  1371.         {
  1372.             switch (token)
  1373.             {
  1374.                 case PITAGSTART:
  1375.                     parsePI(true);
  1376.                     break;
  1377.                 case COMMENT:
  1378.                     parseComment();
  1379.                     break;
  1380.                 default:
  1381.                     return;
  1382.             }
  1383.             if (lookahead != -1)
  1384.             {
  1385.                 nextToken();
  1386.             }
  1387.             else
  1388.             {
  1389.                 token = EOF;
  1390.                 break;
  1391.             }
  1392.             firstLine = false;
  1393.         }
  1394.     }
  1395.      
  1396.     /**
  1397.      * check for document type declaration
  1398.      *
  1399.      * DocTypeDecl ::=  '<!DOCTYPE' S Name (S ExternalID)? S? ('[' internalsubset* ']' S?)? '>'
  1400.      */
  1401.     final void tryDocTypeDecl() throws ParseException
  1402.     {
  1403.         if (token == DECLTAGSTART)
  1404.         {
  1405.             firstLine = false;
  1406.             parseKeyword(DOCTYPE, "Doctype");
  1407.             Element dtdElement = addNewElement( Element.DTD, nameDOCTYPE, false, null);
  1408.             parseToken(NAME, "Doctype name");
  1409.             docType = name;
  1410.             dtd.docType = name;
  1411.             factory.parsedAttribute(dtdElement,nameNAME,docType);
  1412.             parseKeyword(0, "ExternalID");
  1413.             String url = null;
  1414.             switch(token)
  1415.             {
  1416.                 case SYSTEM:
  1417.                     url = scanUrl();
  1418.                     factory.parsedAttribute(dtdElement,nameURL, url);
  1419.                     nextToken();
  1420.                     break;
  1421.                 case PUBLIC:
  1422.                     parseKeyword(0, "String");
  1423.                     if (token == QUOTE) {
  1424.                         expandNamedEntities = false;
  1425.                         scanString(quote, 0xFFFF, 0xFFFF, 0xFFFF);
  1426.                         expandNamedEntities = true;
  1427.                         factory.parsedAttribute(dtdElement,namePUBLICID,text);                         
  1428.                     }
  1429.                     else 
  1430.                         error("Expected " + tokenString(QUOTE) + " instead of " + tokenString(token));
  1431.                     url = scanUrl();
  1432.                     factory.parsedAttribute(dtdElement,nameURL, url);
  1433.                     nextToken();
  1434.                     break;
  1435.             }
  1436.             if (token == LEFTSQB)
  1437.             {
  1438.                 // set up  context for [...]
  1439.                 inTag = false;
  1440.                 breakText = ']';
  1441.                 internalSubset = true;
  1442.                 // Push the dtd element so we can add to it.
  1443.                 push(dtdElement, nameDOCTYPE, Element.DTD);
  1444.                 parseInternalSubset();
  1445.                 if (token != RIGHTSQB)
  1446.                 {
  1447.                     error("Expected " + tokenString(RIGHTSQB));
  1448.                 }
  1449.                 // restore context 
  1450.                 inTag = true;
  1451.                 internalSubset = false;
  1452.                 breakText = 0;
  1453.                 pop();
  1454.                 nextToken();
  1455.             }
  1456.             if (url != null && loadexternal)
  1457.             {
  1458.                 loadDTD(url.toString(), null);
  1459.             }
  1460.             if (token != TAGEND)
  1461.             {
  1462.                 error("Expected " + tokenString(TAGEND) + " instead of " + tokenString(token));
  1463.             }
  1464.             if (lookahead != -1)
  1465.             {
  1466.                 nextToken();
  1467.             }
  1468.         }
  1469.     }
  1470.     
  1471.  
  1472.     public final void loadDTD(String urlStr, Atom nameSpace) throws ParseException
  1473.     {
  1474.         try {
  1475.             URL u = new URL(url, urlStr);
  1476.             Parser parser = new Parser();
  1477.             parser.dtd = this.dtd;
  1478.             parser.setURL(u);
  1479.             parser.setFactory(factory);
  1480.             parser.caseInsensitive = this.caseInsensitive;
  1481.             parser.loadexternal = this.loadexternal;
  1482.             Element root = factory.createElement(null,Element.ELEMENT, nameDOCTYPE,null);
  1483.             parser.newContext(root, null, Element.ELEMENT, false, nameSpace, current.spaceTable);
  1484.             parser.parseInternalSubset();
  1485.             validating = true;
  1486.         } catch (IOException e) {
  1487.             error("Couldn't find external DTD '" + urlStr + "'"); 
  1488.         }
  1489.     }
  1490.  
  1491.     final Element addNewElement(int type, Name name, boolean validate, String text) throws ParseException
  1492.     {
  1493.         // Make sure we don't add multiple whitespace nodes,
  1494.         // as would be the case while parsing ATTLISTS
  1495.         if (type == Element.WHITESPACE) {
  1496.             current.lastWasWS = true;
  1497.         } else {
  1498.             current.lastWasWS = false;
  1499.         }
  1500.         Element e = factory.createElement(current.e,type, name, text);
  1501.  
  1502.         // Entity has children, so we need to check the contents
  1503.         // of the entity, rather than the entityref itself.
  1504.         // For example, the following should parse correctly:
  1505.         //      <!DOCTYPE doc [
  1506.         //      <!ELEMENT doc (foo)>
  1507.         //      <!ELEMENT foo EMPTY>
  1508.         //      <!ENTITY  bar "<foo/>">
  1509.         //      ]>
  1510.         //      <doc>&bar;</doc>
  1511.         // In order to make this work, we link the entity Context to the
  1512.         // parent element so that when we add elements to the entity, we
  1513.         // can get back to the parent element for validation.
  1514.  
  1515.         if (validate && e != null)
  1516.         {
  1517.             Context c = current;
  1518.             if (current.parent != null)
  1519.                 c = current.parent;
  1520.             if (c.ed != null)
  1521.             {
  1522.                 c.ed.checkContent(c, e, this);
  1523.             }
  1524.         }
  1525.         return e;
  1526.     }
  1527.  
  1528.     /**
  1529.      * Parse DTD content
  1530.      */    
  1531.     final void parseInternalSubset() throws ParseException
  1532.     {
  1533.         substitution++;
  1534.         validating = true;  // we have a dtd then, so we should do validation.
  1535.  
  1536.         for (;;)
  1537.         {
  1538.             switch(nextToken())
  1539.             {
  1540.                 case TEXT:
  1541.                     if (lookahead == '%') { // entity reference
  1542.                         advance();
  1543.                         scanEntityRef(true);
  1544.                     }
  1545.                     else 
  1546.                     {
  1547.                         error("Unexpected text in DTD.");
  1548.                         return;
  1549.                     }
  1550.                     break;
  1551.                 case RIGHTSQB:
  1552.                     if (!internalSubset)
  1553.                         System.out.println("Illegal token in DTD: " + token);
  1554.                 case EOF:
  1555.                     substitution--;
  1556.                     return;
  1557.                 case PITAGSTART:
  1558.                     parsePI(true);
  1559.                     break;
  1560.                 case COMMENT:
  1561.                     parseComment();
  1562.                     break;
  1563.                 case DECLTAGSTART:
  1564.                     parseKeyword(0, "ENTITY|...");
  1565.                     switch (token)
  1566.                     {
  1567.                         case ENTITY:
  1568.                             parseEntityDecl();
  1569.                             break;
  1570.                         case ELEMENT:
  1571.                             parseElementDecl();
  1572.                             break;
  1573.                         case ATTLIST:
  1574.                             parseAttListDecl();
  1575.                             break;
  1576.                         case NOTATION:
  1577.                             parseNotation();
  1578.                             break;
  1579.                         default:
  1580.                             error("Unknown DTD keyword " + name);
  1581.                     }
  1582.                     break;
  1583.                 case INCLUDETAGSTART:  
  1584.                     parseIncludeSection();
  1585.                     break;
  1586.                 case IGNORETAGSTART:
  1587.                     parseIgnoreSection();
  1588.                     break;
  1589.                 default:  
  1590.                     return;
  1591.             }
  1592.         }
  1593.     }
  1594.     
  1595.     /**
  1596.      * Parses conditional section with keyword INCLUDE
  1597.      */
  1598.     final void parseIncludeSection() throws ParseException
  1599.     {
  1600.         Element e = addNewElement(Element.INCLUDESECTION, conditionRef, false, null);
  1601.         push(e,conditionRef,Element.INCLUDESECTION);
  1602.         parseInternalSubset();
  1603.         pop();
  1604.     }
  1605.  
  1606.     /**
  1607.      * Parses conditional section with keyword IGNORE
  1608.      */
  1609.     final void parseIgnoreSection() throws ParseException
  1610.     {
  1611.         Element e = addNewElement(Element.IGNORESECTION, conditionRef, false, null);
  1612.         charAt = 0;
  1613.         push(e,conditionRef,Element.IGNORESECTION);
  1614.         parseIgnoreSectContent();
  1615.  
  1616.         // add contents as PCDATA, which includes the conditional section closing tag ']]>'
  1617.         if (charAt > 0)
  1618.         {
  1619.             addNewElement(Element.PCDATA, null, true, 
  1620.                 new String(chars, 0, charAt));
  1621.             charAt = 0;
  1622.         }  
  1623.         pop();
  1624.     }
  1625.  
  1626.  
  1627.     /**
  1628.      *  Adds char into the chars buffer
  1629.      */
  1630.     final void addChar() throws ParseException
  1631.     {
  1632.         if (lookahead == -1)
  1633.             error("Unexpected EOF.");
  1634.  
  1635.         chars[charAt++] = (char)lookahead;
  1636.         if (charAt == chars.length)
  1637.         {
  1638.             addNewElement(Element.PCDATA, null, true,
  1639.                 new String(chars, 0, charAt));
  1640.             charAt = 0;     
  1641.         }
  1642.         advance();
  1643.     }
  1644.  
  1645.     /**
  1646.      *  Checks whether CDEND ']]>' is the next token in Ignore conditional section
  1647.      */
  1648.     final void checkCDEND(boolean expected) throws ParseException
  1649.     {
  1650.         boolean yes = false;
  1651.  
  1652.         if (lookahead == ']')
  1653.         {
  1654.             addChar();
  1655.             if (lookahead == ']')
  1656.             {
  1657.                 addChar();
  1658.                 if (lookahead == '>')
  1659.                 {
  1660.                     if (!expected)
  1661.                         error("Bad Ignore conditional section syntex. ']]>' is not allowed here.");
  1662.                     yes = true;
  1663.                     addChar();
  1664.                 }
  1665.             }
  1666.         }
  1667.  
  1668.         if (!yes && expected)
  1669.             error("Bad Ignore conditional section syntex. Expected ']]>'.");
  1670.     }
  1671.  
  1672.     /**
  1673.      * Parses Ignore conditional section contents
  1674.      */
  1675.     final void parseIgnoreSectContent() throws ParseException
  1676.     {
  1677.         boolean stop = false;
  1678.  
  1679.         while (lookahead != ']')
  1680.         {
  1681.             switch (lookahead)
  1682.             {
  1683.                 case '\'':  
  1684.                 case '\"':  // SkipLit
  1685.                     int quote = lookahead;
  1686.                     addChar();
  1687.                     while (lookahead != quote)
  1688.                     {
  1689.                         checkCDEND(false);
  1690.                         addChar();
  1691.                     }
  1692.                     addChar(); 
  1693.                     break;
  1694.                 case '<':
  1695.                     addChar();
  1696.                     switch (lookahead)
  1697.                     {
  1698.                         case '!':
  1699.                             addChar();
  1700.                             switch (lookahead)
  1701.                             {
  1702.                                 case '-':  // comment
  1703.                                     addChar();
  1704.                                     if (lookahead != '-')
  1705.                                         error("Bad comment syntax. Expected '-'.");
  1706.                                     addChar();
  1707.                                     while (!stop)
  1708.                                     {
  1709.                                         if (lookahead == '-')
  1710.                                         {
  1711.                                             addChar();
  1712.                                             if (lookahead == '-')
  1713.                                             {
  1714.                                                 addChar();
  1715.                                                 if (lookahead == '>')
  1716.                                                 {
  1717.                                                     addChar();
  1718.                                                     stop = true;
  1719.                                                 }
  1720.                                                 else
  1721.                                                 {
  1722.                                                     error("Bad comment syntax. Expected '>'.");         
  1723.                                                 }
  1724.                                             }
  1725.                                         }
  1726.                                         else
  1727.                                         {
  1728.                                             addChar();
  1729.                                         }
  1730.                                     }
  1731.                                     stop = false;
  1732.                                     break;
  1733.                                 case '[':  // IgnoreSectContents
  1734.                                     addChar();
  1735.                                     parseIgnoreSectContent();
  1736.                                     break;
  1737.                                 default:   // Single character
  1738.                                     addChar();
  1739.                                     break;
  1740.                             }
  1741.                             break;
  1742.                         case '?':  // PI
  1743.                             addChar();
  1744.                             while (!stop)
  1745.                             {
  1746.                                 if (lookahead == '?')
  1747.                                 {
  1748.                                     addChar();
  1749.                                     if (lookahead == '>')
  1750.                                     {
  1751.                                         addChar();
  1752.                                         stop = true;
  1753.                                     }
  1754.                                 }
  1755.                                 else
  1756.                                 {
  1757.                                     addChar();
  1758.                                 }
  1759.                             }
  1760.                             stop = false;
  1761.                             break;
  1762.                         default:   // Syntax error
  1763.                             error("Bad character in IGNORE conditional section.");
  1764.                     }
  1765.                     break;
  1766.                 default:
  1767.                     addChar();
  1768.                     break;
  1769.             }
  1770.         }
  1771.  
  1772.         // checking closing tag ']]>'
  1773.         checkCDEND(true);
  1774.     }
  1775.  
  1776.     /**
  1777.      * Parse Entity declaration
  1778.      * EntityDecl ::= '<!ENTITY' S Name S EntityDef S? '>'
  1779.      *                | '<!ENTITY' S '%' S Name S EntityDef S? '>'
  1780.      */    
  1781.     final void parseEntityDecl() throws ParseException
  1782.     {
  1783.         boolean par = false;
  1784.         
  1785.         nouppercase++;
  1786.         if (nextToken() == PERCENT)
  1787.         {
  1788.             par = true;
  1789.             parseToken(NAME, "Entity name");
  1790.         }
  1791.         else
  1792.         {
  1793.             if (token != NAME)
  1794.             {
  1795.                 error("Expected entity name instead of " + tokenString(token));
  1796.             }
  1797.         }
  1798.         nouppercase--;
  1799.  
  1800.         Entity en = dtd.findEntity(name);
  1801.         if (en != null)
  1802.         {
  1803.             System.err.println("Warning: Entity '" + name + "' already defined, using the first definition."); 
  1804.             // soak up the duplicate and throw it away.
  1805.             en = new Entity(name, par);
  1806.         }
  1807.         else
  1808.         {
  1809.             en = new Entity(name, par);
  1810.             dtd.addEntity(en);
  1811.             if (internalSubset) {
  1812.                 // Save this entity in the <!DOCTYPE>
  1813.                 // Element tree so we can save it back out.
  1814.                 if (current.e != null)
  1815.                     current.e.addChild(en,null);
  1816.                 current.lastWasWS = false;
  1817.             }    
  1818.         }
  1819.  
  1820.         // push so that we can give correct error message when an error is found in nextToken()
  1821.         // this is also necessary for NDATA to have name space
  1822.         push(en, name,Element.ENTITY);
  1823.  
  1824.         parseKeyword(0, "String or SYSTEM");
  1825.         if (token == PUBLIC)
  1826.         {
  1827.             parseKeyword(0, "String");
  1828.             if (token == QUOTE) {
  1829.                 expandNamedEntities = false;
  1830.                 scanString(quote, 0xFFFF, 0xFFFF, 0xFFFF);
  1831.                 expandNamedEntities = true;
  1832.                 en.pubid = text; 
  1833.                 token = SYSTEM; // assure to get url
  1834.             }
  1835.             else error("Expected " + tokenString(QUOTE) + " instead of " + tokenString(token));
  1836.         }
  1837.  
  1838.         switch (token)
  1839.         {
  1840.             case QUOTE:
  1841.                 {
  1842.                     int line = reader.line;
  1843.                     int column = reader.column;
  1844.                     expandNamedEntities = false;
  1845.                     scanString(quote, '%', '&', 0xFFFF);
  1846.                     expandNamedEntities = true;
  1847.                     en.setText(text);   
  1848.                     en.setPosition(line, column);
  1849.                 }
  1850.                 nextToken();
  1851.                 break;
  1852.             case SYSTEM:
  1853.                 en.setURL(scanUrl());
  1854.                 parseKeyword(0, "ndata");
  1855.                 if (token == NDATA)
  1856.                 {
  1857.                     parseToken(NAME, "ndata name");
  1858.                     Notation notationName = dtd.findNotation(name);
  1859.                     if (notationName == null)
  1860.                         error("Notation: " + name + " has not been declared yet");
  1861.                     en.setNDATA(name);
  1862.                     nextToken();
  1863.                 }
  1864.                 break;
  1865.             default:
  1866.                 error("Expected " + tokenString(QUOTE) + " or " + tokenString(SYSTEM) + " instead of " + tokenString(token));
  1867.         }
  1868.  
  1869.         checkToken(TAGEND, ">");
  1870.         pop();
  1871.     }
  1872.  
  1873.  
  1874.     private void addNameSpace(Atom as, Atom href, boolean global) throws ParseException
  1875.     {
  1876.         if (as == null || href == null) {
  1877.             error("Name space syntax error.");
  1878.         }
  1879.         if (DTD.isReservedNameSpace(as))
  1880.             error(as.toString() + " is a reserved name space.");
  1881.         if (global) 
  1882.         {
  1883.             Atom aname = dtd.findShortNameSpace(href);
  1884.             if (aname != null)
  1885.             {
  1886.                 if (aname != as)
  1887.                     error("Cannot give two short references '" + aname.toString() + "' and '" + as.toString() + "' to the same name space: '" + href.toString() + "'");
  1888.             }
  1889.             else {
  1890.                 aname = dtd.findLongNameSpace(as);
  1891.                 if (aname != null)
  1892.                     error("Short reference '" + as.toString() + "' is used by name space '" + aname.toString() + "'");
  1893.                 dtd.addNameSpace(as, href);
  1894.             }
  1895.         }
  1896.         else current.addNameSpace(as, href);
  1897.     }
  1898.  
  1899.     /**
  1900.      * Parse notation
  1901.      * NotationDecl ::= '<!NOTATION' S Name S ExternalID S? '>'
  1902.      */    
  1903.     final void parseNotation() throws ParseException
  1904.     {
  1905.         parseToken(NAME, "Notation name");
  1906.                   
  1907.         Notation no = dtd.findNotation(name);
  1908.         if (no != null)
  1909.         {
  1910.             error("Notation already declared " + name);
  1911.         }
  1912.  
  1913.         no = new Notation(name);
  1914.         dtd.addNotation(no);
  1915.         if (internalSubset) {
  1916.             // Save this notation in the <!DOCTYPE>
  1917.             // Element tree so we can save it back out.
  1918.             if (current.e != null)
  1919.                 current.e.addChild(no,null);
  1920.             current.lastWasWS = false;
  1921.         }    
  1922.  
  1923.         // needed to write correct error message
  1924.         push(no, name, Element.NOTATION);
  1925.  
  1926.         parseKeyword(0, "SYSTEM or PUBLIC");
  1927.         if (token != SYSTEM && token != PUBLIC)
  1928.         {
  1929.             error("Expected " + tokenString(SYSTEM) + " or " + tokenString(PUBLIC) + " instead of " + tokenString(token));
  1930.         }
  1931.         no.type = token;
  1932.  
  1933.         if (no.type == PUBLIC) {
  1934.             parseKeyword(0, "String");
  1935.             if (token == QUOTE) {
  1936.                 expandNamedEntities = false;
  1937.                 scanString(quote, 0xFFFF, 0xFFFF, 0xFFFF);
  1938.                 expandNamedEntities = true;
  1939.                 no.pubid = text; 
  1940.             }
  1941.             else error("Expected " + tokenString(QUOTE) + " instead of " + tokenString(token));
  1942.         }
  1943.  
  1944.         no.setURL(scanUrl());   
  1945.         
  1946.         parseToken(TAGEND, ">");
  1947.         pop();
  1948.     }
  1949.  
  1950.     final ElementDecl createElementDecl(Vector v) throws ParseException
  1951.     {
  1952.  
  1953.         if (token != NAME)
  1954.         {
  1955.             error("Expected " + tokenString(NAME) + " instead of " + tokenString(token));
  1956.         }
  1957.         if (dtd.findElementDecl(name) != null)
  1958.         {
  1959.             error("Element '" + name + "' already declared.");
  1960.         }
  1961.         ElementDecl ed = new ElementDecl(name);
  1962.         current.lastWasWS = false;
  1963.         dtd.addElementDecl(ed);
  1964.         v.addElement(ed);
  1965.         return ed;
  1966.     }
  1967.     
  1968.     /**
  1969.      * Parse element declaration
  1970.      */
  1971.     final void parseElementDecl() throws ParseException
  1972.     {
  1973.         Vector v = new Vector();
  1974.         ElementDecl ed;
  1975.  
  1976.         nextToken();
  1977.         ed = createElementDecl(v);
  1978.         if (internalSubset) {
  1979.             // Save this element in the <!DOCTYPE>
  1980.             // Element tree so we can save it back out.
  1981.             if (current.e != null)
  1982.                 current.e.addChild(ed,null);
  1983.         }   
  1984.         // push to handle name space
  1985.         push(ed, name,Element.ELEMENTDECL);
  1986.         ed.parseModel(this);
  1987.         checkToken(TAGEND, ">");
  1988.         pop();
  1989.     }
  1990.     
  1991.     final ElementDecl findElementDecl(Vector v) throws ParseException
  1992.     {
  1993.         if (token != NAME)
  1994.         {
  1995.             error("Expected " + tokenString(NAME) + " instead of " + tokenString(token));
  1996.         }
  1997.         ElementDecl ed = dtd.findElementDecl(name);
  1998.  
  1999.         if (ed == null)
  2000.         {
  2001.             error("Missing Element declaration '" + name + "'");
  2002.         }
  2003.         v.addElement(ed);
  2004.         return ed;
  2005.     }
  2006.     
  2007.     /**
  2008.      * Parse element declaration
  2009.      * AttlistDecl ::= '<!ATTLIST' S Name AttDef+ S? '>'
  2010.      */
  2011.     final void parseAttListDecl() throws ParseException
  2012.     {
  2013.         Vector v = new Vector();
  2014.         ElementDecl ed;
  2015.  
  2016.         nextToken();
  2017.         ed = findElementDecl(v);
  2018.         // push to handle name space
  2019.         push(ed, name,Element.ELEMENTDECL);
  2020.         ed.parseAttList(this);
  2021.         checkToken(TAGEND, ">");
  2022.         pop();
  2023.     }
  2024.  
  2025.  
  2026.     private void reportMismatch(int state, String msg) throws ParseException
  2027.     {
  2028.         if (current.ed == null) // this should never happen unless the parser is in incorrect state
  2029.         {
  2030.             error("Content mismatch. Stopped at state " + state);
  2031.         }
  2032.  
  2033.         Vector els = current.ed.expectedElements(state);
  2034.         error(msg + "  Expected elements " + els);
  2035.     }
  2036.  
  2037.  
  2038.     /**
  2039.      * expect element content
  2040.      */    
  2041.     final void parseElement() throws ParseException
  2042.     {
  2043.         boolean empty = false;
  2044.  
  2045.         for (;;)
  2046.         {
  2047.             if (empty && token != CLOSETAGSTART)
  2048.                 error("Expected " + tokenString(CLOSETAGSTART) + " instead of " + tokenString(token));
  2049.             empty = false;
  2050.  
  2051.             switch(token)
  2052.             {
  2053.                 case TAGSTART:                 
  2054.                     scanName("element tag");
  2055.  
  2056.                     ElementDecl ed = null;
  2057.  
  2058.                     Element e = addNewElement(Element.ELEMENT, name, true,null);
  2059.                     push(e,name,Element.ELEMENT);
  2060.  
  2061.                     if (validating)
  2062.                     {
  2063.                         ed = dtd.findElementDecl(name);
  2064.                         if (ed != null)
  2065.                         {
  2066.                             if (ed.getContent().type == ContentModel.EMPTY)
  2067.                             {
  2068.                                 empty = true;
  2069.                             }
  2070.                             ed.initContent(current, this);
  2071.                         }
  2072.                         else
  2073.                         {
  2074.                             error("Element '" + name + "' used but not declared in the DTD.");
  2075.                         }
  2076.                     }
  2077.                     else
  2078.                     {
  2079.                         // set matched so parsecontent won't complain
  2080.                         current.matched = true;
  2081.                     }
  2082.  
  2083.                     parseAttributes(e);
  2084.                     if (token == EMPTYTAGEND)
  2085.                     {
  2086.                         if (ed != null && !ed.acceptEmpty(current))
  2087.                         {
  2088.                             reportMismatch(0, ed.name.getName() + " cannot be empty.");
  2089.                         }
  2090.                         factory.parsed(current.e);
  2091.                         pop();
  2092.                         empty = false;
  2093.                     }
  2094.                     else if (token != TAGEND )
  2095.                     {
  2096.                         error("Expected " + tokenString(TAGEND) + " instead of " + tokenString(token));
  2097.                     }
  2098.                     else if (lookahead != '<' && empty)
  2099.                     {
  2100.                         error("Expected " + tokenString(TAGEND) + " instead of '" + (char)lookahead + "'");
  2101.                     }
  2102. //                    current.checkAllowText();
  2103.                     break;
  2104.                 case TEXT:
  2105.                     parseText('&', '&');
  2106.                     break;
  2107.                 case PITAGSTART:
  2108.                     parsePI(false);
  2109.                     break;
  2110.                 case COMMENT:
  2111.                     parseComment();
  2112.                     break;
  2113.                 case CDATATAGSTART:
  2114.                     parseCDATA();
  2115.                     break;
  2116.                 case CLOSETAGSTART:
  2117.                     if (!current.matched) // error
  2118.                     {
  2119.                         reportMismatch(current.state, current.e.getTagName().getName() + " is not complete.");
  2120.                     }
  2121.                     //if (lookahead != '>') // allow </>
  2122.                     //{
  2123.                         if (lookahead == '/')
  2124.                             advance();
  2125.                         else
  2126.                         {
  2127.                             // This little hack with the current context pointer
  2128.                             // makes sure that the namespace scope inside the tag
  2129.                             // is not applied to the end tag.  For example, if the
  2130.                             // namespace "foo" is defined to point to DTD "xyz.dtd", 
  2131.                             // then tag <foo::bar> is started, then the namespace
  2132.                             // "foo" is redefined inside it to point to some other
  2133.                             // DTD then the end tag is found </foo::bar>, then the
  2134.                             // namespace for this end tag must point to the xyz.dtd
  2135.                             // otherwise we will have a tag mismatch.  So we temporarily
  2136.                             // pop the context while scanning the end tag name.  We
  2137.                             // cannot do a full pop() here because the context is still
  2138.                             // needed after scanName, for example if an error occurs, and
  2139.                             // in the call to the factory->parsed method.
  2140.                             Context c = current;
  2141.                             current = (Context)contexts.elementAt(contextAt-1);                            scanName("element close tag");
  2142.                             current = c;
  2143.                             if (name != current.tagName)
  2144.                             {
  2145.                                 error("Close tag " + name + " does not match start tag " + current.e.getTagName());
  2146.                             }
  2147.                         }
  2148.                     //}
  2149.  
  2150.                     parseToken(TAGEND, ">");
  2151.                     factory.parsed(current.e);
  2152.                     pop();
  2153.                     break;
  2154.                 case EOF:
  2155.                     if (contextAt == 1) 
  2156.                     {
  2157.                         error("Expected the end of root element instead of end of file.");
  2158.                         break;
  2159.                     }
  2160.                 default:
  2161.                     error("Bad token in element content: " + tokenString(token));
  2162.             }
  2163.             if (contextAt == 0)
  2164.                 break;
  2165.             nextToken();
  2166.         }
  2167.     }
  2168.  
  2169.  
  2170.     /**
  2171.      * parse root element
  2172.      */    
  2173.     final void parseRootElement() throws ParseException
  2174.     {
  2175.         if (token != TAGSTART)  // error
  2176.             error("Start of root element expected instead of " + tokenString(token));
  2177.  
  2178.         scanName("element tag");
  2179.         if (docType != null) {
  2180.             if (name != docType)
  2181.                 error("Root element name must match the DOCTYPE name");
  2182.         }
  2183.  
  2184.         if (name == nameXMLNameSpace)
  2185.         {
  2186.             parseNameSpaceDecl(false, false);
  2187.             return;
  2188.         }
  2189.  
  2190.  
  2191.         ElementDecl ed = null;
  2192.  
  2193.         Element e = addNewElement(Element.ELEMENT, name, false,null);
  2194.         push(e,name,Element.ELEMENT);
  2195.         boolean empty = false;
  2196.  
  2197.         if (validating)
  2198.         {
  2199.             ed = dtd.findElementDecl(name);
  2200.             if (ed != null)
  2201.             {
  2202.                 if (ed.getContent().type == ContentModel.EMPTY)
  2203.                 {
  2204.                     empty = true;
  2205.                 }
  2206.                 ed.initContent(current, this);
  2207.             }
  2208.             else
  2209.             {
  2210.                 error("Element '" + name + "' used but not declared in the DTD.");
  2211.             }
  2212.         }
  2213.         else
  2214.         {
  2215.             // set matched so parsecontent won't complain
  2216.             current.matched = true;
  2217.         }
  2218.  
  2219.         parseAttributes(e);
  2220.         if (token == EMPTYTAGEND)
  2221.         {
  2222.             if (ed != null && !ed.acceptEmpty(current))
  2223.                 reportMismatch(0, "Root element " + e.getTagName().getName() + " cannot be empty.");
  2224.             empty = true;
  2225.         }
  2226.         else if (token != TAGEND)
  2227.         {
  2228.             if (e.getAttributes() == EnumWrapper.emptyEnumeration)
  2229.                 error("No attribute is declared for element '" + e.getTagName() + "', expected " + tokenString(TAGEND));
  2230.             error("Expected " + tokenString(TAGEND) + " instead of " + tokenString(token));
  2231.         }
  2232. //        current.checkAllowText();
  2233.         if (empty)
  2234.         {
  2235.             pop();
  2236.             nextToken();
  2237.         }
  2238.         else {
  2239.             nextToken(); 
  2240.             parseElement();
  2241.         }
  2242.         return;
  2243.     }
  2244.  
  2245.     final void parseAttributes(Element e) throws ParseException
  2246.     {
  2247.         boolean ns = false;
  2248.  
  2249.         while (nextToken() == NAME)
  2250.         {
  2251.             Name currName = name;  // Because parseAttributes may reset name, currName
  2252.                                    // is needed to hold name and check for nameXMLSpace 
  2253.  
  2254.             // make sure xml-space belongs to the XML namespace.
  2255.             if (currName.getName().equals(nameXMLSpace.getName()))
  2256.             {
  2257.                 currName = nameXMLSpace;
  2258.                 ns = true;
  2259.             }
  2260.  
  2261.             if (e != null && e.getAttribute(currName) != null)
  2262.             {
  2263.                 error("An attribute cannot appear more than once in the same start tag");
  2264.             }
  2265.             parseToken(EQ, "=");
  2266.  
  2267.             if (currName == nameXMLLang)
  2268.             {
  2269.                 parseToken(QUOTE, "string");
  2270.                 parseToken(NAME, "lang code");
  2271.                 Name code = name;
  2272.                 parseToken(QUOTE, "string");
  2273.                 factory.parsedAttribute(e, currName, code.getName());
  2274.             } 
  2275.             else if (current.ed != null && e != null)
  2276.             {
  2277.                 current.ed.parseAttribute(e, currName, this);
  2278.             }
  2279.             else 
  2280.             {
  2281.                 parseToken(QUOTE, "string");
  2282.                 scanString(quote, '&', '&', '<');
  2283.                 factory.parsedAttribute(e, currName, text);
  2284.             }
  2285.         }
  2286.  
  2287.         if (ns)
  2288.         {
  2289.             Object attr = e.getAttribute(nameXMLSpace);
  2290.             if (attr == null) attr = e.getAttribute(nameXMLSpace2);
  2291.             if (attr != null)
  2292.             {
  2293.                 String s = null;
  2294.                 if (attr instanceof String) 
  2295.                 {
  2296.                     s = (String)attr;
  2297.                 } 
  2298.                 else if (attr instanceof Atom)
  2299.                 {
  2300.                     s = attr.toString();
  2301.                 }
  2302.                 else if (attr instanceof Name)
  2303.                 {
  2304.                     s = ((Name)attr).getName().toString();   
  2305.                 }
  2306.  
  2307.                 if (s != null && s.equalsIgnoreCase("preserve")) 
  2308.                 {
  2309.                     current.preserveWS = true;
  2310.                 } 
  2311.                 else if (s != null && s.equalsIgnoreCase("default")) 
  2312.                 {
  2313.                     current.preserveWS = false;
  2314.                 } 
  2315.                 else 
  2316.                 {
  2317.                     error("Invalid value '" + s + "' for xml-space attribute.");
  2318.                 }
  2319.             }
  2320.         }
  2321.  
  2322.         if (current.ed != null)
  2323.             current.ed.checkAttributes(e, this);
  2324.     }
  2325.  
  2326.  
  2327.     final void parseContent(Element e) throws ParseException
  2328.     {
  2329.         while (nextToken() != CLOSETAGSTART)
  2330.         {
  2331.             switch (token)
  2332.             {
  2333.                 case TEXT:
  2334.                     parseText('&', '&');
  2335.                     break;
  2336.                 case TAGSTART:
  2337.                     parseElement();
  2338.                     break;
  2339.                 case PITAGSTART:
  2340.                     parsePI(false);
  2341.                     break;
  2342.                 case DECLTAGSTART:
  2343.                     parseElement();
  2344.                     break;
  2345.                 case COMMENT:
  2346.                     parseComment();
  2347.                     break;
  2348.                 case CDATATAGSTART:
  2349.                     parseCDATA();
  2350.                     break;
  2351.                 default:
  2352.                     error("Bad token in element content: " + tokenString(token));
  2353.             }
  2354.         }        
  2355.         if (!current.matched)
  2356.         {
  2357.             error("Content mismatch, stopped at state " + current.state);
  2358.         }
  2359.         if (lookahead != TAGEND)
  2360.         {
  2361.             scanName("element close tag");
  2362.             if (name != current.e.getTagName())
  2363.             {
  2364.                 error("Close tag mismatch: " + name + " instead of " + current.e.getTagName());
  2365.             }
  2366.         }
  2367.         parseToken(TAGEND, ">");
  2368.     }
  2369.  
  2370.     final Element parsePI(boolean global) throws ParseException
  2371.     {
  2372.         parseKeyword(0, "PI name");
  2373.         if (token == XML)
  2374.         {
  2375.             if (!firstLine)
  2376.                 error("An XML declaration can only appear in the very beginning of the document.");
  2377.             else {
  2378.                 parseXMLDecl();
  2379.                 return null;
  2380.             }
  2381.         }
  2382.         else if (token == NAMESPACE)
  2383.             return parseNameSpaceDecl(global, true);
  2384.         else if (token != NAME)
  2385.         {
  2386.             error("Expecting PI name instead of " + tokenString(token));
  2387.         }
  2388.  
  2389.         return finishPI();
  2390.     }
  2391.  
  2392.     final Element parseNameSpaceDecl(boolean global, boolean PI) throws ParseException
  2393.     {
  2394.         Element e = addNewElement(Element.NAMESPACE, name, false,null);
  2395.         push(e,name,Element.NAMESPACE);
  2396.         parseAttributes(e);
  2397.         pop();
  2398.  
  2399.         if (PI && token != PITAGEND) 
  2400.         {
  2401.             error("Expected PI tag end '?>' instead of " + tokenString(token));
  2402.         }
  2403.         else if (!PI && token != EMPTYTAGEND)
  2404.         {
  2405.             error("Expected " + tokenString(EMPTYTAGEND) + " instead of " + tokenString(token));
  2406.         }
  2407.  
  2408.         Object asStr = e.getAttribute(nameXMLAS);
  2409.         Object hrefStr = e.getAttribute(nameXMLHREF);
  2410.         Object nsStr = e.getAttribute(nameXMLNS);
  2411.  
  2412.         if (asStr == null || nsStr == null) // href is now optional 
  2413.         {
  2414.             error("Missing attribute 'ns' or 'prefix'");          
  2415.         }
  2416.  
  2417.         Atom as, ns, href = null;
  2418.         if (caseInsensitive)
  2419.         {
  2420.             as = Atom.create(asStr.toString().toUpperCase());
  2421.             ns = Atom.create(nsStr.toString().toUpperCase());
  2422.             if (hrefStr != null) href = Atom.create(href.toString().toUpperCase());
  2423.         }
  2424.         else
  2425.         {
  2426.             as = Atom.create(asStr.toString());
  2427.             ns = Atom.create(nsStr.toString());
  2428.             if (hrefStr != null) href = Atom.create(href.toString());
  2429.         }
  2430.         if (DTD.isReservedNameSpace(as))
  2431.         {
  2432.             error(as.toString() + " is a reserved name space.");
  2433.         }
  2434.  
  2435.         addNameSpace(as, ns, global); // ns is the unique urn.
  2436.  
  2437.         if (loadexternal && href != null) { // load name space
  2438.             // if the dtd has not been loaded, load it
  2439.             if (dtd.findLoadedNameSpace(href) == null)
  2440.             {
  2441.                 dtd.addLoadedNameSpace(href);
  2442.                 loadDTD(href.toString(), href);
  2443.             }
  2444.         }
  2445.     
  2446.         return e;
  2447.     }
  2448.  
  2449.     final Element finishPI() throws ParseException
  2450.     {
  2451.         Element e = addNewElement(Element.PI, name, false,null);
  2452.  
  2453.         charAt = 0;
  2454.         boolean stop = false;
  2455.         while (lookahead != -1)
  2456.         {
  2457.             chars[charAt++] = (char)lookahead;
  2458.             if (lookahead == '?')
  2459.             {
  2460.                 advance();
  2461.                 if (lookahead == '>')
  2462.                 {
  2463.                     charAt--;
  2464.                     stop = true;
  2465.                 }
  2466.             }
  2467.             else
  2468.             {
  2469.                 advance();
  2470.             }
  2471.             if (charAt == chars.length || stop)
  2472.             {
  2473.                 push(e,name,Element.PI);
  2474.                 addNewElement(Element.CDATA, null, false,
  2475.                     new String(chars, 0, charAt));
  2476.                 pop();
  2477.                 charAt = 0;
  2478.                 if (stop)
  2479.                     break;
  2480.             }
  2481.         }
  2482.  
  2483.         parseToken(TAGEND, "PI end");
  2484.         return e;
  2485.     }
  2486.  
  2487.     final Element parseText(int eref, int cref) throws ParseException
  2488.     {
  2489.         scanText(eref, cref, ! current.preserveWS);
  2490.         return addPCDATA();
  2491.     }
  2492.  
  2493.     final Element addPCDATA() throws ParseException
  2494.     {
  2495.         if (charAt > 0 || seenWS) 
  2496.         {
  2497.             if (seenWS)
  2498.             {
  2499.                 chars[charAt++] = ' ';
  2500.                 seenWS = false;
  2501.             }
  2502.             text = new String(chars, 0, charAt);
  2503.             Element t = addNewElement(Element.PCDATA, null, true, text);
  2504.             return t;
  2505.         }
  2506.         seenWS = false; 
  2507.         return null;
  2508.     }
  2509.  
  2510.  
  2511.     /**
  2512.      * Parses comment <code> '<!--' comment '-->' </code>
  2513.      */
  2514.     final Element parseComment() throws ParseException
  2515.     {
  2516.         Element e = addNewElement(Element.COMMENT, nameComment, false, null);
  2517.  
  2518.         charAt = 0;
  2519.         boolean stop = false;
  2520.         while (lookahead != -1)
  2521.         {
  2522.             chars[charAt++] = (char)lookahead;
  2523.             if (lookahead == '-')
  2524.             {
  2525.                 advance();
  2526.                 if (lookahead == '-')
  2527.                 {
  2528.                     advance();
  2529.                     if (lookahead == TAGEND)
  2530.                     {
  2531.                         charAt--;
  2532.                         stop = true;
  2533.                     }
  2534.                     else if (strict)
  2535.                     {
  2536.                         error("Bad comment syntax. Expected '>'.");
  2537.                     }
  2538.                     else
  2539.                     {
  2540.                         reader.push((char)lookahead);
  2541.                         lookahead = '-';
  2542.                     }
  2543.                 }
  2544.             }
  2545.             else
  2546.             {
  2547.                 advance();
  2548.             }
  2549.             if (charAt == chars.length || stop)
  2550.             {
  2551.                 push(e,nameComment,Element.COMMENT);
  2552.                 addNewElement(Element.CDATA, null, false,
  2553.                     new String(chars, 0, charAt));
  2554.                 pop();
  2555.                 charAt = 0;
  2556.                 if (stop)
  2557.                     break;
  2558.             }
  2559.  
  2560.         }
  2561.  
  2562.         parseToken(TAGEND, "comment end");
  2563.         return e;
  2564.     }
  2565.  
  2566.  
  2567.     /**
  2568.      *  Parses CDATA <code> '<![CDATA[' data ']]>'  </code> 
  2569.      */
  2570.     final void parseCDATA() throws ParseException
  2571.     {
  2572.         charAt = 0;
  2573.         boolean stop = false;
  2574.         while (lookahead != -1)
  2575.         {
  2576.             chars[charAt++] = (char)lookahead;
  2577.             if (lookahead == ']')
  2578.             {
  2579.                 advance();
  2580.                 if (lookahead == ']')
  2581.                 {
  2582.                     advance();
  2583.                     if (lookahead == TAGEND)
  2584.                     {
  2585.                         charAt--;
  2586.                         stop = true;
  2587.                     }
  2588.                     else
  2589.                     {
  2590.                         reader.push((char)lookahead); 
  2591.                         lookahead = ']';
  2592.                     }
  2593.                 }
  2594.             }
  2595.             else
  2596.             {
  2597.                 advance();
  2598.             }
  2599.  
  2600.             if (charAt == chars.length || stop)
  2601.             {
  2602.                 addNewElement(Element.CDATA, nameCDATA, false,
  2603.                     new String(chars, 0, charAt));
  2604.                 charAt = 0;
  2605.                 if (stop)
  2606.                     break;
  2607.             }
  2608.         }
  2609.  
  2610.         parseToken(TAGEND, "CDATA end");
  2611.     }
  2612.  
  2613.  
  2614.     /**
  2615.      * Set url and open an input stream for it
  2616.      */
  2617.     final private void setURL(URL u) throws ParseException
  2618.     {
  2619.         url = u;
  2620.         String vendor = System.getProperty("java.vendor");
  2621.         boolean msft = (vendor.indexOf("Microsoft") == 0);
  2622.         String os = System.getProperty("os.name");
  2623.         boolean windows = (os.indexOf("Windows") == 0);
  2624.         boolean opt = msft && jdk11 && windows;
  2625.             
  2626.         String err;
  2627.         //
  2628.         // on windows system, try to use optimized inputstream
  2629.         //
  2630.         if (opt)
  2631.         {
  2632.             try 
  2633.             {
  2634.                 xmlIn = new XMLInputStream(u); 
  2635.                 reader = new EntityReader(xmlIn, 1, 1, null, this);
  2636.                 advance();
  2637.                 return;
  2638.             } 
  2639.             catch (IOException e) 
  2640.             {
  2641.                 opt = false;
  2642.                 err = e.toString();
  2643.             }
  2644.         } 
  2645.  
  2646.         //
  2647.         // If not on windows, or if on windows but failed to open the optimized
  2648.         // input stream, then use the I/O facilities provided by Java
  2649.         //
  2650.         if (!opt)
  2651.         {
  2652.             try
  2653.             {
  2654.                 setInputStream( new BufferedInputStream(url.openStream()));
  2655.             }
  2656.             catch (IOException e) 
  2657.             {
  2658.                 throw new ParseException("Error opening input stream for \"" + 
  2659.                     url.toString() + "\": " + e.toString());
  2660.             }
  2661.         }
  2662.     }
  2663.  
  2664.     final private void setInputStream(InputStream in) throws ParseException
  2665.     {
  2666.         xmlIn = new XMLInputStream(in);
  2667.         reader = new EntityReader(xmlIn, 1, 1, null, this);
  2668.         advance();
  2669.     }
  2670.  
  2671.     final private void setFactory(ElementFactory f) 
  2672.     {
  2673.         factory = f;
  2674.     }
  2675.     
  2676.     /**
  2677.      * Factory to create elements on the parse tree.
  2678.      */    
  2679.     ElementFactory factory;
  2680.     
  2681.     /**
  2682.      * DTD object.
  2683.      */    
  2684.     DTD dtd;      
  2685.     boolean validating; // whether we loaded any DTD's for validation.
  2686.     
  2687.     /**
  2688.      * Root of tree.
  2689.      */
  2690.     Element root;
  2691.     
  2692.     /**
  2693.      * Stack to keep track of contexts
  2694.      */
  2695.     Vector contexts = new Vector(16);
  2696.     int contextAt = 0;
  2697.  
  2698.     /**
  2699.      * Current element context.
  2700.      */
  2701.     Context current;
  2702.     
  2703.     /**
  2704.      * Inputstream used to read Unicode chars
  2705.      */    
  2706.     EntityReader reader;
  2707.     XMLInputStream xmlIn;
  2708.  
  2709.     /**
  2710.      * True if parsing inside tag
  2711.      */
  2712.     boolean inTag;
  2713.  
  2714.     /**
  2715.      * true when scanner is still collapsing white space.
  2716.      */
  2717.     boolean seenWS;
  2718.     
  2719.     /**
  2720.      * next character
  2721.      */    
  2722.     int lookahead;
  2723.     
  2724.     /**
  2725.      * quote char
  2726.      */    
  2727.     char quote;
  2728.     
  2729.     /**
  2730.      * chars collected up to 8K
  2731.      */    
  2732.     char chars[] = new char[8192];
  2733.     
  2734.     /**
  2735.      * char index into chars[]
  2736.      */    
  2737.     int charAt;
  2738.     
  2739.     /**
  2740.      * buf collected up to 8K
  2741.      */    
  2742.     char buf[] = new char[8192];
  2743.     
  2744.     /**
  2745.      * char index into buf[]
  2746.      */    
  2747.     int bufAt;
  2748.  
  2749.     /** 
  2750.      * counter to allow collecting multiple names into buf
  2751.      */
  2752.     int nameappend;
  2753.     
  2754.     /**
  2755.      * Token matching hashtable
  2756.      */    
  2757.     static Hashtable tokens;
  2758.  
  2759.     /**
  2760.      * token type
  2761.      */    
  2762.     int token;
  2763.     
  2764.     /**
  2765.      * counter to allow keyword checking
  2766.      */
  2767.     int keyword;
  2768.  
  2769.     /**
  2770.      * counter to disable name uppercasing
  2771.      */
  2772.     int nouppercase;
  2773.  
  2774.     /**
  2775.      * counter to allow parameter substition in token
  2776.      */
  2777.     int substitution;
  2778.  
  2779.     /**
  2780.      * break char for parseInternalSubset
  2781.      */
  2782.     int breakText;
  2783.     
  2784.     /**
  2785.      * switch to allow parsing name tokens in scanName
  2786.      */    
  2787.     int nametoken;
  2788.  
  2789.     /**
  2790.      * switch to allow parsing unqualified name in scanName
  2791.      */    
  2792.     int simplename;
  2793.  
  2794.     /**
  2795.      * switch to allow namespace when scan entity reference name in scanName
  2796.      */    
  2797.     int inEntityRef;
  2798.  
  2799.     /**
  2800.      * whether or not to expand named entities.
  2801.      */
  2802.     boolean expandNamedEntities;
  2803.  
  2804.     static boolean jdk11;
  2805.  
  2806.     /**
  2807.      * current name fetched
  2808.     */
  2809.     Name name;
  2810.  
  2811.     /**
  2812.      * current string or text fetched
  2813.     */
  2814.     String text;
  2815.  
  2816.     /**
  2817.       * document name string
  2818.       */
  2819.     URL url;
  2820.  
  2821.     /**
  2822.      * name in DOCTYPE tag
  2823.      */
  2824.     Name docType;
  2825.  
  2826.     /**
  2827.      * whether we are parsing internal subset
  2828.      */
  2829.     boolean internalSubset;
  2830.  
  2831.     /**
  2832.      * whether the document is caseInsensitive
  2833.      */
  2834.     boolean caseInsensitive;
  2835.  
  2836.     /**
  2837.      * whether the parser is parsing the first line of a document
  2838.      */
  2839.     boolean firstLine = true;
  2840.  
  2841.     /**
  2842.      * XML element declaration
  2843.      */
  2844.     static ElementDecl XMLDecl;
  2845.  
  2846.     /**
  2847.      * If the keyword of the latest conditional section is a parameter entity reference, this 
  2848.      * variable records the reference name; otherwise null.
  2849.      */
  2850.     Name conditionRef;
  2851.  
  2852.     /**
  2853.      * Whether standalone was specified in XML declaration.
  2854.      * (default is false).
  2855.      */ 
  2856.     boolean standAlone;
  2857.  
  2858.     /**
  2859.      * Whether to load external DTD's
  2860.      */
  2861.     boolean loadexternal;
  2862.  
  2863.     /**
  2864.      * Char type table
  2865.      */
  2866.     static int chartype[] = new int[256];
  2867.  
  2868.     /**
  2869.      * Char upper case table
  2870.      */
  2871.     static char charupper[] = new char[256];
  2872.  
  2873.     static final int FWHITESPACE    = 1;
  2874.     static final int FDIGIT         = 2;
  2875.     static final int FLETTER        = 4;
  2876.     static final int FMISCNAME      = 8;
  2877.     static final int FSTARTNAME     = 16;
  2878.  
  2879.     static final char nameSpaceSeparator = ':';
  2880.  
  2881.     /**
  2882.      * This flag specifies whether the parser should enforce strict
  2883.      * XML compliance rules or whether to allow some SGML like things
  2884.      * like SGML comment syntax.
  2885.      */
  2886.     static boolean strict = false;
  2887.  
  2888.     /**
  2889.      * predefined names
  2890.      */
  2891.     static Name nameComment;
  2892.     static Name nameCDATA;
  2893.     static Name namePCDATA;
  2894.     static Name nameVERSION;
  2895.     static Name nameENCODING;
  2896.     static Name nameDOCTYPE;
  2897.     static Name nameXML;
  2898.     static Name nameStandalone;
  2899.     static Name nameYes;
  2900.     static Name nameNo;
  2901.     static Name nameURL;
  2902.     static Name namePUBLICID;
  2903.     static Name nameNAME;
  2904.     static Name nameXMLSpace;
  2905.     static Name nameXMLSpace2;
  2906.     static Name nameXMLAS;
  2907.     static Name nameXMLHREF;
  2908.     static Name nameXMLNS;
  2909.     static Name nameXMLNameSpace;
  2910.     static Atom atomXML;
  2911.     static Name nameXMLLang;
  2912.  
  2913.     static
  2914.     {
  2915.         nameComment = Name.create("--");
  2916.         nameCDATA = Name.create("[CDATA[");
  2917.         namePCDATA = Name.create("PCDATA");
  2918.         nameVERSION = Name.create("version");
  2919.         nameENCODING = Name.create("encoding");
  2920.         nameStandalone = Name.create("standalone");
  2921.         nameDOCTYPE = Name.create("DOCTYPE");
  2922.         nameXML = Name.create("xml");        
  2923.         nameYes = Name.create("yes");
  2924.         nameNo = Name.create("no");
  2925.         nameURL = Name.create("URL");
  2926.         namePUBLICID = Name.create("PUBLICID");
  2927.         nameNAME = Name.create("NAME");
  2928.         nameXMLSpace = Name.create("xml-space","xml");
  2929.         nameXMLSpace2 = Name.create("space","xml");
  2930.         nameXMLAS = Name.create("prefix", "xml");
  2931.         nameXMLHREF = Name.create("src", "xml");
  2932.         nameXMLNS = Name.create("ns", "xml");
  2933.         nameXMLNameSpace = Name.create("namespace", "xml");
  2934.         atomXML = Atom.create("xml");
  2935.         nameXMLLang = Name.create("lang","xml");
  2936.  
  2937.         //
  2938.         // '<?XML' VersionInfo EncodingDecl? SDDecl? S? '?>'
  2939.         XMLDecl = new ElementDecl(nameXML);
  2940.         //
  2941.         // VersionInfo ::= S 'version' Eq ('"1.0"' | "'1.0'")
  2942.         XMLDecl.addAttDef(new AttDef(nameVERSION, AttDef.CDATA, "1.0", AttDef.FIXED));
  2943.         //
  2944.         // S 'encoding' Eq QEncoding
  2945.         XMLDecl.addAttDef(new AttDef(nameENCODING, AttDef.CDATA, "UTF-8", AttDef.IMPLIED));
  2946.  
  2947.         // SDDecl ::=  S 'standalone' Eq "'" ('yes' | 'no') "'"  
  2948.         Vector an = new Vector(2);
  2949.         an.addElement(nameYes);
  2950.         an.addElement(nameNo);
  2951.         XMLDecl.addAttDef(new AttDef(nameStandalone, AttDef.ENUMERATION, (Name)an.elementAt(0), AttDef.IMPLIED, an));
  2952.         
  2953.         // add recognized names to the hashtable
  2954.         tokens = new Hashtable();
  2955.         tokens.put("DOCTYPE", new Integer(DOCTYPE));
  2956.         tokens.put("SYSTEM", new Integer(SYSTEM));
  2957.         tokens.put("PUBLIC", new Integer(PUBLIC));
  2958.         tokens.put("ENTITY", new Integer(ENTITY));
  2959.         tokens.put("ELEMENT", new Integer(ELEMENT));
  2960.         tokens.put("EMPTY", new Integer(EMPTY));
  2961.         tokens.put("ANY", new Integer(ANY));
  2962.         tokens.put("PCDATA", new Integer(PCDATA));
  2963.         tokens.put("ATTLIST", new Integer(ATTLIST));
  2964.         tokens.put("CDATA", new Integer(CDATA));
  2965.         tokens.put("ID", new Integer(ID));      
  2966.         tokens.put("IDREF", new Integer(IDREF));  
  2967.         tokens.put("IDREFS", new Integer(IDREFS));  
  2968.         tokens.put("ENTITY", new Integer(ENTITY));  
  2969.         tokens.put("ENTITIES", new Integer(ENTITIES));
  2970.         tokens.put("NMTOKEN", new Integer(NMTOKEN)); 
  2971.         tokens.put("NMTOKENS", new Integer(NMTOKENS));
  2972.         tokens.put("FIXED", new Integer(FIXED));
  2973.         tokens.put("REQUIRED", new Integer(REQUIRED));
  2974.         tokens.put("IMPLIED", new Integer(IMPLIED));
  2975.         tokens.put("NDATA", new Integer(NDATA));
  2976.         tokens.put("NOTATION", new Integer(NOTATION));
  2977.         tokens.put("INCLUDE", new Integer(INCLUDETAGSTART));
  2978.         tokens.put("IGNORE", new Integer(IGNORETAGSTART));
  2979.         tokens.put("namespace", new Integer(NAMESPACE));
  2980.         tokens.put("EXTENDS", new Integer(EXTENDS));
  2981.         tokens.put("IMPLEMENTS", new Integer(IMPLEMENTS));
  2982.         tokens.put("xml", new Integer(XML));
  2983.         tokens.put("version", new Integer(VERSION));
  2984.         tokens.put("encoding", new Integer(ENCODING));
  2985.         tokens.put("standalone", new Integer(STANDALONE));
  2986.  
  2987.         for (int i = 0; i < 256; i++) {
  2988.             char c = (char)i;
  2989.             chartype[i] = 0;
  2990.             if ((jdk11 && Character.isWhitespace(c)) ||
  2991.                 (Character.isSpace(c) || c == 13))
  2992.                 chartype[i] = FWHITESPACE;
  2993.             if (Character.isLetter(c))
  2994.                 chartype[i] |= FLETTER;
  2995.             if (Character.isDigit(c))
  2996.                 chartype[i] |= FDIGIT;
  2997.  
  2998.             charupper[i] = Character.toUpperCase(c);
  2999.         }
  3000.         chartype['.'] |= FMISCNAME;
  3001.         chartype['-'] |= FMISCNAME;
  3002.         chartype['_'] |= FMISCNAME | FSTARTNAME;
  3003.         chartype[0xb7] |= FMISCNAME; // Extender
  3004.     }
  3005. }    
  3006.  
  3007.