Save This Page
Home » openjdk-7 » javax » swing » text » html » [javadoc | source]
    1   /*
    2    * Copyright 1999-2000 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   package javax.swing.text.html;
   26   
   27   import java.io;
   28   
   29   /**
   30    * A CSS parser. This works by way of a delegate that implements the
   31    * CSSParserCallback interface. The delegate is notified of the following
   32    * events:
   33    * <ul>
   34    *   <li>Import statement: <code>handleImport</code>
   35    *   <li>Selectors <code>handleSelector</code>. This is invoked for each
   36    *       string. For example if the Reader contained p, bar , a {}, the delegate
   37    *       would be notified 4 times, for 'p,' 'bar' ',' and 'a'.
   38    *   <li>When a rule starts, <code>startRule</code>
   39    *   <li>Properties in the rule via the <code>handleProperty</code>. This
   40    *       is invoked one per property/value key, eg font size: foo;, would
   41    *       cause the delegate to be notified once with a value of 'font size'.
   42    *   <li>Values in the rule via the <code>handleValue</code>, this is notified
   43    *       for the total value.
   44    *   <li>When a rule ends, <code>endRule</code>
   45    * </ul>
   46    * This will parse much more than CSS 1, and loosely implements the
   47    * recommendation for <i>Forward-compatible parsing</i> in section
   48    * 7.1 of the CSS spec found at:
   49    * <a href=http://www.w3.org/TR/REC-CSS1>http://www.w3.org/TR/REC-CSS1</a>.
   50    * If an error results in parsing, a RuntimeException will be thrown.
   51    * <p>
   52    * This will preserve case. If the callback wishes to treat certain poritions
   53    * case insensitively (such as selectors), it should use toLowerCase, or
   54    * something similar.
   55    *
   56    * @author Scott Violet
   57    */
   58   class CSSParser {
   59       // Parsing something like the following:
   60       // (@rule | ruleset | block)*
   61       //
   62       // @rule       (block | identifier)*; (block with {} ends @rule)
   63       // block       matching [] () {} (that is, [()] is a block, [(){}{[]}]
   64       //                                is a block, ()[] is two blocks)
   65       // identifier  "*" | '*' | anything but a [](){} and whitespace
   66       //
   67       // ruleset     selector decblock
   68       // selector    (identifier | (block, except block '{}') )*
   69       // declblock   declaration* block*
   70       // declaration (identifier* stopping when identifier ends with :)
   71       //             (identifier* stopping when identifier ends with ;)
   72       //
   73       // comments /* */ can appear any where, and are stripped.
   74   
   75   
   76       // identifier - letters, digits, dashes and escaped characters
   77       // block starts with { ends with matching }, () [] and {} always occur
   78       //   in matching pairs, '' and "" also occur in pairs, except " may be
   79   
   80   
   81       // Indicates the type of token being parsed.
   82       private static final int   IDENTIFIER = 1;
   83       private static final int   BRACKET_OPEN = 2;
   84       private static final int   BRACKET_CLOSE = 3;
   85       private static final int   BRACE_OPEN = 4;
   86       private static final int   BRACE_CLOSE = 5;
   87       private static final int   PAREN_OPEN = 6;
   88       private static final int   PAREN_CLOSE = 7;
   89       private static final int   END = -1;
   90   
   91       private static final char[] charMapping = { 0, 0, '[', ']', '{', '}', '(',
   92                                                  ')', 0};
   93   
   94   
   95       /** Set to true if one character has been read ahead. */
   96       private boolean        didPushChar;
   97       /** The read ahead character. */
   98       private int            pushedChar;
   99       /** Temporary place to hold identifiers. */
  100       private StringBuffer   unitBuffer;
  101       /** Used to indicate blocks. */
  102       private int[]          unitStack;
  103       /** Number of valid blocks. */
  104       private int            stackCount;
  105       /** Holds the incoming CSS rules. */
  106       private Reader         reader;
  107       /** Set to true when the first non @ rule is encountered. */
  108       private boolean        encounteredRuleSet;
  109       /** Notified of state. */
  110       private CSSParserCallback callback;
  111       /** nextToken() inserts the string here. */
  112       private char[]         tokenBuffer;
  113       /** Current number of chars in tokenBufferLength. */
  114       private int            tokenBufferLength;
  115       /** Set to true if any whitespace is read. */
  116       private boolean        readWS;
  117   
  118   
  119       // The delegate interface.
  120       static interface CSSParserCallback {
  121           /** Called when an @import is encountered. */
  122           void handleImport(String importString);
  123           // There is currently no way to distinguish between '"foo,"' and
  124           // 'foo,'. But this generally isn't valid CSS. If it becomes
  125           // a problem, handleSelector will have to be told if the string is
  126           // quoted.
  127           void handleSelector(String selector);
  128           void startRule();
  129           // Property names are mapped to lower case before being passed to
  130           // the delegate.
  131           void handleProperty(String property);
  132           void handleValue(String value);
  133           void endRule();
  134       }
  135   
  136       CSSParser() {
  137           unitStack = new int[2];
  138           tokenBuffer = new char[80];
  139           unitBuffer = new StringBuffer();
  140       }
  141   
  142       void parse(Reader reader, CSSParserCallback callback,
  143                  boolean inRule) throws IOException {
  144           this.callback = callback;
  145           stackCount = tokenBufferLength = 0;
  146           this.reader = reader;
  147           encounteredRuleSet = false;
  148           try {
  149               if (inRule) {
  150                   parseDeclarationBlock();
  151               }
  152               else {
  153                   while (getNextStatement());
  154               }
  155           } finally {
  156               callback = null;
  157               reader = null;
  158           }
  159       }
  160   
  161       /**
  162        * Gets the next statement, returning false if the end is reached. A
  163        * statement is either an @rule, or a ruleset.
  164        */
  165       private boolean getNextStatement() throws IOException {
  166           unitBuffer.setLength(0);
  167   
  168           int token = nextToken((char)0);
  169   
  170           switch (token) {
  171           case IDENTIFIER:
  172               if (tokenBufferLength > 0) {
  173                   if (tokenBuffer[0] == '@') {
  174                       parseAtRule();
  175                   }
  176                   else {
  177                       encounteredRuleSet = true;
  178                       parseRuleSet();
  179                   }
  180               }
  181               return true;
  182           case BRACKET_OPEN:
  183           case BRACE_OPEN:
  184           case PAREN_OPEN:
  185               parseTillClosed(token);
  186               return true;
  187   
  188           case BRACKET_CLOSE:
  189           case BRACE_CLOSE:
  190           case PAREN_CLOSE:
  191               // Shouldn't happen...
  192               throw new RuntimeException("Unexpected top level block close");
  193   
  194           case END:
  195               return false;
  196           }
  197           return true;
  198       }
  199   
  200       /**
  201        * Parses an @ rule, stopping at a matching brace pair, or ;.
  202        */
  203       private void parseAtRule() throws IOException {
  204           // PENDING: make this more effecient.
  205           boolean        done = false;
  206           boolean isImport = (tokenBufferLength == 7 &&
  207                               tokenBuffer[0] == '@' && tokenBuffer[1] == 'i' &&
  208                               tokenBuffer[2] == 'm' && tokenBuffer[3] == 'p' &&
  209                               tokenBuffer[4] == 'o' && tokenBuffer[5] == 'r' &&
  210                               tokenBuffer[6] == 't');
  211   
  212           unitBuffer.setLength(0);
  213           while (!done) {
  214               int       nextToken = nextToken(';');
  215   
  216               switch (nextToken) {
  217               case IDENTIFIER:
  218                   if (tokenBufferLength > 0 &&
  219                       tokenBuffer[tokenBufferLength - 1] == ';') {
  220                       --tokenBufferLength;
  221                       done = true;
  222                   }
  223                   if (tokenBufferLength > 0) {
  224                       if (unitBuffer.length() > 0 && readWS) {
  225                           unitBuffer.append(' ');
  226                       }
  227                       unitBuffer.append(tokenBuffer, 0, tokenBufferLength);
  228                   }
  229                   break;
  230   
  231               case BRACE_OPEN:
  232                   if (unitBuffer.length() > 0 && readWS) {
  233                       unitBuffer.append(' ');
  234                   }
  235                   unitBuffer.append(charMapping[nextToken]);
  236                   parseTillClosed(nextToken);
  237                   done = true;
  238                   // Skip a tailing ';', not really to spec.
  239                   {
  240                       int nextChar = readWS();
  241                       if (nextChar != -1 && nextChar != ';') {
  242                           pushChar(nextChar);
  243                       }
  244                   }
  245                   break;
  246   
  247               case BRACKET_OPEN: case PAREN_OPEN:
  248                   unitBuffer.append(charMapping[nextToken]);
  249                   parseTillClosed(nextToken);
  250                   break;
  251   
  252               case BRACKET_CLOSE: case BRACE_CLOSE: case PAREN_CLOSE:
  253                   throw new RuntimeException("Unexpected close in @ rule");
  254   
  255               case END:
  256                   done = true;
  257                   break;
  258               }
  259           }
  260           if (isImport && !encounteredRuleSet) {
  261               callback.handleImport(unitBuffer.toString());
  262           }
  263       }
  264   
  265       /**
  266        * Parses the next rule set, which is a selector followed by a
  267        * declaration block.
  268        */
  269       private void parseRuleSet() throws IOException {
  270           if (parseSelectors()) {
  271               callback.startRule();
  272               parseDeclarationBlock();
  273               callback.endRule();
  274           }
  275       }
  276   
  277       /**
  278        * Parses a set of selectors, returning false if the end of the stream
  279        * is reached.
  280        */
  281       private boolean parseSelectors() throws IOException {
  282           // Parse the selectors
  283           int       nextToken;
  284   
  285           if (tokenBufferLength > 0) {
  286               callback.handleSelector(new String(tokenBuffer, 0,
  287                                                  tokenBufferLength));
  288           }
  289   
  290           unitBuffer.setLength(0);
  291           for (;;) {
  292               while ((nextToken = nextToken((char)0)) == IDENTIFIER) {
  293                   if (tokenBufferLength > 0) {
  294                       callback.handleSelector(new String(tokenBuffer, 0,
  295                                                          tokenBufferLength));
  296                   }
  297               }
  298               switch (nextToken) {
  299               case BRACE_OPEN:
  300                   return true;
  301   
  302               case BRACKET_OPEN: case PAREN_OPEN:
  303                   parseTillClosed(nextToken);
  304                   // Not too sure about this, how we handle this isn't very
  305                   // well spec'd.
  306                   unitBuffer.setLength(0);
  307                   break;
  308   
  309               case BRACKET_CLOSE: case BRACE_CLOSE: case PAREN_CLOSE:
  310                   throw new RuntimeException("Unexpected block close in selector");
  311   
  312               case END:
  313                   // Prematurely hit end.
  314                   return false;
  315               }
  316           }
  317       }
  318   
  319       /**
  320        * Parses a declaration block. Which a number of declarations followed
  321        * by a })].
  322        */
  323       private void parseDeclarationBlock() throws IOException {
  324           for (;;) {
  325               int token = parseDeclaration();
  326               switch (token) {
  327               case END: case BRACE_CLOSE:
  328                   return;
  329   
  330               case BRACKET_CLOSE: case PAREN_CLOSE:
  331                   // Bail
  332                   throw new RuntimeException("Unexpected close in declaration block");
  333               case IDENTIFIER:
  334                   break;
  335               }
  336           }
  337       }
  338   
  339       /**
  340        * Parses a single declaration, which is an identifier a : and another
  341        * identifier. This returns the last token seen.
  342        */
  343       // identifier+: identifier* ;|}
  344       private int parseDeclaration() throws IOException {
  345           int    token;
  346   
  347           if ((token = parseIdentifiers(':', false)) != IDENTIFIER) {
  348               return token;
  349           }
  350           // Make the property name to lowercase
  351           for (int counter = unitBuffer.length() - 1; counter >= 0; counter--) {
  352               unitBuffer.setCharAt(counter, Character.toLowerCase
  353                                    (unitBuffer.charAt(counter)));
  354           }
  355           callback.handleProperty(unitBuffer.toString());
  356   
  357           token = parseIdentifiers(';', true);
  358           callback.handleValue(unitBuffer.toString());
  359           return token;
  360       }
  361   
  362       /**
  363        * Parses identifiers until <code>extraChar</code> is encountered,
  364        * returning the ending token, which will be IDENTIFIER if extraChar
  365        * is found.
  366        */
  367       private int parseIdentifiers(char extraChar,
  368                                    boolean wantsBlocks) throws IOException {
  369           int   nextToken;
  370           int   ubl;
  371   
  372           unitBuffer.setLength(0);
  373           for (;;) {
  374               nextToken = nextToken(extraChar);
  375   
  376               switch (nextToken) {
  377               case IDENTIFIER:
  378                   if (tokenBufferLength > 0) {
  379                       if (tokenBuffer[tokenBufferLength - 1] == extraChar) {
  380                           if (--tokenBufferLength > 0) {
  381                               if (readWS && unitBuffer.length() > 0) {
  382                                   unitBuffer.append(' ');
  383                               }
  384                               unitBuffer.append(tokenBuffer, 0,
  385                                                 tokenBufferLength);
  386                           }
  387                           return IDENTIFIER;
  388                       }
  389                       if (readWS && unitBuffer.length() > 0) {
  390                           unitBuffer.append(' ');
  391                       }
  392                       unitBuffer.append(tokenBuffer, 0, tokenBufferLength);
  393                   }
  394                   break;
  395   
  396               case BRACKET_OPEN:
  397               case BRACE_OPEN:
  398               case PAREN_OPEN:
  399                   ubl = unitBuffer.length();
  400                   if (wantsBlocks) {
  401                       unitBuffer.append(charMapping[nextToken]);
  402                   }
  403                   parseTillClosed(nextToken);
  404                   if (!wantsBlocks) {
  405                       unitBuffer.setLength(ubl);
  406                   }
  407                   break;
  408   
  409               case BRACE_CLOSE:
  410                   // No need to throw for these two, we return token and
  411                   // caller can do whatever.
  412               case BRACKET_CLOSE:
  413               case PAREN_CLOSE:
  414               case END:
  415                   // Hit the end
  416                   return nextToken;
  417               }
  418           }
  419       }
  420   
  421       /**
  422        * Parses till a matching block close is encountered. This is only
  423        * appropriate to be called at the top level (no nesting).
  424        */
  425       private void parseTillClosed(int openToken) throws IOException {
  426           int       nextToken;
  427           boolean   done = false;
  428   
  429           startBlock(openToken);
  430           while (!done) {
  431               nextToken = nextToken((char)0);
  432               switch (nextToken) {
  433               case IDENTIFIER:
  434                   if (unitBuffer.length() > 0 && readWS) {
  435                       unitBuffer.append(' ');
  436                   }
  437                   if (tokenBufferLength > 0) {
  438                       unitBuffer.append(tokenBuffer, 0, tokenBufferLength);
  439                   }
  440                   break;
  441   
  442               case BRACKET_OPEN: case BRACE_OPEN: case PAREN_OPEN:
  443                   if (unitBuffer.length() > 0 && readWS) {
  444                       unitBuffer.append(' ');
  445                   }
  446                   unitBuffer.append(charMapping[nextToken]);
  447                   startBlock(nextToken);
  448                   break;
  449   
  450               case BRACKET_CLOSE: case BRACE_CLOSE: case PAREN_CLOSE:
  451                   if (unitBuffer.length() > 0 && readWS) {
  452                       unitBuffer.append(' ');
  453                   }
  454                   unitBuffer.append(charMapping[nextToken]);
  455                   endBlock(nextToken);
  456                   if (!inBlock()) {
  457                       done = true;
  458                   }
  459                   break;
  460   
  461               case END:
  462                   // Prematurely hit end.
  463                   throw new RuntimeException("Unclosed block");
  464               }
  465           }
  466       }
  467   
  468       /**
  469        * Fetches the next token.
  470        */
  471       private int nextToken(char idChar) throws IOException {
  472           readWS = false;
  473   
  474           int     nextChar = readWS();
  475   
  476           switch (nextChar) {
  477           case '\'':
  478               readTill('\'');
  479               if (tokenBufferLength > 0) {
  480                   tokenBufferLength--;
  481               }
  482               return IDENTIFIER;
  483           case '"':
  484               readTill('"');
  485               if (tokenBufferLength > 0) {
  486                   tokenBufferLength--;
  487               }
  488               return IDENTIFIER;
  489           case '[':
  490               return BRACKET_OPEN;
  491           case ']':
  492               return BRACKET_CLOSE;
  493           case '{':
  494               return BRACE_OPEN;
  495           case '}':
  496               return BRACE_CLOSE;
  497           case '(':
  498               return PAREN_OPEN;
  499           case ')':
  500               return PAREN_CLOSE;
  501           case -1:
  502               return END;
  503           default:
  504               pushChar(nextChar);
  505               getIdentifier(idChar);
  506               return IDENTIFIER;
  507           }
  508       }
  509   
  510       /**
  511        * Gets an identifier, returning true if the length of the string is greater than 0,
  512        * stopping when <code>stopChar</code>, whitespace, or one of {}()[] is
  513        * hit.
  514        */
  515       // NOTE: this could be combined with readTill, as they contain somewhat
  516       // similiar functionality.
  517       private boolean getIdentifier(char stopChar) throws IOException {
  518           boolean lastWasEscape = false;
  519           boolean done = false;
  520           int escapeCount = 0;
  521           int escapeChar = 0;
  522           int nextChar;
  523           int intStopChar = (int)stopChar;
  524           // 1 for '\', 2 for valid escape char [0-9a-fA-F], 3 for
  525           // stop character (white space, ()[]{}) 0 otherwise
  526           short type;
  527           int escapeOffset = 0;
  528   
  529           tokenBufferLength = 0;
  530           while (!done) {
  531               nextChar = readChar();
  532               switch (nextChar) {
  533               case '\\':
  534                   type = 1;
  535                   break;
  536   
  537               case '0': case '1': case '2': case '3': case '4': case '5':
  538               case '6': case '7': case '8': case '9':
  539                   type = 2;
  540                   escapeOffset = nextChar - '0';
  541                   break;
  542   
  543               case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
  544                   type = 2;
  545                   escapeOffset = nextChar - 'a' + 10;
  546                   break;
  547   
  548               case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
  549                   type = 2;
  550                   escapeOffset = nextChar - 'A' + 10;
  551                   break;
  552   
  553               case '\'': case '"': case '[': case ']': case '{': case '}':
  554               case '(': case ')':
  555               case ' ': case '\n': case '\t': case '\r':
  556                   type = 3;
  557                   break;
  558   
  559               case '/':
  560                   type = 4;
  561                   break;
  562   
  563               case -1:
  564                   // Reached the end
  565                   done = true;
  566                   type = 0;
  567                   break;
  568   
  569               default:
  570                   type = 0;
  571                   break;
  572               }
  573               if (lastWasEscape) {
  574                   if (type == 2) {
  575                       // Continue with escape.
  576                       escapeChar = escapeChar * 16 + escapeOffset;
  577                       if (++escapeCount == 4) {
  578                           lastWasEscape = false;
  579                           append((char)escapeChar);
  580                       }
  581                   }
  582                   else {
  583                       // no longer escaped
  584                       lastWasEscape = false;
  585                       if (escapeCount > 0) {
  586                           append((char)escapeChar);
  587                           // Make this simpler, reprocess the character.
  588                           pushChar(nextChar);
  589                       }
  590                       else if (!done) {
  591                           append((char)nextChar);
  592                       }
  593                   }
  594               }
  595               else if (!done) {
  596                   if (type == 1) {
  597                       lastWasEscape = true;
  598                       escapeChar = escapeCount = 0;
  599                   }
  600                   else if (type == 3) {
  601                       done = true;
  602                       pushChar(nextChar);
  603                   }
  604                   else if (type == 4) {
  605                       // Potential comment
  606                       nextChar = readChar();
  607                       if (nextChar == '*') {
  608                           done = true;
  609                           readComment();
  610                           readWS = true;
  611                       }
  612                       else {
  613                           append('/');
  614                           if (nextChar == -1) {
  615                               done = true;
  616                           }
  617                           else {
  618                               pushChar(nextChar);
  619                           }
  620                       }
  621                   }
  622                   else {
  623                       append((char)nextChar);
  624                       if (nextChar == intStopChar) {
  625                           done = true;
  626                       }
  627                   }
  628               }
  629           }
  630           return (tokenBufferLength > 0);
  631       }
  632   
  633       /**
  634        * Reads till a <code>stopChar</code> is encountered, escaping characters
  635        * as necessary.
  636        */
  637       private void readTill(char stopChar) throws IOException {
  638           boolean lastWasEscape = false;
  639           int escapeCount = 0;
  640           int escapeChar = 0;
  641           int nextChar;
  642           boolean done = false;
  643           int intStopChar = (int)stopChar;
  644           // 1 for '\', 2 for valid escape char [0-9a-fA-F], 0 otherwise
  645           short type;
  646           int escapeOffset = 0;
  647   
  648           tokenBufferLength = 0;
  649           while (!done) {
  650               nextChar = readChar();
  651               switch (nextChar) {
  652               case '\\':
  653                   type = 1;
  654                   break;
  655   
  656               case '0': case '1': case '2': case '3': case '4':case '5':
  657               case '6': case '7': case '8': case '9':
  658                   type = 2;
  659                   escapeOffset = nextChar - '0';
  660                   break;
  661   
  662               case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
  663                   type = 2;
  664                   escapeOffset = nextChar - 'a' + 10;
  665                   break;
  666   
  667               case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
  668                   type = 2;
  669                   escapeOffset = nextChar - 'A' + 10;
  670                   break;
  671   
  672               case -1:
  673                   // Prematurely reached the end!
  674                   throw new RuntimeException("Unclosed " + stopChar);
  675   
  676               default:
  677                   type = 0;
  678                   break;
  679               }
  680               if (lastWasEscape) {
  681                   if (type == 2) {
  682                       // Continue with escape.
  683                       escapeChar = escapeChar * 16 + escapeOffset;
  684                       if (++escapeCount == 4) {
  685                           lastWasEscape = false;
  686                           append((char)escapeChar);
  687                       }
  688                   }
  689                   else {
  690                       // no longer escaped
  691                       if (escapeCount > 0) {
  692                           append((char)escapeChar);
  693                           if (type == 1) {
  694                               lastWasEscape = true;
  695                               escapeChar = escapeCount = 0;
  696                           }
  697                           else {
  698                               if (nextChar == intStopChar) {
  699                                   done = true;
  700                               }
  701                               append((char)nextChar);
  702                               lastWasEscape = false;
  703                           }
  704                       }
  705                       else {
  706                           append((char)nextChar);
  707                           lastWasEscape = false;
  708                       }
  709                   }
  710               }
  711               else if (type == 1) {
  712                   lastWasEscape = true;
  713                   escapeChar = escapeCount = 0;
  714               }
  715               else {
  716                   if (nextChar == intStopChar) {
  717                       done = true;
  718                   }
  719                   append((char)nextChar);
  720               }
  721           }
  722       }
  723   
  724       private void append(char character) {
  725           if (tokenBufferLength == tokenBuffer.length) {
  726               char[] newBuffer = new char[tokenBuffer.length * 2];
  727               System.arraycopy(tokenBuffer, 0, newBuffer, 0, tokenBuffer.length);
  728               tokenBuffer = newBuffer;
  729           }
  730           tokenBuffer[tokenBufferLength++] = character;
  731       }
  732   
  733       /**
  734        * Parses a comment block.
  735        */
  736       private void readComment() throws IOException {
  737           int nextChar;
  738   
  739           for(;;) {
  740               nextChar = readChar();
  741               switch (nextChar) {
  742               case -1:
  743                   throw new RuntimeException("Unclosed comment");
  744               case '*':
  745                   nextChar = readChar();
  746                   if (nextChar == '/') {
  747                       return;
  748                   }
  749                   else if (nextChar == -1) {
  750                       throw new RuntimeException("Unclosed comment");
  751                   }
  752                   else {
  753                       pushChar(nextChar);
  754                   }
  755                   break;
  756               default:
  757                   break;
  758               }
  759           }
  760       }
  761   
  762       /**
  763        * Called when a block start is encountered ({[.
  764        */
  765       private void startBlock(int startToken) {
  766           if (stackCount == unitStack.length) {
  767               int[]     newUS = new int[stackCount * 2];
  768   
  769               System.arraycopy(unitStack, 0, newUS, 0, stackCount);
  770               unitStack = newUS;
  771           }
  772           unitStack[stackCount++] = startToken;
  773       }
  774   
  775       /**
  776        * Called when an end block is encountered )]}
  777        */
  778       private void endBlock(int endToken) {
  779           int    startToken;
  780   
  781           switch (endToken) {
  782           case BRACKET_CLOSE:
  783               startToken = BRACKET_OPEN;
  784               break;
  785           case BRACE_CLOSE:
  786               startToken = BRACE_OPEN;
  787               break;
  788           case PAREN_CLOSE:
  789               startToken = PAREN_OPEN;
  790               break;
  791           default:
  792               // Will never happen.
  793               startToken = -1;
  794               break;
  795           }
  796           if (stackCount > 0 && unitStack[stackCount - 1] == startToken) {
  797               stackCount--;
  798           }
  799           else {
  800               // Invalid state, should do something.
  801               throw new RuntimeException("Unmatched block");
  802           }
  803       }
  804   
  805       /**
  806        * @return true if currently in a block.
  807        */
  808       private boolean inBlock() {
  809           return (stackCount > 0);
  810       }
  811   
  812       /**
  813        * Skips any white space, returning the character after the white space.
  814        */
  815       private int readWS() throws IOException {
  816           int nextChar;
  817           while ((nextChar = readChar()) != -1 &&
  818                  Character.isWhitespace((char)nextChar)) {
  819               readWS = true;
  820           }
  821           return nextChar;
  822       }
  823   
  824       /**
  825        * Reads a character from the stream.
  826        */
  827       private int readChar() throws IOException {
  828           if (didPushChar) {
  829               didPushChar = false;
  830               return pushedChar;
  831           }
  832           return reader.read();
  833           // Uncomment the following to do case insensitive parsing.
  834           /*
  835           if (retValue != -1) {
  836               return (int)Character.toLowerCase((char)retValue);
  837           }
  838           return retValue;
  839           */
  840       }
  841   
  842       /**
  843        * Supports one character look ahead, this will throw if called twice
  844        * in a row.
  845        */
  846       private void pushChar(int tempChar) {
  847           if (didPushChar) {
  848               // Should never happen.
  849               throw new RuntimeException("Can not handle look ahead of more than one character");
  850           }
  851           didPushChar = true;
  852           pushedChar = tempChar;
  853       }
  854   }

Save This Page
Home » openjdk-7 » javax » swing » text » html » [javadoc | source]