JFace Text Framework框架的一個重要的功能特征就是自動編輯策略,它允許用戶對輸入的內容(準確的說應該是即將來臨的Document修改^_^)做即時編輯,然后又會透明的將用戶的修改付諸于實現(即應用到當前文檔)。在本節,我們將在前兩節有關TLD Content Model的基礎上開發一個自動編輯策略。
【JFace Text Framework 自動編輯策略原理介紹】
【JDT Java源碼編輯器自動編輯策略演示】
我們每個使用Eclipse JDT進行Java編程的開發者都會對JDT中Java源碼編輯器的自動編輯策略印象深刻,它給編碼人員帶來了很大的方便。舉例如下:

如上圖所示,我們在編寫一個新的函數,圖中黑色豎線“|”就是光標所在處,當我們按下回車鍵的時候,效果變為如下:

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

上圖就演示了自動編輯過程:
1、用戶編輯,鍵盤事件
2、根據鍵盤事件,對事件信息進行分析,拼裝到名為DocumentCommand的數據結構中,該數據結構中包含了用戶的輸入內容(text)、光標位置(offset)等信息,例如,上面JDT的例子中用戶的輸入內容為“\r\n”。
這一步JFace Text Framework幫用戶解決了。
3、調用自動編輯策略,對應DocumentCommand中數據進行自定義矯正,例如,JDT Java源碼編輯器的自動編輯策略將輸入內容矯正為“
\r\n\t\t\r\n\t}”。
用戶自己負責,JDT Java源碼編輯器在這邊干活了,提供了自己的IAutoEditStrategy^_^
4、將用戶矯正后的DocumentCommand應用到對應編輯器(說白了,就是轉化為一個Document replace動作執行)。
這一步JFace Text Framework幫用戶解決了。
我們可以看到,JFace Text Framework已經替我們干了大部分活,我們需要關心的是如下兩個角色:
org.eclipse.jface.text.DocumentCommand:數據載體,封裝了一個文本變化(text modification)的信息,例如輸入內容(text)、光標位置(offset)、長度(length)等等。我們要做的恰恰就是在我們自己的自動編輯策略中對DocumentCommand中的內容做矯正,然后JFace Text Framework會自動幫我們應用到目標IDocument中。
org.eclipse.jface.text.IAutoEditStrategy:自動編輯編輯策略,根據不同應用場景對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】
上面我們已經講了自動編輯的流程和主要角色,可以大家都有個疑問:IAutoEditStrategy到底是如何被自動調用的?
IAutoEditStrategy實現需要被配置到對應的SourceViewerConfiguration中,并和對應的內容類型(很多時候也可以理解為分區類型)綁定。JFace Text Framework會在初始化編輯器實例的時候讀取用戶的配置,然后根據不同的內容類型去查找對應的自動編輯器策略。
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) {
//
}
}
附加說明:1、有關內容類型(分區類型)可以參考前面章節的內容; 2、原有的IAutoIndentStrategy接口Eclipse已經不推薦使用^_^
【定制WTP StructuredTextEditor的自動編輯策略】
【需求】
當用戶輸入標簽結束符“>”時,如果標簽在TLD中定義為不含有標簽體(即bodycontent屬性值為empty),則自動將用戶的輸入內容矯正為“/>”。
【前提知識】
對WTP基本數據模型不很了解的可以參見一下前面的相關章節:
【Eclipse插件開發】基于WTP開發自定義的JSP編輯器(三) :WTP Structured Document
【Eclipse插件開發】基于WTP開發自定義的JSP編輯器(五) :WTP Structured Model
【Eclipse插件開發】基于WTP開發自定義的JSP編輯器(七):WTP數據模型總結和模型管理
【Eclipse插件開發】基于WTP開發自定義的JSP編輯器(十):WTP TLD內容模型介紹
【開發自定義的IAutoEditStrategy實現】
/**
* 針對JSP_DIRECTIVE分區的自動編輯策略
*
* @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();
//執行前提條件:1、用戶正在標簽名區域編輯;2、用戶編輯位置位于標簽名末端
if (currentRegion.getType() == DOMRegionContext.XML_TAG_NAME
&& command.offset >= startOffset + textLength) {
if (!hasBody(structuredDocumentRegion))
command.text = "/>";
}
}
/**
* 查詢對應的TLD內容模型,判斷該標簽在TLD中定義的時候是否含有標簽體
*
* @param structuredDocumentRegion
* @return
*/
private boolean hasBody(IStructuredDocumentRegion structuredDocumentRegion) {
//獲取標簽名稱
IStructuredDocument structuredDocument = structuredDocumentRegion.getParentDocument();
IStructuredModel structuredModel = StructuredModelManager.getModelManager().getModelForRead(structuredDocument);
IDOMElement domElement = (IDOMElement)structuredModel.getIndexedRegion(structuredDocumentRegion.getStartOffset());
String tagName = domElement.getNodeName();
/**
* 1、獲取位置相關的TLD Document列表
* 2、查找對應的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、執行條件判斷,我這邊假設用戶正在標簽名區域編輯(通過判斷編輯區域region的region type來判斷),并且編輯位置位于標簽名內容末端
2、查詢和當前文檔中特定位置相關的WTP TLD內容模型,判斷該標簽在TLD文件中定義的時候是否聲明允許有標簽體
3、如果標簽在TLD中定義為不含有標簽體,則矯正為自動閉合,“>”--->“/>”
PS:有關語法Document、語義Doucment、WTP TLD Content Document等數據類型相關內容請參見前面的章節。
【配置SourceViewerConfiguration】
將我們上面的開發的自動編輯策略配置到我們的SourceViewerConfiguration實現中:
/**
* 自定義StructuredTextViewerConfiguration,基于WTP jst提供的StructuredTextViewerConfigurationJSP,
* 后面會提供自定義的自動提示策略等擴展。
*
* @author zhuxing (mailto:zhu_xing@live.cn)
*/
/*
* 修改歷史
* $Log$
*/
public class JSPStructuredTextViewerConfiguration extends
StructuredTextViewerConfigurationJSP {
/*
* 提供自定義的自動提示策略
*
* @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標簽屬性值自動提示的情況,所以針對的分區類型為IJSPPartitions.JSP_DIRECTIVE
if (partitionType == IJSPPartitions.JSP_DIRECTIVE) {
return new IContentAssistProcessor[]{new CustomizedJSPContentAssistantProcessor(), new JSPContentAssistProcessor()};
}
return super.getContentAssistProcessors(sourceViewer, partitionType);
}
/*
* 提供自定義的自動編輯策略
*
* @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標簽名有關的自動編輯策略,所以針對的分區類型為IJSPPartitions.JSP_DIRECTIVE
if (contentType == IJSPPartitions.JSP_DIRECTIVE) {
List<IAutoEditStrategy> strategies = new ArrayList<IAutoEditStrategy>();
//WTP已配置的自動編輯策略
IAutoEditStrategy[] existingStrategies = super.getAutoEditStrategies(sourceViewer, contentType);
Collections.addAll(strategies, existingStrategies);
//自定義的自動編輯策略
IAutoEditStrategy customizedStrategies = new JSPDirectivePartitionAutoEditStrategy();
strategies.add(customizedStrategies);
return strategies.toArray(new IAutoEditStrategy[strategies.size()]);
}
return super.getAutoEditStrategies(sourceViewer, contentType);
}
}
我們在我們自己的SourceViewerConfiguration實現JSPStructuredTextViewerConfiguration中覆寫了getAutoEditStrategies方法。由于我們只是演示定制標簽名輸入時候的自動編輯策略,所以針對的分區類型為IJSPPartitions.JSP_DIRECTIVE。
【效果演示】

如上圖所示,我們輸入了標簽名test1:test,而且光標位于標簽名文本內容之后,此時WTP的源碼校驗器給出了錯誤提示,說我們的標簽缺少對應的閉合符號。
我們看一下test1:test標簽對應的TLD定義,定位為不允許有標簽體:
<?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>
根據需求,
當我們在圖中光標位置輸入“>”的時候,我們前面開發的自動編輯策略會自動將其矯正為合法的結束符“/>”,效果如下:
對比如下,如果沒有應用自動編輯策略,則會得到如下源碼校驗錯誤:
【后記】
提醒:從需求上講,我們示例中開發的自動編輯策略其實是非常簡單的。但是將其放置到一個定制WTP StructuredTextEditor的場景中,就不可避免的和我們前面介紹的WTP基礎數據模型的知識打交道,在這里再次強調,前面介紹的WTP各種數據模型一定要熟練搞清楚,基礎中的基礎!!!
源碼為實際工程以Export ---> Archive File方式導出的,
下載鏈接:WTP StructuredTextEditor自動編輯策略定制示例源碼
本博客中的所有文章、隨筆除了標題中含有引用或者轉載字樣的,其他均為原創。轉載請注明出處,謝謝!