Live a simple life

沉默(zhu_xing@live.cn)
随笔 - 48, 文章 - 0, 评论 - 132, 引用 - 0
数据加载中……

【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 阅读(3319) 评论(1)  编辑  收藏 所属分类: Eclipse Plug-in & OSGIWTP(Web Tools Platform)

评论

# re: 【Eclipse插件开发】基于WTP开发自定义的JSP编辑器(九):定制StructuredTextEditor源码即时校验  回复  更多评论   

我开发的是 .xml编辑器,我希望的是 .xml编辑器的元素符合xsd文件的规则,就是用xsd文件里的规则去校验???
2012-02-21 10:47 |

只有注册用户登录后才能发表评论。


网站导航: