【Eclipse插件开发】基于WTP开发自定义的JSP编辑器(十二):定制自动编辑策略(Auto Edit Strategy)

【Eclipse插件开发】基于WTP开发自定义的JSP编辑器(十二):定制自动编辑策略(Auto Edit Strategy)

            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:[email protected])
 
*/
/*
 * 修改历史
 * $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:[email protected])
 
*/
/*
 * 修改历史
 * $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 >
           
            根据需求, 当我们在图中光标位置输入“>”的时候,我们前面开发的自动编辑策略会自动将其矫正为合法的结束符“/>”,效果如下:
【Eclipse插件开发】基于WTP开发自定义的JSP编辑器(十二):定制自动编辑策略(Auto Edit Strategy)_第1张图片
            
            对比如下,如果没有应用自动编辑策略,则会得到如下源码校验错误:


         【后记】
            提醒:从需求上讲,我们示例中开发的自动编辑策略其实是非常简单的。但是将其放置到一个定制WTP StructuredTextEditor的场景中,就不可避免的和我们前面介绍的WTP基础数据模型的知识打交道,在这里再次强调,前面介绍的WTP各种数据模型一定要熟练搞清楚,基础中的基础!!!

              源码为实际工程以Export ---> Archive File方式导出的, 下载链接:WTP StructuredTextEditor自动编辑策略定制示例源码

本博客中的所有文章、随笔除了标题中含有引用或者转载字样的,其他均为原创。转载请注明出处,谢谢!

你可能感兴趣的:(【Eclipse插件开发】基于WTP开发自定义的JSP编辑器(十二):定制自动编辑策略(Auto Edit Strategy))