summaryrefslogtreecommitdiff
path: root/javax/swing/text/html/CSSParser.java
blob: 54a16bcc599ee8e3b73488a6357e8c38e08d8f0d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
/* CSSParser.java --
   Copyright (C) 2005 Free Software Foundation, Inc.

This file is part of GNU Classpath.

GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA.

Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.

As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module.  An independent module is a module which is not derived from
or based on this library.  If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so.  If you do not wish to do so, delete this
exception statement from your version. */


package javax.swing.text.html;

import java.io.*;

/**
 * Parses a CSS document. This works by way of a delegate that implements the
 * CSSParserCallback interface. The delegate is notified of the following
 * events: 
 * - Import statement: handleImport 
 * - Selectors handleSelector. This is invoked for each string. For example if 
 * the Reader contained p, bar , a {}, the delegate would be notified 4 times, 
 * for 'p,' 'bar' ',' and 'a'. 
 * - When a rule starts, startRule 
 * - Properties in the rule via the handleProperty. This
 * is invoked one per property/value key, eg font size: foo;, would cause the
 * delegate to be notified once with a value of 'font size'. 
 * - Values in the rule via the handleValue, this is notified for the total value. 
 * - When a rule ends, endRule
 * 
 * @author Lillian Angel (langel@redhat.com)
 */
class CSSParser
{

  /**
   * Receives all information about the CSS document structure while parsing it.
   * The methods are invoked by parser.
   */
  static interface CSSParserCallback
  {
    /**
     * Handles the import statment in the document.
     * 
     * @param imp - the import string
     */
    public abstract void handleImport(String imp);

    /**
     * Called when the start of a rule is encountered.
     */
    public abstract void startRule();

    /**
     * Called when the end of a rule is encountered.
     */
    public abstract void endRule();

    /**
     * Handles the selector of a rule.
     * 
     * @param selector - the selector in the rule
     */
    public abstract void handleSelector(String selector);

    /**
     * Handles the properties in the document.
     * 
     * @param property - the property in the document.
     */
    public abstract void handleProperty(String property);

    /**
     * Handles the values in the document.
     * 
     * @param value - the value to handle.
     */
    public abstract void handleValue(String value);

  }

  /**
   * The identifier of the rule.
   */
  private static final int IDENTIFIER = 1;

  /**
   * The open bracket.
   */
  private static final int BRACKET_OPEN = 2;

  /**
   * The close bracket.
   */
  private static final int BRACKET_CLOSE = 3;

  /**
   * The open brace.
   */
  private static final int BRACE_OPEN = 4;

  /**
   * The close brace.
   */
  private static final int BRACE_CLOSE = 5;

  /**
   * The open parentheses.
   */
  private static final int PAREN_OPEN = 6;

  /**
   * The close parentheses.
   */
  private static final int PAREN_CLOSE = 7;

  /**
   * The end of the document.
   */
  private static final int END = -1;

  /**
   * The character mapping in the document.
   */
  // FIXME: What is this used for?
  private static final char[] charMapping = null;

  /**
   * Set to true if one character has been read ahead.
   */
  private boolean didPushChar;

  /**
   * The read ahead character.
   */
  private int pushedChar;

  /**
   * Temporary place to hold identifiers.
   */
  private StringBuffer unitBuffer;

  /**
   * Used to indicate blocks.
   */
  private int[] unitStack;

  /**
   * Number of valid blocks.
   */
  private int stackCount;

  /**
   * Holds the incoming CSS rules.
   */
  private Reader reader;

  /**
   * Set to true when the first non @ rule is encountered.
   */
  private boolean encounteredRuleSet;

  /**
   * The call back used to parse.
   */
  private CSSParser.CSSParserCallback callback;

  /**
   * nextToken() inserts the string here.
   */
  private char[] tokenBuffer;

  /**
   * Current number of chars in tokenBufferLength.
   */
  private int tokenBufferLength;

  /**
   * Set to true if any whitespace is read.
   */
  private boolean readWS;

  /**
   * Constructor
   */
  CSSParser()
  {
    unitBuffer = new StringBuffer();
    tokenBuffer = new char[10];
  }

  /**
   * Appends a character to the token buffer.
   * 
   * @param c - the character to append
   */
  private void append(char c)
  {
    if (tokenBuffer.length >= tokenBufferLength)
      {
        char[] temp = new char[tokenBufferLength * 2];
        if (tokenBuffer != null)
          System.arraycopy(tokenBuffer, 0, temp, 0, tokenBufferLength);

        temp[tokenBufferLength] = c;
        tokenBuffer = temp;
      }
    else
      tokenBuffer[tokenBufferLength] = c;
    tokenBufferLength++;
  }

  /**
   * Fetches the next token.
   * 
   * @param c - the character to fetch.
   * @return the location
   * @throws IOException - any i/o error encountered while reading
   */
  private int nextToken(char c) throws IOException
  {
    readWS = false;
    int next = readWS();

    switch (next)
      {
      case '\"':
        if (tokenBufferLength > 0)
          tokenBufferLength--;
        return IDENTIFIER;
      case '\'':
        if (tokenBufferLength > 0)
          tokenBufferLength--;
        return IDENTIFIER;
      case '(':
        return PAREN_OPEN;
      case ')':
        return PAREN_CLOSE;
      case '{':
        return BRACE_OPEN;
      case '}':
        return BRACE_CLOSE;
      case '[':
        return BRACKET_OPEN;
      case ']':
        return BRACKET_CLOSE;
      case -1:
        return END;
      default:
        pushChar(next);
        getIdentifier(c);
        return IDENTIFIER;
      }
  }

  /**
   * Reads a character from the stream.
   * 
   * @return the number of characters read or -1 if end of stream is reached.
   * @throws IOException - any i/o encountered while reading
   */
  private int readChar() throws IOException
  {
    if (didPushChar)
      {
        didPushChar = false;
        return pushedChar;
      }
    return reader.read();
  }

  /**
   * Parses the the contents of the reader using the
   * callback.
   * 
   * @param reader - the reader to read from
   * @param callback - the callback instance
   * @param parsingDeclaration - true if parsing a declaration
   * @throws IOException - any i/o error from the reader
   */
  void parse(Reader reader, CSSParser.CSSParserCallback callback, 
             boolean parsingDeclaration)
      throws IOException
  {
    this.reader = reader;
    this.callback = callback;
    
    try
    {
      if (!parsingDeclaration)
        while(getNextStatement())
          ;
      else
        parseDeclarationBlock();
    }
    catch (IOException ioe)
    {
      // Nothing to do here.
    }
  }

  /**
   * Skips any white space, returning the character after the white space.
   * 
   * @return the character after the whitespace
   * @throws IOException - any i/o error from the reader
   */
  private int readWS() throws IOException
  {
    int next = readChar();
    while (Character.isWhitespace((char) next))
      {
        readWS = true;
        int tempNext = readChar();
        if (tempNext == END)
          return next;
        next = tempNext;
      }
    
    // Its all whitespace
    return END;
  }

  /**
   * Gets the next statement, returning false if the end is reached.
   * A statement is either an At-rule, or a ruleset.
   * 
   * @return false if the end is reached
   * @throws IOException - any i/o error from the reader
   */
  private boolean getNextStatement() throws IOException
  {
    int c = nextToken((char) 0);
    switch (c)
      {
        case PAREN_OPEN:
        case BRACE_OPEN:
        case BRACKET_OPEN:
          parseTillClosed(c);
          break;
        case BRACKET_CLOSE:
        case BRACE_CLOSE:
        case PAREN_CLOSE:
          throw new IOException("Not a proper statement.");
        case IDENTIFIER:
          if (tokenBuffer[0] == ('@'))
            parseAtRule();
          else
            parseRuleSet();
          break;  
        case END:
          return false;
      }
    return true;
  }

  /**
   * Parses an @ rule, stopping at a matching brace pair, or ;.
   * 
   * @throws IOException - any i/o error from the reader
   */
  private void parseAtRule() throws IOException
  {    
    // An At-Rule begins with the "@" character followed immediately by a keyword. 
    // Following the keyword separated by a space is an At-rule statement appropriate 
    // to the At-keyword used. If the At-Rule is a simple declarative statement 
    // (charset, import, fontdef), it is terminated by a semi-colon (";".) 
    // If the At-Rule is a conditional or informative statement (media, page, font-face), 
    // it is followed by optional arguments and then a style declaration block inside matching 
    // curly braces ("{", "}".) At-Rules are sometimes nestable, depending on the context. 
    // If any part of an At-Rule is not understood, it should be ignored.
    
    // FIXME: Not Implemented
    // call handleimport 
  }

  /**
   * Parses the next rule set, which is a selector followed by a declaration 
   * block.
   * 
   * @throws IOException - any i/o error from the reader
   */
  private void parseRuleSet() throws IOException
  {
    // call parseDeclarationBlock
    // call parse selectors
    // call parse identifiers
    // call startrule/endrule
    // FIXME: Not Implemented
  }

  /**
   * Parses a set of selectors, returning false if the end of the stream is 
   * reached.
   * 
   * @return false if the end of stream is reached
   * @throws IOException - any i/o error from the reader
   */
  private boolean parseSelectors() throws IOException
  {
    // FIXME: Not Implemented
    // call handleselector
    return false; 
  }

  /**
   * Parses a declaration block. Which a number of declarations followed by a
   * })].
   * 
   * @throws IOException - any i/o error from the reader
   */
  private void parseDeclarationBlock() throws IOException
  {
    // call parseDeclaration
    // FIXME: Not Implemented
  }

  /**
   * Parses a single declaration, which is an identifier a : and another identifier.
   * This returns the last token seen.
   * 
   * @returns the last token
   * @throws IOException - any i/o error from the reader
   */
  private int parseDeclaration() throws IOException
  {
    // call handleValue
    // FIXME: Not Implemented
    return 0; 
  }

  /**
   * Parses identifiers until c is encountered, returning the ending token,
   * which will be IDENTIFIER if c is found.
   * 
   * @param c - the stop character
   * @param wantsBlocks - true if blocks are wanted
   * @return the ending token
   * @throws IOException - any i/o error from the reader
   */
  private int parseIdentifiers(char c, boolean wantsBlocks) throws IOException
  {
    // FIXME: Not implemented
    // call handleproperty?
    return 0;
  }

  /**
   * Parses till a matching block close is encountered. This is only appropriate
   * to be called at the top level (no nesting).
   * 
   * @param i - FIXME
   * @throws IOException - any i/o error from the reader
   */
  private void parseTillClosed(int i) throws IOException
  {
    // FIXME: Not Implemented
  }

  /**
   * Gets an identifier, returning true if the length of the string is greater
   * than 0, stopping when c, whitespace, or one of {}()[] is hit.
   * 
   * @param c - the stop character
   * @return returns true if the length of the string > 0
   * @throws IOException - any i/o error from the reader
   */
  private boolean getIdentifier(char c) throws IOException
  {
    // FIXME: Not Implemented
    return false;
  }

  /**
   * Reads till c is encountered, escaping characters as necessary.
   * 
   * @param c - the stop character
   * @throws IOException - any i/o error from the reader
   */
  private void readTill(char c) throws IOException
  {
    // FIXME: Not Implemented
  }

  /**
   * Parses a comment block.
   * 
   * @throws IOException - any i/o error from the reader
   */
  private void readComment() throws IOException
  {
    // Should ignore comments. Read until end of comment.
    // FIXME: Not implemented
  }

  /**
   * Called when a block start is encountered ({[.
   * 
   * @param start of block
   */
  private void startBlock(int start)
  {
    // FIXME: Not Implemented
  }

  /**
   * Called when an end block is encountered )]}
   * 
   * @param end of block
   */
  private void endBlock(int end)
  {
    // FIXME: Not Implemented
  }

  /**
   * Checks if currently in a block.
   * 
   * @return true if currently in a block.
   */
  private boolean inBlock()
  {
    // FIXME: Not Implemented
    return false; 
  }

  /**
   * Supports one character look ahead, this will throw if called twice in a row.
   * 
   * @param c - the character to push.
   * @throws IOException - if called twice in a row
   */
  private void pushChar(int c) throws IOException
  {
    if (didPushChar)
      throw new IOException("pushChar called twice.");
    didPushChar = true;
    pushedChar = c;
  }
}