Monday, September 22, 2008

Improving Linked Resources in Ganymede - API Changes

Following up on the last post describing the user level changes of our linked resource improvements in Ganymede, I will now go over the underlying API changes.

A important requirement of the implementation of the linked resources improvement was to keep the resource API fully backward compatible: All code previously using the core.resources API should work just fine without any changes.

This is very critical, since so many plugins depend on the core.resources framework that breaking any contract would be unworkable.

Core Resources

The first change is the IResource.setLocation() methods as shown below:
class IResource […]
/**
* Sets the value of the link location for a linked resource.
*
* @param location
* the new location of the target link resource
* @param updateFlags
* bit-wise or of update flag constants ({@link #FORCE},
* {@link #KEEP_HISTORY}, {@link #SHALLOW},
* {@link #BACKGROUND_REFRESH} and {@link #REPLACE}).
* @param monitor
* a progress monitor, or <code>null</code> if progress
* reporting is not desired
* @exception CoreException
* if isLinked() returns false.
*/

public void setLinkLocation(URI location, int updateFlags, IProgressMonitor monitor) throws CoreException;

/**
* Sets the value of the link location for a linked resource.
*
* @param location
* the new location of the target link resource
* @param updateFlags
* bit-wise or of update flag constants ({@link #FORCE},
* {@link #KEEP_HISTORY}, {@link #SHALLOW},
* {@link #BACKGROUND_REFRESH} and {@link #REPLACE}).
* @param monitor
* a progress monitor, or <code>null</code> if progress
* reporting is not desired
* @exception CoreException
* if isLinked() returns false.
*/

public void setLinkLocation(IPath location, int updateFlags, IProgressMonitor monitor) throws CoreException;
}
As documented above, the IResource.setLocation() methods allow changing a linked resource location. The notifiers are then fired appropriatly, so that the UI is automatically updated when a location changes.

Second, since each project now has its own IPathVariableManager, its definition has been updated with the following methods:
class IProject […]

/**
* Returns the path variable manager for this project.
*
* @return the path variable manager
* @see IPathVariableManager
*/

public IPathVariableManager getPathVariableManager();

/**
* Returns a variable relative path equivalent to an absolute path for a
* file or folder in the file system, according to the variables defined in
* this project PathVariableManager. The file or folder need not to exist.
* <p>
*
* @param location
* a path in the local file system
* @return the corresponding variable relative path, or <code>null</code>
* if no such path is available
*/

public IPath getVariableRelativePathLocation(IPath location);
The method getVariableRelativePathLocation() will converts paths such as "C:\foo\bar.c" into "FOO\bar.c", granted than the path variable FOO exists already in the project PathVariableManager and it points to "C:\foo".

A more powerful method is the one available in the IPathVariableManager:
class IPathVariableManager […]

/** Convert an absolute path to path variable relative path.
* For example, converts "C:/foo/bar.txt" into "FOO/bar.txt",
* granted that the path variable "FOO" value is "C:/foo".
*
* The "force" argument allows intermediate path variable to
* be created if for a given path can be relative only to a parent
* of an existing path variable.
*
* For example, if the path "C:/other/file.txt" is to be converted
* and no path variables point to "C:/" or "C:/other" but "FOO"
* points to "C:/foo", an intermediate "OTHER" variable will be
* created relative to "FOO" containing the value "${PARENT-1-FOO}"
* so that the final path returned will be "OTHER/file.txt".
*
* The argument "variableHint" can be used to specify to which
* path variable the path should be made relative to.
*
* @param path The absolute path to be converted
* @param force Set to true if intermediate path variables need to be created if the path is relative only to a parent of an existing path variable.
* @param variableHint The name of the variable to which the path should be relative to, or null for the nearest one.
* @return The converted path
* @exception CoreException if this method fails. Reasons include:
* <ul>
* <li>The variable name is not valid</li>
*/

public IPath convertToRelative(IPath path, boolean force, String variableHint) throws CoreException;
}

Also, a new extension point "variableProviders" is available in the core.resources plugins that allows extending the default path variable list if need be.

Variable providers can be pecified as follows:
<extension

point="org.eclipse.core.resources.variableProviders">

<variableProvider

class="org.myPackage.myPlugin.MyPathVariable"

name="MY_PATH_VARIABLE">

</variableProvider>

</extension>
where "MyPathVariable" must inherit from the "IProjectVariableProvider", defined as follows:

public interface IProjectVariableProvider {

/**
* Returns a variable value
*
* @param variable
* The current variable name.
* @param project
* The project that the variable is being resolved for.
* @return the variable value.
*/

public String getValue(String variable, IProject project);

/**
* If the variable supports extensions (specified as
* "${VARNAME-EXTENSIONNAME}"), this method can return the list of possible
* extensions, or null if none are supported.
*
* @param variable
* The current variable name.
* @param project
* The project that the variable is being resolved for.
* @return the possible variable extensions or null if none are supported.
*/

public Object[] getExtensions(String variable, IProject project);
}


Groups

Groups are seen by clients as a broken folder linked resource. Since groups do not exist on the file system, calling IResource.getLocation() returns null, just as broken folder linked resource do.

All code that handle properly IResource.getLocation() returning null, as they should to support the possibility that a folder linked resource is not resolvable, will work transparently with groups as well.

The groups API changes are as follow:
class IFolder […]
/**
* Creates a new group resource as a member of this handle's parent
* resource. A group is not located anywhere in the file system, and can
* contain only linked files, linked folders and other groups.
* <p>
* This method changes resources; these changes will be reported in a
* subsequent resource change event, including an indication that the folder
* has been added to its parent.
* </p>
* <p>
* This method is long-running; progress and cancellation are provided by
* the given progress monitor.
* </p>
*
* @param updateFlags
* bit-wise or of update flag constants (currently no flags are
* relevant here)
* @param monitor
* a progress monitor, or <code>null</code> if progress
* reporting is not desired
* @exception CoreException
* if this method fails. Reasons include:
* <ul>
* <li> This resource already exists in the workspace.</li>
* <li> The workspace contains a resource of a different type
* at the same path as this resource.</li>
* <li> The parent of this resource does not exist.</li>
* <li> The parent of this resource is not an open project</li>
* <li> The name of this resource is not valid (according to
* <code>IWorkspace.validateName</code>).</li>
* <li> Resource changes are disallowed during certain types
* of resource change event notification. See
* <code>IResourceChangeEvent</code> for more details.</li>
* <li>The team provider for the project which contains this
* folder does not permit groups.</li>
* <li>This folder's project contains a nature which does
* not permit groups.</li>
* </ul>
* @exception OperationCanceledException
* if the operation is canceled. Cancelation can occur even
* if no progress monitor is provided.
* @see IResource#isGroup()
*/

public void createGroup(int updateFlags, IProgressMonitor monitor) throws CoreException;

class IResource […]

/**
* Returns whether this resource is a group.
*
* @return <code>true</code> if this resource is a group, and
* <code>false</code> otherwise
* @see IFolder#createGroup(int, IProgressMonitor)
*/

public boolean isGroup();

No comments: