Eclipse插件開發之定制向導
以前我有一個微型的便攜式電子地址薄。我一直認為它很不錯,直到有一天它停止運行了。銷售該產品的人員無法找回我的聯系地址名冊,卻提議更換一臺。這時候我才知道數據的重要性。這個閃亮的小發明與存儲在它里面的數據相比根本就不值一提。
在這個序列文章的第一部分中,我介紹了Eclipse插件的開發環境,并開發了一個簡單的插件(見天極網《Eclipse插件開發快速入門》)。在第二部分,我添加了工具條按鈕、菜單項和對話框(見天極網《Eclipse插件開發之添加簡單的GUI元素》)。它實際上沒有實現任何具體功能。它簡單地用某種字體顯示了示例文本內容。現在我們要讓它能夠管理實際的數據。我們將修改這個插件,讓它實現我們所需要的功能。本文討論的是編輯器文檔,并演示了如何定制一個向導。
Invokatron的歷史
首先,我們詳細說明一下Invokatron本身。在前面的文章中我們討論過,Invokatron是一個生成Java代碼的的圖形工具。你可以簡單地通過拖放操作建立類的方法。拖入的方法被編輯的方法(也就是插件)"調用"。我們將讓數據來驅動應用程序的設計。在后面一篇文章中,我們將開發這個GUI。現在我們需要做的是,找到插件將輸入和存儲的重要數據。它通常被稱為應用程序的模型(model)。在設計這個系統的時候,我們需要考慮下面一些內容:
· 哪些細節數據需要保存?
· 這些數據在內存中用什么來表現?POJO、JavaBean還是EJB?
· 這些數據的存儲格式是怎樣的?數據庫表、XML文件、屬性文件還是串行二進制文件?
· 輸入數據的方式有哪幾種?用"新建文件"向導還是在文檔屬性頁面上使用彈出對話框、用編輯器繪制、在文本編輯器中輸入的其它向導?
在我們繼續工作之前必須回答這些問題。不可能有適合所有項目的答案;它完全依賴于你的需求。在我們的例子中,我做出了一些隨意的、可能有問題的決定,如下所示:
· 一個Java類,它包含類名、程序包、超類(superclass)和實現接口。我們以它為基礎,在后面的文章中添加更多數據。
· 我將把數據表現為擴展Properties類的類。它建立了編輯器的"文檔類"。
· 我將使用的格式是屬性文件,很容易使用Properties類來分析它。
· 在"新建文件"向導中,我將先尋找數據,接著讓用戶改變屬性窗口或文本編輯器中的數據。這個步驟將在下一篇文章中完成。
Document(文檔)類
下一步是編寫文檔類。建立一個新程序包(invokatron.model)和一個新類(InvokatronDocument)。下面是我們的文檔類的開頭:
使用Properties類可以更簡單地分析和保存我們的數據。Getter和 setter不是必須的,但是如果你想要,也可以加上它們。這個類還沒有完成;我們將添加一個接口,在后面的部分中Eclipse需要使用它。
有了這個類之后,我們要獲取一個屬性就非常簡單了:
定制向導
請看一看前面的文章中所出現的向導。你應該記得,我們可以通過點擊(我們自己添加的)工具條按鈕或者菜單項來訪問它。圖1是它的界面:
它只有一個頁面,右上角沒有圖片。我們想輸入更多的信息,并提供一個很好的圖片。換句話說,我們希望定制這個向導。
我們來分析一下這個向導。請打開InvokatronWizard.java文件。請注意這個類是如何擴展Wizard并實現INewWizard接口的。你應該理解它里面的很多方法。為了定制向導,我們簡單地調用或重載其中的某些方法。下面是一些重要的方法:
生命周期方法
我們應該重載這些方法,把初始化和析構(destruction)代碼插入向導中:
· Constructor(構造函數):向導實例化的時候、在Eclipse給它傳遞信息之前調用。向導的一般初始化實現。通常你希望調用"美化方法"(后面有描述)并設置對話框的默認值。
· init(IWorkbench workbench, IStructuredSelection editorSelection): Eclipse調用它為向導提供工作臺的信息。請重載它,保存IWorkbench和對象的句柄供以后使用。如果它是一個編輯器向導而不是新向導,我們最好把當前的編輯器選項作為第二個參數。
· dispose():Eclipse調用它執行清理工作。重載它來清除向導使用的資源。
· finalize():清除代碼,可能使用dispose()代替。
美化方法
這些方法都是用于裝飾向導窗體的。
· setWindowTitle(String title):設置窗體的標題行字符串。
· setDefaultPageImageDescriptor(ImageDescriptor image):用于提供顯示在向導的所有頁面右上方的圖片。
· setTitleBarColor(RGB color):指定標題欄用什么顏色。
按鈕方法
這些方法控制著向導按鈕的實用性和行為。
· boolean canFinish():重載它用于指定Finish(完成)按鈕是否激活(根據向導的狀態)。
· boolean performFinish():重載它來實現向導的根本的業務邏輯。如果向導沒有完成(錯誤的條件),就返回false。
· boolean performCancel():重載它,在用戶點擊Cancel(取消)按鈕的時候進行清除操作。如果向導不能終止,則返回false。
· boolean isHelpAvailable():重載它用于指定Help(幫助)按鈕是否可視。
· boolean needsPreviousAndNextButtons():重載它來指定Previous(前一步)和Next(后一步)按鈕是否可視。
· boolean needsProgressMonitor():重載它來指定進度條部件是否可視。當點擊Finish按鈕調用performFinish()方法的時候,它就會出現。
頁面方法
這些方法控制著頁面的外觀。
· addPages():向導顯示的時候調用。重載它給向導插入新頁面。
· createPageControls(Composite pageContainer):Eclipse調用它來實例化所有的向導頁面(用前面的addPages()方法已經添加的頁面)。重載它給向導添加持續可視的窗體小部件(除頁面之外的部件)。
· IWizardPage getStartingPage():重載它來檢測哪個頁面是向導的第一個頁面。
· IWizardPage getNextPage(IWizardPage nextPage):在默認情況下,點擊Next按鈕將進入addPages()所提供的數組中的下一個頁面。你可能希望根據用戶選擇進入不同的頁面。重載它來計算后一個頁面。
· IWizardPage getPreviousPage(IWizardPage previousPage):與getNextPage()類似,用于計算前一個頁面。
· int getPageCount():檢索addPages()添加的頁面的數量。在典型情況下,你不必重載它,除非你希望顯示頁面的數量和形式。
其它有用的方法
這些都是有用的輔助方法:
· setDialogSettings(IDialogSettings settings):你可以載入對話框的狀態,并通過在init()中調用這個方法來設置這些值。在典型情況下,這些設置可以作為向導字段的默認值。請查看DialogSettings類了解更詳細的信息。
· IDialogSettings getDialogSettings():當我們需要數據的時候,就調用這個方法來檢索它。在performFinish()的對話框的末尾,你再次可以把數據保存到文件中。
· IWizardContainer getContainer():對于檢索Shell、運行的后臺線程、刷新窗口等非常有用。
向導頁面方法
你已經看到了,向導是由一個或多個頁面組成的。這些頁面擴展了WizardPage類,并實現了IWizardPage接口。為了定制單獨的頁面,你必須了解很多方法。下面是一些重要的方法:
· Constructor:用于實例化頁面。
· dispose():重載它用于實現清除代碼。
· createControl(Composite parent):重載它來給頁面添加控件。
· IWizard getWizard():用于獲取父向導對象。對于調用getDialogSettings()是有用處的。
· setTitle(String title):調用它來設置顯示在向導標題區域中的字符串。
· setDescription(String description):調用它來提供標題下面顯示的文本內容。
· setImageDescriptor(ImageDescriptor image):調用它來提供頁面右上方出現的圖片(用于代替默認的圖片)。
· setMessage(String message):調用它來顯示描述字符串下方的消息文本。這些文本是用于警告或提示用戶的。
· setErrorMessage(String error):調用它來高亮度顯示描述字符串下方的消息文本。它一般意味著向導不能繼續,除非錯誤被修正。
· setPageComplete(boolean complete):如果為true,Next按鈕就可視。
· performHelp():重載它來提供內容敏感的幫助信息。當點擊Help按鈕的時候向導會調用它。
編寫向導的代碼
有了這些方法之后,我們就能夠開發出具有極大的靈活性的向導了。我們現在修改以前建立的Invokatron向導,給它添加一個頁面來請求用戶輸入初始的文檔數據。我們還給向導添加了一個圖片。新代碼是粗體的:
在構造函數中,我們打開了進度條,并設置了向導的圖片。你可以下載并保存下面的圖片:
請把這個圖片保存在Invokatron/icons文件夾之下。為了更容易載入這個圖片,我們使用了便捷的AbstractUIPlugin.imageDescriptorFromPlugin()方法。
請注意:你應該知道,盡管這個向導是INewWizard類型的,但是并非所有的向導都是用于建立新文檔的。你可以參考其它一些資料來了解如何建立"獨立的"向導的信息。
下面是addPages()方法:
在這個方法中,我們添加了一個新頁面(InvokatronWizardPage2),我們在后面編輯它。下面是用戶點擊向導的"完成"按鈕的時候執行的一些方法:
為了保存數據,我們必須做一個后臺事務。該事務是由向導的容器(Eclipse工作臺)來執行的,并且必須實現IRunnableWithProgress接口,包含(唯一)一個run()方法。傳遞進來的IProgressMonitor允許我們報告事務的進度。實際的數據保存工作在一個輔助方法(doFinish())中進行:
我們還做了很多工作:
· 我們檢索了自己希望保存文件的位置(用Eclipse的IFile類)。
· 我們還獲取了該File。
· 我們把屬性保存到了這個位置。
· 接著我們讓Eclipse工作臺刷新項目,這樣就可以顯示該文件了。
· 我們最后調度了一個事務,它在以后執行。這個事務包括在編輯器中打開那個新文件。
· 在整個過程中,我們通過調用IProgressMonitor對象(它是作為參數傳遞進來的)的方法來提示用戶目前的進展情況。
最后一個方法是一個輔助的方法,當該文件保存失敗的時候,它在向導中顯示錯誤信息:
向導可以捕獲CoreException異常,接著可以把它所包含的Status對象顯示給用戶看。向導不會被關閉。
編寫新的向導頁面的代碼
下一步,我們編寫InvokatronWizardPage2。它的整個類都是全新的:
上面的構造函數設置了頁面的標題(在標題欄下方高亮度顯示)和描述(在頁面標題的下方顯示)。我們還有一些輔助方法。 updateStatus處理頁面特定的錯誤信息的顯示。如果沒有錯誤信息,就意味著頁面完成了;因此,"下一步"按鈕就可以使用了。還有數據字段內容的getter(獲取)方法。下面是createControl()方法,它建立了頁面的所有可視化組件:
為了編寫這段代碼,你必須了解SWT(請你自己查看一些這方面的資料)。基本上,這個方法建立了標簽和字段,并把它們放置到網格布局上。字段發生改變的時候,就調用dialogChanged()來驗證它的數據:
這個工作是在三個工具類--PackageValidator、SuperclassValidator和 InterfacesValidator的幫助下完成的。接下來我們編寫這些類。
驗證類
驗證可以在插件的用戶輸入數據的任何部分中進行。因此,把驗證代碼放入可重復使用的類中是有意義的,這樣就不用把它復制到多個位置。下面是一個驗證類的例子。
其它的驗證類與它非常類似。
Eclipse類庫中的另外一個極好的類是JavaConventions,它為我們驗證數據!它包含了很多驗證方法,例如:
· validateJavaTypeName() 檢查類和接口的名稱。
· validatePackageName() 檢查程序包的名稱。
· validateFieldName() 檢查數據成員的名稱。
· validateMethodName() 檢查方法的名稱。
· validateIdentifierName() 檢查變量的名稱。
現在我們不需要ICellEditorValidator接口,但是在以后的文章中,我們是需要它的。
結果
到目前為止,我們擁有了一個可以工作的向導,它擁有一張圖片和兩個頁面,第二個頁面建立了原來的Invokatron文檔。圖2顯示了結果:
閃亮的發明
我們可以看到,通常是數據驅動應用程序的。外表(Presentation)也是很重要的。丑陋的發明難以出售,但是閃亮的發明可能容易出售。但是數據是我們這些程序員實現的非常本質的東西。
在本文中,我們首先決定了自己將處理的數據。然后,我們以定制向導的方式來獲取這些數據。下一篇文章將繼續講解顯示的問題,包括定制的編輯器和屬性頁面。
在這個序列文章的第一部分中,我介紹了Eclipse插件的開發環境,并開發了一個簡單的插件(見天極網《Eclipse插件開發快速入門》)。在第二部分,我添加了工具條按鈕、菜單項和對話框(見天極網《Eclipse插件開發之添加簡單的GUI元素》)。它實際上沒有實現任何具體功能。它簡單地用某種字體顯示了示例文本內容。現在我們要讓它能夠管理實際的數據。我們將修改這個插件,讓它實現我們所需要的功能。本文討論的是編輯器文檔,并演示了如何定制一個向導。
Invokatron的歷史
首先,我們詳細說明一下Invokatron本身。在前面的文章中我們討論過,Invokatron是一個生成Java代碼的的圖形工具。你可以簡單地通過拖放操作建立類的方法。拖入的方法被編輯的方法(也就是插件)"調用"。我們將讓數據來驅動應用程序的設計。在后面一篇文章中,我們將開發這個GUI。現在我們需要做的是,找到插件將輸入和存儲的重要數據。它通常被稱為應用程序的模型(model)。在設計這個系統的時候,我們需要考慮下面一些內容:
· 哪些細節數據需要保存?
· 這些數據在內存中用什么來表現?POJO、JavaBean還是EJB?
· 這些數據的存儲格式是怎樣的?數據庫表、XML文件、屬性文件還是串行二進制文件?
· 輸入數據的方式有哪幾種?用"新建文件"向導還是在文檔屬性頁面上使用彈出對話框、用編輯器繪制、在文本編輯器中輸入的其它向導?
在我們繼續工作之前必須回答這些問題。不可能有適合所有項目的答案;它完全依賴于你的需求。在我們的例子中,我做出了一些隨意的、可能有問題的決定,如下所示:
· 一個Java類,它包含類名、程序包、超類(superclass)和實現接口。我們以它為基礎,在后面的文章中添加更多數據。
· 我將把數據表現為擴展Properties類的類。它建立了編輯器的"文檔類"。
· 我將使用的格式是屬性文件,很容易使用Properties類來分析它。
· 在"新建文件"向導中,我將先尋找數據,接著讓用戶改變屬性窗口或文本編輯器中的數據。這個步驟將在下一篇文章中完成。
Document(文檔)類
下一步是編寫文檔類。建立一個新程序包(invokatron.model)和一個新類(InvokatronDocument)。下面是我們的文檔類的開頭:
| public class InvokatronDocument extends Properties { public static final String PACKAGE = "package"; public static final String SUPERCLASS = "superclass"; public static final String INTERFACES = "interfaces"; } |
使用Properties類可以更簡單地分析和保存我們的數據。Getter和 setter不是必須的,但是如果你想要,也可以加上它們。這個類還沒有完成;我們將添加一個接口,在后面的部分中Eclipse需要使用它。
有了這個類之后,我們要獲取一個屬性就非常簡單了:
| String package =document.getProperty(InvokatronDocument.PACKAGE); |
定制向導
請看一看前面的文章中所出現的向導。你應該記得,我們可以通過點擊(我們自己添加的)工具條按鈕或者菜單項來訪問它。圖1是它的界面:
![]() 圖1:舊的向導 |
它只有一個頁面,右上角沒有圖片。我們想輸入更多的信息,并提供一個很好的圖片。換句話說,我們希望定制這個向導。
我們來分析一下這個向導。請打開InvokatronWizard.java文件。請注意這個類是如何擴展Wizard并實現INewWizard接口的。你應該理解它里面的很多方法。為了定制向導,我們簡單地調用或重載其中的某些方法。下面是一些重要的方法:
生命周期方法
我們應該重載這些方法,把初始化和析構(destruction)代碼插入向導中:
· Constructor(構造函數):向導實例化的時候、在Eclipse給它傳遞信息之前調用。向導的一般初始化實現。通常你希望調用"美化方法"(后面有描述)并設置對話框的默認值。
· init(IWorkbench workbench, IStructuredSelection editorSelection): Eclipse調用它為向導提供工作臺的信息。請重載它,保存IWorkbench和對象的句柄供以后使用。如果它是一個編輯器向導而不是新向導,我們最好把當前的編輯器選項作為第二個參數。
· dispose():Eclipse調用它執行清理工作。重載它來清除向導使用的資源。
· finalize():清除代碼,可能使用dispose()代替。
美化方法
這些方法都是用于裝飾向導窗體的。
· setWindowTitle(String title):設置窗體的標題行字符串。
· setDefaultPageImageDescriptor(ImageDescriptor image):用于提供顯示在向導的所有頁面右上方的圖片。
· setTitleBarColor(RGB color):指定標題欄用什么顏色。
按鈕方法
這些方法控制著向導按鈕的實用性和行為。
· boolean canFinish():重載它用于指定Finish(完成)按鈕是否激活(根據向導的狀態)。
· boolean performFinish():重載它來實現向導的根本的業務邏輯。如果向導沒有完成(錯誤的條件),就返回false。
· boolean performCancel():重載它,在用戶點擊Cancel(取消)按鈕的時候進行清除操作。如果向導不能終止,則返回false。
· boolean isHelpAvailable():重載它用于指定Help(幫助)按鈕是否可視。
· boolean needsPreviousAndNextButtons():重載它來指定Previous(前一步)和Next(后一步)按鈕是否可視。
· boolean needsProgressMonitor():重載它來指定進度條部件是否可視。當點擊Finish按鈕調用performFinish()方法的時候,它就會出現。
頁面方法
這些方法控制著頁面的外觀。
· addPages():向導顯示的時候調用。重載它給向導插入新頁面。
· createPageControls(Composite pageContainer):Eclipse調用它來實例化所有的向導頁面(用前面的addPages()方法已經添加的頁面)。重載它給向導添加持續可視的窗體小部件(除頁面之外的部件)。
· IWizardPage getStartingPage():重載它來檢測哪個頁面是向導的第一個頁面。
· IWizardPage getNextPage(IWizardPage nextPage):在默認情況下,點擊Next按鈕將進入addPages()所提供的數組中的下一個頁面。你可能希望根據用戶選擇進入不同的頁面。重載它來計算后一個頁面。
· IWizardPage getPreviousPage(IWizardPage previousPage):與getNextPage()類似,用于計算前一個頁面。
· int getPageCount():檢索addPages()添加的頁面的數量。在典型情況下,你不必重載它,除非你希望顯示頁面的數量和形式。
其它有用的方法
這些都是有用的輔助方法:
· setDialogSettings(IDialogSettings settings):你可以載入對話框的狀態,并通過在init()中調用這個方法來設置這些值。在典型情況下,這些設置可以作為向導字段的默認值。請查看DialogSettings類了解更詳細的信息。
· IDialogSettings getDialogSettings():當我們需要數據的時候,就調用這個方法來檢索它。在performFinish()的對話框的末尾,你再次可以把數據保存到文件中。
· IWizardContainer getContainer():對于檢索Shell、運行的后臺線程、刷新窗口等非常有用。
向導頁面方法
你已經看到了,向導是由一個或多個頁面組成的。這些頁面擴展了WizardPage類,并實現了IWizardPage接口。為了定制單獨的頁面,你必須了解很多方法。下面是一些重要的方法:
· Constructor:用于實例化頁面。
· dispose():重載它用于實現清除代碼。
· createControl(Composite parent):重載它來給頁面添加控件。
· IWizard getWizard():用于獲取父向導對象。對于調用getDialogSettings()是有用處的。
· setTitle(String title):調用它來設置顯示在向導標題區域中的字符串。
· setDescription(String description):調用它來提供標題下面顯示的文本內容。
· setImageDescriptor(ImageDescriptor image):調用它來提供頁面右上方出現的圖片(用于代替默認的圖片)。
· setMessage(String message):調用它來顯示描述字符串下方的消息文本。這些文本是用于警告或提示用戶的。
· setErrorMessage(String error):調用它來高亮度顯示描述字符串下方的消息文本。它一般意味著向導不能繼續,除非錯誤被修正。
· setPageComplete(boolean complete):如果為true,Next按鈕就可視。
· performHelp():重載它來提供內容敏感的幫助信息。當點擊Help按鈕的時候向導會調用它。
編寫向導的代碼
有了這些方法之后,我們就能夠開發出具有極大的靈活性的向導了。我們現在修改以前建立的Invokatron向導,給它添加一個頁面來請求用戶輸入初始的文檔數據。我們還給向導添加了一個圖片。新代碼是粗體的:
| public class InvokatronWizard extends Wizard implements INewWizard { private InvokatronWizardPage page; private InvokatronWizardPage2 page2; private ISelection selection; public InvokatronWizard() { super(); setNeedsProgressMonitor(true); ImageDescriptor image =AbstractUIPlugin.imageDescriptorFromPlugin("Invokatron", "icons/InvokatronIcon32.GIF"); setDefaultPageImageDescriptor(image); } public void init(IWorkbench workbench,IStructuredSelection selection) { this.selection = selection; } |
在構造函數中,我們打開了進度條,并設置了向導的圖片。你可以下載并保存下面的圖片:
![]() |
請把這個圖片保存在Invokatron/icons文件夾之下。為了更容易載入這個圖片,我們使用了便捷的AbstractUIPlugin.imageDescriptorFromPlugin()方法。
請注意:你應該知道,盡管這個向導是INewWizard類型的,但是并非所有的向導都是用于建立新文檔的。你可以參考其它一些資料來了解如何建立"獨立的"向導的信息。
下面是addPages()方法:
| public void addPages() { page=new InvokatronWizardPage(selection); addPage(page); page2 = new InvokatronWizardPage2(selection); addPage(page2); } |
在這個方法中,我們添加了一個新頁面(InvokatronWizardPage2),我們在后面編輯它。下面是用戶點擊向導的"完成"按鈕的時候執行的一些方法:
| public boolean performFinish() { //首先把所有的頁面數據保存在變量中 final String containerName = page.getContainerName(); final String fileName =page.getFileName(); final InvokatronDocument properties = new InvokatronDocument(); properties.setProperty(InvokatronDocument.PACKAGE,page2.getPackage()); properties.setProperty(InvokatronDocument.SUPERCLASS,page2.getSuperclass()); properties.setProperty(InvokatronDocument.INTERFACES,page2.getInterfaces()); //現在調用完成(finish)方法 IRunnableWithProgress op =new IRunnableWithProgress() { public void run(IProgressMonitor monitor) throws InvocationTargetException { try { doFinish(containerName, fileName,properties,monitor); } catch (CoreException e) { throw new InvocationTargetException(e); } finally { monitor.done(); } } }; try { getContainer().run(true, false, op); } catch (InterruptedException e) { return false; } catch (InvocationTargetException e) { Throwable realException =e.getTargetException(); MessageDialog.openError(getShell(),"Error",realException.getMessage()); return false; } return true; } |
為了保存數據,我們必須做一個后臺事務。該事務是由向導的容器(Eclipse工作臺)來執行的,并且必須實現IRunnableWithProgress接口,包含(唯一)一個run()方法。傳遞進來的IProgressMonitor允許我們報告事務的進度。實際的數據保存工作在一個輔助方法(doFinish())中進行:
| private void doFinish(String containerName,String fileName, Properties properties, IProgressMonitor monitor) throws CoreException { // 建立一個示例文件 monitor.beginTask("Creating " + fileName, 2); IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); IResource resource = root.findMember(new Path(containerName)); if (!resource.exists() || !(resource instanceof IContainer)) { throwCoreException("Container "" + containerName + "" does not exist."); } IContainer container =(IContainer)resource; final IFile iFile = container.getFile(new Path(fileName)); final File file =iFile.getLocation().toFile(); try { OutputStream os = new FileOutputStream(file, false); properties.store(os, null); os.close(); } catch (IOException e) { e.printStackTrace(); throwCoreException("Error writing to file " + file.toString()); } //確保項目已經刷新了,該文件在Eclipse API 之外建立 container.refreshLocal(IResource.DEPTH_INFINITE, monitor); monitor.worked(1); monitor.setTaskName("Opening file for editing..."); getShell().getDisplay().asyncExec(new Runnable() { public void run() { IWorkbenchPage page =PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); try { IDE.openEditor(page,iFile,true); } catch (PartInitException e) { } } }); monitor.worked(1); } |
我們還做了很多工作:
· 我們檢索了自己希望保存文件的位置(用Eclipse的IFile類)。
· 我們還獲取了該File。
· 我們把屬性保存到了這個位置。
· 接著我們讓Eclipse工作臺刷新項目,這樣就可以顯示該文件了。
· 我們最后調度了一個事務,它在以后執行。這個事務包括在編輯器中打開那個新文件。
· 在整個過程中,我們通過調用IProgressMonitor對象(它是作為參數傳遞進來的)的方法來提示用戶目前的進展情況。
最后一個方法是一個輔助的方法,當該文件保存失敗的時候,它在向導中顯示錯誤信息:
| private void throwCoreException(String message) throws CoreException { IStatus status =new Status(IStatus.ERROR,"Invokatron",IStatus.OK,message,null); throw new CoreException(status); } } |
向導可以捕獲CoreException異常,接著可以把它所包含的Status對象顯示給用戶看。向導不會被關閉。
編寫新的向導頁面的代碼
下一步,我們編寫InvokatronWizardPage2。它的整個類都是全新的:
| public class InvokatronWizardPage2 extends WizardPage { private Text packageText; private Text superclassText; private Text interfacesText; private ISelection selection; public InvokatronWizardPage2(ISelection selection) { super("wizardPage2"); setTitle("Invokatron Wizard"); setDescription("This wizard creates a new"+" file with *.invokatron extension."); this.selection = selection; } private void updateStatus(String message) { setErrorMessage(message); setPageComplete(message == null); } public String getPackage() { return packageText.getText(); } public String getSuperclass() { return superclassText.getText(); } public String getInterfaces() { return interfacesText.getText(); } |
上面的構造函數設置了頁面的標題(在標題欄下方高亮度顯示)和描述(在頁面標題的下方顯示)。我們還有一些輔助方法。 updateStatus處理頁面特定的錯誤信息的顯示。如果沒有錯誤信息,就意味著頁面完成了;因此,"下一步"按鈕就可以使用了。還有數據字段內容的getter(獲取)方法。下面是createControl()方法,它建立了頁面的所有可視化組件:
| public void createControl(Composite parent) { Composite controls =new Composite(parent, SWT.NULL); GridLayout layout = new GridLayout(); controls.setLayout(layout); layout.numColumns = 3; layout.verticalSpacing = 9; Label label =new Label(controls, SWT.NULL); label.setText("&Package:"); packageText = new Text(controls,SWT.BORDER | SWT.SINGLE); GridData gd = new GridData(GridData.FILL_HORIZONTAL); packageText.setLayoutData(gd); packageText.addModifyListener( new ModifyListener() { public void modifyText(ModifyEvent e) { dialogChanged(); } }); label = new Label(controls, SWT.NULL); label.setText("Blank = default package"); label = new Label(controls, SWT.NULL); label.setText("&Superclass:"); superclassText = new Text(controls,SWT.BORDER | SWT.SINGLE); gd = new GridData(GridData.FILL_HORIZONTAL); superclassText.setLayoutData(gd); superclassText.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { dialogChanged(); } }); label = new Label(controls, SWT.NULL); label.setText("Blank = Object"); label = new Label(controls, SWT.NULL); label.setText("&Interfaces:"); interfacesText = new Text(controls,SWT.BORDER | SWT.SINGLE); gd = new GridData(GridData.FILL_HORIZONTAL); interfacesText.setLayoutData(gd); interfacesText.addModifyListener( new ModifyListener() { public void modifyText(ModifyEvent e) { dialogChanged(); } }); label = new Label(controls, SWT.NULL); label.setText("Separated by ','"); dialogChanged(); setControl(controls); } |
為了編寫這段代碼,你必須了解SWT(請你自己查看一些這方面的資料)。基本上,這個方法建立了標簽和字段,并把它們放置到網格布局上。字段發生改變的時候,就調用dialogChanged()來驗證它的數據:
| private void dialogChanged() { String aPackage = getPackage(); String aSuperclass = getSuperclass(); String interfaces = getInterfaces(); String status = new PackageValidator().isValid(aPackage); if(status != null) {updateStatus(status); return; } status = new SuperclassValidator().isValid(aSuperclass); if(status != null) {updateStatus(status); return; } status = new InterfacesValidator().isValid(interfaces); if(status != null) {updateStatus(status); return; } updateStatus(null); } } |
這個工作是在三個工具類--PackageValidator、SuperclassValidator和 InterfacesValidator的幫助下完成的。接下來我們編寫這些類。
驗證類
驗證可以在插件的用戶輸入數據的任何部分中進行。因此,把驗證代碼放入可重復使用的類中是有意義的,這樣就不用把它復制到多個位置。下面是一個驗證類的例子。
| public class InterfacesValidator implements ICellEditorValidator { public String isValid(Object value) { if( !( value instanceof String) ) return null; String interfaces = ((String)value).trim(); if( interfaces.equals("")) return null; String[] interfaceArray = interfaces.split(","); for (int i = 0; i < interfaceArray.length; i++) { IStatus status = JavaConventions.validateJavaTypeName(interfaceArray[i]); if (status.getCode() != IStatus.OK) return "Validation of interface " + interfaceArray[i] + ": " + status.getMessage(); } return null; } } |
其它的驗證類與它非常類似。
Eclipse類庫中的另外一個極好的類是JavaConventions,它為我們驗證數據!它包含了很多驗證方法,例如:
· validateJavaTypeName() 檢查類和接口的名稱。
· validatePackageName() 檢查程序包的名稱。
· validateFieldName() 檢查數據成員的名稱。
· validateMethodName() 檢查方法的名稱。
· validateIdentifierName() 檢查變量的名稱。
現在我們不需要ICellEditorValidator接口,但是在以后的文章中,我們是需要它的。
結果
到目前為止,我們擁有了一個可以工作的向導,它擁有一張圖片和兩個頁面,第二個頁面建立了原來的Invokatron文檔。圖2顯示了結果:
![]() 圖2:定制的向導 |
閃亮的發明
我們可以看到,通常是數據驅動應用程序的。外表(Presentation)也是很重要的。丑陋的發明難以出售,但是閃亮的發明可能容易出售。但是數據是我們這些程序員實現的非常本質的東西。
在本文中,我們首先決定了自己將處理的數據。然后,我們以定制向導的方式來獲取這些數據。下一篇文章將繼續講解顯示的問題,包括定制的編輯器和屬性頁面。
編輯推薦:Eclipse插件開發如虎添翼 |


