2 * Copyright (c) 2003-2004 Christopher Lenz and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
9 * Christopher Lenz - initial API and implementation
11 * $Id: DefaultCssScanner.java,v 1.1 2004-09-02 18:07:13 jsurfer Exp $
14 package net.sourceforge.phpeclipse.css.core.internal.parser;
16 import net.sourceforge.phpeclipse.css.core.internal.text.CssTextUtils;
17 import net.sourceforge.phpeclipse.css.core.parser.ICssScanner;
18 import net.sourceforge.phpeclipse.css.core.parser.ICssTokens;
19 import net.sourceforge.phpeclipse.css.core.parser.IProblem;
20 import net.sourceforge.phpeclipse.css.core.parser.LexicalErrorException;
22 import org.eclipse.jface.text.BadLocationException;
23 import org.eclipse.jface.text.IDocument;
24 import org.eclipse.jface.text.IRegion;
25 import org.eclipse.jface.text.Region;
28 * Default implementation of a lexical scanner for CSS.
30 * TODO Add support for character escapes and unicode ranges
32 public class DefaultCssScanner extends AbstractProblemReporter
33 implements ICssScanner {
35 // Instance Variables ------------------------------------------------------
37 private int currentChar;
41 private int tokenOffset;
43 // ICssScanner Implementation ----------------------------------------------
46 * @see ICssScanner#getTokenRegion()
48 public IRegion getTokenRegion() {
49 return new Region(tokenOffset, offset - tokenOffset);
53 * @see ICssScanner#getNextToken()
55 public int getNextToken() throws LexicalErrorException {
56 if (document == null) {
57 throw new IllegalStateException("Source must be set"); //$NON-NLS-1$
59 if (currentChar == -1) {
60 return ICssTokens.EOF;
64 switch (currentChar) {
69 return ICssTokens.COLON;
72 if (Character.isDigit((char) peekNextChar())) {
73 return handleNumber();
78 return ICssTokens.LBRACE;
81 return ICssTokens.LBRACKET;
84 return ICssTokens.LPAREN;
87 return ICssTokens.RBRACE;
90 return ICssTokens.RBRACKET;
93 return ICssTokens.RPAREN;
96 return ICssTokens.SEMICOLON;
99 if (peekNextChar() == '*') {
101 return handleComment();
107 return handleString((char) currentChar);
110 if (peekNextChar() == '!') {
117 if (peekNextChar() == '-') {
120 } else if (CssTextUtils.isCssNumberStart((char)
122 return handleNumber();
127 if (CssTextUtils.isCssIdentifierStart((char) currentChar)) {
128 return handleIdentifier();
129 } else if (Character.isDigit((char) currentChar)) {
130 return handleNumber();
131 } else if (CssTextUtils.isCssWhitespace((char) currentChar)) {
132 return handleWhitespace();
140 * @see ICssScanner#setSource(IDocument)
142 public void setSource(IDocument document) {
143 super.setDocument(document);
149 // Protected Methods -------------------------------------------------------
151 protected int handleCdc() throws LexicalErrorException {
152 if (currentChar != '-') {
153 throw new IllegalStateException(
154 "Not at the beginning of a CDC"); //$NON-NLS-1$
156 if (peekNextChar() != '>') {
157 // not a CDC, rewind and return the start character
159 return getNextCharOrEnd();
162 return ICssTokens.CDC;
165 protected int handleCdo() throws LexicalErrorException {
166 if (currentChar != '!') {
167 throw new IllegalStateException(
168 "Not at the beginning of a CDO"); //$NON-NLS-1$
170 if (peekNextChar() != '-') {
171 // not a CDO, rewind and return the start character
173 return getNextCharOrEnd();
176 if (peekNextChar() != '-') {
177 // not a CDO, rewind and return the start character
178 offset -= "!--".length(); //$NON-NLS-1$
179 return getNextCharOrEnd();
182 return ICssTokens.CDO;
185 protected int handleComment() throws LexicalErrorException {
186 if (currentChar != '*') {
187 throw new IllegalStateException(
188 "Not at the beginning of a comment"); //$NON-NLS-1$
192 getNextCharOrError("unterminatedComment"); //$NON-NLS-1$
193 } while (currentChar != '*');
194 getNextCharOrError("unterminatedComment"); //$NON-NLS-1$
195 while (currentChar == '*') {
196 getNextCharOrError("unterminatedComment"); //$NON-NLS-1$
198 } while (currentChar != '/');
199 return getNextToken();
202 protected int handleIdentifier() throws LexicalErrorException {
203 if (!CssTextUtils.isCssIdentifierStart((char) currentChar)) {
204 throw new IllegalStateException(
205 "Not at the beginning of an identifier"); //$NON-NLS-1$
207 while (CssTextUtils.isCssIdentifierPart((char) peekNextChar())) {
210 return ICssTokens.IDENT;
213 protected int handleNumber() throws LexicalErrorException {
214 if (!CssTextUtils.isCssNumberStart((char) currentChar)) {
215 throw new IllegalStateException(
216 "Not at the beginning of a number"); //$NON-NLS-1$
218 while (CssTextUtils.isCssNumberPart((char) peekNextChar())) {
221 if (peekNextChar() == '.') {
223 while (Character.isDigit((char) peekNextChar())) {
227 return ICssTokens.NUM;
230 protected int handleString(char delim) throws LexicalErrorException {
231 if (currentChar != delim) {
232 throw new IllegalStateException(
233 "Not at the beginning of a string"); //$NON-NLS-1$
236 getNextCharOrError("unterminatedString"); //$NON-NLS-1$
237 if (currentChar == '\\') {
239 getNextCharOrError("unterminatedString"); //$NON-NLS-1$
240 } else if (currentChar == '\n') {
241 reportError("unescapedNewlineInString", //$NON-NLS-1$
242 new Region(tokenOffset, offset - tokenOffset));
244 } while (currentChar != delim);
245 return ICssTokens.STRING;
248 protected int handleWhitespace() throws LexicalErrorException {
249 if (!CssTextUtils.isCssWhitespace((char) currentChar)) {
250 throw new IllegalStateException(
251 "Not at the beginning of white space"); //$NON-NLS-1$
253 while (CssTextUtils.isCssWhitespace((char) peekNextChar())) {
256 return getNextToken();
260 * Positions the scanner over the next character in the source and returns
261 * that character. If the end of the source is reached, an 'unexpected end
262 * of file' error is reported.
264 * @param errorId the ID of the error to report
265 * @return the next character in the source, or -1 if the end of the source
267 * @throws LexicalErrorException If the end of the source has been reached
268 * and no problem collector has been configured to collect the
271 protected final int getNextCharOrError(String errorId)
272 throws LexicalErrorException {
274 if (currentChar == -1) {
275 IProblem error = reportError(errorId,
276 new Region(tokenOffset, offset - tokenOffset));
277 throw new LexicalErrorException(error.getMessage());
283 * Positions the scanner over the next character in the source and returns
286 * @return the next character in the source, or -1 if the end of the source
289 protected final int getNextCharOrEnd() {
291 currentChar = document.getChar(offset);
292 if (currentChar != -1) {
295 } catch (BadLocationException e) {
302 * Returns the next character in the source without changing the scanners
303 * current position (look ahead).
305 * @return the next character in the source, or -1 if the end of the source
308 protected final int peekNextChar() {
311 retVal = document.getChar(offset);
312 } catch (BadLocationException e) {