| /* 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; | |
| /** | |
| * 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() | |
| { | |
| 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; | |
| } | |
| } |