3、编辑器的语法颜色功能 
(1)基本概念 
l         首先是定义各种Partition(文档中不重叠的文本),用以区分语法明显不同的部分 
l         文档中的每个字符必须属于定义得某个Partition或缺省Partition(IDocument.DEFAULT_CONTENT_TYPE) 
l         注释应该属于独立的一种Partition 
l         由于log4j.properties中除了注释,每行多是name=value形式,所以分成3种Partition 
Ø         注释(以#开始) 
Ø         Value值(包括=) 
Ø         缺省Partition(包括name) 
l         需要提供一个Partition扫描器供TextEditor调用,来决定哪些文本属于哪个Partition 
l         还需要提供Token扫描器供TextEditor调用 
l         Token是用来设置颜色的最小文本单元 
l         每个Partition需要有唯一的Token扫描器 
(2)Partition扫描器 
l         Partition扫描器通过提供的解析规则(定义Partition)进行解析,将文本区分成不同的Partition 
l         本例使用JFace提供的基于规则的扫描器,所以需要在plugin.xml清单编辑器的Dependencies页中添加org.eclipse.jface.text插件 
package org.xqtu.log4j.editor;    import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.rules.IPredicateRule; import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; import org.eclipse.jface.text.rules.SingleLineRule; import org.eclipse.jface.text.rules.Token;   public class PropertiesPartitionScanner extends RuleBasedPartitionScanner {      public final static String LOG4J_COMMENT = "__log4j_comment";       public final static String LOG4J_VALUE = "__log4j_value";       public PropertiesPartitionScanner() {        super();           Token commentPartition = new Token(LOG4J_COMMENT);         Token valuePartition = new Token(LOG4J_VALUE);           SingleLineRule commentRule = new SingleLineRule("#", null,                commentPartition, (char) 0, true);         commentRule.setColumnConstraint(0);         SingleLineRule valueRule = new SingleLineRule("=", null,                valuePartition, (char) 0, true);         setPredicateRules(new IPredicateRule[] { commentRule, valueRule });    }       public static String[] getLegalContentTypes() {        return new String[] { IDocument.DEFAULT_CONTENT_TYPE,                PropertiesPartitionScanner.LOG4J_COMMENT,                 PropertiesPartitionScanner.LOG4J_VALUE };     } }  
l         这里扩展了RuleBasedPartitionScanner,通过读取文本,应用提供的Partition规则 
l         在开始部分定义了Partition类型常量 
l         在构造方法中定义了每种Partition类型(这里部包括缺省Partition类型)的规则 
l         首先创建每种Partition自己唯一的Token对象 
l         使用SingleLineRule定义Partition规则,SingleLineRule包括四个参数: 
Ø         开始序列匹配模式 
Ø         结束序列匹配模式 
Ø         Escape字符 
Ø         设置为true表示当到达文件结束时终止 
l         setColumnConstraint()方法设置Partition规则的列限制:只返回匹配从指定列数开始的符合规则的Token 
l         setPredicateRules()方法告诉Partition扫描器定义的Partition规则 
l         最后的getLegalContentTypes()方法返回该Partition扫描器支持的Partition类型 
(3)Token管理器 
l         颜色(SWT Color类的特定实例)是有限的系统资源,需要跟踪它们,对它们进行管理:不多分配,在用完之后释放它们 
l         Token管理器用于对Token和颜色都进行跟踪和管理,主要目的是支持允许用户在Preferences中设置颜色的功能 
package org.xqtu.log4j.editor;   import java.util.HashMap; import java.util.Iterator; import java.util.Map;   import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.resource.StringConverter; import org.eclipse.jface.text.TextAttribute; import org.eclipse.jface.text.rules.IToken; import org.eclipse.jface.text.rules.Token; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Display;   public class TokenManager {      private Map colorTable = new HashMap(10);       private Map tokenTable = new HashMap(10);       private final IPreferenceStore preferenceStore;       public TokenManager(IPreferenceStore preferenceStore) {        this.preferenceStore = preferenceStore;     }       public IToken getToken(String prefKey) {        Token token = (Token) tokenTable.get(prefKey);         if (token == null) {            String colorName = preferenceStore.getString(prefKey);             RGB rgb = StringConverter.asRGB(colorName);             token = new Token(new TextAttribute(getColor(rgb)));             tokenTable.put(prefKey, token);         }         return token;     }       public void dispose() {        Iterator e = colorTable.values().iterator();         while (e.hasNext()) {            ((Color) e.next()).dispose();         }     }       private Color getColor(RGB rgb) {        Color color = (Color) colorTable.get(rgb);         if (color == null) {            color = new Color(Display.getCurrent(), rgb);             colorTable.put(rgb, color);         }         return color;     }       public boolean affectsTextPresentation(PropertyChangeEvent event) {        Token token = (Token) tokenTable.get(event.getProperty());         return (token != null);     }       public void handlePreferenceStoreChanged(PropertyChangeEvent event) {        String prefKey = event.getProperty();         Token token = (Token) tokenTable.get(prefKey);         if (token != null) {            String colorName = preferenceStore.getString(prefKey);             RGB rgb = StringConverter.asRGB(colorName);             token.setData(new TextAttribute(getColor(rgb)));         }     } }  
l         开始部分定义了用于管理的Token和颜色的HashMap列表 
l         getToken()方法获得指定的Token:先在Token列表中查找,如果没有,就根据Preferences中获得的颜色,创建一个新的Token,并保存到Token列表中 
l         在创建Token时调用getColor()方法,获得指定的颜色,其方法类似:先在颜色列表中查找,如果没有就分配一种新的颜色,并保存到颜色列表中 
l         TextAttribute对象用于描述文本的属性,本例只指定前景颜色,也可以指定背景颜色或式样,如粗体、斜体等 
l         dispose()方法用来释放所有的颜色资源 
l         通常,编辑器通过注册在变化发生时调用的方法来监听Preferences的变化 
Ø         affectsTextPresentation()方法判断Preferences中属性的变化是否需要应用到编辑器中的文本,通常的方法只有属性是Token列表中某个Token的名字才需要应用 
Ø         handlePreferenceStoreChanged()方法在Preferences存储变化时应用属性变化到Toke;,同样,只有变化的属性名字是Token列表中某个Token,才会根据Preferences中指定的值替换Token的颜色 
(4)注释Token扫描器 
l         每个Partition需要有一个对应的Token扫描器 
package org.xqtu.log4j.editor.scanners;   import org.eclipse.jface.text.rules.IToken; import org.eclipse.jface.text.rules.RuleBasedScanner; import org.xqtu.log4j.Log4jPlugin; import org.xqtu.log4j.editor.TokenManager;   public class CommentScanner extends RuleBasedScanner {      public CommentScanner(TokenManager tokenManager) {        IToken commentToken = tokenManager                 .getToken(Log4jPlugin.PREF_COMMENT_COLOR);         setDefaultReturnToken(commentToken);     } }  
l         所有的Token扫描器都要扩展RuleBasedScanner 
l         使用TokenManager的getToken()方法获得指定的Token 
l         Token被指派到Partition中文本的每个部分 
l         由于注释Partition中的任何东西都是注释,所以不需要提供任何规则,只要指定返回的缺省Token 
(5)缺省Token扫描器 
package org.xqtu.log4j.editor.scanners;   import org.eclipse.jface.text.rules.IRule; import org.eclipse.jface.text.rules.IToken; import org.eclipse.jface.text.rules.RuleBasedScanner; import org.eclipse.jface.text.rules.WhitespaceRule; import org.xqtu.log4j.Log4jPlugin; import org.xqtu.log4j.editor.TokenManager;   public class DefaultScanner extends RuleBasedScanner {      public DefaultScanner(TokenManager tokenManager) {        IToken propertyToken = tokenManager                 .getToken(Log4jPlugin.PREF_PROPERTY_COLOR);         setDefaultReturnToken(propertyToken);         setRules(new IRule[] { new WhitespaceRule(new WhitespaceDetector()) });    } }  
l         缺省Partition中除了空白字符以外的所有内容都被认为是属性名字,所以需要调用setRules()方法设置空白字符的规则 
l         探测器WhitespaceDetector实现IWhitespaceDetector接口,决定当前内容中的字符是否为空白字符 
package org.xqtu.log4j.editor.scanners;   import org.eclipse.jface.text.rules.IWhitespaceDetector;   public class WhitespaceDetector implements IWhitespaceDetector {      public boolean isWhitespace(char c) {        return Character.isWhitespace(c);     }   }  
(6)Value值Token扫描器 
l         Value值Token扫描器相对要复杂一些,因为需要处理关键字和象%m、%d{hh:mm:ss a}之类的格式 
package org.xqtu.log4j.editor.scanners;   import org.eclipse.jface.text.rules.IRule; import org.eclipse.jface.text.rules.IToken; import org.eclipse.jface.text.rules.RuleBasedScanner; import org.eclipse.jface.text.rules.SingleLineRule; import org.eclipse.jface.text.rules.WhitespaceRule; import org.eclipse.jface.text.rules.WordRule; import org.xqtu.log4j.Log4jPlugin; import org.xqtu.log4j.editor.TokenManager;   public class ValueScanner extends RuleBasedScanner {         String[] keywords = { "ALL", "DEBUG", "ERROR", "FATAL", "INFO", "OFF",                "WARN", "INHERITED", "INHERIT", "NULL", "true", "false", };          public ValueScanner(TokenManager tokenManager) {              IToken defaultToken = tokenManager                        .getToken(Log4jPlugin.PREF_DEFAULT_COLOR);               IToken formatToken = tokenManager                        .getToken(Log4jPlugin.PREF_FORMAT_COLOR);               IToken keywordToken = tokenManager                        .getToken(Log4jPlugin.PREF_KEYWORD_COLOR);                 IRule braceRule = new SingleLineRule("{", "}", formatToken, (char) 0,                       true);                 WordRule keywordRule = new WordRule(new WordDetector());               for (int i = 0; i < keywords.length; i++) {                keywordRule.addWord(keywords[i], keywordToken);               }                 IRule formatRule = new FormatRule(formatToken);                 IRule whitespaceRule = new WhitespaceRule(new WhitespaceDetector());                 setDefaultReturnToken(defaultToken);               setRules(new IRule[] { braceRule, formatRule, keywordRule,                       whitespaceRule, });        } }  
l         Value值Token扫描器处理包括关键字、格式化和缺省三种Token 
l         在开始部分定义了关键字列表,使用WordRule来探测所有关键字Token,并应用了WordDetector探测器 
l         WordDetector探测器实现IWordDetector接口,决定以字母开始,包含字母或数字的内容为Word 
package org.xqtu.log4j.editor.scanners;   import org.eclipse.jface.text.rules.IWordDetector;   public class WordDetector implements IWordDetector {      public boolean isWordStart(char c) {        return Character.isLetter(c);     }       public boolean isWordPart(char c) {        return Character.isLetterOrDigit(c);     }   }  
l         {}内的内容是作为格式化Token,使用SingleLineRule定义Token规则 
l         第三个规则是自定义的格式化Token规则,在后面讲述 
l         同样,需要定义空白字符规则来匹配空白字符 
l         其它文本作为缺省Token返回 
l         最后设置应用的Token规则 
(7)自定义Token规则 
l         由于Eclipse平台没有提供Log4j格式的规则,需要自定义规则 
package org.xqtu.log4j.editor.scanners;   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.Token;   public class FormatRule implements IRule {      private final IToken token;       public FormatRule(IToken token) {        this.token = token;     }       public IToken evaluate(ICharacterScanner scanner) {        int c = scanner.read();         if (c == '%') {            do {                c = scanner.read();             } while (c != ICharacterScanner.EOF                     && (Character.isLetterOrDigit((char) c) || c == '-' || c == '.'));             scanner.unread();               return token;         }         scanner.unread();         return Token.UNDEFINED;     }   }  
l         自定义规则实现IRule接口,使用evaluate()方法逐个字符进行规则匹配 
l         在构造方法中,先保存要匹配的Token 
l         在evaluate()方法,使用IcharacterScanner逐个读取字符,进行规则匹配 
l         如果以%开始就是格式化Token,读取包含字母、数字、“-”和“.”内容,作为Token返回 
l         否则返回Token.UNDEFINED,表示Token没有匹配,以便Token扫描器进行下一条规则的匹配 
l         其中,调用IcharacterScanner的unread()方法对没有匹配的内容进行回退读取操作  
 
  |