Live a simple life

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

【Eclipse插件开发】基于WTP开发自定义的JSP编辑器(四):Strucutured Document分析视图

        在上一篇中,我们详细阐述了WTP中最重要的数据模型之一IStructuredDocument(我们就称之为WTP Document吧,和另外一个核心数据模型WTP Model----IStructuredModel对应),本节中我们将自己开发一个工具来分析IStrucutredDocument。

        PS:千万别着急,后面的文章会对WTP StructuredTextEditor进行功能特征定制的,在真正定制之前一定要搞清楚WTP Document(IStructuredDocument)和WTP Model(IStructuredModel),连核心数据模型都不熟悉,后面谈何定制^_^

        【WTP提供的Properteis视图扩展】
        说明:Properteis视图是Eclipse固有的,允许用户通过相应的类型扩展机制来定制Properties视图中的内容,涉及到的主要知识点包括:
        1、Eclipse的Adapter机制(IAdaptable、IAdapterFactory、AdapterManager),关于Eclipse中的类型适配扩展机制,博客中的另外一篇文章做了分析:
            【Eclipse插件开发】Eclipse中类型扩展机制分析
        2、Properties视图相关的几个重要接口:
               org.eclipse.ui.views.properties.IPropertySource.class
               org.eclipse.ui.views.properties.PropertySheetPage
                ...
        3、WTP就是借助于Eclipse类型扩展机制,实现了对应的功能。我们看一下在WTP的StructuredTextEditor中的getAdapter方法中提供了扩展服务(IWorkbenchPart本身就是声明为IAdaptable):
            
 1 /*
 2      * (non-Javadoc)
 3      * 
 4      * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
 5      */
 6     public Object getAdapter(Class required) {
 7       //
 8       else if (IPropertySheetPage.class.equals(required) && isEditable()) {
 9             if (fPropertySheetPage == null || fPropertySheetPage.getControl() == null || fPropertySheetPage.getControl().isDisposed()) {
10                 PropertySheetConfiguration cfg = createPropertySheetConfiguration();
11                 if (cfg != null) {
12                     ConfigurablePropertySheetPage propertySheetPage = new ConfigurablePropertySheetPage();
13                     propertySheetPage.setConfiguration(cfg);
14                     fPropertySheetPage = propertySheetPage;
15                 }
16             }
17             result = fPropertySheetPage;
18         }
19          //.
20 }
         ConfigurablePropertySheetPage^_^(org.eclipse.wst.sse.ui.internal.properties.ConfigurablePropertySheetPage)。

        WTP的properties视图的主要功能就是,根据用户在编辑器中光标的位置,在Properties视图中展示对应的标签内容,并支持用户编辑,例如:

     当前编辑器中,用户光标位于jsp:directive.page标签内,属性视图就列举了对应的标签内容,允许用户编辑。

        【我们自己的Stuctured Document View】
          如果有一个视图来将当前编辑器中的内容对应的IStrucuturedDocument以树状结构的方式展示出来,再提供一定的用户交互,那对认清楚IStructuredDocument的本质是有作用的哈

            【需求】
            1、提供一个Structured Document View视图,以树状方式将当前编辑器中的IStructuredDocument展示出来
            2、交互(编辑器 ---> Structured Document View视图):
                   激活WTP JSP编辑器(或者是我们前面自己定制的编辑器),即时更新Structured Document View视图
                   当用户光在编辑器中标移动时,自动选中Structured Document View视图中对应的节点
                   当编辑器中的内容改变时,即时更新Structured Document View视图
                   当前激活编辑器关闭时,清空Structured Document View视图内容
            3、交互(Structured Document View视图 ---> 编辑器)
                   双击视图中树状控件中特定节点,对应内容在编辑器中被选中
            4、显示内容:
                   对于IStructuredDocument,则显示对应的具体实现类(对应于JSP类型则为JobSafeStructuredDocument)
                    对于IStrucutredDocumentRegion(ITextRegionCollection),则显示实现类名称、节点类型、位置范围、文本等
                    对于叶子节点的ITextRegion,则显示实现类名称、节点类型、位置范围(说明:相对于父ITextRegionCollection的相对位置,非对于整个文档的相对位置!!!)

            【效果预览】

                如上图所示,双击视图中的节点,编辑器中对应的内容被选中^_^。

             【实现摘要(文章后面会附上完整代码)】
                1、创建插件工程wtp.stuctureddocument,创建视图,不用多说,利用扩展点org.eclipse.ui.views,对应内容如下:        
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <?eclipse version="3.2"?>
 3 <plugin>
 4    <extension
 5          point="org.eclipse.ui.views">
 6       <category
 7             id="wtp.structureddocument.category"
 8             name="Structured Document分析"/>
 9       <view
10             category="wtp.structureddocument.category"
11             class="wtp.structureddocument.view.StructuredDocumentView"
12             id="wtp.structureddocument.view"
13             name="Structured Document分析"/>
14    </extension>
15 </plugin>
                wtp.structureddocument.view.StructuredDocumentView为视图对应的ViewPart实现,里面创建了一个tree viewer控件,并给其配置了对应的content provider和label provider,具体参加附件中的源码。

               2、利用workbench中的选择服务(seleciton service)。前面需求中说过,我们要监听光标在编辑器中的位置选择,所以使用此服务,所以我们的StructuredDocumentView要实现org.eclipse.ui.ISelectionListener接口。
                (PS:selection service是workbench的一个重要特性,也是我们常用的,Eclipse官方网站上有一篇专题文章,看看撒两眼^_^)
    
                  注册selection listener: 
1 /* (non-Javadoc)
2      * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite)
3      */
4     public void init(IViewSite site) throws PartInitException {
5         super.init(site);
6         
7         this.getSite().getPage().getWorkbenchWindow().getSelectionService().addPostSelectionListener(this);
8         this.getSite().getPage().getWorkbenchWindow().getPartService().addPartListener(partListener);
9     }
                
                实现selection事件处理:
 1 /* (non-Javadoc)
 2      * @see org.eclipse.ui.ISelectionListener#selectionChanged(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
 3      */
 4     public void selectionChanged(IWorkbenchPart part, ISelection selection) {
 5         if (part instanceof TextEditor) {
 6             IEditorInput editorInput = ((TextEditor)part).getEditorInput();
 7             IDocument document = ((TextEditor)part).getDocumentProvider().getDocument(editorInput);
 8             
 9             if (!(document instanceof IStructuredDocument)) {
10                 this.viewer.setInput(new Object[0]);
11                 return ;
12             }
13         
14             IStructuredDocument structuredDocument = (IStructuredDocument)document;
15             
16             if (this.needUpdateInput(document)) {
17                 this.viewer.setInput(new Object[]{document});
18                 this.viewer.expandAll();
19                 this.viewer.collapseAll();
20                 
21                 //set source workbench part, will be used in part listener
22                 this.sourcePart = part;
23                 
24                 //注册监听器,便于同步刷新tree viewer
25                 structuredDocument.addDocumentListener(documentListener);
26             }
27             
28             if (selection instanceof ITextSelection) {
29                 int offset = ((ITextSelection)selection).getOffset();
30                 IStructuredDocumentRegion structuredDocumentRegion = structuredDocument.getRegionAtCharacterOffset(offset);
31                 ITextRegion textRegion = structuredDocumentRegion.getRegionAtCharacterOffset(offset);
32                 
33                 this.viewer.collapseAll();
34                 this.viewer.expandToLevel(structuredDocumentRegion, 2);
35                 this.viewer.setSelection(new StructuredSelection(textRegion));
36             }
37         }
38     }
            
 1 /**
 2      * 判断当前document和tree viewer中输入的document是否一致
 3      * 
 4      * @param document
 5      * @return
 6      */
 7     private boolean needUpdateInput(IDocument document) {
 8         if (this.viewer.getInput() != null) {
 9             Object[] oldInput = (Object[])this.viewer.getInput();
10             if (oldInput.length == 1)
11                 return oldInput[0!= document;
12         }
13         
14         return true;
15     }

            看的出来,我们的selection处理逻辑如下:
            1、如果当前选择事件来自TextEditor类型的编辑器中(文本编辑器的共同超类),则去获取编辑器对应的IDocument,如果是IStrucuturedDocument则判断是否需要更新;如果不是,则不是我们的目标(例如如果打开的是java源码编辑器,那对应的IDocument实现肯定不是WTP的IStrucuturedDocument了哈^_^)
            2、如果是IStrucuturedDocument,则确定是否需要更新(也就是判断我们的Structured Document分析视图中现有的input是否就是当前编辑器中内容对应的IStrucuturedDocument)。如果需要更新,则重新设置tree viewer输入
            3、我们根据光标在编辑器中的位置信息(参加org.eclipse.jface.text.ITexSelection),首先利用IStructuredDocument.getRegionAtCharacterOffset(int)来定位对应的IStructuredDocumentRegion树枝节点,然后在利用IStructuredDocumentRegion.getRegionAtCharacterOffset(int)来定位对应的ITextRegion树叶节点,在我们视图的树状控件中选中对应的树叶节点。
                回顾:对于IStructuredDocument来说,它的孩子就是IStructuredDoucmentRegion,并没有提供对应的接口允许用户直接去获取特定offset对应的具体ITextRegion。再看一下前面用到的一幅图吧:
                

                3、处理视图中tree viewer双击,定位编辑器中对应内容
                    
 1 this.viewer.addDoubleClickListener(new IDoubleClickListener() {
 2             public void doubleClick(DoubleClickEvent event) {
 3                 TreeSelection treeSelection = (TreeSelection)event.getSelection();
 4                 TreePath treePath = treeSelection.getPaths()[0];
 5                 
 6                 if (treePath.getSegmentCount() == 1) {
 7                     //选择的是IStructuredDocument
 8                     IStructuredDocument structuredDocument = (IStructuredDocument)treeSelection.getFirstElement();
 9                     
10                     //处理编辑器选中,选中整个文档
11                     ((StructuredTextEditor)sourcePart).getTextViewer().setSelectedRange(0, structuredDocument.getLength());
12                 }
13                 else if (treePath.getSegmentCount() == 2) {
14                     //选择的是IStructuredDocumentRegion
15                     IStructuredDocumentRegion structuredDocumentRegion = (IStructuredDocumentRegion)treePath.getLastSegment();
16                     int selectionOffset = structuredDocumentRegion.getStart();
17                     int selectionLength = structuredDocumentRegion.getLength();
18                     
19                     //处理编辑器选中,选中structured document region区域
20                     ((StructuredTextEditor)sourcePart).getTextViewer().setSelectedRange(selectionOffset, selectionLength);
21                 }
22                 else if (treePath.getSegmentCount() == 3) {
23                     //选择的是非container的ITextRegion,其父节点为IStructuredDocumentRegion
24                     IStructuredDocumentRegion structuredDocumentRegion = (IStructuredDocumentRegion)treePath.getSegment(1);
25                     ITextRegion textRegion = (ITextRegion)treePath.getLastSegment();
26                     
27                     int selectionOffset = structuredDocumentRegion.getStartOffset(textRegion);
28                     int selectionLength = textRegion.getLength();
29                     
30                     //处理编辑器选中,选中的是叶子节点的text region区域
31                     ((StructuredTextEditor)sourcePart).getTextViewer().setSelectedRange(selectionOffset, selectionLength);
32                 }
33             }
34         });
                TreePath(org.eclipse.jface.viewers.TreePath)中的seg count信息其实确定了我们的双击的控件位于整个树状控件的位置,具体自己看吧^_^
            
            4、利用IDocumentListener同步更新视图。逻辑非常简单,如下:          
1 private class StructuredDocumentListener implements IDocumentListener {
2         public void documentChanged(DocumentEvent event) {
3             viewer.refresh();
4         }
5         
6         public void documentAboutToBeChanged(DocumentEvent event) {
7             // nothing to do
8         }
9     }
            我们的listener实在前面handle selection的过程注册的,再决定用一个新的IStructuredDocument作为tree viewer输入的时候,同步注册对应的监听器。
            注意:我们这边并没有wtp自己的document listener,它自己都不建议使用了^_^

        5、处理编辑器关闭行为,利用workbench的part service特性。当对应的编辑器关闭时,视图中的tree viewer
 1 private class PartListener implements IPartListener {
 2         public void partActivated(IWorkbenchPart part) {
 3             // TODO Auto-generated method stub
 4             
 5         }
 6         
 7         public void partBroughtToTop(IWorkbenchPart part) {
 8             // TODO Auto-generated method stub
 9             
10         }
11         
12         /* 
13          * 如果被关闭的workbench part是提供document信息的source part,则情况tree viewer
14          * 
15          * @see org.eclipse.ui.IPartListener#partClosed(org.eclipse.ui.IWorkbenchPart)
16          */
17         public void partClosed(IWorkbenchPart part) {
18             if (sourcePart == part) {
19                 sourcePart = null;
20                 viewer.setInput(new Object[0]);
21             }
22         }
23         
24         public void partDeactivated(IWorkbenchPart part) {
25             // TODO Auto-generated method stub
26             
27         }
28         
29         public void partOpened(IWorkbenchPart part) {
30             // TODO Auto-generated method stub
31         }
32     }
            注意:上面代码的source part是我们的handle selection过程中缓存的,这边就派上用场了啊,能够判断当前关闭的part是否就提供当前IStructuredDocument的text eidtor 了^_^
            注册代码再上面的init方法代码中有,注册了对应的selection listener和part listener
    

            上面基本上就是我们的视图主类wtp.structureddocument.view.StructuredDocumentView的所有代码了,我们开发这样一个视图也只用了200多行代码...

            最后强调一下,我们的这个插件工程需要依赖的插件列表为:
             org.eclipse.ui,
             org.eclipse.core.runtime,
             org.eclipse.core.resources,
             org.eclipse.wst.sse.core,
             org.eclipse.wst.xml.core,
             org.eclipse.wst.sse.ui,
             org.eclipse.jface.text,
             org.eclipse.ui.workbench.texteditor


           【源码下载】
             源码为实际工程以Export ---> Archive File方式导出的,下载链接:source.zip 。

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

posted on 2008-09-11 16:16 zhuxing 阅读(3109) 评论(3)  编辑  收藏 所属分类: Eclipse Plug-in & OSGIWTP(Web Tools Platform)

评论

# re: 【Eclipse插件开发】基于WTP开发自定义的JSP编辑器(四):Strucutured Document分析视图  回复  更多评论   

不要只观察规则的jsp内容对应的document是怎样的
也要看看不规则的(不符合jsp语法的)jsp内容对应的document是怎样的

这很重要!!!直接影响后面你定制的行为是否健壮!!!
2008-09-11 16:36 | zhuxing

# re: 【Eclipse插件开发】基于WTP开发自定义的JSP编辑器(四):Strucutured Document分析视图  回复  更多评论   

明白,谢谢!
2008-09-12 07:16 | srdrm

# re: 【Eclipse插件开发】基于WTP开发自定义的JSP编辑器(四):Strucutured Document分析视图[未登录]  回复  更多评论   

IStructuredDocument structuredDocument = (IStructuredDocument)document; 这一句报错啊,请问楼主Eclipse是哪个版本的呢?
2015-01-21 15:18 | ddd

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


网站导航: