netbeans的插件模块

       Netbeans在近年的发展,可说是长足的进步。它不仅是功能强大的集成开发环境,更可以看作是一个开发框架和平台,基于这个平台,通过模块开发,扩展这个平台的功能,或者根据自己的需求,定 制个性化的IDE环境。


概述
       Netbeans IDE由一个核心运行时环境(core runtime)一组模块组成。这个core runtime为大多数桌面应用提供公共组件和服务,而“模块”,则是运行在这个core runtime之上的java class,譬如,对于Java语言的支持,就是一个"plugin module",所有Netbeans IDE的跟开发相关的功能都是以“模块”的方式提供的。
开发者可以根据Netbeans平台所开放的编程接口,开发自己的"plugin module"来实现特定的功能。一般来说,有这么两个目的:


    •扩展Netbeans IDE。我们可以轻松的加入需要的特性,扩展IDE的功能。譬如,在工具栏上加入Google的搜索框;支持新的文件类型;在Netbeans IDE上支持新的web应用框架的开发;或者实现web 程序在特殊的应用服务器上的部署或调试,等等。
    •在Netbeans核心平台之上,构造自己的rich client应用。譬如,构造一个有丰富的GUI界面的报表生成器。

 

     从程序的角度看,一个module就是一个java的archive(.jar)文件,它包含多个java类,以及一个manifest文件,用来标志该jar文件是一个Plugin Module。当 平台的main class运行时,找到所有可用的module,建立一个内存登记表,并且运行这些module指定的在平台启动时的代码,module的其它代码则根据需要装载。

     从发布的角度看,一个module是一个.nbm文件,使用者可以从利用Netbeans IDE中的“update center”功能,从网站下载.nbm文件并安装,或 者选择本地的.nbm文件进行安装。
 

创建Plugin Module的一般步骤
       Netbeans从5.0版本开始,提供了向导(Wizard)和模板template,来帮助Netbeans module的开发。一般的开发过程是在基于Wizard生成的Template之上,添加功能。下面以一个简单的Plugin Module开发过程(参考:
http://platform.netbeans.org/tutorials/nbm-google.html)为例,来 介绍这个过程:

1. 创建Plugin Module Project

 

在创建project的界面上,有三类:

•Module Project:为一个standalone plugin module创建模板
•Module Suite Project:为一组互相关联的plugin module和library wrapper module创建模板。它们将一起部署。
•Library Wrapper Module Project:为一个外部的jar文件创建一个plugin module,可以包含在module suite project中。
这里选择Module Project就可。

Library Wrapper Module有什么用途?它帮助我们在Netbeans Plugin开发中和第三方jar文件建立依赖关系。举例:如果要申明本模块依赖test.jar,

•将test.jar包装成Library Wrapper Module,命名为TestModule
•在Project property的API versioning页中,设置这个module的版本号,以及public package(public package是指对于module之外的其它类可见的package)
•将这两个模块放到同一个Module Suite Project中
•在原module中的“add dependency”的界面中,加入它和TestModule的public package的依赖关系。
当然,如果模块依赖Netbeans本身提供的module,比如Data System API,只需最后一步即可,不用Library Wrapper Module。

      这和我们在普通的java程序开发过程中,申明和.jar之间的依赖关系的方法有所不同,平时,我们只需将.jar文件放到classpath上即可。Netbeans Module申明依赖关系的方式的好处在于保持很好的模块化,将模块之间的依赖关系最小化,清晰化,并且利用netbeans中的public package的功能,隐藏API的实现细节,利于模块将来的升级和维护。

      在接下来的界面中,给模块命名(MyFirstModule),选择模块所在的目录(d:\test\nbmodule),生成的java的包名(org.myorg.myfirstmodule)。点击“ finish”就完成了模板的建立。

2. 让我们看看模板中都包含些什么:

源文件和单元测试的包。除了包含和大多数其它普通项目一样的源文件,它还包含一个重要文件:

•缺省在org.myorg.myfirstmodule的源文件包下,有一个“layer.xml”文件它以xml的格式定义了系统的可扩展点,如新增加的 Menu,Toolbars,action属性等以及用户自定义的配置信息,这些信息将合并到整个netbeans系统的配置注册表中(以后会谈到,这个系统的配置注册表称为System FileSystem)。浏 览layer.xml文件的内容,会发现它包含了“FileSystem”这样的tag。

 

•Important Files:这个目录下包含了Plugin Module项目中重要的配置文件,其中比较重要的有:

       •XML Layer:它是上述的layer.xml文件的图形表达方式,可以通过图形界面对layer.xml进行浏览和修改

       •Module Manifest:位于项目根目录下的manifest.mf 文件指明该项目是一个plugin module,并 包含layer.xml和bundle文件的位置


        •Project Metadata:包含项目的元数据,如项目类型,该模块和别的模块依赖关系等等。在Library Wrapper Module Project介绍中 提到的依赖关系即在这里申明。

3. 在生成的项目上,就可以利用IDE提供的wizard来创建各种文件模板:

 

        Netbeans为plugin的开发提供了多种模板以供选择。除了常见的Java Package,Java Class和File/Folder等,有些模板是Plugin Module开发专用的,它们提供了扩展系统的一些方式,运用模板能够迅速的搭起框架,或者获得示例的代码:如,Project Template可以以提供的项目结构和源码为基准,建立新的模板,加 入到主菜单的“File”-> “New Project”的可选项目模板中;Wizard可以帮助你建立一系列向导窗口;Window Component可以用来创建以 TopComponent为子类的窗口类,并且可将对应的窗口对象嵌入到IDE的指定位置;等等。值得注意的是,并不是所有的系统扩展和新功能的添加都能够,或者必须通过模板的方式来实现,如,希 望在Editor中添加代码自动完成的功能,则没有可用的模板,开发者应熟悉在layer.xml中可以添加的系统的扩展点,以及对应的code completion API。

       这里,我们以生成一个新的Action为例。目的是要在系统的工具条上加上一个google搜索栏。当在搜索栏中输入文字,回车后,将调用GoogleAction。因此,在模板列表中选择“ Action...”,出现

 

       我们知道,凡菜单项或者工具条选项,都有两个状态,激活或者禁止。这里界面上有两个选项正是为这两种状态设置。“Always Enabled”,如名称所示,这个Action总是处于可调用的状态,因 而界面上的选项将总是处于激活的状态。而“Conditionally Enabled (use CookieAction)”则是根据当前所选择的界面对象的状态和条件,来决定是否激活Action。关 于界面上看到的DataObject,Node和Cookie等,我们将在后续的内容中介绍。这里简单起见,选择“Always Enabled”,下一步的界面“GUI Registration”:

 

      容易推测出,这一步为Action在界面上注册一个调用入口,并且指定它的位置。它可以是一个菜单项,也可以工具条上的按钮,或者对应某个快捷键等。指定Toolbar和Position之后,在 接下来的步骤中指定类名GoogleAction等信息后,就完成了Action模板的建立。

 

让我们看看Netbeans自动为我们做了什么:

     •GoogleAction.java:新生成的CallableSystemAction的子类。注意,在代码中,I mport了org.openide.util等package。这意味着本 module依赖了其它的module。
      •Layer.xml:被修改。因为增加了新的action以及工具条选项。在之前提到的“FileSystem”下,新增加了一些“folder”和“file”。 这些“ folder”和“file”正描述了这个action以及界面注册的情况等等。
      •Project metadata:被修改。因为增加了新的module dependency。


4. 基于自动生成的模板,运用Netbeans API编写代码,实现所需功能。

       本例将创建JPanel 表单,并和GoogleAction关联。在项目名字上右键弹出的菜单中,选择“new”-> “JPanel Form”,在生成的Panel界面上,建立表单,编 写代码实现Google Search。最后重载GoogleAction类的getToolbarPresenter()方法,将JPanel实例作为返回值,即可。具体的步骤请参见 http://platform.netbeans.org/tutorials/nbm-google.html


5. 测试和安装

      这两步也非常简单。选择右键菜单中的“Install/Reload in Target Platform”,即可以测试。选择“Create NBM”,可以创建可发布的nbm文件, 用户则可以用菜单中的“Tools”->“Update Center”来安装。如果想卸载,则可用“Tools”-> “Module Manager”功能。

这是非常简单的例子,但是在这个例中,我们介绍了plugin module的几种类型,创建和发布plugin module的一般步骤,module的基本组成,如何解决 netbeans module dependency,怎么扩展系统的菜单,以及action API的基本用法。但是,在这个示例的介绍中,也留下了很多疑问,比如什么是 System FileSystem,DataObject,Node等等。在下文中,将比较深入的介绍基于netbeans平台,进行Plugin Module开发所涉及到的基本概念,以及其中API的使用。

 

 

基本概念 & API
我们知道,构建一个框架或者平台软件,必须考虑平台的通用性以及模块和平台之间的接口。当我们学习怎么基于netbeans的平台,做模块开发的时候,不仅 是单纯的开发过程,也 是一个了解netbeans的设计思想的过程。


文件系统 (FileSystem)
       在netbeans 3.x的版本的IDE中,开发者经常要mount“filesystem”到一个虚拟的名字空间中,每个文件系统都有一个根(“root”),所有在这个根下的文件都存于这个名字空间中。在netbeans 4.0以后,对于普通IDE使用者而言,在界面上,文件系统的概念已经不存在了,但是类似的概念仍然存在于IDE的基础架构中,开发plugin module仍需涉及。

       Netbeans中,文件系统(FileSystem)扮演着双重角色:它既可以代表磁盘上的物理文件系统,也可以代表IDE和module的配置数据构成的虚拟的文件系统。基于IDE 的配置数据的文件系统称为“System FileSystem”。在当前的FileSystem API中,可以从一个称为Repository的singleton对象里,用 Repository.getDefault().getDefaultFileSystem()方法获得“System FileSystem”,开发者利用System FileSystem,存取系统的配置数据,以及特定配置目录下的磁盘文件。其它常见的文件系统有JarFileSystemLocalFileSystem等。LocalFileSystem代表本地磁盘的文件系统。无论是物理的,还是虚拟的文件系统,它们都共享FileSystem的抽象特性,以及基于其上的FileObject,DataObject,Node等概念。

       从文件系统中,获得的是FileObject对象,它提供关于文件对象的基础信息(名字,父对象,是否存在等),和在对象上的操作(移动,删除等)。FileObject和 java.io.File的主要不同之一在于FileObject提供了change listener的机制。另外,FileObject并不一定是代表磁盘上的物理文件,这种情况在接下来介绍中,可以看到实例。

物理的文件系统(如LocalFileSystem)是比较好理解的,可以参考相关API。这里,我们更需要关注关于System FileSystem的几个基本问题:
1. Netbeans plugin的配置数据是如何表示的?
2. 配置数据与System FileSystem的关系?
3. System FileSystem的根(“root”)在哪里?
4. System FileSystem的扩展点
5. 如何取得System FileSystem根下的物理文件?
6. 如何取得System FileSystem里的虚拟文件对象?


      上文提到,每个module都是通过一个“layer.xml”文件来保存配置数据,文件中包含一些虚拟的“文件”和“目录”。这些layer.xml合并在一起就构成了System FileSystem。module就是通过这种方式把配置数据加载到系统中。在module的jar文件中的manifest中,指明了这个layer.xml所处的位置,例如:

OpenIDE-Module-Layer: org/myorg/myfirstmodule/layer.xml


根据这个信息,可以在classes目录中找到这个文件。

一个简单的layer.xml文件就像:
<folder name="Actions">   
  <folder name="Edit">       
    <file name="org-myorg-myfirstmodule-GoogleAction.instance"/>   
  </folder>
</folder>

 

<folder name="Toolbars">
  <folder name="Edit">
    <attr name="org-openide-actions-FindAction.instance/org-myorg-myfirstmodule-GoogleAction.shadow" boolvalue="true"/>
    <file name="org-myorg-myfirstmodule-GoogleAction.shadow">
      <attr name="originalFile" stringvalue="Actions/Build/org-myorg-myfirstmodule-GoogleAction.instance"/>
    </file>
  </folder>
</folder>

 

<folder name="myFolder"> 
  <file name="vFile">
     <attr name="attr1" stringvalue="attrValue"/>   
  </file>   
  <file name="myFile.txt" url="resource/realfile"/>
</folder>

其中有一些“folder”是系统预先定义好的,比如,例中的“Actions”和“Toolbars”,当module往IDE的toolbar上添加新的选项的时候,在 “Toolbars”以及它的子目录下,必然出现对应与这个新选项的“file”条目和相关的属性。可以看到,这些预定义的folder正是这个系统的扩展点。

还有一些folder是用户自定义的,比如例中的“myFolder”,注册于System FileSystem中,可以动态保存用户自己的配置或者资源信息。这些信息可以以虚拟文件的形式,存于layer.xml中:

FileObject vFile = Repository.getDefault().getDefaultFileSystem().findResource("myFolder/vFile");
System.out.println("the attr is:" + vFile.getAttribute("attr1"));


程序将打印出"the attr is attrValue"。这是一个在System FileSystem中代表虚拟文件的FileObject的例子。当然,如果需要一个物理文件作为实体,也可以用“ url”属性建立虚拟文件和物理文件之间的映射。如例中的 <file name="myFile.txt" url="resource/realfile"/>。 用下面的代码,程序将获得相对于layer.xml路径的"resource/realfile"物理文件,并且构造出File Object:

FileObject res = Repository.getDefault().getDefaultFileSystem().findResource("myFolder/myFile.txt");

另外,在运行过程中,还可以在System FileSystem的根下,往myFolder目录下添加新的物理目录和文件:

FileObject myFolder = Repository.getDefault().getDefaultFileSystem().getRoot().getFileObject("myFolder");

myFolder.createData("newFile");

程序将获得System FileSystem根下myFolder目录对应的FileObject,并调用createData方法,在这个目录下创建一个“newFile"文件。在 module的开发调试环境中这个“根“位于<myModulePath>\build\testuserdir\config,在以nbm的格式安装下,位 于<userdir>\.netbeans\5.5\config目录下(在windows环境中,<userdir>常见于“C:\Documents and Settings\<useraccount>”)。

更cool的是,在System FileSystem中,可以使FileObject成为java对象的工厂。在上例中以.instance为后缀的文件,就是这样的例子。我们在介绍Data System API的时候会详细介绍。

可见,在netbeans中,文件系统以及文件对象扮演了多种角色,但是它们基于同一抽象的特性,使下面介绍的DataObject,Node等能广泛适用。

 

数据系统 (Data System)
       DataObject是FileObject的wrapper。FileObject代表的是一个类似于文件的实体,而DataObject代表的则是文件内容的模型。文件类型是通过后缀名来标志的,一般来说不同的文件类型对应于不同的DataObject的实现。DataLoader是创建DataObject的工厂类,如果程序需要支持新的文件类型,则应实现相应的DataLoader 和DataObject,并在module的manifest中注册,如:


Name: org/netbeans/modules/povray/PovDataLoader.class
OpenIDE-Module-Class: Loader

如果不需要支持新的文件类型,则不用和DataLoader打交道。只须用下面的方法获得某个fileobject的DataObject:

DataObject.find(someFileObject);

       和DataObject交互的方式是通过getCookie()getLookup()方法。这两个方法应该是可以互换的。getCookie()在将来的netbeans版本中,将被getLookup所替代。注意,这里的Cookie和http协议中的Cookie没有关系。它代表的是这个DataObject能够提供的操作或者服务。用法如下:

OpenCookie open = someDataObject.getCookie (OpenCookie.class); 

  if (open != null) {    

             open.open(); 

     }

       如果在someDataObject上如果支持open操作,则从这个DataObject中获得实现了OpenCookie接口的对象,然后调用这个对象上的open操作。这样,DataObject 中并不实现具体的操作,接口实现代码封装在cookie中,通过getCookie和getLookup(关于Lookup,在后面介绍)的编程接口,提供给调用者。

    前面提到,System FileSystem中,以.instance为后缀的文件构造的FileObject,能成为java对象的工厂。让我们看看是怎么实现的:在例中的lay.xml中,

      

//fo中包含了要构造的对象的类名。 
FileObject fo = Repository.getDefault().getDefaultFileSystem().findResource("Actions/Edit/org-myorg-myfirstmodule-GoogleAction.instance"); 
DataObject dobject = DataObject.find(fo); //根据后缀名,获得DataObject对象。 
InstanceCookie ic = dobject.getCookie(InstanceCookie.class) //从DataObject中,获得InstanceCookie的实现 
if (ic != null) { //如果该DataObject支持创建新的实例的操作 
    GoogleAction myAction = (GoogleAction)ic.createInstance(); //利用Cookie接口,创建Java对象 
} 

  


       和以.instance为后缀名的情况类似,如果文件对象的后缀名是.ser,即对象的串行化的结果文件,也可以通过获得InstanceCookie的方法来创建被串行化的对象。

 

 

 


节点(Nodes)

       前面提到,FileObject是文件的抽象,DataObject是文件内容的模型,而Node是表现层的对象。它们有actions(操作),properties(属性),本地化的显示名称以及图标等等,封装了对象在人机接口方面的特性。需要注意的是,Netbeans 的Node不是GUI对象,和JDK中的TreeNode没有继承关系。但类似的是,它一般来说,也 是一个树型结构,每一个Node有Children对象,提供子节点的列表。


•Node和Lookup
     和DataObject类似,每个Node对象都有一个Lookup对象,来存储上下文相关的信息。对于两者而言,调用者都可以通过getLookup方法获得其上下文以及支持的操作。同样Node也有getCookie方法,但是它的典型实现是,代理给了DataObject的getCookie()方法。

     Node实例中的Lookup对象是如何设置的呢?这里列举几种方法:
- 在API中,有可能用到Node的基本实现类AbstractNode(注意,它其实并不是一个抽象类), 在AbstractNode的构造方法中,可以以Lookup为参数。
- 从DataObject中调用用getNodeDelegate()方法获得的Node对象,可能已经包含了Lookup。
- 子类如从FilterNode继承,则子类的Lookup可以从代理Node对象中获得。
- 子类中重载Node的getLookup方法,返回所需的Lookup对象

至于Lookup的详细说明,以及怎么创建一个Lookup对象,见下章节。


•Node和Explorer Views
      Explorer Views是界面对象,Node可以用不同的View展现出来。这些View对象包含在Explorer & Property Sheet API中,比如BeanTreeView用来显示Node 的树型结构,ListView用来显示Node的列表等。至于怎样用View去展现Node,会在Explorer API中介绍。


•Node和DataObject
      DataObject可以用Node,也可以不用Node来表现。同样,Node后面的数据,可以是DataObject,也可以不是DataObject。但是,在某些场景下是互相关联的,它们之间也有API可以方便的互相转换。比如,希望用界面树型组件来显示磁盘上的某个目录结构,或者System FileSystem中的虚拟目录。很自然,从FileSystem,到 FileObject,DataObject,Node,最后用Explorer View显示出来。

 

 

 

现在我们从编程的角度,看看FileSystem,FileObject, DataObject, Node之间调用和转换关系:

    

//Find a file on disk 
FileObject f = Repository.getDefault().getDefaultFilesystem().getRoot().getFileObject("some/folder/someFile.txt"); 

//or if something passes you a File... 
FileObject f = FileUtil.toFileObject (new File("some/folder/someFile.txt")); 

//Turn a FileObject into a File (may fail for virtual filesystems) 
File f = FileUtil.toFile(someFileObject);
        
//Get the DataObject for a FileObject 
DataObject obj = DataObject.find (someFileObject) ;
        
//Get the FileObject a DataObject represents FileObject 
file = someDataObject.getPrimaryFile(); 

//Get the Node that represents a FileObject 
Node n = someDataObject.getNodeDelegate(); 

//Get the DataObject a Node represents (if any) 
DataObject obj = (DataObject) someNode.getLookup().lookup(DataObject.class);

 

•Node和Action
       Node可以提供一组Actions,这些Actions表示在该node被选中的时候,右键弹出菜单中将显示的项目。在这组Actions中,开发者可以加入一个AbstractAction的子类, 也可以从SystemAction中获得系统菜单中支持的如“delete”,“copy”,“save”等Action。如下例所示:

 

 

 

 

public Action[] getActions(boolean popup) {
    DataFolder df = (DataFolder)getLookup().lookup(DataFolder.class);
    return new Action[] {
        new AddRssAction(df),
        SystemAction.get(DeleteAction.class),
        SystemAction.get(SaveAction.class)
    };


       当该Node被选中,在点击右键弹出的菜单中,会有三个选项,一个是“add”操作,操作的实现在AddRssAction中,该操作是在激活状态,因为AddRssAction就是这 个action的实现;第二个是系统Action中的“delete”操作。操作如“delete”,“copy”,“paste”,“cut”,它们的实现可以由ExploreUtil的工厂方法来提供,它们的激活与否是由ExploreManager以及当前的剪贴板等状态来控制的。第三个选项是系统Action中的“save”操作,它的激活与否在于该Node的Lookup或者 cookie中有没有SaveCookie的实现。简而言之,getActions()返回了右键菜单中的选项,但是这些选项是否激活,取决于运行系统是否能以各种方式获得对应action的实现。

 

Lookup
在DataObject和Node的介绍中,多次提到Lookup。Lookup(
http://www.netbeans.org/download/dev/javadoc/org-openide-util/org/openide/util/Lookup.html)类,是被广泛应用于netbeans API中一个很重要的概念,我们也可以把它看成是Netbeans版本的IoC的实现,它也是各个模块之间实现decouple的关键所在。可以先简单的把它理解为,它是一个Map,其中keys是class对象(常用接口),对应每个key的value,是这个class的实例。一般来说,这个class对象是一个interface,定义了某服务的接口,而class实例,是这个服务的实现。

       在Netbeans中,有两种Lookup的用法。一种是用于从全局的服务登记表中获得某个服务的实现,Lookup.getDefault()调用返回的Lookup对象,即这个全局的服务登记表;另 一种,局部的用法,用来查询某个对象是否支持某个服务,并且获得它所支持的服务的实现。很多类型的对象都有自己的Lookup,比如TopComponent,DataObject,Node等等,可以用类似于obj.getLookup()调用来获得obj的Lookup,这个Lookup对象里存的是当前上下文信息。对于调用者来说,只需获得它所关心的对象的Lookup,从中检索并调用相应的服务。

        这里我们主要探讨一下Lookup的局部用法。用户在IDE环境中的常用操作之一是,移动焦点,并且“选择”某个对象,在这个对象上执行相应操作。当前焦点所在的对象,也称当前选中的对象,活动的对象,或者处于激活状态的对象。Netbeans IDE菜单中的常见功能,比如“打开”,是在当前所选中的对象上执行“打开”操作。显然对于不同的对象,“打开”操作的实现是不一样的。当选择不同的对象的时候,也可见属性窗口中的属性值随着对象的不同而变化。那么在同样一个框架下,如何判断某个特定对象是否支持“打开”这样的公用操作,并且执行操作?当焦点对象变化的时候,框架如何得到通知,从而作相应的变化?在 Netbeans中,Lookup就是解决类似问题的答案。

 

例如下面这个例子就是从Node对象中获得它的Lookup:

      

Node[] n = TopComponent.getRegistry().getActivatedNodes(); 
if (n.length == 1) 
{ 
    OpenCookie oc = (OpenCookie) n[0].getLookup().lookup(OpenCookie.class);
     //返回相应的实现实例 
    if (oc != null) 
    { 
        oc.open(); 
    } 
} 

 

 

       这段代码,就可以用于实现上文描述的关于“打开”操作的场景,Lookup的应用,使得“打开”这个操作不需关心当前活动的节点的内容,也不用关心该节点将怎么执行 “打开”操作,只要这个节点的Lookup中能够检索到OpenCookie接口的实现,调用其open函数即可。这样就实现了模块之间的解耦。关于Lookup的更细节的用法说明,请参见:

 

 

 

 

 

 

 

 

 

 

 

Lookup的基本概念:
http://openide.netbeans.org/lookup/

用Lookup SPI构造自己的Lookup:
http://www.netbeans.org/download/dev/javadoc/org-openide-util/org/openide/util/lookup/doc-files/lookup-spi.html

Lookup API的使用:
http://www.netbeans.org/download/dev/javadoc/org-openide-util/org/openide/util/lookup/doc-files/lookup-api.html
 

Window System
      在Window System API中,最常用到的是TopComponent类,它是可嵌入式的窗口组件的父类,负责跟Netbeans的窗口系统交互。在前面介绍Netbeans提供的代码模板类型中,就提到TopComponent。选择“Window Component”模板自动生成的java类,就是TopComponent的子类。Top component可以是一个单独的窗口,也可以嵌入在tab页中。Netbeans的整个界面环境被划分为不同的部分,每个部分都有名称。比如:“explorer”,一般指位于界面左上角位置的窗口部分,“ Project”,“Files”,“Runtime”这些都是位于“explorer”位置的窗口组件。"Editor",指的是位于界面中间的部分,比如各种编辑器都是位于这个部分。生成新的窗口组件时,会 要求指定窗口所处的位置。

       每一个TopComponent实例都有一个Lookup的对象来标志其上下文,有一组Explorer view对象作为其界面元素,和一个或者多个激活的节点(Nodes)。程序中,一般用associateLookup方法来设置TopComponent的Lookup对象,而当前激活的nodes则一般从下文将要介绍的ExplorerManager中得到。

 

Explorer & Explorer Views
     Nodes提供了一组层次化的对象,而Explorer API则提供了界面对象(Explorer View)来显示这些Nodes。但是Nodes和界面对象view之间,并没有直接的调用关系,它们之间的交互是通过ExplorerManager来实现的。用MVC的观点看,ExplorerManager就是Controller,它提供一个或者多个view要显示的node内容,管理了views之间的共享的状态,如当前选中的节点和它的Lookup。当用户在某个view上的操作导致共享状态的改变时,该view对象会调用ExplorerManager的方法更新共享状态,ExplorerManager再将共享状态的改变通知到其它的view。结合这些概念,来看一段代码:

 

class FeedTopComponent extends TopComponent implements ExplorerManager.Provider
{ 
    private final ExplorerManager manager = new ExplorerManager();
    private final BeanTreeView view = new BeanTreeView(); 
    
    private FeedTopComponent() { 
        //添加视图对象 
        add(view, BorderLayout.CENTER); 
        view.setRootVisible(true); 
        
        try { 
            //控制器中加入节点对象 
            manager.setRootContext(new RssNode.RootRssNode()); 
        } catch (DataObjectNotFoundException ex) { 
            ErrorManager.getDefault().notify(ex);
        } 
        //创建Action集合 
        ActionMap map = getActionMap(); 
        map.put("delete", ExplorerUtils.actionDelete(manager, true)); 
        //将Action集合和控制器添加到Window中 
        associateLookup(ExplorerUtils.createLookup(manager, map)); 
    } 
    
    public ExplorerManager getExplorerManager() 
    { 
        return manager;
    }
    
    ........... 
}

 

 

 

 

 

 

 

 

 

       因为ExplorerManager管理了nodes的状态,所以前面提到能从TopComponent窗口实例中获得一组当前激活的节点,实际上,是从其内置的ExplorerManager 中获得。TopComponent的Lookup对象用ExplorerUtils.createLookup(ExplorerManager,ActionMap)方法创建,使得TopComponent的Lookup实际上,代理给了由ExplorerManager管理下的当前活动节点的Lookup和ActionMap中支持的action操作。

       ActionMap对象维护了action的名字和对应的action实现的映射。示例中,ActionMap添加了“delete”操作,通过把ActionMap和ExplorerManager一起加入到TopComponent的lookup中,使“delete”操作能够在ExplorerManager管理下的nodes上执行。也就是说当某个node选中的时候,系统的“delete”菜单会被激活。

 

其它的API
        Netbeans中还有很多别的API,可以参见Netbeans的网站上javadoc和相关资料。当然关于API的了解,往往也是在实际运用中得到增强的。

 

总结
        本文首先介绍了创建Netbeans Plugin Module的一般步骤,然后就开发过程中要涉及到的基本概念做了探讨,目的在于了解Netbeans Plugin Module开发中的基本思想和要素,为更深入的学习和理解打下一定的基础。

参考文档
http://platform.netbeans.org/tutorials/index.html

http://www.netbeans.org/download/dev/javadoc/


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值