JFace Text Framework框架的一個(gè)重要的功能特征就是自動(dòng)編輯策略,它允許用戶(hù)對(duì)輸入的內(nèi)容(準(zhǔn)確的說(shuō)應(yīng)該是即將來(lái)臨的Document修改^_^)做即時(shí)編輯,然后又會(huì)透明的將用戶(hù)的修改付諸于實(shí)現(xiàn)(即應(yīng)用到當(dāng)前文檔)。在本節(jié),我們將在前兩節(jié)有關(guān)TLD Content Model的基礎(chǔ)上開(kāi)發(fā)一個(gè)自動(dòng)編輯策略。
【JFace Text Framework 自動(dòng)編輯策略原理介紹】
【JDT Java源碼編輯器自動(dòng)編輯策略演示】
我們每個(gè)使用Eclipse JDT進(jìn)行Java編程的開(kāi)發(fā)者都會(huì)對(duì)JDT中Java源碼編輯器的自動(dòng)編輯策略印象深刻,它給編碼人員帶來(lái)了很大的方便。舉例如下:

如上圖所示,我們?cè)诰帉?xiě)一個(gè)新的函數(shù),圖中黑色豎線“|”就是光標(biāo)所在處,當(dāng)我們按下回車(chē)鍵的時(shí)候,效果變?yōu)槿缦拢?br />

如上圖所示,當(dāng)我們輸入回車(chē)鍵之后,JDT Java源碼編輯器自動(dòng)幫我們矯正了內(nèi)容(text)和位置(offset):原來(lái)的輸入內(nèi)容應(yīng)該是“
\r\n”,JDT Java源碼編輯器自動(dòng)幫我們矯正為“
\r\n\t\t\r\n\t}”;根據(jù)“\r\n”內(nèi)容推算,輸入后光標(biāo)位置應(yīng)該位于28行的起始處,JDT Java源碼編輯器自動(dòng)幫我們矯正為離28行其實(shí)處兩個(gè)“\t”的距離。
【自動(dòng)編輯流程和主要角色】

上圖就演示了自動(dòng)編輯過(guò)程:
1、用戶(hù)編輯,鍵盤(pán)事件
2、根據(jù)鍵盤(pán)事件,對(duì)事件信息進(jìn)行分析,拼裝到名為DocumentCommand的數(shù)據(jù)結(jié)構(gòu)中,該數(shù)據(jù)結(jié)構(gòu)中包含了用戶(hù)的輸入內(nèi)容(text)、光標(biāo)位置(offset)等信息,例如,上面JDT的例子中用戶(hù)的輸入內(nèi)容為“\r\n”。
這一步JFace Text Framework幫用戶(hù)解決了。
3、調(diào)用自動(dòng)編輯策略,對(duì)應(yīng)DocumentCommand中數(shù)據(jù)進(jìn)行自定義矯正,例如,JDT Java源碼編輯器的自動(dòng)編輯策略將輸入內(nèi)容矯正為“
\r\n\t\t\r\n\t}”。
用戶(hù)自己負(fù)責(zé),JDT Java源碼編輯器在這邊干活了,提供了自己的IAutoEditStrategy^_^
4、將用戶(hù)矯正后的DocumentCommand應(yīng)用到對(duì)應(yīng)編輯器(說(shuō)白了,就是轉(zhuǎn)化為一個(gè)Document replace動(dòng)作執(zhí)行)。
這一步JFace Text Framework幫用戶(hù)解決了。
我們可以看到,JFace Text Framework已經(jīng)替我們干了大部分活,我們需要關(guān)心的是如下兩個(gè)角色:
org.eclipse.jface.text.DocumentCommand:數(shù)據(jù)載體,封裝了一個(gè)文本變化(text modification)的信息,例如輸入內(nèi)容(text)、光標(biāo)位置(offset)、長(zhǎng)度(length)等等。我們要做的恰恰就是在我們自己的自動(dòng)編輯策略中對(duì)DocumentCommand中的內(nèi)容做矯正,然后JFace Text Framework會(huì)自動(dòng)幫我們應(yīng)用到目標(biāo)IDocument中。
org.eclipse.jface.text.IAutoEditStrategy:自動(dòng)編輯編輯策略,根據(jù)不同應(yīng)用場(chǎng)景對(duì)DocumentCommand中的信息做不同的矯正。我們看一下其接口定義就知道了:
package org.eclipse.jface.text;
/**
* An auto edit strategy can adapt changes that will be applied to
* a text viewer's document. The strategy is informed by the text viewer
* about each upcoming change in form of a document command. By manipulating
* this document command, the strategy can influence in which way the text
* viewer's document is changed. Clients may implement this interface.
*
* @since 2.1
*/
public interface IAutoEditStrategy {
/**
* Allows the strategy to manipulate the document command.
*
* @param document the document that will be changed
* @param command the document command describing the change
*/
void customizeDocumentCommand(IDocument document, DocumentCommand command);
}
【配置IAutoEditStrategy到SourceViewer】
上面我們已經(jīng)講了自動(dòng)編輯的流程和主要角色,可以大家都有個(gè)疑問(wèn):IAutoEditStrategy到底是如何被自動(dòng)調(diào)用的?
IAutoEditStrategy實(shí)現(xiàn)需要被配置到對(duì)應(yīng)的SourceViewerConfiguration中,并和對(duì)應(yīng)的內(nèi)容類(lèi)型(很多時(shí)候也可以理解為分區(qū)類(lèi)型)綁定。JFace Text Framework會(huì)在初始化編輯器實(shí)例的時(shí)候讀取用戶(hù)的配置,然后根據(jù)不同的內(nèi)容類(lèi)型去查找對(duì)應(yīng)的自動(dòng)編輯器策略。
package org.eclipse.jface.text.source;
public class SourceViewerConfiguration {
/**
* Returns the auto edit strategies ready to be used with the given source viewer
* when manipulating text of the given content type. For backward compatibility, this implementation always
* returns an array containing the result of {@link #getAutoIndentStrategy(ISourceViewer, String)}.
*
* @param sourceViewer the source viewer to be configured by this configuration
* @param contentType the content type for which the strategies are applicable
* @return the auto edit strategies or <code>null</code> if automatic editing is not to be enabled
* @since 3.1
*/
public IAutoEditStrategy[] getAutoEditStrategies(ISourceViewer sourceViewer, String contentType) {
//
}
}
附加說(shuō)明:1、有關(guān)內(nèi)容類(lèi)型(分區(qū)類(lèi)型)可以參考前面章節(jié)的內(nèi)容; 2、原有的IAutoIndentStrategy接口Eclipse已經(jīng)不推薦使用^_^
【定制WTP StructuredTextEditor的自動(dòng)編輯策略】
【需求】
當(dāng)用戶(hù)輸入標(biāo)簽結(jié)束符“>”時(shí),如果標(biāo)簽在TLD中定義為不含有標(biāo)簽體(即bodycontent屬性值為empty),則自動(dòng)將用戶(hù)的輸入內(nèi)容矯正為“/>”。
【前提知識(shí)】
對(duì)WTP基本數(shù)據(jù)模型不很了解的可以參見(jiàn)一下前面的相關(guān)章節(jié):
【Eclipse插件開(kāi)發(fā)】基于WTP開(kāi)發(fā)自定義的JSP編輯器(三) :WTP Structured Document
【Eclipse插件開(kāi)發(fā)】基于WTP開(kāi)發(fā)自定義的JSP編輯器(五) :WTP Structured Model
【Eclipse插件開(kāi)發(fā)】基于WTP開(kāi)發(fā)自定義的JSP編輯器(七):WTP數(shù)據(jù)模型總結(jié)和模型管理
【Eclipse插件開(kāi)發(fā)】基于WTP開(kāi)發(fā)自定義的JSP編輯器(十):WTP TLD內(nèi)容模型介紹
【開(kāi)發(fā)自定義的IAutoEditStrategy實(shí)現(xiàn)】
/**
* 針對(duì)JSP_DIRECTIVE分區(qū)的自動(dòng)編輯策略
*
* @author zhuxing (mailto:zhu_xing@live.cn)
*/
/*
* 修改歷史
* $Log$
*/
public class JSPDirectivePartitionAutoEditStrategy implements IAutoEditStrategy {
/* (non-Javadoc)
* @see org.eclipse.jface.text.IAutoEditStrategy#customizeDocumentCommand(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.DocumentCommand)
*/
public void customizeDocumentCommand(IDocument document, DocumentCommand command) {
String text = command.text;
if (!">".equals(text))
return ;
IStructuredDocument structuredDocument = (IStructuredDocument)document;
IStructuredDocumentRegion structuredDocumentRegion = structuredDocument.getRegionAtCharacterOffset(command.offset);
ITextRegion currentRegion = structuredDocumentRegion.getRegionAtCharacterOffset(command.offset);
int startOffset = structuredDocumentRegion.getStartOffset(currentRegion);
int textLength = currentRegion.getTextLength();
//執(zhí)行前提條件:1、用戶(hù)正在標(biāo)簽名區(qū)域編輯;2、用戶(hù)編輯位置位于標(biāo)簽名末端
if (currentRegion.getType() == DOMRegionContext.XML_TAG_NAME
&& command.offset >= startOffset + textLength) {
if (!hasBody(structuredDocumentRegion))
command.text = "/>";
}
}
/**
* 查詢(xún)對(duì)應(yīng)的TLD內(nèi)容模型,判斷該標(biāo)簽在TLD中定義的時(shí)候是否含有標(biāo)簽體
*
* @param structuredDocumentRegion
* @return
*/
private boolean hasBody(IStructuredDocumentRegion structuredDocumentRegion) {
//獲取標(biāo)簽名稱(chēng)
IStructuredDocument structuredDocument = structuredDocumentRegion.getParentDocument();
IStructuredModel structuredModel = StructuredModelManager.getModelManager().getModelForRead(structuredDocument);
IDOMElement domElement = (IDOMElement)structuredModel.getIndexedRegion(structuredDocumentRegion.getStartOffset());
String tagName = domElement.getNodeName();
/**
* 1、獲取位置相關(guān)的TLD Document列表
* 2、查找對(duì)應(yīng)的TLD Element(Tag)定義
* 3、判斷TLD Element(Tag)定義中定義的bodycontent屬性是否為“empty”
*/
TLDCMDocumentManager manager = TaglibController.getTLDCMDocumentManager(structuredDocument);
List list = manager.getCMDocumentTrackers(structuredDocumentRegion.getStartOffset());
for (Iterator iter = list.iterator(); iter.hasNext();) {
TaglibTracker tracker = (TaglibTracker) iter.next();
TLDDocument tlDocument = (TLDDocument)tracker.getDocument();
CMNode cmnode = tlDocument.getElements().getNamedItem(tagName);
if (cmnode == null)
continue ;
String bodyType = ((TLDElementDeclaration)cmnode).getBodycontent();
if ("empty".equals(bodyType))
return false;
return true;
}
return false;
}
}
基本流程如下:
1、執(zhí)行條件判斷,我這邊假設(shè)用戶(hù)正在標(biāo)簽名區(qū)域編輯(通過(guò)判斷編輯區(qū)域region的region type來(lái)判斷),并且編輯位置位于標(biāo)簽名內(nèi)容末端
2、查詢(xún)和當(dāng)前文檔中特定位置相關(guān)的WTP TLD內(nèi)容模型,判斷該標(biāo)簽在TLD文件中定義的時(shí)候是否聲明允許有標(biāo)簽體
3、如果標(biāo)簽在TLD中定義為不含有標(biāo)簽體,則矯正為自動(dòng)閉合,“>”--->“/>”
PS:有關(guān)語(yǔ)法Document、語(yǔ)義Doucment、WTP TLD Content Document等數(shù)據(jù)類(lèi)型相關(guān)內(nèi)容請(qǐng)參見(jiàn)前面的章節(jié)。
【配置SourceViewerConfiguration】
將我們上面的開(kāi)發(fā)的自動(dòng)編輯策略配置到我們的SourceViewerConfiguration實(shí)現(xiàn)中:
/**
* 自定義StructuredTextViewerConfiguration,基于WTP jst提供的StructuredTextViewerConfigurationJSP,
* 后面會(huì)提供自定義的自動(dòng)提示策略等擴(kuò)展。
*
* @author zhuxing (mailto:zhu_xing@live.cn)
*/
/*
* 修改歷史
* $Log$
*/
public class JSPStructuredTextViewerConfiguration extends
StructuredTextViewerConfigurationJSP {
/*
* 提供自定義的自動(dòng)提示策略
*
* @see org.eclipse.jst.jsp.ui.StructuredTextViewerConfigurationJSP#getContentAssistProcessors(org.eclipse.jface.text.source.ISourceViewer, java.lang.String)
*/
protected IContentAssistProcessor[] getContentAssistProcessors(ISourceViewer sourceViewer, String partitionType) {
//我們目前只自定義JSP標(biāo)簽屬性值自動(dòng)提示的情況,所以針對(duì)的分區(qū)類(lèi)型為IJSPPartitions.JSP_DIRECTIVE
if (partitionType == IJSPPartitions.JSP_DIRECTIVE) {
return new IContentAssistProcessor[]{new CustomizedJSPContentAssistantProcessor(), new JSPContentAssistProcessor()};
}
return super.getContentAssistProcessors(sourceViewer, partitionType);
}
/*
* 提供自定義的自動(dòng)編輯策略
*
* @see org.eclipse.jst.jsp.ui.StructuredTextViewerConfigurationJSP#getAutoEditStrategies(org.eclipse.jface.text.source.ISourceViewer, java.lang.String)
*/
public IAutoEditStrategy[] getAutoEditStrategies(ISourceViewer sourceViewer, String contentType) {
//我們目前只自定義JSP標(biāo)簽名有關(guān)的自動(dòng)編輯策略,所以針對(duì)的分區(qū)類(lèi)型為IJSPPartitions.JSP_DIRECTIVE
if (contentType == IJSPPartitions.JSP_DIRECTIVE) {
List<IAutoEditStrategy> strategies = new ArrayList<IAutoEditStrategy>();
//WTP已配置的自動(dòng)編輯策略
IAutoEditStrategy[] existingStrategies = super.getAutoEditStrategies(sourceViewer, contentType);
Collections.addAll(strategies, existingStrategies);
//自定義的自動(dòng)編輯策略
IAutoEditStrategy customizedStrategies = new JSPDirectivePartitionAutoEditStrategy();
strategies.add(customizedStrategies);
return strategies.toArray(new IAutoEditStrategy[strategies.size()]);
}
return super.getAutoEditStrategies(sourceViewer, contentType);
}
}
我們?cè)谖覀冏约旱腟ourceViewerConfiguration實(shí)現(xiàn)JSPStructuredTextViewerConfiguration中覆寫(xiě)了getAutoEditStrategies方法。由于我們只是演示定制標(biāo)簽名輸入時(shí)候的自動(dòng)編輯策略,所以針對(duì)的分區(qū)類(lèi)型為IJSPPartitions.JSP_DIRECTIVE。
【效果演示】

如上圖所示,我們輸入了標(biāo)簽名test1:test,而且光標(biāo)位于標(biāo)簽名文本內(nèi)容之后,此時(shí)WTP的源碼校驗(yàn)器給出了錯(cuò)誤提示,說(shuō)我們的標(biāo)簽缺少對(duì)應(yīng)的閉合符號(hào)。
我們看一下test1:test標(biāo)簽對(duì)應(yīng)的TLD定義,定位為不允許有標(biāo)簽體:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.0</jspversion>
<shortname>test1</shortname>
<uri>http://www.tkk7.com/zhuxing/tags/test1</uri>
<tag>
<name>test</name>
<tagclass>any</tagclass>
<bodycontent>empty</bodycontent>
<attribute>
<name>scope</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
根據(jù)需求,
當(dāng)我們?cè)趫D中光標(biāo)位置輸入“>”的時(shí)候,我們前面開(kāi)發(fā)的自動(dòng)編輯策略會(huì)自動(dòng)將其矯正為合法的結(jié)束符“/>”,效果如下:
對(duì)比如下,如果沒(méi)有應(yīng)用自動(dòng)編輯策略,則會(huì)得到如下源碼校驗(yàn)錯(cuò)誤:
【后記】
提醒:從需求上講,我們示例中開(kāi)發(fā)的自動(dòng)編輯策略其實(shí)是非常簡(jiǎn)單的。但是將其放置到一個(gè)定制WTP StructuredTextEditor的場(chǎng)景中,就不可避免的和我們前面介紹的WTP基礎(chǔ)數(shù)據(jù)模型的知識(shí)打交道,在這里再次強(qiáng)調(diào),前面介紹的WTP各種數(shù)據(jù)模型一定要熟練搞清楚,基礎(chǔ)中的基礎(chǔ)?。?!
源碼為實(shí)際工程以Export ---> Archive File方式導(dǎo)出的,
下載鏈接:WTP StructuredTextEditor自動(dòng)編輯策略定制示例源碼
本博客中的所有文章、隨筆除了標(biāo)題中含有引用或者轉(zhuǎn)載字樣的,其他均為原創(chuàng)。轉(zhuǎn)載請(qǐng)注明出處,謝謝!