<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
    數(shù)據(jù)加載中……

    【Eclipse插件開發(fā)】基于WTP開發(fā)自定義的JSP編輯器(九):定制StructuredTextEditor源碼即時校驗

                上一節(jié)我們定制了WTP StructuredTextEditor的自動提示功能特征,本節(jié)將定制另外一個功能特征即時源碼校驗。所謂源碼即時校驗,就是在用戶編輯過程中(并未保存),針對用戶編輯的內(nèi)容改變做即時校驗,并給用戶即時反饋相關(guān)的錯誤或者其他類型的提示信息。在本節(jié)中,我們將以標(biāo)簽的即時校驗為例,演示如何定制WTP StructuredTextEditor的源碼即時校驗。
                
                在定制之前,我們先來看一下WTP StructuredTextEditor已有的源碼即時校驗功能:

                我們看到,我們刪除</jsp:text>的瞬間,WTP StructuredTextEditor的即時校驗就給出了錯誤提示。其實我們在很多其他的編輯器,例如java源碼編輯器等,都可以看到類似的即時校驗功能。

                【JFace Text Framework中相關(guān)內(nèi)容】
                說白了,我們的源碼編輯對應(yīng)的控件就是ISourceViewer,那么這個校驗也理所當(dāng)然應(yīng)該是ISourceViewer所提供的一個服務(wù)。JFace Text Framework中確實針對源碼即時校驗提供了相應(yīng)的機制,我們看一下相應(yīng)的接口和運行原理。
                 
                【相關(guān)接口】
                1、IReconciler(org.eclipse.jface.text.reconciler.IReconciler),調(diào)解者,當(dāng)文檔發(fā)生變化時,根據(jù)分區(qū)類型(如果這個概念忘記了,翻一下前面的文章)提供相應(yīng)的調(diào)解策略(直接說成是驗證策略吧^_^)。
    public interface IReconciler {

        
    /**
         * Installs the reconciler on the given text viewer. After this method has been
         * finished, the reconciler is operational, i.e., it works without requesting
         * further client actions until <code>uninstall</code> is called.
         *
         * 
    @param textViewer the viewer on which the reconciler is installed
         
    */
        
    void install(ITextViewer textViewer);

        
    /**
         * Removes the reconciler from the text viewer it has
         * previously been installed on.
         
    */
        
    void uninstall();

        
    /**
         * Returns the reconciling strategy registered with the reconciler
         * for the specified content type.
         *
         * 
    @param contentType the content type for which to determine the reconciling strategy
         * 
    @return the reconciling strategy registered for the given content type, or
         *        <code>null</code> if there is no such strategy
         
    */
        IReconcilingStrategy getReconcilingStrategy(String contentType);
    }
                
                2、IReconcilingStrategy(org.eclipse.jface.text.reconciler.IReconcilingStrategy),真正的驗證邏輯角色。結(jié)合上面的IReconciler接口介紹,我們可以清晰的看出這是,是策略模式的一個應(yīng)用(驗證邏輯角色這個概念的具體實現(xiàn)隨著分區(qū)類型的不同而可能變化,IReconciler可以看成是IReconcilingStrategy的管理者,在需要應(yīng)用源碼即時驗證的上下文中,給定具體分區(qū)類型向IReconciler索取驗證策略)。  
    public interface IReconcilingStrategy {

        
    /**
         * Tells this reconciling strategy on which document it will
         * work. This method will be called before any other method
         * and can be called multiple times. The regions passed to the
         * other methods always refer to the most recent document
         * passed into this method.
         *
         * 
    @param document the document on which this strategy will work
         
    */
        
    void setDocument(IDocument document);

        
    /**
         * Activates incremental reconciling of the specified dirty region.
         * As a dirty region might span multiple content types, the segment of the
         * dirty region which should be investigated is also provided to this
         * reconciling strategy. The given regions refer to the document passed into
         * the most recent call of {
    @link #setDocument(IDocument)}.
         *
         * 
    @param dirtyRegion the document region which has been changed
         * 
    @param subRegion the sub region in the dirty region which should be reconciled
         
    */
        
    void reconcile(DirtyRegion dirtyRegion, IRegion subRegion);

        
    /**
         * Activates non-incremental reconciling. The reconciling strategy is just told
         * that there are changes and that it should reconcile the given partition of the
         * document most recently passed into {
    @link #setDocument(IDocument)}.
         *
         * 
    @param partition the document partition to be reconciled
         
    */
        
    void reconcile(IRegion partition);
    }
      
                到這里,關(guān)于調(diào)解(驗證)的兩個最重要的接口也介紹了,那這個IReconcilerIReconcilingStrategy)怎么和source viewer聯(lián)系起來呢?

                3、SourceViewerConfiguration(org.eclipse.jface.text.source.SourceViewerConfiguration)中定義了提供IReconciler實現(xiàn)的接口。
    public class SourceViewerConfiguration {
    /**
         * Returns the reconciler ready to be used with the given source viewer.
         * This implementation always returns <code>null</code>.
         *
         * 
    @param sourceViewer the source viewer to be configured by this configuration
         * 
    @return a reconciler or <code>null</code> if reconciling should not be supported
         
    */
        
    public IReconciler getReconciler(ISourceViewer sourceViewer) {
            
    return null;
        }
         
         
    //其他方法省略
    }

                
                【執(zhí)行原理】
                疑問:雖然SourceViewerConfiguration中定義了提供IReconciler實現(xiàn)的接口,但是我們目前看到只是一個靜態(tài)關(guān)系啊。當(dāng)source viewer中用戶對文檔內(nèi)容進行編輯的時候,IReconciler怎么會被自動調(diào)用嗎?
                我們知道,在IEditorPart(ITextEditor)創(chuàng)建控件(createPartControl)的過程中,會對其含有的source viewer進行配置(config),而SourceViewerConfiguration會在這個配置過程中被應(yīng)用,我們簡要看一下SourceViewer.config實現(xiàn):

    public void configure(SourceViewerConfiguration configuration) {

            
    if (getTextWidget() == null)
                
    return;

            setDocumentPartitioning(configuration.getConfiguredDocumentPartitioning(
    this));

            
    // install content type independent plug-ins
            fPresentationReconciler= configuration.getPresentationReconciler(this);
            
    if (fPresentationReconciler != null)
                fPresentationReconciler.install(
    this);

            fReconciler= configuration.getReconciler(this);
            if (fReconciler != null
    )
                fReconciler.install(this
    );
            //其他代碼省略
    }

                也就是說,我們提供的IReconciler實現(xiàn)會在source viewer配置的過程中被install,那么,如果我們在我們自己的IReconciler install實現(xiàn)中做進一步的關(guān)聯(lián)實現(xiàn)不就可以了^_^   例如,我們可以在自己的IReconciler  install實現(xiàn)中,將自己注冊為當(dāng)前IDocument(ITextViewer.getDocument)的document change listener,這樣當(dāng)編輯器中的內(nèi)容發(fā)生變化(IDocument發(fā)生改變)時候,我們就可以根據(jù)變化區(qū)域?qū)?yīng)的內(nèi)容類型去獲取相應(yīng)的IReconcilingStrategy,并執(zhí)行驗證了^_^
                
                為了更好的說明這一點,我們看一下Eclipse中Java源碼編輯器是如何處理的: 

    public class JavaReconciler extends MonoReconciler {
        
    /**
         * Internal Java element changed listener
         *
         * 
    @since 3.0
         
    */
        
    private class ElementChangedListener implements IElementChangedListener {
            
    /*
             * @see org.eclipse.jdt.core.IElementChangedListener#elementChanged(org.eclipse.jdt.core.ElementChangedEvent)
             
    */
            
    public void elementChanged(ElementChangedEvent event) {
                
    if (event.getDelta().getFlags() == IJavaElementDelta.F_AST_AFFECTED)
                    
    return;
                setJavaModelChanged(
    true);
                
    if (!fIsReconciling && isEditorActive() )
                    JavaReconciler.
    this.forceReconciling();
            }
        }

       
    /** The part listener */
        
    private IPartListener fPartListener;

        
    /** The shell listener */
        
    private ShellListener fActivationListener;

         
    /**
         * The Java element changed listener.
         * 
    @since 3.0
         
    */
        
    private IElementChangedListener fJavaElementChangedListener;

         
    /**
         * The resource change listener.
         * 
    @since 3.0
         
    */
        
    private IResourceChangeListener fResourceChangeListener;


         
    /*
         * @see org.eclipse.jface.text.reconciler.IReconciler#install(org.eclipse.jface.text.ITextViewer)
         
    */
        
    public void install(ITextViewer textViewer) {
            
    super.install(textViewer);

            fPartListener
    = new PartListener();
            IWorkbenchPartSite site
    = fTextEditor.getSite();
            IWorkbenchWindow window
    = site.getWorkbenchWindow();
            window.getPartService().addPartListener(fPartListener);

            fActivationListener
    = new ActivationListener(textViewer.getTextWidget());
            Shell shell
    = window.getShell();
            shell.addShellListener(fActivationListener);

            fJavaElementChangedListener
    = new ElementChangedListener();
            JavaCore.addElementChangedListener(fJavaElementChangedListener);

            fResourceChangeListener
    = new ResourceChangeListener();
            IWorkspace workspace
    = JavaPlugin.getWorkspace();
            workspace.addResourceChangeListener(fResourceChangeListener);
        }

         
    //其他代碼省略
    }

                上面的JavaReconciler就是java源碼編輯器中source viwer使用的IReconciler實現(xiàn),我們看到在其install的過程中,其實是注冊了相應(yīng)的監(jiān)聽器,當(dāng)監(jiān)聽到相應(yīng)的變化時,就做相應(yīng)的驗證處理就可以了^_^

                IReconciler安裝和工作:
                

                     同樣,當(dāng)對應(yīng)的IEditorPart實例銷毀時,那么內(nèi)含的source viewer肯定要銷毀,并執(zhí)行unconfig操作,在unconfig的過程中,會調(diào)用相應(yīng)IReconciler實現(xiàn)的uninstall操作。
                    

                     
                    PS:IReconciler.install中做了監(jiān)聽器注冊等行為,別忘記在IReconciler.uninstall中做相應(yīng)清理!!!


             【W(wǎng)TP StructuredTextEditor源碼即時校驗實現(xiàn)分析】
            
     我想通過上面JFace Text Framework中相應(yīng)機制的分析,再回過頭來看WTP StructuredTextEditor中如何實現(xiàn)源碼校驗的,應(yīng)該不是很難^_^。我們將重點關(guān)注兩點:1、對應(yīng)的IReconciler實現(xiàn);2、具體的IReconcilingStrategy實現(xiàn);3、如何擴展?

                前面我們已經(jīng)定義了一個source viewer configuration,沿著繼承關(guān)系,我們找到了對應(yīng)的IReconciler實現(xiàn):
    public class StructuredTextViewerConfiguration extends TextSourceViewerConfiguration {
         
    final public IReconciler getReconciler(ISourceViewer sourceViewer) {
            
    boolean reconcilingEnabled = fPreferenceStore.getBoolean(CommonEditorPreferenceNames.EVALUATE_TEMPORARY_PROBLEMS);
            
    if (sourceViewer == null || !reconcilingEnabled)
                
    return null;

            
    /*
             * Only create reconciler if sourceviewer is present
             
    */
            
    if (fReconciler == null && sourceViewer != null) {
                StructuredRegionProcessor reconciler 
    = new StructuredRegionProcessor();

                
    // reconciler configurations
                reconciler.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer));

                fReconciler 
    = reconciler;
            }
            
    return fReconciler;
        }
    }
                上面紅色標(biāo)志的StructuredRegionProcessor(org.eclipse.wst.sse.ui.internal.reconcile.StructuredRegionProcessor)就是WTP提供的IReconciler接口的具體實現(xiàn)。繼承關(guān)系圖:
                

                順藤摸瓜,我們找到了對應(yīng)的IReconciler.install實現(xiàn):   
    public class DirtyRegionProcessor extends Job implements IReconciler, IReconcilerExtension, IConfigurableReconciler {
        
    class DocumentListener implements IDocumentListener {
          //處理document change事件,執(zhí)行即時校驗
      }

        
    class TextInputListener implements ITextInputListener {
            
    public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
                
    // do nothing
            }

            
    public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
                handleInputDocumentChanged(oldInput, newInput);
            }
        }

        
    /**
         * 
    @see org.eclipse.jface.text.reconciler.IReconciler#install(ITextViewer)
         
    */
        
    public void install(ITextViewer textViewer) {
            
    // we might be called multiple times with the same viewe.r,
            
    // maybe after being uninstalled as well, so track separately
            if (!isInstalled()) {
                fViewer 
    = textViewer;
                fTextInputListener 
    = new TextInputListener();
                textViewer.addTextInputListener(fTextInputListener);
                setInstalled(
    true);
            }
        }

        
    /**
         * 
         * 
    @param oldInput
         * 
    @param newInput
         
    */
        
    void handleInputDocumentChanged(IDocument oldInput, IDocument newInput) {
            
    // don't bother if reconciler not installed
            if (isInstalled()) {

                reconcilerDocumentChanged(newInput);
                setDocument(newInput);
                setEntireDocumentDirty(newInput);
            }
        }

         
    /**
         * Reinitializes listeners and sets new document onall strategies.
         * 
         * 
    @see org.eclipse.jface.text.reconciler.AbstractReconciler#reconcilerDocumentChanged(IDocument)
         
    */
        
    void reconcilerDocumentChanged(IDocument newDocument) {
            IDocument currentDoc 
    = getDocument();

            
    // unhook old document listener
            if (currentDoc != null)
                currentDoc.removeDocumentListener(fDocumentListener);

            
    // hook up new document listener
            if (newDocument != null)
                newDocument.addDocumentListener(fDocumentListener);

            
    // sets document on all strategies
            setDocument(newDocument);
        }

          //其他代碼省略...
    }
               我們看到,WTP提供的IReconciler實現(xiàn)在install的過程中,注冊了text input listener,當(dāng)input變化的時候,則獲取和input對應(yīng)的document,并針對該document注冊相應(yīng)的document change listener,當(dāng)document發(fā)生變化的時候(用戶即時編輯內(nèi)容會造成這種變化),執(zhí)行相應(yīng)的驗證邏輯
                
                到這里我們先停一下,我們看到WTP提供的StructuredTextViewerConfiguration(也就是我們定義的viewer configuration的間接父類)對getReconciler方法做了final處理。既然我們自定義的viewer configuration間接繼承自StructuredTextViewerConfiguration,所以我們提供自定義的IReconciler實現(xiàn)的方式就走不通了哈^_^。那我們?nèi)绾螖U展呢?

               我們看一下WTP提供的具體reconciling處理過程吧:
    public class DocumentRegionProcessor extends DirtyRegionProcessor {

       
    /**
         * 
    @param dirtyRegion
         
    */
        
    protected void process(DirtyRegion dirtyRegion) {
            
    if (!isInstalled())
                
    return;

            
    super.process(dirtyRegion);

            
    // Also call the validation and spell-check strategies
            ITypedRegion[] partitions = computePartitioning(dirtyRegion);

            DirtyRegion dirty 
    = null;
            
    for (int i = 0; i < partitions.length; i++) {
                
    // [source]validator (extension) for this partition
                if (getValidatorStrategy() != null) {
                    dirty 
    = createDirtyRegion(partitions[i], DirtyRegion.INSERT);
                    getValidatorStrategy().reconcile(partitions[i], dirty);
                }
            }

            
    // single spell-check for everything
            if (getSpellcheckStrategy() != null) {
                getSpellcheckStrategy().reconcile(dirtyRegion, dirtyRegion);
            }
        }

         
    protected ValidatorStrategy getValidatorStrategy() {
         
    //收集擴展點org.eclipse.wst.sse.ui.sourcevalidation對應(yīng)擴展
         }
    }

                
                我們看到WTP并沒有,WTP提供了一個全能的IReconcilingStrategy實現(xiàn)(org.eclipse.wst.sse.ui.internal.reconcile.validator.ValidatorStrategy),這個IReconcilingStrategy實現(xiàn)定義了擴展點org.eclipse.wst.sse.ui.sourcevalidation來邀請用戶進行擴展,擴展的方式是:提供一個org.eclipse.wst.validation.internal.provisional.core.IValidator接口的實現(xiàn),并和特定的分區(qū)類型(partition type)相關(guān)聯(lián)。ValidatorStrategy根據(jù)特定分區(qū)類型去調(diào)用對應(yīng)的擴展功能,然后負責(zé)創(chuàng)建對應(yīng)Annotation列表并放入編輯器對應(yīng)的IAnnotationModel中去,用戶校驗邏輯提供的信息就可以再編輯器上顯示了,就像文章剛開頭我們看到的那張圖片一樣^_^。

                PS:有關(guān) Annotation(org.eclipse.jface.text.source.Annotation)和IAnnotationModel(org.eclipse.jface.text.source.IAnnotationModel)也是JFace Text Framework提供的特性,用于處理用戶對編輯器中文檔內(nèi)容注釋信息的顯示。有關(guān)具體內(nèi)容,有興趣的可以去看一下,后面有時間,我會再blog里面發(fā)一套有關(guān)JFace Text Framework關(guān)鍵特性分析和如何擴展的文章^_^。
               
               我們回過頭來回顧一下WTP提供的reconciling實現(xiàn)吧:
                

               不再接著分析下去了,有興趣的同學(xué)可以自己去看一下WTP中對應(yīng)的更詳細地代碼實現(xiàn)。我們只要記住通過org.eclipse.wst.sse.ui.sourcevalidation來注冊自己的校驗邏輯就可以了。


              【自定義標(biāo)簽即時校驗實現(xiàn)摘要】
              在上一節(jié)中我們提供了自動提示的擴展定制,針對的是屬性值提示。在本節(jié),我們也主要是針對屬性值提示做一個示例,需求大致如下:
                   1、當(dāng)用戶編輯標(biāo)簽的屬性值時,對其做即時語義驗證,如果屬性值不合法,則顯示相應(yīng)錯誤。
                   2、便于用戶擴展,需要提供相關(guān)擴展點,別的開發(fā)者可以針對特定標(biāo)簽動態(tài)掛入校驗邏輯實現(xiàn)

             【創(chuàng)建并注冊validator擴展】
              根據(jù)前面的分析我們知道,想擴展WTP默認(rèn)的即時源碼校驗,我們需要借助org.eclipse.wst.sse.ui.sourcevalidation擴展點動態(tài)掛入一個我們自己的IValidator實現(xiàn)。

    public class JSPTagSourceValidator implements IValidatorISourceValidator{
        
    private IStructuredDocument document;
        
        
    public void cleanup(IReporter reporter) {
            
    // TODO Auto-generated method stub
            
        }
        
        
    public void validate(IValidationContext helper, IReporter reporter) throws ValidationException {
            
    // TODO Auto-generated method stub
            
        }

        
    public void connect(IDocument document) {
            
    this.document = (IStructuredDocument)document;
        }
        
        
    public void disconnect(IDocument document) {
            
    // TODO Auto-generated method stub
            
        }
        
        
    public void validate(IRegion dirtyRegion, IValidationContext helper, IReporter reporter) {
            IStructuredModel structuredModel 
    = StructuredModelManager.getModelManager().getModelForRead(this.document);
            
    if (structuredModel == null)
                
    return ;
            
            
    try {
                IStructuredDocumentRegion[] structuredRegions 
    = this.document.getStructuredDocumentRegions(dirtyRegion.getOffset(), dirtyRegion.getOffset());
                
                
    for (int i = 0; i < structuredRegions.length; i++) {
                    
    //判斷是否是標(biāo)簽
                    if (structuredRegions[i].getType() != DOMRegionContext.XML_TAG_NAME)
                        
    continue ;
                    
                    IndexedRegion indexRegioned 
    = structuredModel.getIndexedRegion(structuredRegions[i].getStartOffset());
                    
    if (!(indexRegioned instanceof IDOMElement))
                        
    continue ;
                    
                    
    //執(zhí)行標(biāo)簽驗證,并設(shè)置信息
                    IDOMElement domElement = (IDOMElement)indexRegioned;
                    String tagName 
    = domElement.getNodeName();
                    ITagValidator tagValidator = TagValidatorManager.getInstance().getTagValidator(tagName);

                    
    if (tagValidator != null) {
                        IMessage[] messages 
    = tagValidator.validate(domElement, reporter);
                        
    for (int j = 0; j < messages.length; j++) {
                            reporter.addMessage(
    this, messages[i]);
                        }
                    }
                }
            } 
    catch (Exception e) {
                
    //log exception
                IStatus status = new Status(IStatus.ERROR, "jspeditor"1001"校驗過程過程出錯", e);
                Activator.getDefault().getLog().log(status);
            } 
    finally {
                
    //重要:削減模型引用計數(shù)
                if (structuredModel != null)
                    structuredModel.releaseFromRead();
            }
        }
    }

              上面代碼的細節(jié)我們先不看,ITagValidator接口使我們定義的,后面會看到。我們創(chuàng)建了自己的validator實現(xiàn),分析變化region,如果所在標(biāo)簽有對應(yīng)的ITagValidator實現(xiàn),就執(zhí)行相應(yīng)的驗證邏輯。我們的擴展是有了,下面就是將其動態(tài)掛入WTP了:

    <extension
             
    point="org.eclipse.wst.sse.ui.sourcevalidation">
             
    <validator
                
    scope="partial"
                class
    ="jspeditor.internal.sourcevalidation.JSPTagSourceValidator"
                id
    ="jspeditor.attrbuteValueValidator">
                
    <contentTypeIdentifier
                    
    id="org.eclipse.jst.jsp.core.jspsource">
                    
    <partitionType id="org.eclipse.jst.jsp.JSP_DIRECTIVE"/>
                
    </contentTypeIdentifier>
            
    </validator>
       
    </extension>

              注意上面的partitionType,由于我們主要是執(zhí)行JSP Tag的即時校驗,我們關(guān)注的分區(qū)類型自然就是org.eclipse.jst.jsp.JSP_DIRECTIVE。
              PS:關(guān)于partiton type的詳細分析前面的章節(jié)中有,不清楚的可以取看一下。如果你自己的校驗器有其他的驗證需求,則搞清楚對應(yīng)區(qū)域的分區(qū)類型應(yīng)該是什么,然后添加到以上擴展的contentTypeIdentifier中,一個vaidator可以跟多個partition type相綁定。

                  【定義自己的標(biāo)簽驗證擴展點】
                    分析需求可以發(fā)現(xiàn),對于不同的標(biāo)簽執(zhí)行驗證,只是驗證的具體邏輯不同,抽象概念還是一個。又希望以后的開發(fā)者可以提供擴展,所以我們再次采用擴展點機制(這個上一節(jié)中定制自動提示是非常相似的^_^)。代碼結(jié)構(gòu)基本上也是按照策略模式來吧(驗證算法封裝為對象)^_^

    public interface ITagValidator {
        
    /**
         * 
    @param domElement 待驗證標(biāo)簽的doom element
         * 
    @param reporter
         * 
    @return           無信息,請返回IMessage[0]
         
    */
        
    public IMessage[] validate(IDOMElement domElement, IReporter reporter);
    }

                    
                對應(yīng)的默認(rèn)適配器類如下,主要兩個作用:1、提供一個算法估計骨架(template method^_^);2、封裝一些公共操作。

    /**
     * ITagValidator接口對應(yīng)的default adapter類。
     *
     * 
    @author zhuxing (mailto:zhu_xing@live.cn)
     
    */
    /*
     * 修改歷史
     * $Log$ 
     
    */
    public abstract class AbstractTagValidator implements ITagValidator {
        
    /* 
         * 簡單示例算法流程:
         * 1、執(zhí)行標(biāo)簽級別的驗證
         * 2、執(zhí)行屬性級別的驗證
         * 
         * @see jspeditor.sourcevalidation.ITagValidator#validate(org.eclipse.wst.xml.core.internal.provisional.document.IDOMElement, org.eclipse.wst.validation.internal.provisional.core.IReporter)
         
    */
        
    public IMessage[] validate(IDOMElement domElement, IReporter reporter) {
            
    if (domElement == null)
                
    return new IMessage[0];
            
            List
    <IMessage> messages = new ArrayList<IMessage>();
            
            
    //執(zhí)行標(biāo)簽級別驗證
            Collections.addAll(messages, this.validateElement(domElement, reporter));
            
            
    //執(zhí)行屬性級別驗證
            NamedNodeMap attributes = domElement.getAttributes();
            
    if (attributes != null) {
                
    for (int index = 0; index < attributes.getLength(); index++) {
                    IDOMAttr domAttr 
    = (IDOMAttr)attributes.item(index);
                    Collections.addAll(messages, 
    this.validateAttr(domAttr, reporter));
                }
            }
            
            
    return messages.toArray(new IMessage[messages.size()]);
        }
        
        
    /**
         * 執(zhí)行element級別的驗證,例如可以執(zhí)行標(biāo)簽屬性是否完整、是否有多余屬性等
         * 
         * 
    @param domElement
         * 
    @param reporter
         * 
    @return           無信息,請返回IMessage[0]
         
    */
        
    public abstract IMessage[] validateElement(IDOMElement domElement, IReporter reporter);
        
        
    /**
         * 
    @param domAttr    執(zhí)行attribute級別的驗證,例如驗證屬性值是否合法等
         * 
    @param reporter
         * 
    @return           無信息,請返回IMessage[0]
         
    */
        
    public abstract IMessage[] validateAttr(IDOMAttr domAttr, IReporter reporter);
    }

                    
                   我們同樣為ITagValidator類型提供一個實例管理器(具體代碼請參見附件源碼下載鏈接),負責(zé)處理加載擴展細節(jié),并提供一個接口允許客戶端以tag name獲取對應(yīng)的ITagValidator實現(xiàn)。這個在上面的validator擴展類(JSPTagSourceValidator)上下文中已經(jīng)使用,紅色加粗部分的代碼^_^ 
                    
                    需求也分析了,變化也得到封裝了,那就看一下擴展點的定義吧:

                    我們的擴展點定義的很簡單:為指定tag id 配置一個ITagValidator實現(xiàn)^_^


                  【注冊ITagVaildator擴展并測試】
                    我們延用上一節(jié)中的<test:test>標(biāo)簽,我們還記得里面定義了一個scope屬性,允許的屬性值是request或者是session。那我們就為其開發(fā)一個ITagValidator驗證擴展,如果scope的屬性值不是request或者session,則報錯^_^      

    public class TestTagValidator extends AbstractTagValidator {
        
    private static final String ATTR_NAME_SCOPE = "scope";
        
        
    /* (non-Javadoc)
         * @see jspeditor.sourcevalidation.AbstractTagValidator#validateElement(org.eclipse.wst.xml.core.internal.provisional.document.IDOMElement, org.eclipse.wst.validation.internal.provisional.core.IReporter)
         
    */
        
    public IMessage[] validateElement(IDOMElement domElement, IReporter reporter) {
            
    return new IMessage[0];
        }
        
        
    /* (non-Javadoc)
         * @see jspeditor.sourcevalidation.AbstractTagValidator#validateAttr(org.eclipse.wst.xml.core.internal.provisional.document.IDOMAttr, org.eclipse.wst.validation.internal.provisional.core.IReporter)
         
    */
        
    public IMessage[] validateAttr(IDOMAttr domAttr, IReporter reporter) {
            
    if (domAttr == null)
                
    return new IMessage[0];
            
            
    if (ATTR_NAME_SCOPE.equals(domAttr.getName())) {
                
    if (!("session".equalsIgnoreCase(domAttr.getValue())) && !("request".equalsIgnoreCase(domAttr.getValue()))) {
                    String messageText 
    = "scope屬性值錯誤,只能為session或者request";
                    IMessage message 
    = TagValidatorHelper.createMessage(IMessage.HIGH_SEVERITY, messageText, domAttr);
                    
    return new IMessage[]{message};
                }
            }
            
            
    return new IMessage[0];
        }

    }

                   
                 為test:test標(biāo)簽注冊驗證擴展:

      
              
                效果演示:    
          
             當(dāng)我們給scope輸入的屬性值不是request或者session時,即時報錯了^_^

            【后記】
             在這一節(jié)中,其實提供了一個基于編輯器的標(biāo)簽驗證框架的縮影^_^

             PS:如果沒有擴展需求,可以考慮重新設(shè)計擴展方式,將依賴于擴展點的動態(tài)掛入擴展方式修改為默認(rèn)的靜態(tài)注入的方式。有關(guān)合理利用擴展點機制的問題,博客上又兩篇相關(guān)的隨筆:
            【Eclipse插件開發(fā)】在什么情況下創(chuàng)建擴展點 
            【Eclipse插件開發(fā)】Eclipse中的擴展點機制存在的理由

           【源碼下載】
             即時校驗定制工程源碼



    本博客中的所有文章、隨筆除了標(biāo)題中含有引用或者轉(zhuǎn)載字樣的,其他均為原創(chuàng)。轉(zhuǎn)載請注明出處,謝謝!

    posted on 2008-09-25 15:22 zhuxing 閱讀(3345) 評論(1)  編輯  收藏 所屬分類: Eclipse Plug-in & OSGIWTP(Web Tools Platform)

    評論

    # re: 【Eclipse插件開發(fā)】基于WTP開發(fā)自定義的JSP編輯器(九):定制StructuredTextEditor源碼即時校驗  回復(fù)  更多評論   

    我開發(fā)的是 .xml編輯器,我希望的是 .xml編輯器的元素符合xsd文件的規(guī)則,就是用xsd文件里的規(guī)則去校驗???
    2012-02-21 10:47 |
    主站蜘蛛池模板: 亚洲黄色免费网址| 亚洲黄色在线观看| 成人au免费视频影院| 精品国产免费一区二区三区香蕉| 亚洲成在人线aⅴ免费毛片| 久久青青草原亚洲AV无码麻豆| 亚洲av区一区二区三| 成年人免费观看视频网站| 久久综合给合久久国产免费| 精品免费久久久久国产一区| 免费亚洲视频在线观看| 亚洲中文字幕AV在天堂| 亚洲成综合人影院在院播放| 亚洲国产AV无码专区亚洲AV| AV在线播放日韩亚洲欧| 免费a级毛片无码a∨性按摩| 成**人免费一级毛片| 色妞WWW精品免费视频| 成年人网站免费视频| 精品无码无人网站免费视频| 女人体1963午夜免费视频| 中文字幕视频免费在线观看| 免费人成视频在线播放| 美女18毛片免费视频| 青青久久精品国产免费看| 无码天堂亚洲国产AV| 精品一区二区三区免费毛片| 亚洲AV日韩AV永久无码色欲 | 一级中文字幕乱码免费| 国产精品亚洲色图| 菠萝菠萝蜜在线免费视频| 鲁死你资源站亚洲av| 羞羞漫画登录页面免费| 免费一级毛suv好看的国产网站 | 成熟女人特级毛片www免费| 国产精品色拉拉免费看| 99精品全国免费观看视频| 成人在线免费观看| 国产在线播放免费| 国产亚洲精品影视在线产品| 亚洲日韩激情无码一区|