擴展(Extension)是Eclipse中一個關鍵的機制,plug-in利用擴展向Eclipse平臺添加新功能。但是擴展不能隨意地創建,必須按照擴展點(extension point)定義的規範進行明確的聲明,Eclipse才能認出這些擴展。我們不僅可以使用Eclipse提供的衆多現成的擴展點,而且還可以定義新的擴展點,並在該擴展點上進行擴展。
當然,擴展點的定義比較複雜。不過Eclipse爲用戶提供了圖形化的編輯界面,我們只要輸入一些信息,Eclipse就會自動生成代碼,使擴展點的定義變得非常簡單。
下面我們就來看看如何在Eclipse中創建一個新的擴展點,並在這個擴展點上進行擴展。
我們需要做以下的工作:
1.設計該擴展點
2.定義擴展點,即編寫擴展點的清單文件
3.編寫代碼來載入該擴展點的擴展
我們以創建workList擴展點爲例,進行詳細介紹。
worklist完成的功能是:創建一個view,在其中以樹狀顯示系統中可用的功能模塊,通過雙擊某個模塊節點,執行該擴展定義的方法(method)。其實相當於一個控制檯,通過控制檯來運行不同的功能。
由於Eclipse是由一個運行時核心(runtime core)和衆多插件組成的,我們也將workList擴展點定義在一個插件中,有關workList的代碼文件也放在這個插件中,這樣便於查找和修改,也不影響Eclipse本身的代碼。
1. 定義擴展點
首先我們要創建一個存放新擴展點信息的插件net.softapp.worklist,這個插件對org.eclipse.ui.views進行擴展,以下是插件的plugin.xml文件在views擴展點的信息:
<extension
point="org.eclipse.ui.views">
<category
name="WorkListCategory"
id="WorkListCategory"/>
<view
icon="icons/sample.gif"
class="net.softapp.internal.worklist.WorkListView"
category="WorkListCategory"
name="WorkList視圖"
id="net.softapp.internal.worklist.WorkListView"/>
</extension>
這樣就可以通過“window->show view->other”,在彈出的“Show view”對話框中選擇“WorkList視圖”,打開視圖,我們用這個視圖顯示workList擴展點的所有擴展信息。“Show View”對話框顯示了Eclipse中定義所有視圖,即所有org.eclipse.views擴展點的擴展。瞭解這一點很重要,因爲我們在編寫workList擴展點代碼時,就可以模仿甚至拷貝views擴展點的代碼。
下面,我們要在net.softapp.worklist插件中定義workList擴展點。
擴展點的定義文件按照Eclipse的存放方式,一般存放在schema目錄下,我們把文件命名爲worklist.exsd。內容如下,此內容由PDE生成:
<?xml version='1.0' encoding='UTF-8'?>
<!-- Schema file written by PDE -->
<schema targetNamespace="mtn.esip.worklist">
<annotation>
<appInfo>
<meta.schema plugin="net.softapp.worklist" id="workList" name="workList"/>
<!--通過這個定義,我們可以看出,定義的擴展點的id是 net.softapp.worklist.workList,以後引用時要注意,同時注意大小寫-->
</appInfo>
<documentation>
[Enter description of this extension point.]
</documentation>
</annotation>
<element name="extension">
<complexType>
<choice minOccurs="0" maxOccurs="unbounded">
<element ref="category" minOccurs="0" maxOccurs="1"/>
<element ref="worklist" minOccurs="0" maxOccurs="1"/>
</choice>
<attribute name="point" type="string" use="required"><!--定義point-->
<annotation>
<documentation>
</documentation>
</annotation>
</attribute>
<attribute name="id" type="string"><!--定義id-->
<annotation>
<documentation>
</documentation>
</annotation>
</attribute>
<attribute name="name" type="string"><!--定義name-->
<annotation>
<documentation>
</documentation>
</annotation>
</attribute>
</complexType>
</element>
<!--定義category-->
<element name="category">
<complexType>
<attribute name="name" type="string"><!--定義category/name-->
<annotation>
<documentation>
</documentation>
</annotation>
</attribute>
<attribute name="id" type="string"><!--定義category/id。引用category時,必須指出應用的id,而name給出了一個可供顯示的直觀的名字-->
<annotation>
<documentation>
</documentation>
</annotation>
</attribute>
<attribute name="parentCategory" type="string"><!--定義父category,也就是說我們的category可以嵌套形成樹狀結構-->
<annotation>
<documentation>
</documentation>
</annotation>
</attribute>
</complexType>
</element>
<!--定義worklist,注意大小寫-->
<element name="worklist">
<complexType>
<attribute name="name" type="string"><!--定義worklist/name,可供顯示的直觀的名字-->
<annotation>
<documentation>
</documentation>
</annotation>
</attribute>
<attribute name="icon" type="string"><!--定義worklist/icon,可供顯示的直觀的圖標-->
<annotation>
<documentation>
</documentation>
</annotation>
</attribute>
<attribute name="category" type="string">!--定義worklist/category,存放的category位置。如果引用嵌套形式的category,則採用 parent_id/child_id的形式 -->
<annotation>
<documentation>
</documentation>
</annotation>
</attribute>
<attribute name="class" type="string"><!--定義worklist/class,實現功能的類名稱-->
<annotation>
<documentation>
</documentation>
<appInfo>
<meta.attribute kind="java"/>
</appInfo>
</annotation>
</attribute>
<attribute name="id" type="string" use="required"><!--定義worklist/id,唯一標誌-->
<annotation>
<documentation>
</documentation>
</annotation>
</attribute>
</complexType>
</element>
<!--以下內容爲PDE自動生成,與我們的編程無關-->
<annotation>
<appInfo>
<meta.section type="since"/>
</appInfo>
<documentation>
[Enter the first release in which this extension point appears.]
</documentation>
</annotation>
<annotation>
<appInfo>
<meta.section type="examples"/>
</appInfo>
<documentation>
[Enter extension point usage example here.]
</documentation>
</annotation>
<annotation>
<appInfo>
<meta.section type="apiInfo"/>
</appInfo>
<documentation>
[Enter API information here.]
</documentation>
</annotation>
<annotation>
<appInfo>
<meta.section type="implementation"/>
</appInfo>
<documentation>
[Enter information about supplied implementation of this extension point.]
</documentation>
</annotation>
<annotation>
<appInfo>
<meta.section type="copyright"/>
</appInfo>
<documentation>
</documentation>
</annotation>
</schema>
這樣我們就定義好了擴展的屬性。
然後在plugin.xml加入:
<extension-point id="workList" name="workList" schema="schema/workList.exsd"/>
就定義好了!
2. 實現擴展
定義完擴展之後,接下來要編寫解析此擴展的相關代碼。可喜的是,Eclipse爲我們提供了大量的API可以調用,省下了若干代碼的編寫。另外我們還可以借鑑Eclipse實現的其他代碼,通過模仿來編寫我們自己的解析代碼。本例參考了View的解析部分。同View,我們定義了WorkListDescriptor,WorkListRegistry,WorkListRegistryReader.其中WorkListDescriptor完成對上述定義的解析,WorkListRegistry存放了其他插件對workList擴展的相關信息,WorkListRegistryReader則從WorkListRegistry讀取信息供我們使用。
此處代碼從略,具體請參考View實現部分的ViewDescriptor,ViewRegistry,ViewRegistryReader相關代碼。
3. 編寫界面部分
根據1對View的擴展,我們需要編寫界面部分。此處請參考View插件的編寫。我們在此對WorkListPlugin添加了一個方法用以從註冊表中讀取擴展信息:
public IWorkListRegistry getWorkListRegistry() {
if (workListRegistry == null) {
workListRegistry = new WorkListRegistry();
try {
WorkListRegistryReader reader = new WorkListRegistryReader();
reader.readWorkList(Platform.getExtensionRegistry(), workListRegistry);
} catch (CoreException e) {
// cannot safely show a dialog so log it
WorkbenchPlugin.log("Unable to read workList registry.", e.getStatus()); //$NON-NLS-1$
}
}
return workListRegistry;
}
其中WorkListRegistryReader.readWorkList定義如下:
/**
* Read the workList extensions within a registry.
*/
public void readWorkList(IExtensionRegistry in, WorkListRegistry out)
throws CoreException {
// this does not seem to really ever be throwing an the exception
workListRegistry = out;
readRegistry(in, WorkListPlugin.getDefault().getPluginId(), "workList");
out.mapWorkListToCategories();
}
可見,我們不需要編寫複雜的代碼就可以讀取註冊表中存放的擴展信息!
我們對workList擴展的顯示是採用了TreeView,代碼如下(WorkListView):
protected TreeViewer createViewer(Composite parent) {
TreeViewer viewer =
new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
viewer.setUseHashlookup(true);
viewer.setContentProvider(new WorkListContentProvider());
viewer.setLabelProvider(new WorkListLabelProvider());
workListReg = WorkListPlugin.getDefault().getWorkListRegistry();
viewer.setInput(workListReg);
initListeners(viewer);
return viewer;
}
這樣,就可以實現顯示了。
那麼,怎樣實現選擇某個擴展後,通過雙擊執行其功能呢?我們對TreeViewer添加了鼠標雙擊事件支持,關鍵代碼如下:
protected void handleDoubleClick(DoubleClickEvent event) {
IStructuredSelection selection = (IStructuredSelection) event.getSelection();
Object element = selection.getFirstElement();
TreeViewer viewer = getWorkListViewer();
if (viewer.isExpandable(element)) {
viewer.setExpandedState(element, !viewer.getExpandedState(element));
}else {
WorkListDescriptor workList = (WorkListDescriptor)element;
try {
IWorkListPart workListPart = (IWorkListPart) workList.createWorkList();
workListPart.run();
} catch (CoreException e) {
// should add something to handle the exception
}
}
}
其中IWorkListPart很簡單,使所有實現workList擴展必須實現的接口:
public interface IWorkListPart {
public void run();
}
只有一個run方法(可以自行添加其他的支持)。
其中WorkListDescriptor.createWorkList方法實現根據class的字符串創建一個對象,也是超級簡單,因爲Eclipse已經爲我們編好了:
public Object createWorkList() throws CoreException {
Object obj = WorkbenchPlugin.createExtension(configElement, "class");
return obj;
}
這樣就可以執行擴展的功能了。
但是別忘了,還要編寫pluin.xml,否則Eclipse可不認吆:
<extension
point="net.softapp.worklist.workList">
<category
name="HelloTest"
id="HelloTest"/>
<worklist
icon="icons/example.ico"
class="net.softapp.internal.worklist.Hello"
category="HelloTest"
name="Hello"
id="net.softapp.internal.worklist.Hello"/>
</extension>
4.測試新擴展點
OK,開始運行Eclipse的plugin調試環境,打開WorkList視圖,看看在樹狀列表裏是不是有一個HelloTest目錄,下面有Hello。雙擊它,你編寫的代碼出來了吧!