1 /* CSSParser.java -- 2 Copyright (C) 2005 Free Software Foundation, Inc. 3 4 This file is part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301 USA. 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 39 package javax.swing.text.html; 40 41 import java.io.*; 42 43 /** 44 * Parses a CSS document. This works by way of a delegate that implements the 45 * CSSParserCallback interface. The delegate is notified of the following 46 * events: 47 * - Import statement: handleImport 48 * - Selectors handleSelector. This is invoked for each string. For example if 49 * the Reader contained p, bar , a {}, the delegate would be notified 4 times, 50 * for 'p,' 'bar' ',' and 'a'. 51 * - When a rule starts, startRule 52 * - Properties in the rule via the handleProperty. This 53 * is invoked one per property/value key, eg font size: foo;, would cause the 54 * delegate to be notified once with a value of 'font size'. 55 * - Values in the rule via the handleValue, this is notified for the total value. 56 * - When a rule ends, endRule 57 * 58 * @author Lillian Angel (langel@redhat.com) 59 */ 60 class CSSParser 61 { 62 63 /** 64 * Receives all information about the CSS document structure while parsing it. 65 * The methods are invoked by parser. 66 */ 67 static interface CSSParserCallback 68 { 69 /** 70 * Handles the import statment in the document. 71 * 72 * @param imp - the import string 73 */ handleImport(String imp)74 public abstract void handleImport(String imp); 75 76 /** 77 * Called when the start of a rule is encountered. 78 */ startRule()79 public abstract void startRule(); 80 81 /** 82 * Called when the end of a rule is encountered. 83 */ endRule()84 public abstract void endRule(); 85 86 /** 87 * Handles the selector of a rule. 88 * 89 * @param selector - the selector in the rule 90 */ handleSelector(String selector)91 public abstract void handleSelector(String selector); 92 93 /** 94 * Handles the properties in the document. 95 * 96 * @param property - the property in the document. 97 */ handleProperty(String property)98 public abstract void handleProperty(String property); 99 100 /** 101 * Handles the values in the document. 102 * 103 * @param value - the value to handle. 104 */ handleValue(String value)105 public abstract void handleValue(String value); 106 107 } 108 109 /** 110 * The identifier of the rule. 111 */ 112 private static final int IDENTIFIER = 1; 113 114 /** 115 * The open bracket. 116 */ 117 private static final int BRACKET_OPEN = 2; 118 119 /** 120 * The close bracket. 121 */ 122 private static final int BRACKET_CLOSE = 3; 123 124 /** 125 * The open brace. 126 */ 127 private static final int BRACE_OPEN = 4; 128 129 /** 130 * The close brace. 131 */ 132 private static final int BRACE_CLOSE = 5; 133 134 /** 135 * The open parentheses. 136 */ 137 private static final int PAREN_OPEN = 6; 138 139 /** 140 * The close parentheses. 141 */ 142 private static final int PAREN_CLOSE = 7; 143 144 /** 145 * The end of the document. 146 */ 147 private static final int END = -1; 148 149 /** 150 * The character mapping in the document. 151 */ 152 // FIXME: What is this used for? 153 private static final char[] charMapping = null; 154 155 /** 156 * Set to true if one character has been read ahead. 157 */ 158 private boolean didPushChar; 159 160 /** 161 * The read ahead character. 162 */ 163 private int pushedChar; 164 165 /** 166 * Used to indicate blocks. 167 */ 168 private int[] unitStack; 169 170 /** 171 * Number of valid blocks. 172 */ 173 private int stackCount; 174 175 /** 176 * Holds the incoming CSS rules. 177 */ 178 private Reader reader; 179 180 /** 181 * Set to true when the first non @ rule is encountered. 182 */ 183 private boolean encounteredRuleSet; 184 185 /** 186 * The call back used to parse. 187 */ 188 private CSSParser.CSSParserCallback callback; 189 190 /** 191 * nextToken() inserts the string here. 192 */ 193 private char[] tokenBuffer; 194 195 /** 196 * Current number of chars in tokenBufferLength. 197 */ 198 private int tokenBufferLength; 199 200 /** 201 * Set to true if any whitespace is read. 202 */ 203 private boolean readWS; 204 205 /** 206 * Constructor 207 */ CSSParser()208 CSSParser() 209 { 210 tokenBuffer = new char[10]; 211 } 212 213 /** 214 * Appends a character to the token buffer. 215 * 216 * @param c - the character to append 217 */ append(char c)218 private void append(char c) 219 { 220 if (tokenBuffer.length >= tokenBufferLength) 221 { 222 char[] temp = new char[tokenBufferLength * 2]; 223 if (tokenBuffer != null) 224 System.arraycopy(tokenBuffer, 0, temp, 0, tokenBufferLength); 225 226 temp[tokenBufferLength] = c; 227 tokenBuffer = temp; 228 } 229 else 230 tokenBuffer[tokenBufferLength] = c; 231 tokenBufferLength++; 232 } 233 234 /** 235 * Fetches the next token. 236 * 237 * @param c - the character to fetch. 238 * @return the location 239 * @throws IOException - any i/o error encountered while reading 240 */ nextToken(char c)241 private int nextToken(char c) throws IOException 242 { 243 readWS = false; 244 int next = readWS(); 245 246 switch (next) 247 { 248 case '\"': 249 if (tokenBufferLength > 0) 250 tokenBufferLength--; 251 return IDENTIFIER; 252 case '\'': 253 if (tokenBufferLength > 0) 254 tokenBufferLength--; 255 return IDENTIFIER; 256 case '(': 257 return PAREN_OPEN; 258 case ')': 259 return PAREN_CLOSE; 260 case '{': 261 return BRACE_OPEN; 262 case '}': 263 return BRACE_CLOSE; 264 case '[': 265 return BRACKET_OPEN; 266 case ']': 267 return BRACKET_CLOSE; 268 case -1: 269 return END; 270 default: 271 pushChar(next); 272 getIdentifier(c); 273 return IDENTIFIER; 274 } 275 } 276 277 /** 278 * Reads a character from the stream. 279 * 280 * @return the number of characters read or -1 if end of stream is reached. 281 * @throws IOException - any i/o encountered while reading 282 */ readChar()283 private int readChar() throws IOException 284 { 285 if (didPushChar) 286 { 287 didPushChar = false; 288 return pushedChar; 289 } 290 return reader.read(); 291 } 292 293 /** 294 * Parses the the contents of the reader using the 295 * callback. 296 * 297 * @param reader - the reader to read from 298 * @param callback - the callback instance 299 * @param parsingDeclaration - true if parsing a declaration 300 * @throws IOException - any i/o error from the reader 301 */ parse(Reader reader, CSSParser.CSSParserCallback callback, boolean parsingDeclaration)302 void parse(Reader reader, CSSParser.CSSParserCallback callback, 303 boolean parsingDeclaration) 304 throws IOException 305 { 306 this.reader = reader; 307 this.callback = callback; 308 309 try 310 { 311 if (!parsingDeclaration) 312 while(getNextStatement()) 313 ; 314 else 315 parseDeclarationBlock(); 316 } 317 catch (IOException ioe) 318 { 319 // Nothing to do here. 320 } 321 } 322 323 /** 324 * Skips any white space, returning the character after the white space. 325 * 326 * @return the character after the whitespace 327 * @throws IOException - any i/o error from the reader 328 */ readWS()329 private int readWS() throws IOException 330 { 331 int next = readChar(); 332 while (Character.isWhitespace((char) next)) 333 { 334 readWS = true; 335 int tempNext = readChar(); 336 if (tempNext == END) 337 return next; 338 next = tempNext; 339 } 340 341 // Its all whitespace 342 return END; 343 } 344 345 /** 346 * Gets the next statement, returning false if the end is reached. 347 * A statement is either an At-rule, or a ruleset. 348 * 349 * @return false if the end is reached 350 * @throws IOException - any i/o error from the reader 351 */ getNextStatement()352 private boolean getNextStatement() throws IOException 353 { 354 int c = nextToken((char) 0); 355 switch (c) 356 { 357 case PAREN_OPEN: 358 case BRACE_OPEN: 359 case BRACKET_OPEN: 360 parseTillClosed(c); 361 break; 362 case BRACKET_CLOSE: 363 case BRACE_CLOSE: 364 case PAREN_CLOSE: 365 throw new IOException("Not a proper statement."); 366 case IDENTIFIER: 367 if (tokenBuffer[0] == ('@')) 368 parseAtRule(); 369 else 370 parseRuleSet(); 371 break; 372 case END: 373 return false; 374 } 375 return true; 376 } 377 378 /** 379 * Parses an @ rule, stopping at a matching brace pair, or ;. 380 * 381 * @throws IOException - any i/o error from the reader 382 */ parseAtRule()383 private void parseAtRule() throws IOException 384 { 385 // An At-Rule begins with the "@" character followed immediately by a keyword. 386 // Following the keyword separated by a space is an At-rule statement appropriate 387 // to the At-keyword used. If the At-Rule is a simple declarative statement 388 // (charset, import, fontdef), it is terminated by a semi-colon (";".) 389 // If the At-Rule is a conditional or informative statement (media, page, font-face), 390 // it is followed by optional arguments and then a style declaration block inside matching 391 // curly braces ("{", "}".) At-Rules are sometimes nestable, depending on the context. 392 // If any part of an At-Rule is not understood, it should be ignored. 393 394 // FIXME: Not Implemented 395 // call handleimport 396 } 397 398 /** 399 * Parses the next rule set, which is a selector followed by a declaration 400 * block. 401 * 402 * @throws IOException - any i/o error from the reader 403 */ parseRuleSet()404 private void parseRuleSet() throws IOException 405 { 406 // call parseDeclarationBlock 407 // call parse selectors 408 // call parse identifiers 409 // call startrule/endrule 410 // FIXME: Not Implemented 411 } 412 413 /** 414 * Parses a set of selectors, returning false if the end of the stream is 415 * reached. 416 * 417 * @return false if the end of stream is reached 418 * @throws IOException - any i/o error from the reader 419 */ parseSelectors()420 private boolean parseSelectors() throws IOException 421 { 422 // FIXME: Not Implemented 423 // call handleselector 424 return false; 425 } 426 427 /** 428 * Parses a declaration block. Which a number of declarations followed by a 429 * })]. 430 * 431 * @throws IOException - any i/o error from the reader 432 */ parseDeclarationBlock()433 private void parseDeclarationBlock() throws IOException 434 { 435 // call parseDeclaration 436 // FIXME: Not Implemented 437 } 438 439 /** 440 * Parses a single declaration, which is an identifier a : and another identifier. 441 * This returns the last token seen. 442 * 443 * @returns the last token 444 * @throws IOException - any i/o error from the reader 445 */ parseDeclaration()446 private int parseDeclaration() throws IOException 447 { 448 // call handleValue 449 // FIXME: Not Implemented 450 return 0; 451 } 452 453 /** 454 * Parses identifiers until c is encountered, returning the ending token, 455 * which will be IDENTIFIER if c is found. 456 * 457 * @param c - the stop character 458 * @param wantsBlocks - true if blocks are wanted 459 * @return the ending token 460 * @throws IOException - any i/o error from the reader 461 */ parseIdentifiers(char c, boolean wantsBlocks)462 private int parseIdentifiers(char c, boolean wantsBlocks) throws IOException 463 { 464 // FIXME: Not implemented 465 // call handleproperty? 466 return 0; 467 } 468 469 /** 470 * Parses till a matching block close is encountered. This is only appropriate 471 * to be called at the top level (no nesting). 472 * 473 * @param i - FIXME 474 * @throws IOException - any i/o error from the reader 475 */ parseTillClosed(int i)476 private void parseTillClosed(int i) throws IOException 477 { 478 // FIXME: Not Implemented 479 } 480 481 /** 482 * Gets an identifier, returning true if the length of the string is greater 483 * than 0, stopping when c, whitespace, or one of {}()[] is hit. 484 * 485 * @param c - the stop character 486 * @return returns true if the length of the string > 0 487 * @throws IOException - any i/o error from the reader 488 */ getIdentifier(char c)489 private boolean getIdentifier(char c) throws IOException 490 { 491 // FIXME: Not Implemented 492 return false; 493 } 494 495 /** 496 * Reads till c is encountered, escaping characters as necessary. 497 * 498 * @param c - the stop character 499 * @throws IOException - any i/o error from the reader 500 */ readTill(char c)501 private void readTill(char c) throws IOException 502 { 503 // FIXME: Not Implemented 504 } 505 506 /** 507 * Parses a comment block. 508 * 509 * @throws IOException - any i/o error from the reader 510 */ readComment()511 private void readComment() throws IOException 512 { 513 // Should ignore comments. Read until end of comment. 514 // FIXME: Not implemented 515 } 516 517 /** 518 * Called when a block start is encountered ({[. 519 * 520 * @param start of block 521 */ startBlock(int start)522 private void startBlock(int start) 523 { 524 // FIXME: Not Implemented 525 } 526 527 /** 528 * Called when an end block is encountered )]} 529 * 530 * @param end of block 531 */ endBlock(int end)532 private void endBlock(int end) 533 { 534 // FIXME: Not Implemented 535 } 536 537 /** 538 * Checks if currently in a block. 539 * 540 * @return true if currently in a block. 541 */ inBlock()542 private boolean inBlock() 543 { 544 // FIXME: Not Implemented 545 return false; 546 } 547 548 /** 549 * Supports one character look ahead, this will throw if called twice in a row. 550 * 551 * @param c - the character to push. 552 * @throws IOException - if called twice in a row 553 */ pushChar(int c)554 private void pushChar(int c) throws IOException 555 { 556 if (didPushChar) 557 throw new IOException("pushChar called twice."); 558 didPushChar = true; 559 pushedChar = c; 560 } 561 } 562