<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Live a simple life

    沉默(zhu_xing@live.cn)
    隨筆 - 48, 文章 - 0, 評論 - 132, 引用 - 0
    數據加載中……

    【Eclipse插件開發】基于WTP開發自定義的JSP編輯器(八):定制StructuredTextEditor自動提示

                前面介紹的內容集中在兩點:StructuredTextEditor框架和WTP數據模型,在本節中就可以定制一個我們最常用的WTP StructuredTextEditor的功能,那就是自動提示。

                【WTP StructuredTextEditor提示功能實現分析】
                有關Eclipse文本編輯器框架、JFace Text Framework和WTP StructuredTextEditor的簡要知識,參見:
               【Eclipse插件開發】基于WTP開發自定義的JSP編輯器(二):基于WTP StructuredTextEditor創建自己的JSPEditor 
                
                【SourceViewer提示策略配置】
                在章節二中,我們說過如果要對一個ISourceViewer進行自動提示策略的定制,在ISourceViewer對應的SourceViewerConfiguration中配置就可以了。對于WTP JSP StructuredTextEditor而言,這里的ISourceViewer就是StructuredTextViewer,這里的SourceViewerConfiguration就是StructuredTextViewerConfigurationJSP。那我們來看一下WTP StructuredTextViewerConfigurationJSP中對自動提示策略的配置:
               (以下代碼摘取子StructuredTextViewerConfigurationJSP類中):
    protected IContentAssistProcessor[] getContentAssistProcessors(ISourceViewer sourceViewer, String partitionType) {
            IContentAssistProcessor[] processors 
    = null;
            
            //其他代碼省略......
            
    else if ((partitionType == IXMLPartitions.XML_DEFAULT) || (partitionType == IHTMLPartitions.HTML_DEFAULT) || (partitionType == IHTMLPartitions.HTML_COMMENT) || (partitionType == IJSPPartitions.JSP_DEFAULT) || (partitionType == IJSPPartitions.JSP_DIRECTIVE) || (partitionType == IJSPPartitions.JSP_CONTENT_DELIMITER) || (partitionType == IJSPPartitions.JSP_CONTENT_JAVASCRIPT) || (partitionType == IJSPPartitions.JSP_COMMENT)) {
                
    // jsp
                processors = new IContentAssistProcessor[]{new JSPContentAssistProcessor()};
            }
            
    else if ((partitionType == IXMLPartitions.XML_CDATA) || (partitionType == IJSPPartitions.JSP_CONTENT_JAVA)) {
                
    // jsp java
                processors = new IContentAssistProcessor[]{new JSPJavaContentAssistProcessor()};
            }
            //其他代碼省略......

            
    return processors;
        }
                以上代碼,我們可以看的出來,IContentAssistProcessor是和具體分區類型(partition type)相關聯的。想搞懂這個問題,就需要看一下這個具體分區類型(partition type)是怎么計算出來的。
                PS:分區類型是JFace Text Framework中的概念,相關的知識大家有興趣可以進一步去了解一下JFace Text Framework。

                【分區類型(partition type)】
                我們先來看一下JFace Text Framework中的基礎知識吧。再JFace Text Framework中有個分區劃分器的角色(org.eclipse.jface.text.IDocumentPartitioner),這個角色中一個核心操作就是判斷文檔(org.eclipse.jface.text.IDocument)中特定位置所在區域(region)的分區類型是什么,其實這里的分區類型說白了就是在一定程度上反應了該區域(region)的內容是什么語義性質的^_^。    
                我們接著看一下,WTP提供了什么樣的IDocumentPartitioner呢?
                
                
               上圖中的org.eclipse.jst.jsp.core.internal.text.StructuredTextPartitionerForJSP就是我們針對jsp文件類型的分區器了,看一下相應的實現代碼:    
    public String getPartitionType(ITextRegion region, int offset) {
            String result 
    = null;
            
    final String region_type = region.getType();
            
    if (region_type == DOMJSPRegionContexts.JSP_CONTENT) {
                result 
    = getPartitionTypeForDocumentLanguage();
            }
            
    else if (region_type == DOMJSPRegionContexts.JSP_COMMENT_TEXT || region_type == DOMJSPRegionContexts.JSP_COMMENT_OPEN || region_type == DOMJSPRegionContexts.JSP_COMMENT_CLOSE)
                result 
    = IJSPPartitions.JSP_COMMENT;
            
    else if (region_type == DOMJSPRegionContexts.JSP_DIRECTIVE_NAME || region_type == DOMJSPRegionContexts.JSP_DIRECTIVE_OPEN || region_type == DOMJSPRegionContexts.JSP_DIRECTIVE_CLOSE)
                result 
    =
     IJSPPartitions.JSP_DIRECTIVE
    ;
            
    else if (region_type == DOMJSPRegionContexts.JSP_CLOSE || region_type == DOMJSPRegionContexts.JSP_SCRIPTLET_OPEN || region_type == DOMJSPRegionContexts.JSP_EXPRESSION_OPEN || region_type == DOMJSPRegionContexts.JSP_DECLARATION_OPEN)
                result 
    = IJSPPartitions.JSP_CONTENT_DELIMITER;
            
    else if (region_type == DOMJSPRegionContexts.JSP_ROOT_TAG_NAME)
                result 
    = IJSPPartitions.JSP_DEFAULT;
            
    else if (region_type == DOMJSPRegionContexts.JSP_EL_OPEN || region_type == DOMJSPRegionContexts.JSP_EL_CONTENT || region_type == DOMJSPRegionContexts.JSP_EL_CLOSE || region_type == DOMJSPRegionContexts.JSP_EL_DQUOTE || region_type == DOMJSPRegionContexts.JSP_EL_SQUOTE || region_type == DOMJSPRegionContexts.JSP_EL_QUOTED_CONTENT)
                result 
    = IJSPPartitions.JSP_DEFAULT_EL;
            
                //其他代碼省略。。。
            
    else {
                result 
    = getEmbeddedPartitioner().getPartitionType(region, offset);
            }
            
    return result;
        }
                
                我們可以看到,對于WTP結構化文本(當然包括JSP)而言,分區類型(partition type)基本上是根據ITextRegion的type信息確定的。有關ITextRegion的type相關知識,也是我們前面在介紹語法Document(IStructuredDocument)的時候重點內容之一,忘記的話,去看一下。

                【自動提示流程】
                既然在StructuredTextViewerConfigurationJSP中根據分區類型(partition type)對提示進行了配置,那么是如何來利用這個配置的呢?
                我們在source viewer中觸發提示的時候,會有一個相應的offset信息,前面也說過IDocumentPartitioner(WTP 對應于JSP的實現為StructuredTextPartitionerForJSP)提供了根據offset判斷相應區域分區類型(partition type)的接口操作,那提示的流程也就出來了:
                
                
                           上圖中的WTP StructuredTextViewerConfigurationJSP對應于我們自己的jsp編輯器中的類型為jspeditor.configuration.JSPStructuredTextViewerConfiguration,這個我們在前面第二節中就定義了。

            【定制WTP StructuredTextEditor的提示功能】
                    通過上面的自動提示流程的分析,我們可以看的出來,如果想在我們自己的JSP編輯器中定制WTP提供的特定分區類型下的自動提示,只要覆寫WTP StructuredTextViewerConfigurationJSP中的getContentAssistProcessors實現,用我們自定義的IContentAssistProcessor實現和特定的分區類型想綁定就可以了
                   【需求】
                    1、提供針對標簽屬性值提示的定制。
                    2、提供屬性值提示擴展點,允許用戶以動態掛入的方式提供特定標簽的屬性值提示

                    【實現摘要】
                    1、定義自己的IContentAssistProcessor實現
                        
    public class CustomizedJSPContentAssistantProcessor extends AbstractContentAssistProcessor{
        
        
    /* 
         * 定制自動提示策略:提供自定義的屬性值提示。
         * 
         * @see org.eclipse.wst.xml.ui.internal.contentassist.AbstractContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer, int)
         
    */
        
    public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
            
    if (!this.isOffsetValid(viewer, offset))
                
    return new ICompletionProposal[0];
            
            
    //利用IModelManager獲取對應的模型
            IStructuredDocument structuredDocument = (IStructuredDocument)viewer.getDocument();
            IStructuredModel structuredModel 
    = StructuredModelManager.getModelManager().getModelForRead(structuredDocument);
            
    if (structuredModel == null)
                
    return new ICompletionProposal[0];
            
            
    try {
                
    //如果當前offset不是位于屬性區域
                IDOMAttr attrNode = StructuredModelUtil.getAttrAtOffset(structuredModel, offset);
                
    if (attrNode != null)
                    
    return this.computeCustomizedCompletionProposals(viewer, offset, attrNode);
                
    else
                    
    return new ICompletionProposal[0];
            } 
    catch (Exception e) {
                
    //log exception
                IStatus status = new Status(IStatus.ERROR, "jspeditor"100"自動提示失敗", e);
                Activator.getDefault().getLog().log(status);
                
                
    return new ICompletionProposal[0];
            } 
    finally {
                
    //注意,削減引用計數
                if (structuredModel != null)
                    structuredModel.releaseFromRead();
            }
        }
        
        
    /**
         * 判斷當前位置是否需要啟動我們自定義的JSP標簽屬性值自動提示,標準:
         * 1、當前offset對應的區域的分區類型(partition type)為IJSPPartitions.JSP_DIRECTIVE
         * 2、當前offset對應的text region的類型為DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE
         * 
         * 
    @param viewer
         * 
    @param offset
         * 
    @return
         
    */
        
    private boolean isOffsetValid(ITextViewer viewer, int offset) {
            
    try {
                IStructuredDocument structuredDocument 
    = (IStructuredDocument)viewer.getDocument();
                
                
    //判斷分區類型
                if (IJSPPartitions.JSP_DIRECTIVE != structuredDocument.getPartition(offset).getType()) 
                    
    return false;
                
    //判斷葉子text region對應的region type信息
                ITextRegion textRegion = StructuredDocumentUtil.getTextRegion(structuredDocument, offset);
                
    return DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE == textRegion.getType();
            } 
    catch (Exception e) {
                
    return false;
            }
        }
        
        
    /**
         * 自定義提示結果:自定義屬性值提示
         * 
         * 
    @param viewer
         * 
    @param offset
         * 
    @param attrNode
         * 
    @return
         * 
    @throws Exception
         
    */
        
    private ICompletionProposal[] computeCustomizedCompletionProposals(ITextViewer viewer, int offset, IDOMAttr attrNode) throws Exception{
            
    //準備上下文數據
            String tagName = attrNode.getOwnerElement().getNodeName();
            String attrbuteName 
    = attrNode.getName();
            String inputText 
    = attrNode.getStructuredDocument().get(attrNode.getValueRegionStartOffset() + 1, offset - attrNode.getValueRegionStartOffset() - 1);
            
            
    //獲取相應通過擴展點注冊的屬性值提示擴展
            IAssistantContributor contributor = AssistantContributorManager.getInstance().getAssistantContributor(tagName);
            
    return contributor.computeProposals(attrbuteName, inputText, viewer, offset);
        }
        
    }
       
                  關于細節實現暫且不說,后面會附上相應的源碼。目前只需要了解大致的算法流程就可以:1、如果offset位于特定的JSP標簽屬性值范圍內,則去獲取對應的屬性值自定義提示。

                2、將自定義的IContentAssistProcessor配置到自定義的SourceViewerConfiguration中。再配置之前,我們首先看一下如果offset位于一個屬性值的ITextRegion中,那么分區類型(partition type)是怎樣的。回過頭看一下,上面WTP StructuredTextPartitionerForJSP代碼黑體加粗部分,我們的屬性值區域對應的分區類型為org.eclipse.jst.jsp.JSP_DIRECTIVE(IJSPPartitions.JSP_DIRECTIVE)。所以在我們自定義的JSPStructuredTextViewerConfiguration中覆寫WTP提供的StructuredTextViewerConfigurationJSP中的相應方法:
    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標簽屬性值自動提示的情況
            if (partitionType == IJSPPartitions.JSP_DIRECTIVE) {
                
    return new IContentAssistProcessor[]{new CustomizedJSPContentAssistantProcessor(), new JSPContentAssistProcessor()};
            }
            
            
    return super.getContentAssistProcessors(sourceViewer, partitionType);
        }
    }
                    由于我們只想定制JSP標簽的屬性值提示,所以我們將我們自定義的Content Assistant Processor配置為和IJSPPartitions.JSP_DIRECTIVE分區類型相綁定。

                    說明:如果你想定制類型的自動提示呢? 前面已經說過了^_^

                3、定義相關抽象接口
                      由于針對不同JSP標簽的屬性值自動提示的邏輯是不同的,因為提示的內容相同,但是屬性值提示這一概念是一致的。所以,我們就定義了IAssistantContributor接口來代表這一抽象概念,行為的變化通過對該接口的繼承來封裝。
    public interface IAssistantContributor {
        
    /**
         * 提供屬性值內容提示
         * 
         * 
    @param attrbuteName 屬性名
         * 
    @param inputText    已輸入屬性值
         * 
    @param viewer       structured text viewer
         * 
    @param offset       光標位置
         * 
    @return
         
    */
        
    public ICompletionProposal[] computeProposals(String attributeName, String inputText, ITextViewer viewer, int offset);
    }
                    上面接口一看就知道是用的策略模式的手法,我在博客的前面的文章中說明了在使用策略模式時候的注意點,其中重點之一就是要關注上下文信息。  我們在本接口中提供了屬性名(attributeName)和用戶已經輸入的屬性值(inputText),這兩個上下文信息對于一般的簡單提示情況下已經足夠了。但是,也有可能有比較為復雜的情況,例如要處理嵌套標簽或者標簽之間有依賴等等情況,通過viewer參數可以獲取到對應的IStructuredDocument和IStructuredModel,在加上offset信息,用戶可以利用這兩個信息自己去分析出進一步的上下文信息^_^。

                   PS:我們在代碼中針對IAssistantContributor接口提供了一個默認適配器類,針對的也就是簡單的提示情況,即只提供屬性名(attributeName)和用戶已經輸入的屬性值(inputText)就可以完成提示的情況了。
                    
    public abstract class AbstractAssistantContributor implements
            IAssistantContributor {
        
        
    /* 
         * 子類可以覆寫,提供較為簡單的模版方法,分為計算替代字符串和構建completion proposals兩步。
         * 
         * @see jspeditor.assist.contributor.IAssistantContributor#computeProposals(java.lang.String, java.lang.String, org.eclipse.jface.text.ITextViewer, int)
         
    */
        
    public ICompletionProposal[] computeProposals(String attrbuteName, String inputText, ITextViewer viewer, int offset) {
            String[] replaceStrings 
    = this.computeReplaceStrings(attrbuteName, inputText, viewer, offset);
            
    return this.buildCompletionProposals(replaceStrings, inputText, viewer, offset);
        } 
        
        
    /**
         * 子類可以覆寫.
         * 說明:如果需要提供自定義的display string、image等信息,請直接覆寫computeProposals方法。
         * 
         * 
    @param attrbuteName
         * 
    @param inputText
         * 
    @return
         
    */
        
    protected String[] computeReplaceStrings(String attrbuteName, String inputText, ITextViewer viewer, int offset) {
            
    return new String[0];
        }
        
        
    protected final IDOMAttr getDOMAttr(ITextViewer viewer, int offset) {
            IStructuredDocument structuredDocument 
    = (IStructuredDocument)viewer.getDocument();
            IStructuredModel structuredModel 
    = StructuredModelManager.getModelManager().getModelForRead(structuredDocument);
            
            IDOMAttr attr 
    = StructuredModelUtil.getAttrAtOffset(structuredModel, offset);
            
            structuredModel.releaseFromRead();
            
    return attr;
        }
        

        
    /**
         * 構造ICompletionProposal實例,只提供簡單的ICompletionProposal實例
         * 
         * 
    @param replaceStrings
         * 
    @param inputText
         * 
    @param viewer
         * 
    @param offset
         * 
    @return
         
    */
        
    protected ICompletionProposal[] buildCompletionProposals(String[] replaceStrings, String inputText, ITextViewer viewer, int offset) {
            
    if (replaceStrings == null || replaceStrings.length == 0)
                
    return new ICompletionProposal[0];
            
            
    //計算ICompletionProposal相關參數
            IDOMAttr attrNode = getDOMAttr(viewer, offset);
            
    int replaceOffset = attrNode.getValueRegionStartOffset() + 1;
            
    int replaceLength = attrNode.getValueSource().length();
            
            List
    <ICompletionProposal> proposals = new ArrayList<ICompletionProposal>();
            
    for (int i = 0; i < replaceStrings.length; i++) {
                
    //根據用戶輸入執行過濾
                if (replaceStrings[i].toLowerCase().startsWith(inputText.trim().toLowerCase())) {
                    
    int cursorPosition = replaceStrings[i].length();
                    ICompletionProposal proposal 
    = new CompletionProposal(replaceStrings[i], replaceOffset, replaceLength, cursorPosition);
                    proposals.add(proposal);
                }
            }
            
    return proposals.toArray(new ICompletionProposal[proposals.size()]);
        }
    }
                可以看的出來,我們這個默認適配器類簡單的應用了模版方法的手法去處理簡單情景下的自動提示。
                
              4、定義屬性值自動提示擴展點
                由于JSP默認提供的一些標簽實際業務意義不強,而我們自己在開發應用的時候,往往會不斷提供有業務意義的標簽供用戶使用,所以我們假設我們要處理的屬性值自動提示需要允許后續開發人員以動態開發的方式掛入,支持方便擴展。我們想到了擴展點機制^_^。
                 
                上面的擴展點定義其實很簡單,就是針對特定的JSP tag id(也就是tag名稱,這是唯一的)提供一個 IAssistantContributor接口的實現。
                
                  我們針對用戶掛入的擴展提供了一個管理器AssistantContributorManager,能夠支持以tag id獲取對應的IAssistantContributor實現,看一下上面我們定義的content assistant processor實現中黑體部分代碼就利用了該manager實例。具體請在附件源碼中看吧^_^

                5、示例
                 我們首先提供一個非常簡單的tld(test.tld),里面包含了一個簡單的測試標簽test,如下
    <?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>test</shortname>
        
    <uri>http://www.tkk7.com/zhuxing/tags/test</uri>
        <tag>
            
    <name>test</name>
            
    <tagclass>any</tagclass>
            
    <bodycontent>empty</bodycontent>
            
    <attribute>
                
    <name>scope</name>
                
    <required>true</required>
                
    <rtexprvalue>true</rtexprvalue>
            
    </attribute>
        
    </tag>
    </taglib>
                 我們在其中定義了一個scope屬性,下面我們就以擴展點的方式掛入我們的擴展。
                
                 首先實現提供一個IAssistantContributor接口實現:
    public class TestTagAssistantContributor extends AbstractAssistantContributor {
        
    /**
         * scope attribute name
         
    */
        
    private static final String ATTR_NAME_SCOPE = "scope";
        
        
    /**
         * scope attribute value
         
    */
        
    private static final String[] ATTR_VALUE_SCOPE = {"request""session"};
        
        
    /* (non-Javadoc)
         * @see jspeditor.assist.contributor.AbstractAssistantContributor#computeReplaceStrings(java.lang.String, java.lang.String, org.eclipse.jface.text.ITextViewer, int)
         
    */
        
    protected String[] computeReplaceStrings(String attrbuteName, String inputText, ITextViewer viewer, int offset) {
            
    if (ATTR_NAME_SCOPE.equals(attrbuteName)) {
                
    return ATTR_VALUE_SCOPE;
            }
            
            
    return new String[0];
        }

    }
                通過上面簡單的代碼可以看的出來,我們的提示邏輯很簡單,如果是scope屬性,就提供“requst”和“session”兩種選擇。

                下面,我們將我們提供的針對test標簽的屬性值提示擴展掛入:  

                6、效果演示

                看到了嗎,我們為test標簽掛入了自動提示實現之后,還真的提示了哈^_^            
                
                【后記】
                  其實本節中的內容為定制自動提示提供了一個完整解決方案的雛形^_^               

               【源碼下載】

                  源碼下載(自動提示定制)

    本博客中的所有文章、隨筆除了標題中含有引用或者轉載字樣的,其他均為原創。轉載請注明出處,謝謝!

    posted on 2008-09-22 18:17 zhuxing 閱讀(3499) 評論(4)  編輯  收藏 所屬分類: Eclipse Plug-in & OSGIWTP(Web Tools Platform)

    評論

    # re: 【Eclipse插件開發】基于WTP開發自定義的JSP編輯器(八):定制StructuredTextEditor自動提示  回復  更多評論   

    下班了,先把源碼附上,里面有測試用test.tld
    2008-09-22 18:36 | zhuxing

    # re: 【Eclipse插件開發】基于WTP開發自定義的JSP編輯器(八):定制StructuredTextEditor自動提示  回復  更多評論   

    期待作者的下文
    2008-09-22 21:54 | 冷月

    # re: 【Eclipse插件開發】基于WTP開發自定義的JSP編輯器(八):定制StructuredTextEditor自動提示  回復  更多評論   

    內容已經補充完畢,并更新了代碼,希望對大家有幫助

    下一節內容:為我們的編輯器配置自定義即時校驗
    2008-09-23 15:42 | zhuxing

    # re: 【Eclipse插件開發】基于WTP開發自定義的JSP編輯器(八):定制StructuredTextEditor自動提示  回復  更多評論   

    我喜歡這篇, zhuxing 哥,這兩天我要好好看看。
    2008-09-24 17:28 | srdrm
    主站蜘蛛池模板: 国产zzjjzzjj视频全免费| 久久天天躁狠狠躁夜夜免费观看| 午夜一区二区免费视频| 久久免费线看线看| 亚洲午夜福利精品无码| 午夜在线免费视频 | 免费毛片在线看片免费丝瓜视频| 亚洲无限乱码一二三四区| 67194成手机免费观看| 亚洲国产精品午夜电影| 成年女人视频网站免费m| 亚洲精品无码专区久久久| 在线免费播放一级毛片 | 国产成人yy免费视频| 亚洲一区二区三区四区视频| 国产精品免费观看久久| 亚洲a∨国产av综合av下载| 亚洲精品tv久久久久久久久久| 亚洲日韩区在线电影| 蜜臀AV免费一区二区三区| 色噜噜亚洲男人的天堂| 国产国产人免费视频成69大陆 | 午夜免费福利影院| 一道本不卡免费视频| 国产av无码专区亚洲av桃花庵| 亚洲高清视频免费| 亚洲色少妇熟女11p| 亚洲片国产一区一级在线观看 | 永久免费av无码不卡在线观看| 亚洲AV成人片无码网站| 国产午夜亚洲精品国产成人小说| 免费人成黄页在线观看日本| 亚洲图片激情小说| 亚洲 无码 在线 专区| 色九月亚洲综合网| 亚洲AV无码一区二区二三区软件| 18观看免费永久视频| 无套内谢孕妇毛片免费看看| 久久久青草青青亚洲国产免观| 国产va精品免费观看| 成人福利在线观看免费视频|