Source Code Cross Referenced for CSSParser.java in  » 6.0-JDK-Core » swing » javax » swing » text » html » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Home
Java Source Code / Java Documentation
1.6.0 JDK Core
2.6.0 JDK Modules
3.6.0 JDK Modules com.sun
4.6.0 JDK Modules com.sun.java
5.6.0 JDK Modules sun
6.6.0 JDK Platform
7.Ajax
8.Apache Harmony Java SE
9.Aspect oriented
10.Authentication Authorization
11.Blogger System
12.Build
13.Byte Code
14.Cache
15.Chart
16.Chat
17.Code Analyzer
18.Collaboration
19.Content Management System
20.Database Client
21.Database DBMS
22.Database JDBC Connection Pool
23.Database ORM
24.Development
25.EJB Server
26.ERP CRM Financial
27.ESB
28.Forum
29.Game
30.GIS
31.Graphic 3D
32.Graphic Library
33.Groupware
34.HTML Parser
35.IDE
36.IDE Eclipse
37.IDE Netbeans
38.Installer
39.Internationalization Localization
40.Inversion of Control
41.Issue Tracking
42.J2EE
43.J2ME
44.JBoss
45.JMS
46.JMX
47.Library
48.Mail Clients
49.Music
50.Net
51.Parser
52.PDF
53.Portal
54.Profiler
55.Project Management
56.Report
57.RSS RDF
58.Rule Engine
59.Science
60.Scripting
61.Search Engine
62.Security
63.Sevlet Container
64.Source Control
65.Swing Library
66.Template Engine
67.Test Coverage
68.Testing
69.UML
70.Web Crawler
71.Web Framework
72.Web Mail
73.Web Server
74.Web Services
75.Web Services apache cxf 2.2.6
76.Web Services AXIS2
77.Wiki Engine
78.Workflow Engines
79.XML
80.XML UI
Java Source Code / Java Documentation » 6.0 JDK Core » swing » javax.swing.text.html 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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