最大的網站源碼資源下載下傳站,
以前我有一個微型的便攜式電子位址薄。我一直認為它很不錯,直到有一天它停止運作了。銷售該産品的人員無法找回我的聯系位址名冊,卻提議更換一台。這時候我才知道資料的重要性。這個閃亮的小發明與存儲在它裡面的資料相比根本就不值一提。
在這個序列文章的第一部分中,我介紹了eclipse插件的開發環境,并開發了一個簡單的插件。在第二部分,我添加了工具條按鈕、菜單項和對話框。它實際上沒有實作任何具體功能。它簡單地用某種字型顯示了示例文本内容。現在我們要讓它能夠管理實際的資料。我們将修改這個插件,讓它實作我們所需要的功能。本文讨論的是編輯器文檔,并示範了如何定制一個向導。
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)也是很重要的。醜陋的發明難以出售,但是閃亮的發明可能容易出售。但是資料是我們這些程式員實作的非常本質的東西。
在本文中,我們首先決定了自己将處理的資料。然後,我們以定制向導的方式來擷取這些資料。下一篇文章将繼續講解顯示的問題,包括定制的編輯器和屬性頁面。