/******************************************************************************* * Copyright (c) 2007 Exadel, Inc. and Red Hat, Inc. * Distributed under license by Red Hat, Inc. All rights reserved. * This program is made available under the terms of the * Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Exadel, Inc. and Red Hat, Inc. - initial API and implementation ******************************************************************************/ package org.jboss.tools.common.text.ext.util; import java.io.InputStream; import java.io.Reader; import org.eclipse.jface.text.rules.ICharacterScanner; import org.eclipse.jface.text.rules.IToken; /** * @author Jeremy * */ public class CSSTextScanner extends TextScanner { public static final String CSS_CLASS_NAME = "___css_class_name"; //$NON-NLS-1$ public static final String CSS_CLASS_NAME_SEPARATOR = "___css_class_name_separator"; //$NON-NLS-1$ public static final String CSS_CLASS_BODY = "___css_class_body"; //$NON-NLS-1$ public static final String CSS_CLASS_COMMENT = "___css_class_comment"; //$NON-NLS-1$ private static final int STATE_START = 0; private static final int STATE_NAME = 1; private static final int STATE_NAME_SEPARATOR = 2; private static final int STATE_BODY = 3; private static final int STATE_COMMENT = 4; private static final int STATE_END = 5; private int state; private int savedForCommentState; /** * @param stream */ public CSSTextScanner(InputStream stream) { super(stream); this.state = STATE_START; } /** * @param reader */ public CSSTextScanner(Reader reader) { super(reader); this.state = STATE_START; } private void setState(int newState) { if (this.state != STATE_COMMENT) this.savedForCommentState = this.state; this.state = newState; } /* (non-Javadoc) * @see org.jboss.tools.jsf.text.ext.util.TextScanner#nextToken() */ public IToken nextToken() { offset += length; switch (state) { case STATE_START: case STATE_NAME_SEPARATOR: return nextNameSeparatorToken(); case STATE_NAME: return nextNameToken(); case STATE_BODY: return nextBodyToken(); case STATE_COMMENT: return nextCommentToken(); } return nextEndToken(); } private IToken nextNameToken() { // Check first one char in the stream int ch = read(); if (ch == ICharacterScanner.EOF) { return getToken(CSS_CLASS_NAME); } if (!NMTOKEN_DETECTOR.isWordStart((char)ch)) { // Emulate END of text clearText(); return getToken(CSS_CLASS_NAME); } ch = read(); int readsCount = 0; while (ch != ICharacterScanner.EOF) { if (!NMTOKEN_DETECTOR.isWordPart((char)ch)) { unread(); state = STATE_NAME_SEPARATOR; return getToken(CSS_CLASS_NAME); } readsCount++; ch = read(); } return getToken(CSS_CLASS_NAME); } private IToken nextNameSeparatorToken() { int count = skipWhitespaceToken(); if (count > 0) { state = STATE_NAME_SEPARATOR; return getToken(null); } int ch = read(); int readsCount = 0; while (ch != ICharacterScanner.EOF) { if (NMTOKEN_DETECTOR.isWordStart((char)ch)) { unread(); state = STATE_NAME; return (readsCount > 0 ? getToken(CSS_CLASS_NAME_SEPARATOR) : nextNameToken()); } if (ch == '{') { unread(); state = STATE_BODY; return (readsCount > 0 ? getToken(CSS_CLASS_NAME_SEPARATOR) : nextBodyToken()); } if (ch == '/') { ch = read(); if (ch == ICharacterScanner.EOF) { break; } if (ch == '*') { // Comment openning unread(); // Unread '*'-char unread(); // Unread '/'-char setState(STATE_COMMENT); if (readsCount > 0) { return getToken(CSS_CLASS_NAME_SEPARATOR); } else { return nextCommentToken(); } } // Broken file ??? Emulate END of text clearText(); return getToken(CSS_CLASS_NAME_SEPARATOR); } readsCount++; ch = read(); } state = STATE_END; return getToken(CSS_CLASS_NAME_SEPARATOR); } private IToken nextBodyToken() { int level = 0; int ch = read(); while (ch != ICharacterScanner.EOF) { if (ch == '{') { level++; } else if (ch == '}') { level--; if (level <= 0) { state = STATE_START; return getToken(CSS_CLASS_BODY); } } if (ch == '/') { ch = read(); if (ch == ICharacterScanner.EOF) { break; } if (ch == '*') { // Comment openning unread(); // Unread '*'-char unread(); // Unread '/'-char setState(STATE_COMMENT); return getToken(CSS_CLASS_BODY); } // Broken file ??? Emulate END of text clearText(); return getToken(CSS_CLASS_BODY); } ch = read(); } state = STATE_END; return getToken(CSS_CLASS_BODY); } private IToken nextCommentToken() { int ch = read(); while (ch != ICharacterScanner.EOF) { if (ch == '*') { ch = read(); if (ch == ICharacterScanner.EOF) break; if (ch == '/') { // Comment closing setState(savedForCommentState); return getToken(CSS_CLASS_COMMENT); } unread(); // Unread last char } ch = read(); } setState(savedForCommentState); return getToken(CSS_CLASS_COMMENT); } private IToken nextEndToken () { state = STATE_END; // Emulate END of text clearText(); return getToken(null); } }