Eclipse 插件开发 —— 深入理解查找(Search)功能及其扩展点
引言
查找功能是計算機語言開發環境 / 平臺的一個非常重要的特性。Eclipse 也不例外,它提供了豐富的查找功能(用戶可以輸入正則表達式或任意字符串,指定查找范圍和匹配選項等等),并且提供了簡單易用的接口方便開發人員擴展。Eclipse 的查找功能是基于 MVC 設計模式架構的,因此如果讀者先前對 MVC 模式了解的話,有助于讀者理解 Eclipse 的查找框架。在 Eclipse 的開發平臺中,通過快捷鍵 CTRL+H 或者 Search 菜單可以激活其查找功能,如圖 1 為 Eclipse3.4 插件開發版本按下快捷鍵時彈出的查找對話框(Search Dialog)。
圖 1. Eclipse3.4 版本的查找對話框
如圖 2 為 IBM RSA7.5.2 版本按下快捷鍵時彈出的查找對話框,提供了多大八種查找功能。
圖 2. IBM RSA7.5.2 版本的查找對話框
總體而言,Eclipse 中的查找功能由三部分組成:查找對話框、查找頁面和查找結果集視圖;下面分別就這三部分進行具體描述。
查找對話框是 Eclipse 查找功能的入口,Eclipse 中所有的查找功能都可以在該對話框中找到,如圖 1 和圖 2 顯示了 Eclipse 和 IBM RSA 中的查找對話框。查找對話框中包含了當前 Eclipse 開發平臺提供的所有查找頁面,如果用戶頁面覺得頁面太多,可以通過“Customize …”按鈕定制,只顯示使用頻率較高的查找頁面,隱藏其它頁面。雖然我們不能對這些頁面排序,然而,Eclipse 提供了相應的擴展點(Extension point)讓開發人員定制其查找頁面的位置(通過 tabPosition 屬性可以指定,下一節會進行詳細介紹)。
查找頁面是查找功能的入口,提供了查找模式和條件等內容的輸入界面 GUI。在 Eclipse 中,為了保持查找功能的一致性,當我們的插件需要提供查找功能,通常會為其創建一個查找頁面,并放在查找對話框里面。每一個查找功能對應有一個查找頁面,用于接收用戶進行查找的內容,范圍等輸入條件。如圖 3 為 Clear Case 插件的查找頁面。
圖 3. Eclipse 中 ClearCase 的查找頁面
作為一個完整的查找框架,還需要提供一個顯示查找到結果集的界面。在 Eclipse 中,這個界面叫做查找結果集視圖,它提供了豐富的工具欄和菜單,諸如上 / 下一個匹配項、重新查找、展開和收縮結果集等操作,并且可以根據查找結果的類型用相應的編輯器打開。Eclipse 中,所有的查找功能共用一個視圖,該視圖提供兩種顯示方式:表格(TableViewer)和樹狀結構(TreeViewer)。查找結果視圖的標題為“Search”,如圖 4 所示為作者使用 Eclipse 自帶的文件查找搜索“*”的結果集視圖。
圖 4. Eclipse 中顯示查找結果的視圖
回頁首
Eclipse 查找功能的擴展點
由上一節我們可知,Eclipse 的查找功能主要是由三部分組成:查找對話框、查找頁面和查找結果集視圖,至此,我們對 Eclipse 的查找功能有了感觀的認識,本小節將從 Eclipse 提供的擴展點,函數,接口和類等方面詳細說明 Eclipse 的查找功能。
查找對話框 / 查找頁面的擴展點
如果我們想要插入一個查找頁面到查找對話框中,那么需要添加擴展點 org.eclipse.search. searchPages。
該擴展點允許其他插件為特定的查找功能注冊屬于自己的查找頁面。下面說明需要注意的擴展點的一些屬性:
id – 查找頁面的唯一標志。
label – 顯示在查找對話框中頁面的標題。
class –創建查找頁面顯示的 control 的類,該類通常繼承抽象類 org.eclipse. jface.dialogs. DialogPage,并且實現接口 org.eclipse.search.ui. ISearchPage。
sizeHint – 暗示該查找頁面激活時的大小,其輸入格式為"width, height", 如"50, 60"。
tabPosition – 整形數字,表示該查找頁面在查找對話框中顯示的位置,查找對話框中包含一個 TabFolder,TabFolder 包含了若干個頁面,如果該元素沒有指定,那么 Eclipse 將按照這些頁面標題的字母順序進行排列。數字越小,其對應的頁面越靠前。
enabled – 如果該屬性沒有指定或者為 false,那么當用戶通過 CTRL+H,或者 search 菜單激活查找對話框時,該頁面不會自動顯示,用戶可以通過“Customize” 按鈕手動顯示該頁面。
canSearchEnclosingProjects 和 showScopeSection – 如果這兩個屬性都設置為 true,那么 Eclipse 會自動添加一個 Scope 到你的查找頁面的最下面,如圖 5 所示為 File 查找的 Scope 域,不需要用戶單獨創建該區域。
圖 5. 文件查找頁面的 Scope 區域
為了更好地說明如何使用該擴展點,舉一個例子說明,如下為 org.eclipse.search 插件中的文件查找頁面實現的擴展點代碼,位于 plugin.xml 文件中。
<extension point="org.eclipse.search.searchPages">
<page
id="org.eclipse.search.internal.ui.text.TextSearchPage"
label="%fileSearch"
icon="$nl$/icons/full/elcl16/tsearch_obj.gif"
sizeHint="250,160"
tabPosition="1"
extensions="*:1"
showScopeSection="true"
canSearchEnclosingProjects="true"
class="org.eclipse.search.internal.ui.text.TextSearchPage">
</page>
</extension>
|
查找結果視圖(Search Result View)的擴展點
對 org.eclipse.search.searchPages 擴展后,當我們按下查找(Search)按鈕后,Eclipse 的查找框架會激活查找結果集視圖,那么如何讓我們自己設計的界面顯示在查找結果集視圖中呢?答案很明顯,通過添加 org.eclipse.search.seacrhResultViewPages 擴展點,填充相應的擴展點屬性,便可以激活我們自己設計的界面,并用該界面顯示查找到的匹配結果集。下面講解擴展點 seacrhResultViewPages 中比較重要的一些屬性。
searchResultClass – 用于表示查找結果集的類,需要實現接口 ISearchResult,由于實現該接口需要實現較多的方法,因此 Eclipse 提供了一個抽象類,用于表示文本查找的結果集,該抽象類(類名為 AbstractTextSearchResult)實現了 ISearchResult 的大部分接口,如果我們實現的查找功能是文本查找,那么就可以繼承該類,同時實現少量的方法即可,該類需要和類 AbstractTextSearchViewPage 同時使用
class - 用于顯示查找結果的頁面類,需要實現接口 org.eclipse.search.ui. ISearchResultPage,由于實現該接口需要實現較多的方法,因此 Eclipse 提供一個抽象類(類名為 AbstractTextSearchViewPage)實現了該接口,該類主要用于顯示文本查找的結果集。如果我們實現的查找功能是文本查找,那么只需繼承該類并實現少量的方法即可。AbstractTextSearchViewPage 和 AbstractTextSearchResult 通常一起使用,當然還需要用到 Match 類和 MatchEvent 等類,下小節將會詳細說明。
為了更好地說明如何使用該擴展點,舉一個例子說明,如下為 org.eclipse.search 插件中的文件查找結果集視圖實現的擴展點代碼,位于 plugin.xml 文件中。
<extension
id="FileSearchPage"
point="org.eclipse.search.searchResultViewPages">
<viewPage
id="org.eclipse.search.text.FileSearchResultPage"
searchResultClass="org.eclipse.search.internal.ui.text.FileSearchResult"
helpContextId="org.eclipse.search.file_search_result_page"
class="org.eclipse.search.internal.ui.text.FileSearchPage">
</viewPage>
</extension>
|
Eclipse 主菜單的查找(Search)菜單中加入你的菜單項
對一些重要的查找功能,我們希望提供多種途徑激活這些功能。如添加一個菜單項到查找(Search)主菜單中,如圖 6 所示為文件查找的菜單項。
圖 6. Eclipse 自帶文件查找的菜單
那么如何添加我們自己的菜單項到 Eclipse 的查找主菜單中呢?答案是:添加 org.eclipse.ui. actionSets 擴展點,然后創建一個 action, 并且 action 的 menuBarPath 設置為 org.eclipse.search. menu/dialogGroup。
為了更好地說明如何使用該擴展點,舉一個例子說明,如下為創建一個”Am Search …”菜單項到 Search 主菜單中實現的擴展點代碼,位于 plugin.xml 文件中。
<extension
point="org.eclipse.ui.actionSets">
<actionSet
id="org.eclipse.am.ui.workbenchActionSet"
label="%actionSet.am.label"
visible="true">
<action
class="org.eclipse.am.ui.handlers.ESearchHandler"
definitionId=" org.eclipse.am.ui.commands.ESearch"
icon="icons/Search.gif"
id=" org.eclipse.am.ui.commands.BGSearch"
label="AM Search …"
menubarPath="org.eclipse.search.menu/dialogGroup"
>
</action>
</actionSet>
</extension>
|
相關的函數、接口和類
對查找結果集排序
通常我們以表格(TableViewer)和樹狀結構(TreeViewer)顯示匹配的結果集,因此,自然涉及到對結果集排序的問題。在 Eclipse3.0 版本之前,如果需要對查找結果集視圖中的 TableViewer 或 TreeViewer 進行排序,則需要使用擴展點 org.eclipse.search.searchResultSorters。但是,3.0 以后的版本不需要添加該擴展點,可以直接寫代碼對 TableViewer 或 TreeViewer 排序,只需繼承 org.eclipse.jface.viewers.ViewerSorter。下面給出了一個簡單的排序類代碼。如果想讓下面代碼工作,則需要設置該類為 TableViewer 的 Sorter,通過調用函數 setSorter 方法實現。
public class ESearchResultSorter extends ViewerSorter {
private int columnIndex = -1
private int dir = SWT.DOWN;
public BGSearchResultSorter(int columnIndex, int dir) {
super();
this. columnIndex = columnIndex;
this.dir = dir;
}
public int compare(Viewer viewer, Object e1, Object e2) {
int returnValue = 0;
If(0 == columnIndex){
returnValue = StringUtil.getCollator().compare(e1,e2);
}
if (this.dir == SWT.DOWN) {
returnValue = returnValue * -1;
}
return returnValue;
}
}
|
Match 和 SearchResultEvent 類
Eclipse 針對文本查找,除了提供前面講到的 AbstractTextSearchViewPage 和 AbstractTextSearchResult 類外,還提供了配套的類 Match,MatchEvent 和 RemoveAllEvent(均繼承類 SearchResultEvent),從而實現文本查找功能。如果我們也需要開發基于文本查找的功能,那么完全可以繼承或使用這些類,否則,為了與 Eclipse 的查找框架保持一致和加強我們插件的可擴展性,建議大家也創建類似的類,并實現相應的功能。下面分別就這些類進行說明。
1. org.eclipse.search.ui.text.Match 類
該類用于表示查找過程中匹配的對象,是 Eclipse 為文本查找定義的 Match 類,該類包含匹配的元素(文件或資源等等),查找字符串在該元素中的起始偏移量(offset)和匹配長度,如果我們的查找功能是基于文本的,那么可以繼承該類,否則,我們可以自己定義 Match 類(不用繼承該類)。
2. org.eclipse.search.ui.SearchResultEvent 類
該類保存了提供給事件(Event)接收者需要的對象,例如,如果該事件為增加一個 Match 類對象,那么通過這個事件可以獲取該對象。基于文本查找的兩個時間 MatchEvent 和 RemoveAllEvent 均繼承該類,其中 MatchEvent 類用于增加或刪除 Match 對象,RemoveAllEvent 為刪除所有 Match 對象。
因此,我們在開發查找功能時需要繼承該類 SearchResultEvent,提供我們的查找功能與該事件相關的對象,由 org.eclipse.search.ui.ISearchResult 激活該事件。一般而言,繼承類會提供事件的類型(如增加,刪除匹配項等),匹配的結果集(如包含 Match 的集合(Collection)),可以參考 MatchEvent 和 RemoveAllEvent 類。
3. org.eclipse.search.ui.ISearchResult 接口
該接口用于表示查找結果集。前面講到的 org.eclipse.search.searchPages 擴展點的屬性 searchResultClass 對應的類需要實現該接口。Eclipse 提供的表示文本查找結果集的抽象類 org.eclipse.search.ui.text.AbstractTextSearchResult 實現了該接口。如果我們實現基于文本查找的功能,那么可以繼承該類,實現少量的方法即可。一般而言,在實現該接口的繼承類中會描述匹配結果集的結構,例如,包括了 Match 集合和 org.eclipse.search.ui.ISearchResultListener(下面將會講到)集合等。
4. org.eclipse.search.ui.ISearchResultListener 接口
該接口表示查找結果集發生變化的事件接口。當結果集發生變化時,通知事件監聽者(這里為查找結果集視圖)作出相應的行為。該接口中提供了一個方法 searchResultChanged,其參數為 SearchResultEvent。
就實現而言,我們可以讓查找結果集視圖實現該接口,并調用模型(Model,我們這里表現為 ISearchResult)中的方法注冊 / 注銷該事件(繼承方式),另外,我們也可以在查找結果集視圖的構造函數中定義匿名類,實現該接口中的方法,同時調用模型中的方法注冊 / 注銷該匿名類事件(組合方式)。從大部分的實現方法來看,我們會使用后者(組合方式),因為該接口中只有一個方法,并且使用匿名類也更加靈活。Eclipse 針對文本查找的結果集視圖抽象類 org.eclipse.search.ui.text.AbstractTextSearchViewPage 也是采用匿名類的方式。
5. org.eclipse.search.ui.NewSearchUI 類
該類提供了訪問 Eclipse 查找 GUI 的入口方法,它采用 Facade 設計模式。下面就該類中的一些重要函數進行說明。
調用下面這個方法表示在當前的 Page 里激活查找結果集視圖(search result view)。
activateSearchResultView() |
調用下面這個方法表示發送‘ cancel ’命令到后臺運行的 query。
cancelQuery(ISearchQuery query) |
調用下面這個方法表示獲取當前的查找結果集視圖。
getSearchResultView() |
調用下面這個方法表示打開查找對話框,并激活由 pageId 指定的查找頁。
openSearchDialog(IWorkbenchWindow window, String pageId) |
調用下面這些方法表示運行指定的 query,可以在前臺或者后臺運行,此時 Eclipse 會啟動一個 job 運行。
runQueryInBackground(ISearchQuery query)
runQueryInBackground(ISearchQuery query, ISearchResultViewPart view)
runQueryInForeground(IRunnableContext context, ISearchQuery query)
runQueryInForeground(IRunnableContext context, ISearchQuery query,
ISearchResultViewPart view)
|
回頁首
編程實踐
通過前兩節的講述,相信大家對 Eclipse 的查找框架已經很清楚了,下面將給出一個例子說明如何使用這些擴展點,如何實現接口和繼承類,如何讓我們的查找功能在 Eclipse 的查找框架下工作。
首先,如圖 7 給出了 Eclipse 查找框架的流程,本文將按照這個流程圖中的步驟及其各個步驟涉及到的方法,接口和類,給出它們的代碼框架,讀者想要讓其運行,必須實現相應的方法,接口和類。
圖 7. Eclipse 查找框架的流程
開始階段(彈出查找對話框)
當用戶按下 Ctrl + H 鍵,或者通過 Eclipse 的 Search 菜單選擇相應的查找項(如果我們定義了自己的 Action 在 Search 菜單中),Eclipse 將會彈出查找對話框。
如果只需要 Crtl+H 激活查找對話框,那么添加 searchPages 擴展點,并填寫相應的屬性,而不需要其他額外的代碼,在查找對話框中就會有我們的查找頁(還記得前面講的 searchPages 擴展點的 enable 屬性嗎,如果該屬性設為 true,那么擴展的查找頁將會出現在對話框中,否則將被隱藏,需要通過“Customize …”按鈕激活)。
如果需要在 search 菜單中定義自己的菜單,那么首先添加 actionSets 擴展點,如下所示。
<extension
point="org.eclipse.ui.actionSets">
<actionSet
id="com.ibm.bg.ui.workbenchActionSet"
label="Example"
visible="true">
<action
class="com.ibm.bg.ui.handlers.ESearchHandler"
definitionId="com.ibm.bg.ui.commands.ESearch"
icon="icons/lookup_criteria.gif"
id="com.ibm.bg.ui.commands.ESearch"
label="E Search"
menubarPath="org.eclipse.search.menu/dialogGroup"
>
</action>
</actionSet>
</extension>
|
然后再事件處理中通過 NewSearchUI 提供的 openSearchDialog 函數打開對話框,此時需要提供查找頁的 ID,這里我們定義為 ESearchPage,后面將會講到。
public class ESearchHandler implements IWorkbenchWindowActionDelegate {
private IWorkbenchWindow fWindow;
public void init(IWorkbenchWindow window) {
fWindow = window;
}
public void run(IAction action) {
if (fWindow == null || fWindow.getActivePage() == null) {
Activator.beep();
return;
}
NewSearchUI.openSearchDialog(fWindow, ESearchPage.ID);
}
}
|
用戶輸入(查找頁面)
所有查找功能的輸入都是在查找頁面進行的,因此,我們需要使用 searchPages 擴展點,提供自己的查找頁面,從而在該頁面內定制我們的 GUI(如何擺放 Widget,如何布局等等)。以下是擴展點的實現。
<extension
point="org.eclipse.search.searchPages">
<page
class="org.eclipse.book.example.pages.ESearchPage"
enabled="true"
id="org.eclipse.book.example.pages.ESearchPage"
label="Search Example">
</page>
</extension>
|
下面分析 ESearchPage 的代碼,該類繼承 DialogPage,并且實現 ISearchPage 接口。該類中,我們需要重載 createControl 方法,用于創建 Widgets,布局我們的查找頁面。通常,我們需要把當前用戶的輸入和選擇保存到 DialogSetting 中,如果查找頁面需要輸入文本,那么我們可以定制歷史輸入的個數,該值通常為 20(Eclipse 文件查找)。需要注意:在 createControl 方法中,我們必須調用方法 setControl 來設置查找頁面的上層控件,否則的話,查找頁面會創建失敗(后面會給出失敗的原因)。下面給出了該類的一些關鍵代碼為參考之用。
public class ESearchPage extends DialogPage implements ISearchPage {
private ISearchPageContainer fContainer;
// 定義頁面需要的 control
public ESearchPage() {
}
@Override
public void createControl(Composite parent) {
// 創建顯示在查找頁面的 control
……
setControl(parent);
……
}
public boolean performAction() {
NewSearchUI.activateSearchResultView();
NewSearchUI.runQueryInBackground(getSearchQuery());
return true;
}
public ISearchQuery getSearchQuery() {
return new ESearchQuery(this.fPattern.getText().trim());
}
protected ISearchPageContainer getContainer() {
return this.fContainer;
}
public void setContainer(ISearchPageContainer container) {
this.fContainer = container;
}
}
|
前面講到,如果不在 createControl 方法中調用 Setting 方法,那么查找頁面的 widgets 將會創建失敗,為什么呢?如果你單步跟蹤調試,會發現類 org.eclipse.search.internal.ui.SearchDialog 中包含調用我們創建的查找頁面代碼,如下所示,從代碼中可以看出,如果沒有調用 setControl 方法,那么 page.getControl() 將返回 Null 值,故創建失敗,Eclipse 將提示“The creation of the page '…' failed.”錯誤信息。
private Control createPageControl(Composite parent,
final SearchPageDescriptor descriptor) {
……
ISearchPage page= descriptor.getPage();
if (page == null || page.getControl() == null) {
Composite container= new Composite(parent, SWT.NONE);
Label label= new Label(container, SWT.WRAP);
label.setText(Messages.format(SearchMessages.
SearchDialog_error_pageCreationFailed, descriptor.getLabel()));
container.setLayout(new GridLayout());
label.setLayoutData(new GridData());
return container;
}
}
|
查找入口(啟動 Job)
當我們點擊查找對話框中“Search”按鈕,這時候會調用 ESearchPage 類中的 performAction() 方法。該方法將啟動 Job 執行 Query(實現了 ISearchQuery 的類),我們可以選擇在前臺還是后臺運行(具體方法,可以參考上一節中 NewSearchUI 提供的靜態方法)。一般而言,performAction 方法首先調用函數 activateSearchResultView() 激活查找結果視圖,然后調用函數 runQueryInBackground() 運行指定的 Query。
上面講到,在運行 Query 之前,通常會調用方法 NewSearchUI.activateSearchResultView() 激活查找結果視圖(需要使用 searchResultPages 擴展點),此時僅僅是當前查找功能的結果集界面顯示。查找結果視圖繼承 Page 類,并實現 ISearchResultPage 接口,下面分析該類中的一些關鍵代碼。通常我們會在構造函數中定義 ISearchResultListener,并提供相應的監聽方法。在 setInput 方法中,需要根據當前顯示在查找結果視圖中的匹配結果集合和新的匹配結果集合狀態和值,注冊或注銷該事件。所有的匹配結果集合均通過調用查找結果集視圖中 TableViewer 的內容提供者(Content Provider)逐個插入到 TableViewer 中。getLabel() 用于顯示在 TableViewer/TreeViewer 上面的一個 label 的內容,通常用于顯示查找到多少個匹配項。下面給出了部分關鍵代碼,讀者可以參考 AbstractTextSearchViewPage 類中的一些實現。
public class ESearchResultPage extends Page implements ISearchResultPage{
private TableViewer fTableViewer;
protected ESearchResult fInput;
protected ISearchResultViewPart fViewPart;
protected ISearchResultListener fListener;
protected String fId;
public ESearchResultPage() {
this.fListener = new ISearchResultListener() {
public void searchResultChanged(SearchResultEvent e) {
ESearchResultPage.this.handleSearchResultChanged(e);
}
};
}
public void createControl(Composite parent) {
}
public String getLabel() {
ESearchResult result = getInput();
if (result == null)
return "";
return result.getLabel();
}
public void setInput(ISearchResult search, Object uiState) {
ISearchResult oldSearch = (ISearchResult) this.fTableViewer.getInput();
this.fTableViewer.setInput(null);
if (oldSearch != null) {
oldSearch.removeListener(this.fListener);
}
this.fInput = ((ESearchResult) search);
if ( fInput != null && fInput.getElements().size() > 0) {
search.addListener(this.fListener);
this.fTableViewer.setInput(search);
if (uiState instanceof ISelection)
this.fTableViewer.setSelection((ISelection) uiState, true);
}
}
|
查找(執行 Query)
當調用 runQueryInBackground 或 runQueryInForeground 方法之后,Eclipse 查找框架會啟動一個 Job 運行我們定義的 Query,該類是 Eclipse 查找框架中最主要的一個類之一,它定義了如何進行查找,同時把查找匹配項添加到 ISearchResult,并且以友好的用戶體驗方式即 progressMonitor 顯示目前查找的進度。該類提供了很多方法,例如,是否可以重新運行,是否可以在后臺運行及顯示進度的對話框的標題等等,下面給出類 ESearchQuery 的部分關鍵代碼的實現。
public class ESearchQuery implements ISearchQuery {
ESearchResult fSearchResult;
String fPattern;
public ESearchQuery(String pattern) {
this.fPattern = pattern;
this.fSearchResult = ESearchResult.createInstance(this);
}
public boolean canRerun() {
return true;
}
public boolean canRunInBackground() {
return true;
}
public String getLabel() {
return "Example Search";
}
public ISearchResult getSearchResult() {
return fSearchResult;
}
public IStatus run(IProgressMonitor monitor)
throws OperationCanceledException {
try{
// 表明查找開始
monitor.beginTask(taskname, totalCount);
// 在指定的范圍逐個查找結果集,如果找到,添加到 ISearchResult
// 其流程大致如下
For( … ){
找到匹配項
構造一個 Match
調用 ISearchResult 的函數添加該 Match
Monitor.work(1)
}
} catch (RuntimeException e) {
} finally {
if (monitor != null)
monitor.done();
}
}
}
|
從上面的描述可知,在類 ESearchQuery 會調用 ESearchResult 類中的方法添加查找的匹配項,接下來將講解查找結果類 ESearchResult。
EsearchResult 類實現 ISearchResult 接口,用于保存查找到的匹配項。該類除了要實現最重要的兩個方法,添加和刪除 ISearchResultListener 方法外,同時需要定義添加匹配的結果集,刪除結果集等方法。下面給出該類中的一些關鍵代碼,其中添加匹配的結果集的方法 addMatch,添加傳入的匹配項到匹配結果集合中,同時通知 ISearchResultListener 事件的監聽者。
public class ESearchResult implements ISearchResult {
protected List<EMatch> elements;
……
public void addMatch(Match match) {
boolean hasAdded= false;
synchronized (elements) {
hasAdded= doAddMatch(match);
}
if (hasAdded)
fireSearchResultEvent(ESearchResultEvent.ADDED, match)
}
protected void fireSearchResultEvent(ESearchEventType eventType,
EMatch match) {
ESearchResultEvent event = new ESearchResultEvent(this, eventType);
event.setMatche(match);
for (Iterator<ISearchResultListener> e = this.searchResultsListeners
.iterator(); e.hasNext();) {
ISearchResultListener listener = (ISearchResultListener) e.next();
listener.searchResultChanged(event);
}
}
|
上面講到 ESearchResult 類包含了匹配項的集合,一般我們會定義一個用于保存匹配對象的類,如 Eclipse 中為文本查找的匹配項提供了 Match 類,該類定義了匹配項的偏移量,長度,匹配的對象等等,如果我們的匹配項具有這些特征,那么可以繼承該類,否則新建一個 Match 類即可。下面是一個簡單的匹配類的實現 EMatch 類。
public class EMatch {
EDataObject fMatchElement;
public String getName(){
return fMatchElement.getName();
}
public EMatch(EDataObject matchElement) {
fMatchElement = matchElement;
}
}
|
同時,在查找到匹配項,需要通知事件的監聽者當前事件的類型(通常有三種:增加了匹配項,刪除匹配項和清空匹配項),Eclipse 查找框架提供了 SearchResultEvent 基類,文本查找定義了兩個類:MatchEvent(包含增加和刪除匹配項類型事件),RemoveAllEvent(清空匹配項事件)。通常在我們開始查找之前,發送清空匹配項類型事件,讓查找結果集視圖清空上一次的匹配項,然后再查找的過程,如果查找到匹配項,則發送增加匹配項事件,使查找結果集視圖把該匹配項添加到界面上(TableViewer 或 TreeViewer)。這里不給出查找結果集事件的關鍵代碼,因為 MatchEvent 和 RemoveAllEvent 的源碼已經很清晰,讀者可以參考這兩個類的實現。
顯示查找結果集
前面在講激活查找結果集視圖的時候,已經說明在 Eclipse 的查找框架內,并不是通過 TableViewer 的 setInput 方法把匹配的結果集顯示在 Search 視圖中,而是通過 TableViewer 的 add 方法把匹配項添加到查找結果集視圖中。具體而言,Eclipse 查找框架會啟動一個 UIJob 定時地把查找到的匹配項添加到 Search 視圖中,這樣可以增強用戶體驗。以 AbstractTextSearchViewPage 中的 UpdateUIJob 為例,其間隔時間為 500 毫秒。通過調用 runBatchedUpdates 方法把當前查找到的匹配項添加到 Search 視圖中,以下是其中的一些關鍵代碼。
private class UpdateUIJob extends UIJob {
public UpdateUIJob() {
super("updating"); //$NON-NLS-1$
setSystem(true);
}
public IStatus runInUIThread(IProgressMonitor monitor) {
Control control= getControl();
if (control == null || control.isDisposed()) {
// disposed the control while the UI was posted.
return Status.OK_STATUS;
}
runBatchedClear();
runBatchedUpdates();
if (hasMoreUpdates() || isQueryRunning()) {
schedule(500);
} else {
fIsUIUpdateScheduled= false;
updateBusyLabel();
if (fScheduleEnsureSelection) {
fScheduleEnsureSelection= false;
AbstractSearchResult result = getInput();
if (result != null && fViewer.getSelection().isEmpty()) {
navigateNext(true);
}
}
}
fViewPart.updateLabel();
return Status.OK_STATUS;
}
}
|
下面我們分析一下 runBatchedUpdates 中的代碼,該函數會調用 elementsChanged 函數觸發 TableViewer 調用 add 方法,把相應的匹配項添加到 TableViewer 中,然后清除從需要更新的匹配項集合中刪除,如下為其關鍵代碼。
private synchronized void runBatchedUpdates() {
elementsChanged(fBatchedUpdates.toArray());
fBatchedUpdates.clear();
updateBusyLabel();
}
|
前面講到,runBatchedUpdates 方法會將當前查找到的匹配項添加到 Search 視圖中,那么如何獲取當前查找到的匹配項呢?這就需要借助 ISearchResultListener,即當前查找到的匹配項通過我們定義的事件監聽函數獲取,如下為定義查找結果集發生變化的類及其監聽函數 handleSearchResultChanged 中的關鍵代碼。我們注意到,MatchEvent 中包含事件的類型和當前查找到的匹配項,如果當前事件為 REMOVED_ALL 類型,那么從查找結果視圖中清除所有的匹配項。
fListener = new ISearchResultListener() {
public void searchResultChanged(SearchResultEvent e) {
handleSearchResultChanged(e);
}
};
protected void handleSearchResultChanged(final SearchResultEvent e) {
if (e instanceof MatchEvent){
MatchEvent event = (MatchEvent) e;
if(event.getKind() == MatchEvent.REMOVED_ALL){
postClear();
}
else{
postUpdate(((MatchEvent) e).getMatches());
}
}
}
|
由于增加 / 刪除匹配項設計 GUI 操作,因此我們需要啟動 UIJob 運行,因此,runBatchedUpdates 函數調用 postUpdate 函數啟動 Job,把匹配項顯示到查找結果視圖上。如下為 postUpdate 的一些關鍵代碼。
private synchronized void postUpdate(Match[] matches) {
evaluateChangedElements(matches, fBatchedUpdates);
scheduleUIUpdate();
}
|
回頁首
總結
至此為止,已經把 Eclipse 查找框架的流程及涉及到的類進行了詳細的說明,相信讀者已經有清晰的認識了,但是,由于 Eclipse 中的所有查找功能都共用一個視圖,導致這些查找功能不能同時使用,而且,有時候我們可能需要定義自己的查找結果集視圖,那么 Eclipse 查找框架中的有些類就不能使用了,如 ISearchQuery,此時,我們不能再給其查找框架了,但是,這個框架的思想我們可以借鑒,讀者有興趣的話,可以開發類似于 Eclipse 的查找框架,但是使用的是我們自己定義的查找結果集視圖。
下面給出如何使用自己的查找結果集視圖顯示匹配項的關鍵代碼,其大致思想是新建一個 Job,在 Job 運行時進行查找,并把查找的匹配項保存起來,待 Job 運行結束后,激活我們自定義的查找結果集視圖,最后把結果集填充到該視圖中。
提示:Eclipse 查找功能是一個非常典型的 MVC 模式,設計我們自己的查找框架(Search Framework)時,完全可以參考該模式,簡潔方便。
public class SearchJob extends Job {
protected String fJobName;
protected String fTaskName;
protected IStatus run(IProgressMonitor monitor) {
// 查找代碼,保存查找到的匹配項
}
protected void performJobDone() {
// 啟動 UI 線程,激活查找結果視圖,并填充匹配項到視圖中
}
public SearchJob (String name) {
super(name);
fJobName = name;
this.addJobChangeListener(new JobChangeAdapter() {
@Override
public void done(IJobChangeEvent event) {
if (event.getResult().getSeverity() == IStatus.OK) {
performJobDone();
}
}
});
}
}
|
總結
以上是生活随笔為你收集整理的Eclipse 插件开发 —— 深入理解查找(Search)功能及其扩展点的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: npm 安装与卸载模块
- 下一篇: css处理文字不换行、换行截断、溢出省略