/* Copyright 2000-2004 The Apache Software Foundation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package org.apache.batik.css.parser; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import org.apache.batik.util.io.NormalizingReader; import org.apache.batik.util.io.StreamNormalizingReader; import org.apache.batik.util.io.StringNormalizingReader; /** * This class represents a CSS scanner - an object which decodes CSS lexical * units. * * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a> * @version $Id: Scanner.java,v 1.1 2009/12/06 10:40:09 rsternber Exp $ */ public class Scanner { /** * The reader. */ protected NormalizingReader reader; /** * The current char. */ protected int current; /** * The recording buffer. */ protected char[] buffer = new char[128]; /** * The current position in the buffer. */ protected int position; /** * The type of the current lexical unit. */ protected int type; /** * The start offset of the last lexical unit. */ protected int start; /** * The end offset of the last lexical unit. */ protected int end; /** * The characters to skip to create the string which represents the * current token. */ protected int blankCharacters; /** * Creates a new Scanner object. * @param r The reader to scan. */ public Scanner(Reader r) throws ParseException { try { reader = new StreamNormalizingReader(r); current = nextChar(); } catch (IOException e) { throw new ParseException(e); } } /** * Creates a new Scanner object. * @param is The input stream to scan. * @param enc The encoding to use to decode the input stream, or null. */ public Scanner(InputStream is, String enc) throws ParseException { try { reader = new StreamNormalizingReader(is, enc); current = nextChar(); } catch (IOException e) { throw new ParseException(e); } } /** * Creates a new Scanner object. * @param s The string to scan. */ public Scanner(String s) throws ParseException { try { reader = new StringNormalizingReader(s); current = nextChar(); } catch (IOException e) { throw new ParseException(e); } } /** * Returns the current line. */ public int getLine() { return reader.getLine(); } /** * Returns the current column. */ public int getColumn() { return reader.getColumn(); } /** * Returns the buffer used to store the chars. */ public char[] getBuffer() { return buffer; } /** * Returns the start offset of the last lexical unit. */ public int getStart() { return start; } /** * Returns the end offset of the last lexical unit. */ public int getEnd() { return end; } /** * Clears the buffer. */ public void clearBuffer() { if (position <= 0) { position = 0; } else { buffer[0] = buffer[position-1]; position = 1; } } /** * The current lexical unit type like defined in LexicalUnits. */ public int getType() { return type; } /** * Returns the string representation of the current lexical unit. */ public String getStringValue() { return new String(buffer, start, end - start); } /** * Scans a @rule value. This method assumes that the current * lexical unit is a at keyword. */ public void scanAtRule() throws ParseException { try { // waiting for EOF, ';' or '{' loop: for (;;) { switch (current) { case '{': int brackets = 1; for (;;) { nextChar(); switch (current) { case '}': if (--brackets > 0) { break; } case -1: break loop; case '{': brackets++; } } case -1: case ';': break loop; } nextChar(); } end = position; } catch (IOException e) { throw new ParseException(e); } } /** * Returns the next token. */ public int next() throws ParseException { blankCharacters = 0; start = position - 1; nextToken(); end = position - endGap(); return type; } /** * Returns the end gap of the current lexical unit. */ protected int endGap() { int result = (current == -1) ? 0 : 1; switch (type) { case LexicalUnits.FUNCTION: case LexicalUnits.STRING: case LexicalUnits.S: case LexicalUnits.PERCENTAGE: result += 1; break; case LexicalUnits.COMMENT: case LexicalUnits.HZ: case LexicalUnits.EM: case LexicalUnits.EX: case LexicalUnits.PC: case LexicalUnits.PT: case LexicalUnits.PX: case LexicalUnits.CM: case LexicalUnits.MM: case LexicalUnits.IN: case LexicalUnits.MS: result += 2; break; case LexicalUnits.KHZ: case LexicalUnits.DEG: case LexicalUnits.RAD: result += 3; break; case LexicalUnits.GRAD: result += 4; } return result + blankCharacters; } /** * Returns the next token. */ protected void nextToken() throws ParseException { try { switch (current) { case -1: type = LexicalUnits.EOF; return; case '{': nextChar(); type = LexicalUnits.LEFT_CURLY_BRACE; return; case '}': nextChar(); type = LexicalUnits.RIGHT_CURLY_BRACE; return; case '=': nextChar(); type = LexicalUnits.EQUAL; return; case '+': nextChar(); type = LexicalUnits.PLUS; return; case ',': nextChar(); type = LexicalUnits.COMMA; return; case ';': nextChar(); type = LexicalUnits.SEMI_COLON; return; case '>': nextChar(); type = LexicalUnits.PRECEDE; return; case '[': nextChar(); type = LexicalUnits.LEFT_BRACKET; return; case ']': nextChar(); type = LexicalUnits.RIGHT_BRACKET; return; case '*': nextChar(); type = LexicalUnits.ANY; return; case '(': nextChar(); type = LexicalUnits.LEFT_BRACE; return; case ')': nextChar(); type = LexicalUnits.RIGHT_BRACE; return; case ':': nextChar(); type = LexicalUnits.COLON; return; case ' ': case '\t': case '\r': case '\n': case '\f': do { nextChar(); } while (ScannerUtilities.isCSSSpace((char)current)); type = LexicalUnits.SPACE; return; case '/': nextChar(); if (current != '*') { type = LexicalUnits.DIVIDE; return; } // Comment nextChar(); start = position - 1; do { while (current != -1 && current != '*') { nextChar(); } do { nextChar(); } while (current != -1 && current == '*'); } while (current != -1 && current != '/'); if (current == -1) { throw new ParseException("eof", reader.getLine(), reader.getColumn()); } nextChar(); type = LexicalUnits.COMMENT; return; case '\'': // String1 type = string1(); return; case '"': // String2 type = string2(); return; case '<': nextChar(); if (current != '!') { throw new ParseException("character", reader.getLine(), reader.getColumn()); } nextChar(); if (current == '-') { nextChar(); if (current == '-') { nextChar(); type = LexicalUnits.CDO; return; } } throw new ParseException("character", reader.getLine(), reader.getColumn()); case '-': nextChar(); if (current != '-') { type = LexicalUnits.MINUS; return; } nextChar(); if (current == '>') { nextChar(); type = LexicalUnits.CDC; return; } throw new ParseException("character", reader.getLine(), reader.getColumn()); case '|': nextChar(); if (current == '=') { nextChar(); type = LexicalUnits.DASHMATCH; return; } throw new ParseException("character", reader.getLine(), reader.getColumn()); case '~': nextChar(); if (current == '=') { nextChar(); type = LexicalUnits.INCLUDES; return; } throw new ParseException("character", reader.getLine(), reader.getColumn()); case '#': nextChar(); if (ScannerUtilities.isCSSNameCharacter((char)current)) { start = position - 1; do { nextChar(); if (current == '\\') { nextChar(); escape(); } } while (current != -1 && ScannerUtilities.isCSSNameCharacter ((char)current)); type = LexicalUnits.HASH; return; } throw new ParseException("character", reader.getLine(), reader.getColumn()); case '@': nextChar(); switch (current) { case 'c': case 'C': start = position - 1; if (isEqualIgnoreCase(nextChar(), 'h') && isEqualIgnoreCase(nextChar(), 'a') && isEqualIgnoreCase(nextChar(), 'r') && isEqualIgnoreCase(nextChar(), 's') && isEqualIgnoreCase(nextChar(), 'e') && isEqualIgnoreCase(nextChar(), 't')) { nextChar(); type = LexicalUnits.CHARSET_SYMBOL; return; } break; case 'f': case 'F': start = position - 1; if (isEqualIgnoreCase(nextChar(), 'o') && isEqualIgnoreCase(nextChar(), 'n') && isEqualIgnoreCase(nextChar(), 't') && isEqualIgnoreCase(nextChar(), '-') && isEqualIgnoreCase(nextChar(), 'f') && isEqualIgnoreCase(nextChar(), 'a') && isEqualIgnoreCase(nextChar(), 'c') && isEqualIgnoreCase(nextChar(), 'e')) { nextChar(); type = LexicalUnits.FONT_FACE_SYMBOL; return; } break; case 'i': case 'I': start = position - 1; if (isEqualIgnoreCase(nextChar(), 'm') && isEqualIgnoreCase(nextChar(), 'p') && isEqualIgnoreCase(nextChar(), 'o') && isEqualIgnoreCase(nextChar(), 'r') && isEqualIgnoreCase(nextChar(), 't')) { nextChar(); type = LexicalUnits.IMPORT_SYMBOL; return; } break; case 'm': case 'M': start = position - 1; if (isEqualIgnoreCase(nextChar(), 'e') && isEqualIgnoreCase(nextChar(), 'd') && isEqualIgnoreCase(nextChar(), 'i') && isEqualIgnoreCase(nextChar(), 'a')) { nextChar(); type = LexicalUnits.MEDIA_SYMBOL; return; } break; case 'p': case 'P': start = position - 1; if (isEqualIgnoreCase(nextChar(), 'a') && isEqualIgnoreCase(nextChar(), 'g') && isEqualIgnoreCase(nextChar(), 'e')) { nextChar(); type = LexicalUnits.PAGE_SYMBOL; return; } break; default: if (!ScannerUtilities.isCSSIdentifierStartCharacter ((char)current)) { throw new ParseException("identifier.character", reader.getLine(), reader.getColumn()); } start = position - 1; } do { nextChar(); if (current == '\\') { nextChar(); escape(); } } while (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)); type = LexicalUnits.AT_KEYWORD; return; case '!': do { nextChar(); } while (current != -1 && ScannerUtilities.isCSSSpace((char)current)); if (isEqualIgnoreCase(current, 'i') && isEqualIgnoreCase(nextChar(), 'm') && isEqualIgnoreCase(nextChar(), 'p') && isEqualIgnoreCase(nextChar(), 'o') && isEqualIgnoreCase(nextChar(), 'r') && isEqualIgnoreCase(nextChar(), 't') && isEqualIgnoreCase(nextChar(), 'a') && isEqualIgnoreCase(nextChar(), 'n') && isEqualIgnoreCase(nextChar(), 't')) { nextChar(); type = LexicalUnits.IMPORTANT_SYMBOL; return; } if (current == -1) { throw new ParseException("eof", reader.getLine(), reader.getColumn()); } else { throw new ParseException("character", reader.getLine(), reader.getColumn()); } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': type = number(); return; case '.': switch (nextChar()) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': type = dotNumber(); return; default: type = LexicalUnits.DOT; return; } case 'u': case 'U': nextChar(); switch (current) { case '+': boolean range = false; for (int i = 0; i < 6; i++) { nextChar(); switch (current) { case '?': range = true; break; default: if (range && !ScannerUtilities.isCSSHexadecimalCharacter ((char)current)) { throw new ParseException("character", reader.getLine(), reader.getColumn()); } } } nextChar(); if (range) { type = LexicalUnits.UNICODE_RANGE; return; } if (current == '-') { nextChar(); if (!ScannerUtilities.isCSSHexadecimalCharacter ((char)current)) { throw new ParseException("character", reader.getLine(), reader.getColumn()); } nextChar(); if (!ScannerUtilities.isCSSHexadecimalCharacter ((char)current)) { type = LexicalUnits.UNICODE_RANGE; return; } nextChar(); if (!ScannerUtilities.isCSSHexadecimalCharacter ((char)current)) { type = LexicalUnits.UNICODE_RANGE; return; } nextChar(); if (!ScannerUtilities.isCSSHexadecimalCharacter ((char)current)) { type = LexicalUnits.UNICODE_RANGE; return; } nextChar(); if (!ScannerUtilities.isCSSHexadecimalCharacter ((char)current)) { type = LexicalUnits.UNICODE_RANGE; return; } nextChar(); if (!ScannerUtilities.isCSSHexadecimalCharacter ((char)current)) { type = LexicalUnits.UNICODE_RANGE; return; } nextChar(); type = LexicalUnits.UNICODE_RANGE; return; } case 'r': case 'R': nextChar(); switch (current) { case 'l': case 'L': nextChar(); switch (current) { case '(': do { nextChar(); } while (current != -1 && ScannerUtilities.isCSSSpace ((char)current)); switch (current) { case '\'': string1(); blankCharacters += 2; while (current != -1 && ScannerUtilities.isCSSSpace ((char)current)) { blankCharacters++; nextChar(); } if (current == -1) { throw new ParseException ("eof", reader.getLine(), reader.getColumn()); } if (current != ')') { throw new ParseException ("character", reader.getLine(), reader.getColumn()); } nextChar(); type = LexicalUnits.URI; return; case '"': string2(); blankCharacters += 2; while (current != -1 && ScannerUtilities.isCSSSpace ((char)current)) { blankCharacters++; nextChar(); } if (current == -1) { throw new ParseException ("eof", reader.getLine(), reader.getColumn()); } if (current != ')') { throw new ParseException ("character", reader.getLine(), reader.getColumn()); } nextChar(); type = LexicalUnits.URI; return; case ')': throw new ParseException("character", reader.getLine(), reader.getColumn()); default: if (!ScannerUtilities.isCSSURICharacter ((char)current)) { throw new ParseException ("character", reader.getLine(), reader.getColumn()); } start = position - 1; do { nextChar(); } while (current != -1 && ScannerUtilities.isCSSURICharacter ((char)current)); blankCharacters++; while (current != -1 && ScannerUtilities.isCSSSpace ((char)current)) { blankCharacters++; nextChar(); } if (current == -1) { throw new ParseException ("eof", reader.getLine(), reader.getColumn()); } if (current != ')') { throw new ParseException ("character", reader.getLine(), reader.getColumn()); } nextChar(); type = LexicalUnits.URI; return; } } } } while (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)) { nextChar(); } if (current == '(') { nextChar(); type = LexicalUnits.FUNCTION; return; } type = LexicalUnits.IDENTIFIER; return; default: if (ScannerUtilities.isCSSIdentifierStartCharacter ((char)current)) { // Identifier do { nextChar(); if (current == '\\') { nextChar(); escape(); } } while (current != -1 && ScannerUtilities.isCSSNameCharacter ((char)current)); if (current == '(') { nextChar(); type = LexicalUnits.FUNCTION; return; } type = LexicalUnits.IDENTIFIER; return; } nextChar(); throw new ParseException("identifier.character", reader.getLine(), reader.getColumn()); } } catch (IOException e) { throw new ParseException(e); } } /** * Scans a single quoted string. */ protected int string1() throws IOException { start = position; // fix bug #29416 loop: for (;;) { switch (nextChar()) { case -1: throw new ParseException("eof", reader.getLine(), reader.getColumn()); case '\'': break loop; case '"': break; case '\\': switch (nextChar()) { case '\n': case '\f': break; default: escape(); } break; default: if (!ScannerUtilities.isCSSStringCharacter((char)current)) { throw new ParseException("character", reader.getLine(), reader.getColumn()); } } } nextChar(); return LexicalUnits.STRING; } /** * Scans a double quoted string. */ protected int string2() throws IOException { start = position; // fix bug #29416 loop: for (;;) { switch (nextChar()) { case -1: throw new ParseException("eof", reader.getLine(), reader.getColumn()); case '\'': break; case '"': break loop; case '\\': switch (nextChar()) { case '\n': case '\f': break; default: escape(); } break; default: if (!ScannerUtilities.isCSSStringCharacter((char)current)) { throw new ParseException("character", reader.getLine(), reader.getColumn()); } } } nextChar(); return LexicalUnits.STRING; } /** * Scans a number. */ protected int number() throws IOException { loop: for (;;) { switch (nextChar()) { case '.': switch (nextChar()) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return dotNumber(); } throw new ParseException("character", reader.getLine(), reader.getColumn()); default: break loop; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': } } return numberUnit(true); } /** * Scans the decimal part of a number. */ protected int dotNumber() throws IOException { loop: for (;;) { switch (nextChar()) { default: break loop; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': } } return numberUnit(false); } /** * Scans the unit of a number. */ protected int numberUnit(boolean integer) throws IOException { switch (current) { case '%': nextChar(); return LexicalUnits.PERCENTAGE; case 'c': case 'C': switch(nextChar()) { case 'm': case 'M': nextChar(); if (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)) { do { nextChar(); } while (current != -1 && ScannerUtilities.isCSSNameCharacter ((char)current)); return LexicalUnits.DIMENSION; } return LexicalUnits.CM; default: while (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)) { nextChar(); } return LexicalUnits.DIMENSION; } case 'd': case 'D': switch(nextChar()) { case 'e': case 'E': switch(nextChar()) { case 'g': case 'G': nextChar(); if (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)) { do { nextChar(); } while (current != -1 && ScannerUtilities.isCSSNameCharacter ((char)current)); return LexicalUnits.DIMENSION; } return LexicalUnits.DEG; } default: while (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)) { nextChar(); } return LexicalUnits.DIMENSION; } case 'e': case 'E': switch(nextChar()) { case 'm': case 'M': nextChar(); if (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)) { do { nextChar(); } while (current != -1 && ScannerUtilities.isCSSNameCharacter ((char)current)); return LexicalUnits.DIMENSION; } return LexicalUnits.EM; case 'x': case 'X': nextChar(); if (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)) { do { nextChar(); } while (current != -1 && ScannerUtilities.isCSSNameCharacter ((char)current)); return LexicalUnits.DIMENSION; } return LexicalUnits.EX; default: while (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)) { nextChar(); } return LexicalUnits.DIMENSION; } case 'g': case 'G': switch(nextChar()) { case 'r': case 'R': switch(nextChar()) { case 'a': case 'A': switch(nextChar()) { case 'd': case 'D': nextChar(); if (current != -1 && ScannerUtilities.isCSSNameCharacter ((char)current)) { do { nextChar(); } while (current != -1 && ScannerUtilities.isCSSNameCharacter ((char)current)); return LexicalUnits.DIMENSION; } return LexicalUnits.GRAD; } } default: while (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)) { nextChar(); } return LexicalUnits.DIMENSION; } case 'h': case 'H': nextChar(); switch(current) { case 'z': case 'Z': nextChar(); if (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)) { do { nextChar(); } while (current != -1 && ScannerUtilities.isCSSNameCharacter ((char)current)); return LexicalUnits.DIMENSION; } return LexicalUnits.HZ; default: while (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)) { nextChar(); } return LexicalUnits.DIMENSION; } case 'i': case 'I': switch(nextChar()) { case 'n': case 'N': nextChar(); if (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)) { do { nextChar(); } while (current != -1 && ScannerUtilities.isCSSNameCharacter ((char)current)); return LexicalUnits.DIMENSION; } return LexicalUnits.IN; default: while (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)) { nextChar(); } return LexicalUnits.DIMENSION; } case 'k': case 'K': switch(nextChar()) { case 'h': case 'H': switch(nextChar()) { case 'z': case 'Z': nextChar(); if (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)) { do { nextChar(); } while (current != -1 && ScannerUtilities.isCSSNameCharacter ((char)current)); return LexicalUnits.DIMENSION; } return LexicalUnits.KHZ; } default: while (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)) { nextChar(); } return LexicalUnits.DIMENSION; } case 'm': case 'M': switch(nextChar()) { case 'm': case 'M': nextChar(); if (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)) { do { nextChar(); } while (current != -1 && ScannerUtilities.isCSSNameCharacter ((char)current)); return LexicalUnits.DIMENSION; } return LexicalUnits.MM; case 's': case 'S': nextChar(); if (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)) { do { nextChar(); } while (current != -1 && ScannerUtilities.isCSSNameCharacter ((char)current)); return LexicalUnits.DIMENSION; } return LexicalUnits.MS; default: while (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)) { nextChar(); } return LexicalUnits.DIMENSION; } case 'p': case 'P': switch(nextChar()) { case 'c': case 'C': nextChar(); if (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)) { do { nextChar(); } while (current != -1 && ScannerUtilities.isCSSNameCharacter ((char)current)); return LexicalUnits.DIMENSION; } return LexicalUnits.PC; case 't': case 'T': nextChar(); if (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)) { do { nextChar(); } while (current != -1 && ScannerUtilities.isCSSNameCharacter ((char)current)); return LexicalUnits.DIMENSION; } return LexicalUnits.PT; case 'x': case 'X': nextChar(); if (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)) { do { nextChar(); } while (current != -1 && ScannerUtilities.isCSSNameCharacter ((char)current)); return LexicalUnits.DIMENSION; } return LexicalUnits.PX; default: while (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)) { nextChar(); } return LexicalUnits.DIMENSION; } case 'r': case 'R': switch(nextChar()) { case 'a': case 'A': switch(nextChar()) { case 'd': case 'D': nextChar(); if (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)) { do { nextChar(); } while (current != -1 && ScannerUtilities.isCSSNameCharacter ((char)current)); return LexicalUnits.DIMENSION; } return LexicalUnits.RAD; } default: while (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)) { nextChar(); } return LexicalUnits.DIMENSION; } case 's': case 'S': nextChar(); return LexicalUnits.S; default: if (current != -1 && ScannerUtilities.isCSSIdentifierStartCharacter ((char)current)) { do { nextChar(); } while (current != -1 && ScannerUtilities.isCSSNameCharacter((char)current)); return LexicalUnits.DIMENSION; } return (integer) ? LexicalUnits.INTEGER : LexicalUnits.REAL; } } /** * Scans an escape sequence, if one. */ protected void escape() throws IOException { if (ScannerUtilities.isCSSHexadecimalCharacter((char)current)) { nextChar(); if (!ScannerUtilities.isCSSHexadecimalCharacter((char)current)) { if (ScannerUtilities.isCSSSpace((char)current)) { nextChar(); } return; } nextChar(); if (!ScannerUtilities.isCSSHexadecimalCharacter((char)current)) { if (ScannerUtilities.isCSSSpace((char)current)) { nextChar(); } return; } nextChar(); if (!ScannerUtilities.isCSSHexadecimalCharacter((char)current)) { if (ScannerUtilities.isCSSSpace((char)current)) { nextChar(); } return; } nextChar(); if (!ScannerUtilities.isCSSHexadecimalCharacter((char)current)) { if (ScannerUtilities.isCSSSpace((char)current)) { nextChar(); } return; } nextChar(); if (!ScannerUtilities.isCSSHexadecimalCharacter((char)current)) { if (ScannerUtilities.isCSSSpace((char)current)) { nextChar(); } return; } } if ((current >= ' ' && current <= '~') || current >= 128) { nextChar(); return; } throw new ParseException("character", reader.getLine(), reader.getColumn()); } /** * Compares the given int with the given character, ignoring case. */ protected static boolean isEqualIgnoreCase(int i, char c) { return (i == -1) ? false : Character.toLowerCase((char)i) == c; } /** * Sets the value of the current char to the next character or -1 if the * end of stream has been reached. */ protected int nextChar() throws IOException { current = reader.read(); if (current == -1) { return current; } if (position == buffer.length) { char[] t = new char[position * 3 / 2]; for (int i = 0; i < position; i++) { t[i] = buffer[i]; } buffer = t; } /* BEGIN Modification for Theme Editor */ customBuffer.append( (char)current ); /* END Modification for Theme Editor */ return buffer[position++] = (char)current; } /* BEGIN Modification for Theme Editor */ protected StringBuffer customBuffer = new StringBuffer(); public void clearCustomBuffer() { customBuffer = new StringBuffer(); } public String getCustomBufferData() { return customBuffer.toString(); } /* END Modification for Theme Editor */ }