BaNg@taobao

          Just Do It!

            BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
            20 Posts :: -1 Stories :: 202 Comments :: 0 Trackbacks
          Eclipse提供了非常多的view,從表現(xiàn)形式來說可分為table view和tree view;從結(jié)構(gòu)上來說可分成三類:Common navigator view, Pagebook view, Task-oriented view。一般情況下,CNV與Resource有關(guān),pagebook跟selection有關(guān),而task-oriented 為自定義的視圖。基本所有的** explorer都是CNV型的view,基本所有主要插件都有CNV的影子比如IDE,Navigator, Team,JDT, CDT, DTP等。為什么要使用CNV? Paper Napkin的文章說的很清楚了,我的看法是,對于怎樣面對大批量+復雜應用的二次抽象(view超多,view內(nèi)容聯(lián)系超復雜緊密),CNV提供了一個很好的完整實例。

          ==>> 代碼下載,下載后import到Eclipse 3.4.1+JDK 1.6,run/debug即可。

          CNV的視圖特征:

          10分鐘,一個CNV Resource View
          1. 新建一個plugin項目,名字 com.lifesting.hush,將圖標解壓縮到項目下,刷新,打開MANIFEST.MF,在build項里面將icon目錄鉤上。
          2. 定位Dependencies項,依次加入 org.eclipse.ui.navigator,org.eclipse.ui.navigator.resources,org.eclipse.ui.ide,org.eclipse.jface.text,org.eclipse.ui.editors, org.eclipse.core.resources,org.eclipse.ui.views插件。
          3. 配置一個view extension, 如下圖:


            需要注意的是,這個view的implementation是navigator插件里面的CommonNavigator,目前我們不需要手寫任何代碼。
          4. 使用Extension Point org.eclipse.ui.navigator.viewer, new一個viewer,viewId設置為 com.lifesting.hush.view.cnf,popMenuId暫時置空;new一個viewerContentBinding,viewId不變,添加一個includes子節(jié)點,然后在其上添加一個contentExtension,屬性pattern為org.eclipse.ui.navigator.resourceContent,isRoot為true.
          5. 啟動,點擊菜單Window->Show View->General->Html Explorer,就可以看到效果了,如果view是空白,也不是bug,在左邊的Pakcage Explorer或Resource Explorer新建一個項目,然后關(guān)閉Html Explorer再打開,就會看到Html Explorer顯示的和Resource Explorer一模一樣的項目結(jié)構(gòu)。

          雖然這個Html Explorer出來了,但設置的org.eclipse.ui.navigator.resourceContent哪來的?怎么定義的?怎么添加右鍵菜單?Link為啥無效?怎樣定制這個顯示?CNF好像也沒有顯著的特點阿?不著急,逐一搞定,從頭開始,最終的效果會是這樣的:


          CNV的核心是navigatorContent,所有操作都是圍繞它展開的(可以選擇org.eclipse.ui.navigator.navigatorContent,選擇find references,看看SDK都提供了哪些content),我們這個Html Explorer為了把過程將的更清楚,將使用兩個自定義的navigatorContent。下面是步驟:

          1. 通過extension point org.eclipse.ui.navigator.navigatorContent 新建一個id為com.lifesting.cnf.directorycontent的navigatorContent,activatorByDefault=true,LabelProvider=
            org.eclipse.ui.model.WorkbenchLabelProvider,而contentProvider需要新建一個類,非常簡單,就是遍歷IProject或IFolder的子資源(Folder或File)。它的getElement方法實現(xiàn):
                @Override
                
            public Object[] getElements(Object inputElement) {
                    
            if (inputElement instanceof IProject)
                    {
                        
            try {
                            
            return ((IProject)inputElement).members();
                        } 
            catch (CoreException e) {
                            e.printStackTrace();
                        }
                    }
                    
            else if (inputElement instanceof IFolder){
                        
            try {
                            
            return ((IFolder)inputElement).members();
                        } 
            catch (CoreException e) {
                            e.printStackTrace();
                        }
                    }
                    
            return EMPTY;
                }
            1. 每個navigatorContent都有triggerPoints,很顯然剛才定義的content通過IProject和IFolder來觸發(fā)view tree生成。在這個content下面new 一個triggerPoints,再new兩個instanceof分別指向IProject和IFile。
            2. 在定義actionProvider的時候,需要知道selection大致的類型,在這個content下面new一個possibleChildren,再new一個instanceof 先后IResource(IFile或者IFolder)。

          2. 通過extension point org.eclipse.ui.viewActions給ui view添加一個action用來設置content的Root,它的class如下:
            //bind to mycnfview
            public class OpenDirectoryAction implements IViewActionDelegate {
                
            private MyCnfView view;
                
            public OpenDirectoryAction() {
                }

                @Override
                
            public void init(IViewPart view) {
                    
            this.view = (MyCnfView) view;
                }
                @Override
                
            public void run(IAction action) {
                    DirectoryDialog dir_dialog 
            = new DirectoryDialog(view.getSite()
                            .getShell());
                    String dir_location 
            = retriveSavedDirLocation();
                    initDialog(dir_dialog, dir_location);
                    String dir 
            = dir_dialog.open();
                    
            if (null != dir && !dir.equals(dir_location)) {
                        saveDirLocation(dir);
                        createPhantomProject(dir);
                        fireDirChanged(dir);
                    }
                }

                
            private void createPhantomProject(String dir_location) {
                    IProject project 
            = ResourcesPlugin.getWorkspace().getRoot().getProject(MyCnfView.PHANTOM_PROJECT_NAME);
                    
            // 1 delete previous defined project
                    if (project.exists()) {
                        
            try {
                            project.delete(
            falsetruenull);
                        } 
            catch (CoreException e) {
                            e.printStackTrace();
                        }
                    }
                    
            // 2 create new project with the same name
                    final IProjectDescription desc = ResourcesPlugin.getWorkspace().newProjectDescription(MyCnfView.PHANTOM_PROJECT_NAME);
                    desc.setLocationURI(
            new File(dir_location).toURI());
                    IRunnableWithProgress op 
            = new IRunnableWithProgress() {
                        
            public void run(IProgressMonitor monitor)
                                
            throws InvocationTargetException {
                            CreateProjectOperation op 
            = new CreateProjectOperation(desc,
                                    
            "Build Algorithm Library");
                            
            try {
                                PlatformUI.getWorkbench().getOperationSupport()
                                        .getOperationHistory().execute(
                                                op,
                                                monitor,
                                                WorkspaceUndoUtil
                                                        .getUIInfoAdapter(view.getSite().getShell()));
                            } 
            catch (ExecutionException e) {
                                
            throw new InvocationTargetException(e);
                            }
                        }
                    };
                    
            try {
                        view.getSite().getWorkbenchWindow().run(
            falsefalse, op);
                    } 
            catch (InvocationTargetException e) {
                        e.printStackTrace();
                    } 
            catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    
            // 3 add the new created project to default workingset
                    if (project.exists()) {
                        view.getSite().getWorkbenchWindow().getWorkbench().getWorkingSetManager().addToWorkingSets(project,
                                
            new IWorkingSet[] {});
                        
            //4 waiting the project is ready(file structure is built)
                        try {
                            project.refreshLocal(IResource.DEPTH_INFINITE, 
            null);
                        } 
            catch (CoreException e) {
                            e.printStackTrace();
                        }
                    }  
                }

               
            //...略..輔助方法
            }
            代碼要表達的就是建立一個隱含的project,將action取得的directory下所有的文件都倒入到項目中來

          3. 將ui view的class從CommonNavigator變?yōu)橐粋€它的子類MyCnfView:
            public class MyCnfView extends CommonNavigator {

                
            public static final String KEY_DIR_LOCATION="com.lifesting.cnf.myview_location";
                
            public static final String PHANTOM_PROJECT_NAME=".htmlproject";
                
            public MyCnfView() {
                }
                
            public IAdaptable getProjectInput(){
                    IWorkspaceRoot ws_root 
            = ResourcesPlugin.getWorkspace().getRoot();
                    IProject proj 
            = ws_root.getProject(PHANTOM_PROJECT_NAME);
                    
            if (!proj.exists()) return getSite().getPage().getInput();
                    
            return proj;
                }
                
            public void reset()
                {
                    getCommonViewer().setInput(getProjectInput());
                    getCommonViewer().refresh();
                }
                @Override
                
            protected IAdaptable getInitialInput() {
                    
            return getProjectInput();
                }

            }

          4. 將viewerContentBinding/includes的contentExtension的pattern替換為剛才定義的com.lifesting.cnf.directorycontent。

          5. 因為是Html Explorer,需要過濾掉非html文件,需要設置一個過濾器。通過extension point org.eclipse.ui.navigator.navigatorContent 新建一個id為com.lifesting.cnf.filter.nothtml的filter,它的class非常簡單:
            public class NotHtmlFilter extends ViewerFilter {

                
            public NotHtmlFilter() {
                }

                @Override
                
            public boolean select(Viewer viewer, Object parentElement, Object element) {
                    
            if (element instanceof IFile)
                    {
                        
            return Util.isHtmlFile((IFile)element);
                    }
                    
            return true;
                }
            }
          6. 再將此filter配置到cnv的viewerContentBinding/includes中去,跟contentExtension配置過程一樣。
          7. 啟動后,cnv已經(jīng)可以工作,為了演示navigatorContent的可重復利用性,再定義一個只包含html文檔標題的html title content(為方便只掃描標題),掛在前面定義的directory content上。directory content的model是IProject/IFile/IFolder,html title content需要定義一個model,一個html文檔掃描器,還有contentPrvoider和lableProvider。
            • model
              public class HeadTitle {
                  
              private String title;
                  
              private IFile file;
                  
              private int from = 0;
                  
              public int getFrom() {
                      
              return from;
                  }
                      
              //略set/get
              }
            • scaner
                  public static HeadTitle parse(InputStream in) throws IOException {
                      BufferedReader br 
              = new BufferedReader(new InputStreamReader(in));
                      
              int c = -1;
                      StringBuffer sb 
              = new StringBuffer();
                      
              boolean tag = false;
                      
              boolean found_title = false;
                      String to_match 
              = "title";
                      HeadTitle title 
              = new HeadTitle();
                      
              int counter = 0;
                      
              int start = 0;
                      outer: 
              while ((c = br.read()) != -1) {
                          
              if (c == '<') {
                              br.mark(
              3);
                              
              if (br.read() == '!' && br.read() == '-' && br.read() == '-') {
                                  
              // loop over html comment until -->
                                  counter += 3;
                                  
              int t1, t2, t3;
                                  t1 
              = t2 = t3 = 0;
                                  
              while ((c = br.read()) != -1) {
                                      t3 
              = t2;
                                      t2 
              = t1;
                                      t1 
              = c;
                                      counter
              ++;
                                      
              if (t3 == '-' && t2 == '-' && t1 == '>') {
                                          counter
              ++// '<' also need be countered
                                          continue outer;
                                      }
                                  }
                                  
              break outer; //reach the end
                              } else {
                                  br.reset();
                              }
                              tag 
              = true;
                              
              if (found_title) {
                                  title.setTitle(sb.toString());
                                  title.setFrom(start);
                                  title.setTo(counter);
                                  
              return title;
                              }
                          } 
              else if (c == '>') {
                              start 
              = counter + 1;
                              
              if (tag) {
                                  String s 
              = sb.toString().trim();
                                  found_title 
              = to_match.equalsIgnoreCase(s);
                                  sb.setLength(
              0);
                                  tag 
              = false;
                              }
                          } 
              else {
                              sb.append((
              char) c);
                          }
                          counter
              ++;
                      }
                      title.setTitle(
              "No title");
                      
              return title;
                  }
            • contentProvider只有一個getChildren比較重要
                  private static final Object[] EMPTY = new Object[0];
                  @Override
                  
              public Object[] getChildren(Object parentElement) {
                      
              if (parentElement instanceof IFile)
                      {
                          IFile f 
              = (IFile) parentElement;
                          
              if(Util.isHtmlFile(f))
                          {
                              
              try {
                                  HeadTitle head 
              = SimpleHtmlParser.parse(f.getContents());
                                  head.setFile(f);
                                  
              return new HeadTitle[]{head};
                              } 
              catch (IOException e) {
                                  e.printStackTrace();
                              } 
              catch (CoreException e) {
                                  e.printStackTrace();
                              }
                          }
                      }
                      
              return EMPTY;
                  }
            • labelProivder
              public class HtmlTitleLabelProvider extends LabelProvider {
                  
              public static final String KEY_TITLE_IMAGE="icon/title.GIF";
                  @Override
                  
              public String getText(Object element) {
                      
              if (element instanceof HeadTitle)
                          
              return ((HeadTitle)element).getTitle();
                      
              else if (element instanceof IFile)
                          
              return ((IFile)element).getName();
                      
              return super.getText(element);
                  }
                  @Override
                  
              public Image getImage(Object element) {
                      
              if (element instanceof HeadTitle)
                      {
                          
                          Image img 
              = Activator.getDefault().getImageRegistry().get(KEY_TITLE_IMAGE);
                          
              if (img == null)
                          {
                              Activator.getDefault().getImageRegistry().put(KEY_TITLE_IMAGE, (img 
              = Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, KEY_TITLE_IMAGE).createImage()));
                          }
                          
              return img;            
                      }
                      
              return super.getImage(element);
                  }
              }

          8. html title content利用directory content找到文件,提取標題,但二者有個東西來觸發(fā)這個過程。在html title content下定義個一個triggerPoints,使用instanceof=IFile來觸發(fā)。
          9. 所有的功能基本完成,剩下popmenu和link,popmenu可以有兩種方式, contribute或cnv下的popmenu子節(jié)點.contribute會在popmenu下建一堆比如group.*的menu placeholder。content下可以配置actionProvider來完成popmenu的功能,為簡單只在popmenu上放置一個open的動作,即open html file,如果是html file,直接打開;如果是html file title,還須將html title高亮顯示,以示不通,actionProivder:
            public class MyCommonActionProvider extends CommonActionProvider {

                
            private IAction action;
                
            public MyCommonActionProvider() {
                    
                }
                @Override
                
            public void init(ICommonActionExtensionSite site) {
                    
            super.init(site);
                    ICommonViewerSite check_site 
            = site.getViewSite();
                    
            if (check_site instanceof ICommonViewerWorkbenchSite)
                    {
                        ICommonViewerWorkbenchSite commonViewerWorkbenchSite 
            = (ICommonViewerWorkbenchSite)check_site;
                        action 
            = new OpenFileAction(commonViewerWorkbenchSite.getPage(),commonViewerWorkbenchSite.getSelectionProvider());
                    }
                }
                @Override
                
            public void fillActionBars(IActionBars actionBars) {
                    
            super.fillActionBars(actionBars);
                    actionBars.setGlobalActionHandler(ICommonActionConstants.OPEN, action);
                }
                @Override
                
            public void fillContextMenu(IMenuManager menu) {
                    
            super.fillContextMenu(menu);
                    
            if (action.isEnabled())
                        menu.appendToGroup(
            "group.edit", action);
                }
            }
            open file action:
            public class OpenFileAction extends Action {

                
            private IWorkbenchPage page;
                
            private ISelectionProvider provider;
                
            private Object selected = null;
                
                
            public OpenFileAction(IWorkbenchPage page,
                        ISelectionProvider selectionProvider) {
                    
            this.page = page;
                    
            this.provider = selectionProvider;
                    setText(
            "Open");
                    setDescription(
            "Doo");
                    setImageDescriptor(Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, 
            "icon/lookin.GIF"));
                }
                @Override
                
            public boolean isEnabled() {
                    ISelection selection 
            = provider.getSelection();
                    
            if(!selection.isEmpty())
                    {
                        IStructuredSelection structuredSelection 
            = (IStructuredSelection)selection;
                        Object element 
            = structuredSelection.getFirstElement();
                        selected 
            = element;
                        
            return element instanceof IFile || element instanceof HeadTitle;
                    }
                    selected 
            = null;
                    
            return false;
                }
                @Override
                
            public void run() {
                    
            if (null == selected) return ;
                    IFile file 
            = ((selected instanceof HeadTitle) ? ((HeadTitle)selected).getFile() : (IFile)selected);
                    FileEditorInput fileEditInput 
            = new FileEditorInput(file);
                    
            try {
                        TextEditor editor 
            = (TextEditor) page.openEditor(fileEditInput, "org.eclipse.ui.DefaultTextEditor");
                        
            if (selected instanceof HeadTitle)
                        {
                            
            int from = ((HeadTitle)selected).getFrom();
                            
            int to = ((HeadTitle)selected).getTo();
                            editor.selectAndReveal(from, to
            -from);
                        }
                    } 
            catch (PartInitException e) {
                        e.printStackTrace();
                    }
                }
                
            }
          10. Link功能非常簡單,使用extension point org.eclipse.ui.navigator.linkHelper,它有兩個子節(jié)點selectionEnablement和editorinputEnablement,分別對應在view中的selection和打開editor中的editorInput,class為:
            public class SimpleHtmlLinkHelper implements ILinkHelper {

                @Override
                
            public void activateEditor(IWorkbenchPage page,
                        IStructuredSelection selection) {
                    Object obj 
            = selection.getFirstElement();
                    
            if (obj instanceof IFile)
                    {
                        FileEditorInput input 
            = new FileEditorInput((IFile) obj);
                        IEditorPart editor 
            = page.findEditor(input);
                        
            if(editor != null)
                        {
                            page.bringToTop(editor);
                        }
                    }
                }

                @Override
                
            public IStructuredSelection findSelection(IEditorInput anInput) {
                    
            if (anInput instanceof IFileEditorInput)
                    {
                        IFile file 
            = ((IFileEditorInput)anInput).getFile();
                        StructuredSelection selection 
            = new StructuredSelection(file);
                        
            return selection;
                    }
                    
            return null;
                }

            }
          插件太復雜,不適合一篇blog講清楚,如果有人對cnv有些比明白,歡迎來郵件討論。

          posted on 2008-11-30 01:28 Always BaNg. 閱讀(4346) 評論(2)  編輯  收藏 所屬分類: JavaEclipse源代碼分析

          Feedback

          # re: 插件開發(fā)之:Common Navigator View (CNV) 通用導航試圖[未登錄] 2008-12-01 09:03 寒武紀
          不錯,支持一下  回復  更多評論
            

          # re: 插件開發(fā)之:Common Navigator View (CNV) 通用導航試圖 2012-03-09 15:50 songlei
          不知博主的這篇文章還更新不,我在想個問題:selectAndReveal的效果是定位+高亮。但是否可以模仿selectAndReveal的效果,不同的是高亮的顏色由代碼控制,不知博主是否考慮過?  回復  更多評論
            

          主站蜘蛛池模板: 南汇区| 安溪县| 南阳市| 大港区| 庆城县| 靖宇县| 广德县| 金坛市| 土默特左旗| 汉阴县| 平舆县| 通许县| 汉中市| 从江县| 黑河市| 祥云县| 寻甸| 灌云县| 卫辉市| 盐池县| 和龙市| 巴楚县| 娱乐| 黑龙江省| 全州县| 大荔县| 合阳县| 松原市| 南汇区| 明光市| 石首市| 齐河县| 桑植县| 贵港市| 永定县| 乌拉特前旗| 常德市| 平顶山市| 桦甸市| 西和县| 连城县|