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.blogjava.net/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自动编辑策略定制示例源码
本博客中的所有文章、随笔除了标题中含有引用或者转载字样的,其他均为原创。转载请注明出处,谢谢!