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