资源和工作空间
一个工作区可以包含多个工程,并且这些工程可以存在于同一个文件系统的不同路径中。
工作空间中的资源的名称是大小写敏感的,因此工作空间中的文件是可以,所以工作区运行多个文件名相同,只是文件名的大小写有区别的文件存在。(是这样么?)工作空间同样没有文件名是否合法,长度是否过长,以及该资源在硬盘大小是否满足一定要求的限制。当然,如果你存储文件的文件系统不是大小写敏感的,或者该文件系统对于文件名有约束等,那么这些约束就会在你创建或者操作这些文件的时候显现出来。
一棵简单的资源树
下面的这颗树(在工作台的navigator视图中显示)展示了一个典型workspace中的继承关系,这颗树的根是workspace root,工程是worspace root的第一层级的子,其他的每一个节点,都是一个树类型的资源文件,并且每一个节点的名字都与他的兄弟节点不相同。
资源名可以是任意字符(必须是合法的文件系统的名字),eclipse平台不关心资源的名称,也没有指定任何名字是不可以使用的。(除了.metada这个文件名外,他已经被系统使用,你不能用他来作为工程名)
工程可以包含文件和目录,但是不可以包含其他工程,工程和目录就像是文件系统中的文件夹,当你删除一个工程的时候,系统将会询问你是否删除该工程所包含的所有的文件和目录。删除一个工程将会删除该工程的所有内容,而删除一个文件,跟在文件系统中删除一个文件类似。
资源和文件系统
IWorkspace workspace = ResourcesPlugin.getWorkspace();
当资源插件没有加载的时候,工作空间是在文件系统中独立存在的,用户可以通过标准的文件操作的方式来操作它,在使用资源插件相关的API之前,我们先来看一下工作空间在硬盘中是什么样子的。
硬盘上的资源树
当你启动平台的SDK的时候,系统会提示你让你指定一个工作空间的目录。这就是不同的插件存放各自元数据信息的地方。默认的,资源插件会将工程以子目录的形式存放于工作空间中,在每一个子目录中包含该工作相关的文件和文件夹.
假设,你选择的工作区的目录为c:\MySDK\workspace。在该目录里面,我们可以看到两个以工程名命名的文件夹,他们被称为工程目录,当用户创建一个工程的时候,eclipse平台会相应的创建一个工程目录。
在每个目录里面,我们可以看到这个工程中所包含的文件和文件夹,和他们在工作区的资源树种展示的是一样的。一样的文件名,一样的内容,唯一的不同就是.project文件,这个我们稍后再讲。
C:\MySDK\workspace (workspace root) .metadata\ (platform metadata directory MyWeb\ (project content directory for MyWeb) .project index.html images\ logo.png MyServlet\ (project content directory for MyServlet) .project src\ main.java bin\ main.class
平台有一个特殊的.metadata目录,用来保存平台的内部信息,你可以吧.metada目录比作为一个“黑盒子”,关于工程结构的重要信息,比如工程的引用关系,资源的一些属性等等,是以工作空间的一部分元数据的形式存储的,并且只能通过平台提供的API来访问,这些文件最好不要通过操作系统文件的方式来直接编辑他们。
除此之外,每一个工程都有他的.project文件,用来存放于工程相关的元数据信息。这个文件中的信息可以通过工程的IProjectDescription来获取到。
除了.metada目录和.project文件,工作空间目录下面的文件夹和文件其他任何工具都可以访问或者对其进行操作。唯一的问题就是用户不管在工作台中还是在外部编辑这些文件都得格外小心。(这跟用户用两个不同的工具同时操作一份文件没有什么不同)工作台提供了一种刷新的操作来保持工作区中的资源与文件系统中的资源保持一致。
代码中的资源树
操作资源与通过java.io,File操作文件类似,这些API都是基于句柄的,当你使用一个类似于getProject或者getFolder的API的时候,你获取到的只是该资源的一个句柄,没人会保证这个资源是否存在只到你需要用这个句柄做某些事情的时候。如果你希望这个资源的确是存在的,他们你可以使用exists 方法来保证他是否存在。
为了在我们的插件中展示workspace,我们首先得或得IWorkspaceRoot,workspace顶层节点
IWorkspaceRoot myWorkspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
一旦我们获取到了workspace root,我们就可以用过workspace访问工程了
IProject myWebProject = myWorkspaceRoot.getProject("MyWeb"); // open if necessary if (myWebProject.exists() && !myWebProject.isOpen()) myWebProject.open(null);
在我们操作一个工程之前,我们必须打开他。打开工程,将工程结构从硬盘中读取出来,并且将工程资源树缓存到内存中。打开工程的操作将会消耗部分内存用来存放工程实例,并且伴随的大量的资源相关的事件(比如build的过程),这将消耗很长时间。一般来讲,关闭后的工程是无法房访问的,并且不会触发任何资源相关的事件,尽管该文件仍然在文件系统中存在。
你可能注意到了,许多资源操作相关的例子中,当我们操作资源的时候都传了一个空的参数进去。大多数操作资源的动作时间都很长,应该展示出进度条,并且允许用户取消该操作。如果你的代码提供用户界面,你可以传一个IProgressMonitor进去,这个将会呈现一个进度条提示用户操作正在进行,并且允许用户取消该操作。不过现在,我们先传null进去,不展示进度条。
一旦我们打开了一个工程,我们就可以操作它的文件和文件夹了,同时也可以创建新的资源。下面的例子我们将一个工作区外的资源创建成该工程下的一个资源。
IFolder imagesFolder = myWebProject.getFolder("images"); if (imagesFolder.exists()) { // create a new file IFile newLogo = imagesFolder.getFile("newLogo.png"); FileInputStream fileStream = new FileInputStream( "c:/MyOtherData/newLogo.png"); newLogo.create(fileStream, false, null); // create closes the file stream, so no worries. }
在上面的例子中,第一行代码是获取一个图片目录的句柄,在我们对它做任何操作之前,我们必须对其是否存在的状态进行检查,同样的,当我们获取newLogo的文件的时候,这个句柄并不是一个实实在在存在的文件,只到最后一行创建文件完成之后。在这个例子中我们用logo.png中的内容创建了一个新的文件。
下面这个例子跟上面那个比较相似,不同的是,这次是拷贝了一份而不是通过内容重新创建一份。
The next snippet is similar to the previous one, except that it copies the newLogo file from the original logo rather than create a new one from its contents.
IFile logo = imagesFolder.getFile("logo.png"); if (logo.exists()) { IPath newLogoPath = new Path("newLogo.png"); logo.copy(newLogoPath, false, null); IFile newLogo = imagesFolder.getFile("newLogo.png"); ... }
最后,我们创建了另外一个图片目录,并且把刚创建的文件移动到该目录下,并且顺带着将该文件改名
Finally, we'll create another images folder and move the newly created file to it. We rename the file as a side effect of moving it.
... IFolder newImagesFolder = myWebProject.getFolder("newimages"); newImagesFolder.create(false, true, null); IPath renamedPath = newImagesFolder.getFullPath().append("renamedLogo.png"); newLogo.move(renamedPath, false, null); IFile renamedLogo = newImagesFolder.getFile("renamedLogo.png");
大多数文件操作API的方法有一个force标志,用来指定当资源与系统文件不同步时是否强制刷新。更多信息请查看IResource 接口。你同样可以使用 IResource.isSynchronized 来判断单个资源是否与文件系统中的资源同步。
资源与硬盘文件的对应
在简单资源树种,我们假定所有的工程所包含的目录都在工作空间的子目录下(C:\MySDK\workspace).这个是默认的配置,然而,工程中包含的目录可以对应到任何目录中,甚至是另一台机器。
这种指定工程路径的能力独立与其他任何工程,他允许用户存储工程到任何合理的工程目录中。工程的内容可以被用户通过工作台或者插件创建修改或者删除,也可以通过基于文件系统的工具或者编辑器进行操作。
资源路径的名称中不包含文件系统的路径,资源的路径总是基于工程的路径(通常是workspace 目录)。为了获取一个资源的文件系统全路径,你必须通过 IResource.getLocationURI方法来查询。但是你不能通过 IProjectDescription.setLocation来改变它的路径,因为这仅仅是一个简单的setter数据结构。
相反,如果你想获取通过一个文件系统路径来获取其对应的resource,你可以通过IWorkspaceRoot.findFilesForLocationURI 或者IWorkspaceRoot.findContainersForLocationURI来获取.
资源API和文件系统
大部分资源API中的方法包含一个强制刷新的参数,来指定如果文件系统不同步时该如何处理。并且API提供了一个通过程序控制刷新的方法比如IResource.refreshLocal(int depth, IProgressMonitor monitor).
如果开发人员自己的插件希望提供一套机制来定期刷新工作空间,可以使用org.eclipse.core.resources.refreshProviders扩展点。