Resources and the file system
When the platform is running and the resources plug-in is active, the workspace is represented by an instance of
IWorkspace
, which provides
protocol for accessing the resources it contains. An
IWorkspace
instance represents an associated collection of files and directories in one or more file systems. You can access the
workspace from the resources plug-in class (defined in
org.eclipse.core.resources
).
IWorkspace workspace = ResourcesPlugin.getWorkspace();
When the resources plug-in is not running, the workspace exists solely in the file
system and is viewed or manipulated by the user via standard file-based tools.
Let's look at what a workspace looks like on disk as we explain the
resources plug-in API.
Our sample tree on disk
When you started the platform SDK, you were prompted to provide a workspace
directory. This is the directory where various plug-ins store interesting metadata
that is unique to a particular instance of the platform. By default, the resources
plug-in stores each project in a sub-directory of the workspace directory.
Within these subdirectories are the folders and files that each project contains.
Let's say your chose the directory c:\MySDK\workspace for your workspace.
Inside this directory we find subdirectories named after the workspace's projects,
MyWeb and MyServlet. These are called the projects' content directories.
Content directories are created by the platform when the user creates a project.
Inside each directory, we find the files and folders within the project, laid out exactly the same as they are in the
workspace's resource tree. All file names are the same, and the files' contents are the same
whether accessed from the file system or from the workspace. The only surprise
is the .project file, explained in a moment.
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
The platform has a special .metadata directory for holding platform internal information. The
.metadata directory of a workspace is considered to be a "black box." Important information about the
workspace structure, such as a project's references or a resource's properties,
is stored in the metadata portion of the workspace and should only be accessed by tools through the
platform API. These files should never be edited or manipulated using
generic file system API.
In addition, each project has its own .project file, where metadata
about the project is kept. This file is basically an on-disk equivalent of
the information found in a project's
IProjectDescription
.
Apart from the .metadata directory and the .project files, the folders and files in the workspace directory are fair game for other
tools. The files and folders can be manipulated by non-integrated tools, such as text editors and file system
utilities. The only issue is that the user must be careful when editing
these files both in the workbench and externally. (This is no
different than when a user edits a file using two independent stand-alone
tools.) The workbench provides refresh operations to reconcile the
workspace view of resources with the actual state in the file system and
there is an option to periodically refresh the workspace based on the state of the file system.
Our sample tree in code
The resource API allows us to manipulate this resource tree in code. Here we will look at some code snippets for a quick taste of the resource
API. The resource API is defined in a series of interfaces in
org.eclipse.core.resources
. There are interfaces for all of the resource types, such as
IProject
,
IFolder
, and
IFile
. Extensive common protocol is defined in
IResource
. We also make use of the
org.eclipse.core.runtime
interface
IPath
, which represents segmented paths such as resource or file system paths.
Manipulating resources is very similar to manipulating files using java.io.File.
The API is based on handles. When you use API like getProject
or getFolder, you are returned a handle to the
resource. There is no guarantee or requirement that the resource itself exists
until you try to do something with the handle. If you expect a resource to
exist, you can use exists method to ensure
this is the case.
To navigate the workspace from a plug-in, we must first obtain the
IWorkspaceRoot, which represents the top of the resource hierarchy in the workspace.
IWorkspaceRoot myWorkspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
Once we have a workspace root, we can access the projects in the workspace.
IProject myWebProject = myWorkspaceRoot.getProject("MyWeb");
// open if necessary
if (myWebProject.exists() && !myWebProject.isOpen())
myWebProject.open(null);
Before we can manipulate a project, we must open it. Opening the project reads the project's
structure from disk and creates the in-memory object representation of the project's resource tree. Opening a project is an explicit operation since each open project consumes memory to represent the resource tree
internally and open projects participate in various resource lifecycle events
(such as building) which can be lengthy. In general, closed projects
cannot be accessed and will appear empty even though the resources are still present in the file system.
You'll notice that many of these resource examples pass a null parameter when manipulating resources.
Many resource operations are potentially heavyweight enough to warrant progress reporting and
user cancellation. If your code has a user interface, you will typically pass an
IProgressMonitor
,
which allows the
resources plug-in to report progress as the resource is manipulated and allows the user to cancel the operation if desired.
For now, we simply pass null, indicating no
progress monitor.
Once we have an open project, we can access its folders and files, as well as create additional ones.
In the following example we create a file resource from the contents of a file
located outside of our workspace.
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.
}
In the example above, the
first line obtains a handle to the images folder. We must check that the
folder exists before we can do anything
interesting with it. Likewise, when we get the file newLogo,
the handle does not represent a real file until we create the file in the last
line. In this example, we create the file by populating it with the
contents of 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");
Many of the resource API methods include a force
boolean flag which specifies whether resources that are out of synch with the
corresponding files in the file system will be updated anyway. See
IResource
for more information. You can also use
IResource
.isSynchronized
to determine whether a particular resource is in synch with the file system.
In the sample resource tree, we've assumed that all of the project content directories are
in the workspace directory underneath the platform root
directory (C:\MySDK\workspace). This is the default configuration
for projects. However, a project's content directory can be mapped to any
arbitrary directory in some backing file system, perhaps even on a different machine.
The ability to map the location of a project independent of other projects allows the user to
store the contents of a project in a place that makes sense for the project and the project team.
A project's content directory should be considered "out in the open."
This means that users can create, modify, and delete resources
by using the workbench and plug-ins, or by directly using file system based tools and editors.
Resource path names are not complete file system paths. Resource paths are always
based on the project's location (usually the workspace
directory). To obtain the full file system path to a resource, you must query its location using
IResource.getLocationURI.
Howevever, you cannot use
IProjectDescription.setLocation
to change its location, because that method is just a simple setter for a
data structure.
Conversely, if you want to get the corresponding resource object given a file
system path, you can use
IWorkspaceRoot.findFilesForLocationURI
or
IWorkspaceRoot.findContainersForLocationURI.
Resource API and the file system
When we use the resources API to modify our workspace's resource tree, the files
are changed in the file system in addition to updating our resource objects. What about changes to
resource files that happen outside of the platform's API?
External changes to resources will not be reflected in the workspace and resource objects until
detected by the resources plug-in. The resources plug-in also uses a mechanism appropriate for
each particular native operating system for discovering external changes made in the file system.
In addition, clients can use resource API to
reconcile workspace and resource objects with the local file system quietly and without user intervention.
The user can also explicitly force a refresh in the resource navigator view of the workbench.
Many of the methods in the resource APIs include a force parameter which specifies how resources that
are out of sync with the file system should be handled. The API Reference for each method provides specific information
about this parameter. Additional methods in the API allow programmatic control of file system refresh, such as
IResource.refreshLocal(int depth, IProgressMonitor monitor). See
IResource
for information on correct usage and costs.
Plug-ins that wish to supply their own mechanism for periodically refreshing the workspace based on
the state of the external file system may do so using the
org.eclipse.core.resources.refreshProviders
extension point. See
Refresh providers for more information.