/*
* Copyright (c) 2003-2004 Christopher Lenz and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Christopher Lenz - initial API and implementation
*
* $Id: CssCodeScanner.java,v 1.1 2004-09-02 18:11:48 jsurfer Exp $
*/
package net.sourceforge.phpeclipse.css.ui.internal.text;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import net.sourceforge.phpeclipse.css.core.internal.text.CssTextUtils;
import net.sourceforge.phpeclipse.css.core.profiles.IProfile;
import net.sourceforge.phpeclipse.css.ui.internal.CssUIPreferences;
import net.sourceforge.phpeclipse.css.ui.text.IColorManager;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.rules.ICharacterScanner;
import org.eclipse.jface.text.rules.IRule;
import org.eclipse.jface.text.rules.IToken;
import org.eclipse.jface.text.rules.IWhitespaceDetector;
import org.eclipse.jface.text.rules.Token;
import org.eclipse.jface.text.rules.WhitespaceRule;
/**
* Rule based scanner responsible for syntax highlighting CSS source.
*/
public class CssCodeScanner extends AbstractCssScanner {
// Inner Classes -----------------------------------------------------------
/**
* Custom rule that detects the SGML/XML comment delimiters
* (<!--
and -->
which are allowed at the
* beginning and the end of CSS content.
*/
private class CdoCdcRule implements IRule {
/**
* The associated token.
*/
private IToken token;
/**
* Constructor.
*
* @param token the associated token
*/
public CdoCdcRule(IToken token) {
this.token = token;
}
/**
* @see IRule#evaluate(ICharacterScanner)
*/
public synchronized IToken evaluate(ICharacterScanner scanner) {
IToken retVal = Token.UNDEFINED;
int count = 1;
int c = scanner.read();
if (c == '<') {
count++;
c = scanner.read();
if (c == '!') {
count++;
c = scanner.read();
if (c == '-') {
count++;
c = scanner.read();
if (c == '-') {
return token;
}
}
}
} else if (c == '-') {
count++;
c = scanner.read();
if (c == '-') {
count++;
c = scanner.read();
if (c == '>') {
return token;
}
}
}
while (count-- > 0) {
scanner.unread();
}
return retVal;
}
}
/**
* Custom rule that can detect an at-keyword such as @import
.
*/
private class AtKeywordRule implements IRule {
/**
* The associated token.
*/
private IToken token;
/**
* Collection of known at-keywords.
*/
private Collection atKeywords;
/**
* Constructor.
*
* @param token the associated token
*/
public AtKeywordRule(IToken token) {
this.token = token;
atKeywords = getProfile().getAtKeywords();
}
/**
* @see IRule#evaluate(ICharacterScanner)
*/
public synchronized IToken evaluate(ICharacterScanner scanner) {
IToken retVal = Token.UNDEFINED;
int count = 1;
int c = scanner.read();
if (c == '@') {
c = scanner.read();
if (CssTextUtils.isCssIdentifierStart((char) c)) {
resetBuffer();
do {
appendToBuffer((char) c);
c = scanner.read();
count++;
} while (CssTextUtils.isCssIdentifierPart((char) c));
String candidate = getBufferContent().toLowerCase();
if (atKeywords.contains(candidate)) {
return token;
}
}
}
while (count-- > 0) {
scanner.unread();
}
return retVal;
}
}
/**
* Custom rule that can detect a known property.
*/
private class PropertyRule implements IRule {
/**
* The associated token.
*/
private IToken token;
/**
* Collection of known properties.
*/
private Collection properties;
/**
* Constructor.
*
* @param token the associated token
*/
public PropertyRule(IToken token) {
this.token = token;
properties = getProfile().getProperties();
}
/**
* @see IRule#evaluate(ICharacterScanner)
*/
public synchronized IToken evaluate(ICharacterScanner scanner) {
IToken retVal = Token.UNDEFINED;
int count = 1;
int c = scanner.read();
if (CssTextUtils.isCssIdentifierStart((char) c)) {
resetBuffer();
do {
appendToBuffer((char) c);
c = scanner.read();
count++;
} while (CssTextUtils.isCssIdentifierPart((char) c));
String candidate = getBufferContent().toLowerCase();
if (properties.contains(candidate)) {
while (CssTextUtils.isCssWhitespace((char) c)) {
c = scanner.read();
count++;
}
if (c == ':') {
scanner.unread();
return token;
}
}
}
while (count-- > 0) {
scanner.unread();
}
return retVal;
}
}
/**
* Custom rule that can detect a pseudo-class in a selector.
*/
private class PseudoClassRule implements IRule {
/**
* The associated token.
*/
private IToken token;
/**
* Collection of known pseudo-classes.
*/
private Collection pseudoClasses;
/**
* Constructor.
*
* @param token the associated token
*/
public PseudoClassRule(IToken token) {
this.token = token;
pseudoClasses = getProfile().getPseudoClassNames();
}
/**
* @see IRule#evaluate(ICharacterScanner)
*/
public synchronized IToken evaluate(ICharacterScanner scanner) {
IToken retVal = Token.UNDEFINED;
int count = 1;
int c = scanner.read();
if (c == ':') {
c = scanner.read();
if (CssTextUtils.isCssIdentifierStart((char) c)) {
resetBuffer();
do {
appendToBuffer((char) c);
c = scanner.read();
count++;
} while (CssTextUtils.isCssIdentifierPart((char) c));
String candidate = getBufferContent().toLowerCase();
if (pseudoClasses.contains(candidate)) {
return token;
}
}
}
while (count-- > 0) {
scanner.unread();
}
return retVal;
}
}
/**
* Detects CSS white space.
*/
private static class WhitespaceDetector implements IWhitespaceDetector {
/**
* @see IWhitespaceDetector#isWhitespace(char)
*/
public boolean isWhitespace(char c) {
return CssTextUtils.isCssWhitespace(c);
}
}
// Instance Variables ------------------------------------------------------
/**
* The current CSS profile.
*/
private IProfile profile;
/**
* Shared buffer used by the word detectors.
*/
private StringBuffer buffer = new StringBuffer();
// Constructors ------------------------------------------------------------
/**
* Constructor.
*
* @param store The preference store
* @param manager The color manager
* @param profile The CSS profile to use
*/
public CssCodeScanner(IPreferenceStore store, IColorManager manager,
IProfile profile) {
super(store, manager);
this.profile = profile;
List rules = new ArrayList();
rules.add(new WhitespaceRule(new WhitespaceDetector()));
rules.add(new CdoCdcRule(createToken(
CssUIPreferences.EDITOR_COMMENT_COLOR,
CssUIPreferences.EDITOR_COMMENT_BOLD)));
rules.add(new AtKeywordRule(createToken(
CssUIPreferences.EDITOR_AT_KEYWORD_COLOR,
CssUIPreferences.EDITOR_AT_KEYWORD_BOLD)));
rules.add(new PropertyRule(createToken(
CssUIPreferences.EDITOR_PROPERTY_COLOR,
CssUIPreferences.EDITOR_PROPERTY_BOLD)));
rules.add(new PseudoClassRule(createToken(
CssUIPreferences.EDITOR_PSEUDO_CLASS_COLOR,
CssUIPreferences.EDITOR_PSEUDO_CLASS_BOLD)));
setRules((IRule[]) rules.toArray(new IRule[rules.size()]));
setDefaultReturnToken(createToken(
CssUIPreferences.EDITOR_DEFAULT_COLOR,
CssUIPreferences.EDITOR_DEFAULT_BOLD));
}
// Private Methods ---------------------------------------------------------
private IProfile getProfile() {
return profile;
}
private void appendToBuffer(char c) {
buffer.append(c);
}
private String getBufferContent() {
return buffer.toString();
}
private void resetBuffer() {
buffer.setLength(0);
}
}