This proposal deals with improving the Eclipse Platform's support for allowing a Team provider to control the movement of existing resources under its control. The proposal would give Team providers a (headless) way of doing this sort of thing.
Allow pre-validation of rename/move/delete. VCM providers that need to manage a project's namespace would like advance notification of impending resource moves, renames, and deletes. (Other clients would like a similar opportunity to veto inappropriate name changes to their resources; this is a different concern.) We will add a callback so that the relevant VCM provider will be able to register for advance notification with an opportunity to veto. These changes will affect the UI and Core components.The title and description for this item is not quite right. The work item would be better described as:
Allow VCM control over rename/move/delete. Team providers sometimes need tighter control over how project resources are manipulated in the local file system. For instance, a project directory might be a specially mounted remote file system located on a Team server, and require special server communication in order to delete, move, or change the name of a resource. Or the Team provider may track version history across move/renames. (Other clients would like a similar opportunity to veto inappropriate name changes to their resources; this is a different concern.) We will add a headless callback so that the relevant Team provider will be able to control moves, renames, and deletes. These changes will affect the Core and Team components.
(Note that newly-created files are a separate matter; until they exist and have been placed under VCM control, changes to their names are not of interest. This is why create and copy operations are not included in this discussion.)
The workspace operations for moving resources are as follows:
This means that none of the existing operations can ever bring up a UI - all must run headless. If we add hooks to these operations, the hooks must run headless.
The alternative is to add a parallel set of operations that would allow a UI-aware client to pass in a UI context in which to converse with the user (by employing exactly the same design as the recently-added validate-edit support). These new API methods could have more stringent contracts that spell out the conditions which UIs would need. However, this does nothing for existing plug-ins that manipulate resources with the exisiting API methods; these would still need to run headless.
This hook would be filled by the Team component, which would delegate the request to the Team provider for the project that owns the resource. The Team provider would not be obliged to participate in these operations; if it decided to pass, the Core would fall back on its own internal implementation the operation much as it does currently.
Newly-created files and folders are not under version control automatically. Adding a file or folder to version control is a separate follow-on operation that each Team provider will give the user. This is why create and copy operations are not included in this hook.
Resource
public IStatus move(
IPath destination,
boolean force,
boolean keepHistory,
IProgressMonitor monitor)
throws CoreException {
// do easy precondition checks before calling
hook
if (!this.exists()) throw new CoreException(...);
if (findResource(destination) != null) throw
new CoreException(...);
IResourceTree rto = ...;
INamespaceControlHook hook = ...;
boolean done = false;
if (hook != null) {
done = hook.move(this,
destination, force, keepHistory, monitor, rto);
}
if (!done) {
internalMove(this, destination,
force, keepHistory, monitor, rto);
}
if (rto.getStatus().isOK()) {
return rto.getStatus();
} else {
throw new CoreException(rto.getStatus());
}
}
IResource
public IStatus delete(
boolean force,
boolean keepHistory,
IProgressMonitor monitor)
throws CoreException {
// do easy precondition checks before calling
hook
if (!this.exists()) throw new CoreException(...);
IResourceTree rto = ...;
INamespaceControlHook hook = ...;
boolean done = false;
if (hook != null) {
done = hook.delete(this,
force, keepHistory, monitor, rto);
}
if (!done) {
ops = internalDelete(this,
force, keepHistory, monitor, rto);
}
if (rto.getStatus().isOK()) {
return rto.getStatus();
} else {
throw new CoreException(rto.getStatus());
}
}
(The API operations for moving or deleting a project have a slightly extended contract, but the general flavor of the hook is the same.)
The INamespaceControlHook interface appears in the new core extension point, and would be implemented by the Team component, and likely by each Team provider.
package org.eclipse.core.resources.team
public interface INamespaceControlHook {
public boolean delete(IResource resource,
boolean force,
boolean keepHistory,
IProgressMonitor monitor,
IResourceTree rto);
public boolean move(IResource resource,
IResource destination,
boolean force,
boolean keepHistory,
IProgressMonitor monitor,
IResourceTree rto);
//... others for project move, project delete
}
The IResourceTree interface provides the operations that manipulate the resource tree (move markers, properties, collect result statuses) without touching the local file system. The sole implementation would be internal to core, and would be passed as a parameter to the INamespaceControlHook implementation.
package org.eclipse.core.resources.team
public interface IResourceTree {
public void deletedFile(IResource file, IStatus status);
public void deletedFolder(IResource folder, IStatus
status);
public void movedFile(IResource source, IResource dest,
long
destTimestamp, IStatus status);
public void movedFolder(IResource source, IResource
dest, IStatus status);
public void movedEmptyFolder(IResource source,
IResource dest, IStatus status);
public void moved(IResource source, IResource
dest);
public void createdFile(IResource file, long timestamp,
IStatus status);
public void createdFolder(IResource folder, IStatus
status);
public boolean copyToLocalHistory(IResource file, boolean stealable);
// standard building blocks
public void standardDeleteResource(IResource resource,
boolean force, boolean keepHistory,
IProgressMonitor monitor);
public void standardMoveResource(IResource resource,
IResource dest,
boolean force, boolean keepHistory,
IProgressMonitor monitor);
...
//... plus others for moving or deleting a project
}
Identifier: org.eclipse.core.resources.namespaceControl
Description: For providing an implementation of an INamespaceControlHook
to be used in implementing resource moves and deletes. This extension point
tolerates at most one extension.
Configuration Markup:
<!ELEMENT namespaceControl EMPTY>
<!ATTLIST namespaceControl class CDATA #REQUIRED>
class - a fully qualified name of a class which implements org.eclipse.core.resource.team.INamespaceControlHook.
Examples:
The following is an example of using the namespaceControl extension
point.
(in file plugin.xml)
<extension point="org.eclipse.core.resources.namespaceControl">
<namespaceControl class="org.eclipse.team.internal.VCMNamespaceControlHook"/>
</extension>
In order to modify the resource tree, they call IResourceTree methods. These operations affect the resource tree (immediately). If the hook passes the timestamp for a file, it is used; if no timestamp is passed, the local file system is queried for the value.
Likewise for delete methods. All single resource delete should funnel through a single delete operation taking a source resource, a force flag, a keepHistory flag, and a progress monitor. IWorkspace.delete deletes multiple independent resources, and would pass them through the delete pinch point one at a time. Resources scattered across different projects will get routed automatically to the appropriate project-specific Team provider.
Note that each project in the workspace could have a different Team provider (or none at all), and each could map the project root directory to a different drive or OS file system.
The IResourceTree API should also include standard implementations of the operations as additional building blocks. This would make it simpler for a Team provider to embellish the standard behavior instead of having to reimplement it from scratch.
Sequence of calls:
Folder[/P/f].move(dest=Path[/P/g],force=false,keepHistory=true,monitor)
- VCMNSHook.move(source=Folder[/P/f],dest=Folder[/P/g],force,keepHistory,monitor,rto)
-- VCMProvider1.move(source,dest,force,keepHistory,monitor,rto)
--- java.io creates folder /P/g
--- rto.createdFolder(dest, OK)
---- Folder[/P/g] created in resource tree
--- rto.copyToLocalHistory(File[P/f/a.txt],steal=false)
--- check timestamp on /P/f/a.txt is t1
--- java.io rename file /P/f/a.txt to /P/g/a.txt
--- new timestamp of /P/g/a.txt is t1'
--- rto.movedFile(File[/P/f/a.txt], t1, File[/P/g/a.txt], t1', OK)
---- File[/P/g/a.txt] created in resource tree with timestamp t1'
---- markers and properties copied from File[/P/f/a.txt] to File[/P/g/a.txt]
---- File[/P/f/a.txt] deleted from resource tree
---- delta for File[/P/g/a.txt] records moved from File[/P/f/a.txt]
---- delta for File[/P/f/a.txt] records moved to File[/P/g/a.txt]
--- check timestamp on /P/f/b.txt is t2
--- java.io rename file /P/f/b.txt to /P/g/b.txt
--- new timestamp of /P/g/b.txt is t2'
--- rto.movedFile(File[/P/f/b.txt], t2, File[/P/g/b.txt], t2', OK)
---- File[/P/g/b.txt] created in resource tree with timestamp t2'
---- markers and properties copied from File[/P/f/b.txt] to File[/P/g/b.txt]
---- File[/P/f/b.txt] deleted from resource tree
---- delta for File[/P/g/b.txt] records moved from File[/P/f/b.txt]
---- delta for File[/P/f/b.txt] records moved to File[/P/g/b.txt]
--- java.io deletes folder /P/f
--- rto.moved(source,dest)
---- markers and properties copied from Folder[/P/f] to Folder[/P/g]
---- delta for Folder[/P/g] records moved from Folder[/P/f]
---- delta for Folder[/P/f] records moved to Folder[/P/g]
--- return true
-- return true
- return OK