intial source from ttp://www.sf.net/projects/wdte
[phpeclipse.git] / archive / net.sourceforge.phpeclipse.css.ui / src / net / sourceforge / phpeclipse / css / ui / internal / text / CssCodeScanner.java
1 /*
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
7  * 
8  * Contributors:
9  *     Christopher Lenz - initial API and implementation
10  * 
11  * $Id: CssCodeScanner.java,v 1.1 2004-09-02 18:11:48 jsurfer Exp $
12  */
13
14 package net.sourceforge.phpeclipse.css.ui.internal.text;
15
16 import java.util.ArrayList;
17 import java.util.Collection;
18 import java.util.List;
19
20 import net.sourceforge.phpeclipse.css.core.internal.text.CssTextUtils;
21 import net.sourceforge.phpeclipse.css.core.profiles.IProfile;
22 import net.sourceforge.phpeclipse.css.ui.internal.CssUIPreferences;
23 import net.sourceforge.phpeclipse.css.ui.text.IColorManager;
24
25 import org.eclipse.jface.preference.IPreferenceStore;
26 import org.eclipse.jface.text.rules.ICharacterScanner;
27 import org.eclipse.jface.text.rules.IRule;
28 import org.eclipse.jface.text.rules.IToken;
29 import org.eclipse.jface.text.rules.IWhitespaceDetector;
30 import org.eclipse.jface.text.rules.Token;
31 import org.eclipse.jface.text.rules.WhitespaceRule;
32
33 /**
34  * Rule based scanner responsible for syntax highlighting CSS source.
35  */
36 public class CssCodeScanner extends AbstractCssScanner {
37
38         // Inner Classes -----------------------------------------------------------
39
40         /**
41          * Custom rule that detects the SGML/XML comment delimiters
42          * (<code>&lt;!--</code> and <code>--&gt;</code> which are allowed at the 
43          * beginning and the end of CSS content.
44          */
45         private class CdoCdcRule implements IRule {
46
47                 /**
48                  * The associated token.
49                  */
50                 private IToken token;
51
52                 /**
53                  * Constructor.
54                  * 
55                  * @param token the associated token
56                  */
57                 public CdoCdcRule(IToken token) {
58                         this.token = token;
59                 }
60
61                 /**
62                  * @see IRule#evaluate(ICharacterScanner)
63                  */
64                 public synchronized IToken evaluate(ICharacterScanner scanner) {
65                         IToken retVal = Token.UNDEFINED;
66                         int count = 1;
67                         int c = scanner.read();
68                         if (c == '<') {
69                                 count++;
70                                 c = scanner.read();
71                                 if (c == '!') {
72                                         count++;
73                                         c = scanner.read();
74                                         if (c == '-') {
75                                                 count++;
76                                                 c = scanner.read();
77                                                 if (c == '-') {
78                                                         return token;
79                                                 }
80                                         }
81                                 }
82                         } else if (c == '-') {
83                                 count++;
84                                 c = scanner.read();
85                                 if (c == '-') {
86                                         count++;
87                                         c = scanner.read();
88                                         if (c == '>') {
89                                                 return token;
90                                         }
91                                 }
92                         }
93                         while (count-- > 0) {
94                                 scanner.unread();
95                         }
96                         return retVal;
97                 }
98                 
99         }
100
101         /**
102          * Custom rule that can detect an at-keyword such as <code>@import</code>.
103          */
104         private class AtKeywordRule implements IRule {
105
106                 /**
107                  * The associated token.
108                  */
109                 private IToken token;
110
111                 /**
112                  * Collection of known at-keywords.
113                  */
114                 private Collection atKeywords;
115
116                 /**
117                  * Constructor.
118                  * 
119                  * @param token the associated token
120                  */
121                 public AtKeywordRule(IToken token) {
122                         this.token = token;
123                         atKeywords = getProfile().getAtKeywords();
124                 }
125
126                 /**
127                  * @see IRule#evaluate(ICharacterScanner)
128                  */
129                 public synchronized IToken evaluate(ICharacterScanner scanner) {
130                         IToken retVal = Token.UNDEFINED;
131                         int count = 1;
132                         int c = scanner.read();
133                         if (c == '@') {
134                                 c = scanner.read();
135                                 if (CssTextUtils.isCssIdentifierStart((char) c)) {
136                                         resetBuffer();
137                                         do {
138                                                 appendToBuffer((char) c);
139                                                 c = scanner.read();
140                                                 count++;
141                                         } while (CssTextUtils.isCssIdentifierPart((char) c));
142                                         String candidate = getBufferContent().toLowerCase();
143                                         if (atKeywords.contains(candidate)) {
144                                                 return token;
145                                         }
146                                 }
147                         }
148                         while (count-- > 0) {
149                                 scanner.unread();
150                         }
151                         return retVal;
152                 }
153                 
154         }
155
156         /**
157          * Custom rule that can detect a known property.
158          */
159         private class PropertyRule implements IRule {
160
161                 /**
162                  * The associated token.
163                  */
164                 private IToken token;
165
166                 /**
167                  * Collection of known properties.
168                  */
169                 private Collection properties;
170
171                 /**
172                  * Constructor.
173                  * 
174                  * @param token the associated token
175                  */
176                 public PropertyRule(IToken token) {
177                         this.token = token;
178                         properties = getProfile().getProperties();
179                 }
180
181                 /**
182                  * @see IRule#evaluate(ICharacterScanner)
183                  */
184                 public synchronized IToken evaluate(ICharacterScanner scanner) {
185                         IToken retVal = Token.UNDEFINED;
186                         int count = 1;
187                         int c = scanner.read();
188                         if (CssTextUtils.isCssIdentifierStart((char) c)) {
189                                 resetBuffer();
190                                 do {
191                                         appendToBuffer((char) c);
192                                         c = scanner.read();
193                                         count++;
194                                 } while (CssTextUtils.isCssIdentifierPart((char) c));
195                                 String candidate = getBufferContent().toLowerCase();
196                                 if (properties.contains(candidate)) {
197                                         while (CssTextUtils.isCssWhitespace((char) c)) {
198                                                 c = scanner.read();
199                                                 count++;
200                                         }
201                                         if (c == ':') {
202                                                 scanner.unread();
203                                                 return token;
204                                         }
205                                 }
206                         }
207                         while (count-- > 0) {
208                                 scanner.unread();
209                         }
210                         return retVal;
211                 }
212                 
213         }
214
215         /**
216          * Custom rule that can detect a pseudo-class in a selector.
217          */
218         private class PseudoClassRule implements IRule {
219
220                 /**
221                  * The associated token.
222                  */
223                 private IToken token;
224
225                 /**
226                  * Collection of known pseudo-classes.
227                  */
228                 private Collection pseudoClasses;
229
230                 /**
231                  * Constructor.
232                  * 
233                  * @param token the associated token
234                  */
235                 public PseudoClassRule(IToken token) {
236                         this.token = token;
237                         pseudoClasses = getProfile().getPseudoClassNames();
238                 }
239
240                 /**
241                  * @see IRule#evaluate(ICharacterScanner)
242                  */
243                 public synchronized IToken evaluate(ICharacterScanner scanner) {
244                         IToken retVal = Token.UNDEFINED;
245                         int count = 1;
246                         int c = scanner.read();
247                         if (c == ':') {
248                                 c = scanner.read();
249                                 if (CssTextUtils.isCssIdentifierStart((char) c)) {
250                                         resetBuffer();
251                                         do {
252                                                 appendToBuffer((char) c);
253                                                 c = scanner.read();
254                                                 count++;
255                                         } while (CssTextUtils.isCssIdentifierPart((char) c));
256                                         String candidate = getBufferContent().toLowerCase();
257                                         if (pseudoClasses.contains(candidate)) {
258                                                 return token;
259                                         }
260                                 }
261                         }
262                         while (count-- > 0) {
263                                 scanner.unread();
264                         }
265                         return retVal;
266                 }
267                 
268         }
269
270         /**
271          * Detects CSS white space.
272          */
273         private static class WhitespaceDetector implements IWhitespaceDetector {
274
275                 /**
276                  * @see IWhitespaceDetector#isWhitespace(char)
277                  */
278                 public boolean isWhitespace(char c) {
279                         return CssTextUtils.isCssWhitespace(c);
280                 }
281
282         }
283
284         // Instance Variables ------------------------------------------------------
285
286         /**
287          * The current CSS profile.
288          */
289         private IProfile profile;
290
291         /**
292          * Shared buffer used by the word detectors.
293          */
294         private StringBuffer buffer = new StringBuffer();
295
296         // Constructors ------------------------------------------------------------
297
298         /**
299          * Constructor.
300          * 
301          * @param store The preference store
302          * @param manager The color manager
303          * @param profile The CSS profile to use
304          */
305         public CssCodeScanner(IPreferenceStore store, IColorManager manager, 
306                 IProfile profile) {
307                 super(store, manager);
308                 this.profile = profile;
309
310                 List rules = new ArrayList();
311
312                 rules.add(new WhitespaceRule(new WhitespaceDetector()));
313
314                 rules.add(new CdoCdcRule(createToken(
315                         CssUIPreferences.EDITOR_COMMENT_COLOR,
316                         CssUIPreferences.EDITOR_COMMENT_BOLD)));
317                 rules.add(new AtKeywordRule(createToken(
318                         CssUIPreferences.EDITOR_AT_KEYWORD_COLOR,
319                         CssUIPreferences.EDITOR_AT_KEYWORD_BOLD)));
320                 rules.add(new PropertyRule(createToken(
321                         CssUIPreferences.EDITOR_PROPERTY_COLOR,
322                         CssUIPreferences.EDITOR_PROPERTY_BOLD)));
323                 rules.add(new PseudoClassRule(createToken(
324                         CssUIPreferences.EDITOR_PSEUDO_CLASS_COLOR,
325                         CssUIPreferences.EDITOR_PSEUDO_CLASS_BOLD)));
326
327                 setRules((IRule[]) rules.toArray(new IRule[rules.size()]));
328
329                 setDefaultReturnToken(createToken(
330                         CssUIPreferences.EDITOR_DEFAULT_COLOR,
331                         CssUIPreferences.EDITOR_DEFAULT_BOLD));
332         }
333
334         // Private Methods ---------------------------------------------------------
335
336         private IProfile getProfile() {
337                 return profile;
338         }
339
340         private void appendToBuffer(char c) {
341                 buffer.append(c);
342         }
343
344         private String getBufferContent() {
345                 return buffer.toString();
346         }
347         
348         private void resetBuffer() {
349                 buffer.setLength(0);
350         }
351
352 }