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