今天終于可以閑一天,想來想去就亂寫點(diǎn)東西吧,說不定對(duì)有些新人有點(diǎn)幫助呢~_~
用Eclipse API的方式來打開編輯器,可能對(duì)任何一個(gè)插件開發(fā)者都不是很陌生的操作了。但是,還是建議你忍著看一下,全當(dāng)是復(fù)習(xí)吧~_~。
【打開editor的接口討論】
先來看一下workbench吧,workbench從靜態(tài)劃分應(yīng)該大致如下:

從結(jié)構(gòu)圖我們大致就可以猜測(cè)出來,workbench page作為一個(gè)IWorkbenchPart(無論是eidtor part還是view part)的容器,肯定會(huì)接受workbench page的管理。看了一下,IWorkbenchPage接口定義中確實(shí)提供給了如下打開編輯器的操作:
【IWokbenchPage提供的接口】
1 public interface IWorkbenchPage extends IPartService, ISelectionService,ICompatibleWorkbenchPage {
2
3 public IEditorPart openEdito(IEditorInput input, String editorId)throws PartInitException;
4
5 public IEditorPart openEdito(IEditorInput input, String editorId, boolean activate) throws PartInitException;
6
7 public IEditorPart openEditor(final IEditorInput input, final String editorId, final boolean activate, final int matchFlags)throws PartInitException;
8 }
那到這邊,可能很多人已經(jīng)知道了怎么調(diào)用這些接口了:
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().openEditor(...)
(說明:PlatformUI可以看作是整個(gè)eclipse ui框架的門面類,當(dāng)然最核心的作用就是讓用戶獲取到workbench。Eclipse中存在的其他一些門面類如:ResourcesPlugin、Platform、JavaCore、JavaUI等)
我們?cè)僮屑?xì)看一下IWorkbenchPage對(duì)應(yīng)的實(shí)現(xiàn)類(org.eclipse.ui.internal.WorkbenchPage)中的以上接口的實(shí)現(xiàn)代碼,真正在管理Editor的是一個(gè)叫做EditorManager的東東(同理,view part對(duì)應(yīng)的管理器角色類是叫做ViewFactory的東東)。
這里的EditorManager和View Factory是workbench實(shí)現(xiàn)中非常精華的部分,看一下里面的實(shí)現(xiàn)就會(huì)很大程度上理解workbench所謂懶加載、懶初始化是如何實(shí)現(xiàn)的了,如何實(shí)現(xiàn)part 復(fù)用的...等等。

上圖就用來說明workbench是如何來管理各種part的,其中descriptor角色的核心作用是延遲加載擴(kuò)展(延遲加載用戶通過editors或者views提供的擴(kuò)展),reference角色的核心作用是用來延遲初時(shí)化具體的part(例如避免過早的創(chuàng)建對(duì)應(yīng)的control等等)。再說下去有點(diǎn)偏離主題了,這部分,以后有時(shí)間再寫
【IDE工具類提供的接口】
上面IWorkbenchPage提供接口都需要用戶準(zhǔn)備兩樣?xùn)|西:一是創(chuàng)建IEditorInput實(shí)例,二是指定editor id。有些用戶可能不想干這兩件事情,所以在工具類org.eclipse.ui.ide.IDE中提供了其他的接口:
1 public static IEditorPart openEditor(IWorkbenchPage page, IFile input) throws PartInitException { }
2
3 public static IEditorPart openEditor(IWorkbenchPage page, IFile input, boolean activate) throws PartInitException { }
4
5 public static IEditorPart openEditor(IWorkbenchPage page, IFile input, boolean activate, boolean determineContentType) { }
6
7 public static IEditorPart openEditor(IWorkbenchPage page, IFile input, String editorId) throws PartInitException { }
8
9 public static IEditorPart openEditor(IWorkbenchPage page, IFile input, String editorId, boolean activate) throws PartInitException { }
10
11
上面5個(gè)接口操作中, 對(duì)于上面的三個(gè)操作,Eclipse會(huì)自動(dòng)為你準(zhǔn)備IEditorInput實(shí)例,并動(dòng)態(tài)綁定合適的編輯器類型。對(duì)于下面的兩個(gè)操作,Eclipse會(huì)為你自動(dòng)準(zhǔn)備IEditorInput實(shí)例,但是需要用戶自己指定editor id。
接下來我們看兩個(gè)問題,一是如何創(chuàng)建IEditorInput實(shí)例的;而是如何動(dòng)態(tài)計(jì)算對(duì)應(yīng)的editor id的。
【有關(guān)FileEditorInput】
在IDE工具類中提供的5個(gè)接受IFile對(duì)象的openEditor接口中,在對(duì)應(yīng)的實(shí)現(xiàn)中都是默認(rèn)構(gòu)造了一個(gè)FileEditorInput(org.eclipse.ui.part.FileEditorInput)實(shí)例,這個(gè)實(shí)例也是org.eclipse.ui.IFileEditorInput接口的默認(rèn)實(shí)現(xiàn)類(注意:Eclipse中很多地方都使用這種Interface/Default Impl的方式,Interface會(huì)暴露,Default Impl則根據(jù)情況選擇是否暴露,一般是如果Interface希望用戶來擴(kuò)展繼承,則會(huì)暴露對(duì)應(yīng)的Default Impl,如果Interface不希望用戶來擴(kuò)展繼承,例如IResource系列接口,則一般會(huì)將Default Impl丟如對(duì)應(yīng)的internal包中)。
我們看一下org.eclipse.ui.part.FileEditorInput中是如何實(shí)現(xiàn)IEditorInput.exists()接口的:
1 public class FileEditorInput implements IFileEditorInput,IPathEditorInput,IPersistableElement {
2 private IFile file;
3
4 public boolean exists() {
5 return file.exists();
6 }
7 }
我們看到內(nèi)部的實(shí)現(xiàn)是持有了IFile句柄,如果IFile代表的資源沒有存在于工作區(qū)之內(nèi),那么就會(huì)返回false。(疑問:如果我們打開工作區(qū)外部的文件呢???顯然,F(xiàn)ileEditorInput并不合適,稍后看...)
【動(dòng)態(tài)計(jì)算editor id】
下面,我們?cè)賮砜匆幌翴DE類是如何計(jì)算所謂的默認(rèn)eidtor id的。追蹤實(shí)現(xiàn),我們看到了IDE.getDefaultEditor
1 public static IEditorDescriptor getDefaultEditor(IFile file, boolean determineContentType) {
2 // Try file specific editor.
3 IEditorRegistry editorReg = PlatformUI.getWorkbench()
4 .getEditorRegistry();
5 try {
6 String editorID = file.getPersistentProperty(EDITOR_KEY);
7 if (editorID != null) {
8 IEditorDescriptor desc = editorReg.findEditor(editorID);
9 if (desc != null) {
10 return desc;
11 }
12 }
13 } catch (CoreException e) {
14 // do nothing
15 }
16
17 IContentType contentType = null;
18 if (determineContentType) {
19 contentType = getContentType(file);
20 }
21 // Try lookup with filename
22 return editorReg.getDefaultEditor(file.getName(), contentType);
23 }
上面的代碼大致趕了如下兩件事情:
1、如果對(duì)應(yīng)的資源設(shè)定了一個(gè)特定的持久化屬性EDITOR_KEY,則會(huì)使用EDITOR_KEY屬性值所代表的編輯器(說明:有關(guān)Eclipse資源的屬性支持,請(qǐng)參閱其他文檔)。那如果一個(gè)資源不在工作區(qū)之內(nèi),又如何設(shè)定EDITOR_KEY屬性呢??? (~_~確實(shí)沒法設(shè)定)
2、查找對(duì)應(yīng)的content type,用戶通過org.eclipse.core.runtime.contentTypes擴(kuò)展點(diǎn)來注冊(cè)自定義的內(nèi)容類型,在內(nèi)容類型中會(huì)指定對(duì)應(yīng)的文件擴(kuò)展名和默認(rèn)編碼,例如JDT中注冊(cè)了如下內(nèi)容類型(摘自org.eclipse.jdt.core/plugin.xml):
<!-- =================================================================================== -->
<!-- Extension: Java Content Types -->
<!-- =================================================================================== -->
<extension point="org.eclipse.core.runtime.contentTypes">
<!-- declares a content type for Java Properties files -->
<content-type id="javaProperties" name="%javaPropertiesName"
base-type="org.eclipse.core.runtime.text"
priority="high"
file-extensions="properties"
default-charset="ISO-8859-1"/>
<!-- Associates .classpath to the XML content type -->
<file-association
content-type="org.eclipse.core.runtime.xml"
file-names=".classpath"/>
<!-- declares a content type for Java Source files -->
<content-type id="javaSource" name="%javaSourceName"
base-type="org.eclipse.core.runtime.text"
priority="high"
file-extensions="java"/>
<!-- declares a content type for Java class files -->
<content-type id="javaClass" name="%javaClassName"
priority="high"
file-extensions="class">
<describer
class="org.eclipse.core.runtime.content.BinarySignatureDescriber">
<parameter name="signature" value="CA, FE, BA, BE"/>
</describer>
</content-type>
<!-- declares a content type for JAR manifest files -->
<content-type id="JARManifest" name="%jarManifestName"
base-type="org.eclipse.core.runtime.text"
priority="high"
file-names="MANIFEST.MF"
default-charset="UTF-8"/>
</extension>
那如果我們?cè)谧?cè)編輯器的時(shí)候和對(duì)應(yīng)的content type綁定,這不就聯(lián)系起來了嗎~_~。那我們看一下java源碼編輯器擴(kuò)展描述(摘自org.eclipse.jdt.ui/plugin.xml):
<editor
name="%JavaEditor.label"
default="true"
icon="$nl$/icons/full/obj16/jcu_obj.gif"
contributorClass="org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditorActionContributor"
class="org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor"
symbolicFontName="org.eclipse.jdt.ui.editors.textfont"
id="org.eclipse.jdt.ui.CompilationUnitEditor">
<contentTypeBinding
contentTypeId="org.eclipse.jdt.core.javaSource"
/>
</editor>
我們看到上面的xml中有contentTypeBinding元素,里面指定了綁定java源碼content type。
那如果我們?cè)谧?cè)編輯器的時(shí)候,沒有綁定對(duì)應(yīng)的content type呢?Eclipse允許你配置,往下看:


我想看到這邊對(duì)eclipse如何動(dòng)態(tài)計(jì)算一個(gè)文件對(duì)應(yīng)的editor應(yīng)該是明白了吧,再回顧一下吧:
1、查看資源本身是否有EIDTOR_ID持久屬性(注意:一、只有工作區(qū)中存在的資源才允許設(shè)置持久屬性;二、資源屬性知識(shí)針對(duì)特定資源,不會(huì)影響同類型資源,即你對(duì)工作區(qū)中特定的.java文件設(shè)定了EIDTOR_ID持久屬性,并不會(huì)影響工作區(qū)中其他.java文件資源的編輯器綁定操作)
2、查找對(duì)應(yīng)的content type,然后查找對(duì)應(yīng)的editor擴(kuò)展或者查找Eclipse中的Content Types和File Associations配置
3、如果都找不到,則直接給一個(gè)默認(rèn)的編輯器。例如,我們經(jīng)常碰到是"org.eclipse.ui.DefaultTextEditor"
【IDE工具類提供的接口 VS IWorkbenchPage提供的接口】
看一下以上提到的各個(gè)角色之間的調(diào)用關(guān)系圖吧:
【使用Eclipse提供的打開editor的接口】
還是那句話,需求決定一切。我們看一下打開編輯器的需求:
1、打開工作區(qū)中工程內(nèi)的文件資源
2、打開工作區(qū).metadata目錄中的文件資源
3、打開工作區(qū)外部的文件資源
【說明】Eclipse工作區(qū)實(shí)際上是有數(shù)據(jù)區(qū)和元數(shù)據(jù)區(qū)兩個(gè)區(qū)域組成的,示意如下:


對(duì)于Eclipse來說,.metadata目錄下存放的是插件運(yùn)行時(shí)的關(guān)鍵狀態(tài)數(shù)據(jù),不建議用戶再工作區(qū)實(shí)例運(yùn)行期間做相應(yīng)修改,為此eclipse干了兩件事情:1、運(yùn)行期間會(huì)自動(dòng)在.metadata目錄下產(chǎn)生一個(gè)進(jìn)程鎖定的.lock文件;2、Eclipse不允許用戶通過IResource系列接口直接訪問或修改.meatadata目錄下的資源
【打開工作區(qū)工程內(nèi)的資源】
假設(shè)工作區(qū)中有測(cè)試工程TestProject,工程下有文本文件java_file.txt。對(duì)應(yīng)創(chuàng)建代碼如下:
1 try {
2 //創(chuàng)建工程
3 IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject("TestProject");
4 if (!project.exists())
5 project.create(null);
6 if (!project.isOpen())
7 project.open(null);
8
9 //創(chuàng)建文件
10 IFile java_file = project.getFile(new Path("/java_file.txt"));
11 InputStream inputStreamJava = new ByteArrayInputStream("class MyType{}".getBytes());
12 if (!java_file.exists())
13 java_file.create(inputStreamJava, false, null);
14 } catch (CoreException e) {
15 IStatus status = new Status(IStatus.ERROR, "myplugin", 101, "創(chuàng)建資源失敗", e);
16 Activator.getDefault().getLog().log(status);
17 }
打開方式一:Eclipse默認(rèn)計(jì)算對(duì)應(yīng)的editor id,會(huì)用default text editor打開
1 try {
2 IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
3 IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject("TestProject");
4
5 IFile java_file = project.getFile(new Path("/java_file.txt"));
6 IDE.openEditor(page, java_file);
7 } catch (CoreException e) {
8 IStatus status = new Status(IStatus.ERROR, "myplugin", 102, "打開工作區(qū)內(nèi)文件出錯(cuò)", e);
9 Activator.getDefault().getLog().log(status);
10 }
打開方式二:指定java源碼編輯器打開,會(huì)用java源碼編輯器打開
1 try {
2 IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
3 IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject("TestProject");
4
5 IFile java_file = project.getFile(new Path("/java_file.txt"));
6 IDE.openEditor(page, java_file, "org.eclipse.jdt.ui.CompilationUnitEditor");
7 } catch (CoreException e) {
8 IStatus status = new Status(IStatus.ERROR, "myplugin", 102, "打開工作區(qū)內(nèi)文件出錯(cuò)", e);
9 Activator.getDefault().getLog().log(status);
10 }
打開方式三:設(shè)定editor id屬性,該文件以后默認(rèn)都用此editor id打開
1 try {
2 IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
3 IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject("TestProject");
4
5 IFile java_file = project.getFile(new Path("/java_file.txt"));
6 java_file.setPersistentProperty(IDE.EDITOR_KEY, "org.eclipse.jdt.ui.CompilationUnitEditor");
7 IDE.openEditor(page, java_file);
8 } catch (CoreException e) {
9 IStatus status = new Status(IStatus.ERROR, "myplugin", 102, "打開工作區(qū)內(nèi)文件出錯(cuò)", e);
10 Activator.getDefault().getLog().log(status);
11 }
說明:對(duì)于工作區(qū)工程內(nèi)的資源,可以有兩種方式:一是local的,那就是物理存在與工程之內(nèi);二是link進(jìn)入的。打開編輯器的時(shí)候,不需要做區(qū)分。
【打開工作區(qū)外部的資源】
說明:既存在于工作區(qū)外部,同時(shí)又沒有被link進(jìn)工程。
在Eclipse中有個(gè)功能,就是File->Open File,可以打開一個(gè)外部文件。那我們看一下它是怎么實(shí)現(xiàn)的。我們只需要打開對(duì)應(yīng)的對(duì)話框,然后掛起主線程,就可以找到對(duì)應(yīng)的action了(掛起線程可以幫我們很方便的調(diào)試很多類型的問題,以后細(xì)說~_~):
分析一下OpenExternalFileAction的實(shí)現(xiàn),我們發(fā)現(xiàn)它自己構(gòu)建了一個(gè)editor input
本博客中的所有文章、隨筆除了標(biāo)題中含有引用或者轉(zhuǎn)載字樣的,其他均為原創(chuàng)。轉(zhuǎn)載請(qǐng)注明出處,謝謝!