< prev index next >

src/java.desktop/share/classes/javax/swing/text/html/CSSParser.java

Print this page




  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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * 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)


 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                             }


 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     // similar 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) {


 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;




  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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * 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}
  35  *   <li>Selectors {@code handleSelector}. 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}
  39  *   <li>Properties in the rule via the {@code handleProperty}. 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}, this is notified
  43  *       for the total value.
  44  *   <li>When a rule ends, {@code endRule}
  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)


 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} 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                             }


 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}, whitespace, or one of {}()[] is
 513      * hit.
 514      */
 515     // NOTE: this could be combined with readTill, as they contain somewhat
 516     // similar 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) {


 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} 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;


< prev index next >