Live a simple life

          沉默(zhu_xing@live.cn)
          隨筆 - 48, 文章 - 0, 評論 - 132, 引用 - 0

          導航

          <2008年9月>
          31123456
          78910111213
          14151617181920
          21222324252627
          2829301234
          567891011

          常用鏈接

          留言簿(9)

          隨筆分類

          隨筆檔案

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

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

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

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

                      【JFace Text Framework中相關內容】
                      說白了,我們的源碼編輯對應的控件就是ISourceViewer,那么這個校驗也理所當然應該是ISourceViewer所提供的一個服務。JFace Text Framework中確實針對源碼即時校驗提供了相應的機制,我們看一下相應的接口和運行原理。
                       
                      【相關接口】
                      1、IReconciler(org.eclipse.jface.text.reconciler.IReconciler),調解者,當文檔發生變化時,根據分區類型(如果這個概念忘記了,翻一下前面的文章)提供相應的調解策略(直接說成是驗證策略吧^_^)。
          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),真正的驗證邏輯角色。結合上面的IReconciler接口介紹,我們可以清晰的看出這是,是策略模式的一個應用(驗證邏輯角色這個概念的具體實現隨著分區類型的不同而可能變化,IReconciler可以看成是IReconcilingStrategy的管理者,在需要應用源碼即時驗證的上下文中,給定具體分區類型向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);
          }
            
                      到這里,關于調解(驗證)的兩個最重要的接口也介紹了,那這個IReconcilerIReconcilingStrategy)怎么和source viewer聯系起來呢?

                      3、SourceViewerConfiguration(org.eclipse.jface.text.source.SourceViewerConfiguration)中定義了提供IReconciler實現的接口。
          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;
              }
               
               
          //其他方法省略
          }

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

          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實現會在source viewer配置的過程中被install,那么,如果我們在我們自己的IReconciler install實現中做進一步的關聯實現不就可以了^_^   例如,我們可以在自己的IReconciler  install實現中,將自己注冊為當前IDocument(ITextViewer.getDocument)的document change listener,這樣當編輯器中的內容發生變化(IDocument發生改變)時候,我們就可以根據變化區域對應的內容類型去獲取相應的IReconcilingStrategy,并執行驗證了^_^
                      
                      為了更好的說明這一點,我們看一下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實現,我們看到在其install的過程中,其實是注冊了相應的監聽器,當監聽到相應的變化時,就做相應的驗證處理就可以了^_^

                      IReconciler安裝和工作:
                      

                           同樣,當對應的IEditorPart實例銷毀時,那么內含的source viewer肯定要銷毀,并執行unconfig操作,在unconfig的過程中,會調用相應IReconciler實現的uninstall操作。
                          

                           
                          PS:IReconciler.install中做了監聽器注冊等行為,別忘記在IReconciler.uninstall中做相應清理!!!


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

                      前面我們已經定義了一個source viewer configuration,沿著繼承關系,我們找到了對應的IReconciler實現:
          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;
              }
          }
                      上面紅色標志的StructuredRegionProcessor(org.eclipse.wst.sse.ui.internal.reconcile.StructuredRegionProcessor)就是WTP提供的IReconciler接口的具體實現。繼承關系圖:
                      

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

              
          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實現在install的過程中,注冊了text input listener,當input變化的時候,則獲取和input對應的document,并針對該document注冊相應的document change listener,當document發生變化的時候(用戶即時編輯內容會造成這種變化),執行相應的驗證邏輯
                      
                      到這里我們先停一下,我們看到WTP提供的StructuredTextViewerConfiguration(也就是我們定義的viewer configuration的間接父類)對getReconciler方法做了final處理。既然我們自定義的viewer configuration間接繼承自StructuredTextViewerConfiguration,所以我們提供自定義的IReconciler實現的方式就走不通了哈^_^。那我們如何擴展呢?

                     我們看一下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對應擴展
               }
          }

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

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

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


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

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

          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++) {
                          
          //判斷是否是標簽
                          if (structuredRegions[i].getType() != DOMRegionContext.XML_TAG_NAME)
                              
          continue ;
                          
                          IndexedRegion indexRegioned 
          = structuredModel.getIndexedRegion(structuredRegions[i].getStartOffset());
                          
          if (!(indexRegioned instanceof IDOMElement))
                              
          continue ;
                          
                          
          //執行標簽驗證,并設置信息
                          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 {
                      
          //重要:削減模型引用計數
                      if (structuredModel != null)
                          structuredModel.releaseFromRead();
                  }
              }
          }

                    上面代碼的細節我們先不看,ITagValidator接口使我們定義的,后面會看到。我們創建了自己的validator實現,分析變化region,如果所在標簽有對應的ITagValidator實現,就執行相應的驗證邏輯。我們的擴展是有了,下面就是將其動態掛入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,由于我們主要是執行JSP Tag的即時校驗,我們關注的分區類型自然就是org.eclipse.jst.jsp.JSP_DIRECTIVE。
                    PS:關于partiton type的詳細分析前面的章節中有,不清楚的可以取看一下。如果你自己的校驗器有其他的驗證需求,則搞清楚對應區域的分區類型應該是什么,然后添加到以上擴展的contentTypeIdentifier中,一個vaidator可以跟多個partition type相綁定。

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

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

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

          /**
           * ITagValidator接口對應的default adapter類。
           *
           * 
          @author zhuxing (mailto:zhu_xing@live.cn)
           
          */
          /*
           * 修改歷史
           * $Log$ 
           
          */
          public abstract class AbstractTagValidator implements ITagValidator {
              
          /* 
               * 簡單示例算法流程:
               * 1、執行標簽級別的驗證
               * 2、執行屬性級別的驗證
               * 
               * @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>();
                  
                  
          //執行標簽級別驗證
                  Collections.addAll(messages, this.validateElement(domElement, reporter));
                  
                  
          //執行屬性級別驗證
                  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()]);
              }
              
              
          /**
               * 執行element級別的驗證,例如可以執行標簽屬性是否完整、是否有多余屬性等
               * 
               * 
          @param domElement
               * 
          @param reporter
               * 
          @return           無信息,請返回IMessage[0]
               
          */
              
          public abstract IMessage[] validateElement(IDOMElement domElement, IReporter reporter);
              
              
          /**
               * 
          @param domAttr    執行attribute級別的驗證,例如驗證屬性值是否合法等
               * 
          @param reporter
               * 
          @return           無信息,請返回IMessage[0]
               
          */
              
          public abstract IMessage[] validateAttr(IDOMAttr domAttr, IReporter reporter);
          }

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

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


                        【注冊ITagVaildator擴展并測試】
                          我們延用上一節中的<test:test>標簽,我們還記得里面定義了一個scope屬性,允許的屬性值是request或者是session。那我們就為其開發一個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標簽注冊驗證擴展:

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

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

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

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



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

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

          評論

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

          我開發的是 .xml編輯器,我希望的是 .xml編輯器的元素符合xsd文件的規則,就是用xsd文件里的規則去校驗???
          2012-02-21 10:47 |
          主站蜘蛛池模板: 凤冈县| 留坝县| 永胜县| 墨脱县| 和林格尔县| 肃南| 长春市| 桃江县| 马边| 手机| 南通市| 邳州市| 烟台市| 绥宁县| 峡江县| 苍溪县| 湖南省| 吴江市| 南投市| 客服| 同仁县| 澄迈县| 五常市| 甘南县| 银川市| 随州市| 环江| 富宁县| 睢宁县| 米脂县| 来安县| 乌苏市| 南宫市| 沐川县| 青铜峡市| 秀山| 嘉义县| 河曲县| 兴山县| 邓州市| 广安市|