/*******************************************************************************
* Copyright (c) 2000, 2010 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Red Hat Incorporated - loadProjectDescription(InputStream)
* Serge Beauchamp (Freescale Semiconductor) - [252996] add resource filtering
* Serge Beauchamp (Freescale Semiconductor) - [229633] Group and Project Path Variable Support
*******************************************************************************/
package org.eclipse.core.internal.resources;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.internal.events.*;
import org.eclipse.core.internal.localstore.FileSystemResourceManager;
import org.eclipse.core.internal.properties.IPropertyManager;
import org.eclipse.core.internal.refresh.RefreshManager;
import org.eclipse.core.internal.utils.*;
import org.eclipse.core.internal.watson.*;
import org.eclipse.core.resources.*;
import org.eclipse.core.resources.team.*;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.Bundle;
import org.xml.sax.InputSource;
/**
* The workspace class is the monolithic nerve center of the resources plugin. All interesting
* functionality stems from this class. </p>
* <p>
* The lifecycle of the resources plugin is encapsulated by the {@link #open(IProgressMonitor)} and
* {@link #close(IProgressMonitor)} methods. A closed workspace is completely unusable - any attempt
* to access or modify interesting workspace state on a closed workspace will fail.
* </p>
* <p>
* All modifications to the workspace occur within the context of a workspace operation. A workspace
* operation is implemented using the following sequence:
*
* <pre>
* try {
* prepareOperation(...);
* //check preconditions
* beginOperation(...);
* //perform changes
* } finally {
* endOperation(...);
* }
* </pre>
*
* Workspace operations can be nested arbitrarily. A "top level" workspace operation is an operation
* that is not nested within another workspace operation in the current thread. See the javadoc of
* {@link #prepareOperation(ISchedulingRule, IProgressMonitor)}, {@link #beginOperation(boolean)},
* and {@link #endOperation(ISchedulingRule, boolean, IProgressMonitor)} for more details.
* </p>
* <p>
* Major areas of functionality are farmed off to various manager classes. Open a type hierarchy on
* {@link IManager} to see all the different managers. Each manager is typically referenced three
* times in this class: Once in {@link #startup(IProgressMonitor)} when it is instantiated, once in
* {@link #shutdown(IProgressMonitor)} when it is destroyed, and once in a manager accessor method.
* </p>
*/
public class Workspace extends PlatformObject implements IWorkspace, ICoreConstants {
public static final boolean caseSensitive= Platform.OS_MACOSX.equals(Platform.getOS()) ? false : new java.io.File("a").compareTo(new java.io.File("A")) != 0; //$NON-NLS-1$ //$NON-NLS-2$
// whether the resources plugin is in debug mode.
public static boolean DEBUG= false;
/**
* Work manager should never be accessed directly because accessor asserts that workspace is
* still open.
*/
protected WorkManager _workManager;
protected AliasManager aliasManager;
protected BuildManager buildManager;
protected IProject[] buildOrder= null;
protected CharsetManager charsetManager;
protected ContentDescriptionManager contentDescriptionManager;
/** indicates if the workspace crashed in a previous session */
protected boolean crashed= false;
protected final IWorkspaceRoot defaultRoot= new WorkspaceRoot(Path.ROOT, this);
protected WorkspacePreferences description;
protected FileSystemResourceManager fileSystemManager;
protected final HashSet lifecycleListeners= new HashSet(10);
protected LocalMetaArea localMetaArea;
/**
* Helper class for performing validation of resource names and locations.
*/
protected final LocationValidator locationValidator= new LocationValidator(this);
protected MarkerManager markerManager;
/**
* The currently installed Move/Delete hook.
*/
protected IMoveDeleteHook moveDeleteHook= null;
protected NatureManager natureManager;
protected FilterTypeManager filterManager;
protected long nextMarkerId= 0;
protected long nextNodeId= 1;
protected NotificationManager notificationManager;
protected boolean openFlag= false;
protected ElementTree operationTree; // tree at the start of the current operation
protected PathVariableManager pathVariableManager;
protected IPropertyManager propertyManager;
protected RefreshManager refreshManager;
/**
* Scheduling rule factory. This field is null if the factory has not been used yet. The
* accessor method should be used rather than accessing this field directly.
*/
private IResourceRuleFactory ruleFactory;
protected SaveManager saveManager;
/**
* File modification validation. If it is true and validator is null, we try/initialize
* validator first time through. If false, there is no validator.
*/
protected boolean shouldValidate= true;
/**
* Job that performs periodic string pool canonicalization.
*/
private StringPoolJob stringPoolJob;
/**
* The synchronizer
*/
protected Synchronizer synchronizer;
/**
* The currently installed team hook.
*/
protected TeamHook teamHook= null;
/**
* The workspace tree. The tree is an in-memory representation of the resources that make up the
* workspace. The tree caches the structure and state of files and directories on disk (their
* existence and last modified times). When external parties make changes to the files on disk,
* this representation becomes out of sync. A local refresh reconciles the state of the files on
* disk with this tree (@link {@link IResource#refreshLocal(int, IProgressMonitor)}). The tree
* is also used to store metadata associated with resources in the workspace (markers,
* properties, etc).
*
* While the ElementTree data structure can handle both concurrent reads and concurrent writes,
* write access to the tree is governed by {@link WorkManager}.
*/
protected ElementTree tree;
/**
* This field is used to control access to the workspace tree during resource change
* notifications. It tracks which thread, if any, is in the middle of a resource change
* notification. This is used to cause attempts to modify the workspace during notifications to
* fail.
*/
protected Thread treeLocked= null;
/**
* The currently installed file modification validator.
*/
protected IFileModificationValidator validator= null;
/**
* Deletes all the files and directories from the given root down (inclusive). Returns false if
* we could not delete some file or an exception occurred at any point in the deletion. Even if
* an exception occurs, a best effort is made to continue deleting.
*/
public static boolean clear(java.io.File root) {
boolean result= clearChildren(root);
try {
if (root.exists())
result&= root.delete();
} catch (Exception e) {
result= false;
}
return result;
}
/**
* Deletes all the files and directories from the given root down, except for the root itself.
* Returns false if we could not delete some file or an exception occurred at any point in the
* deletion. Even if an exception occurs, a best effort is made to continue deleting.
*/
public static boolean clearChildren(java.io.File root) {
boolean result= true;
if (root.isDirectory()) {
String[] list= root.list();
// for some unknown reason, list() can return null.
// Just skip the children If it does.
if (list != null)
for (int i= 0; i < list.length; i++)
result&= clear(new java.io.File(root, list[i]));
}
return result;
}
public static WorkspaceDescription defaultWorkspaceDescription() {
return new WorkspaceDescription("Workspace"); //$NON-NLS-1$
}
/**
* Returns true if the object at the specified position has any other copy in the given array.
*/
private static boolean isDuplicate(Object[] array, int position) {
if (array == null || position >= array.length)
return false;
for (int j= position - 1; j >= 0; j--)
if (array[j].equals(array[position]))
return true;
return false;
}
public Workspace() {
super();
localMetaArea= new LocalMetaArea();
tree= new ElementTree();
/* tree should only be modified during operations */
tree.immutable();
treeLocked= Thread.currentThread();
tree.setTreeData(newElement(IResource.ROOT));
}
/**
* Indicates that a build is about to occur. Broadcasts the necessary deltas before the build
* starts. Note that this will cause POST_BUILD to be automatically done at the end of the
* operation in which the build occurs.
*/
protected void aboutToBuild(Object source, int trigger) {
//fire a POST_CHANGE first to ensure everyone is up to date before firing PRE_BUILD
broadcastPostChange();
broadcastBuildEvent(source, IResourceChangeEvent.PRE_BUILD, trigger);
}
/**
* Adds a listener for internal workspace lifecycle events. There is no way to remove lifecycle
* listeners.
*/
public void addLifecycleListener(ILifecycleListener listener) {
lifecycleListeners.add(listener);
}
/* (non-Javadoc)
* @see IWorkspace#addResourceChangeListener(IResourceChangeListener)
*/
public void addResourceChangeListener(IResourceChangeListener listener) {
notificationManager.addListener(listener, IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_DELETE | IResourceChangeEvent.POST_CHANGE);
}
/* (non-Javadoc)
* @see IWorkspace#addResourceChangeListener(IResourceChangeListener, int)
*/
public void addResourceChangeListener(IResourceChangeListener listener, int eventMask) {
notificationManager.addListener(listener, eventMask);
}
/* (non-Javadoc)
* @see IWorkspace#addSaveParticipant(Plugin, ISaveParticipant)
*/
public ISavedState addSaveParticipant(Plugin plugin, ISaveParticipant participant) throws CoreException {
Assert.isNotNull(plugin, "Plugin must not be null"); //$NON-NLS-1$
Assert.isNotNull(participant, "Participant must not be null"); //$NON-NLS-1$
return saveManager.addParticipant(plugin.getBundle().getSymbolicName(), participant);
}
/* (non-Javadoc)
* @see IWorkspace#addSaveParticipant(String, ISaveParticipant)
*/
public ISavedState addSaveParticipant(String pluginId, ISaveParticipant participant) throws CoreException {
Assert.isNotNull(pluginId, "Plugin id must not be null"); //$NON-NLS-1$
Assert.isNotNull(participant, "Participant must not be null"); //$NON-NLS-1$
return saveManager.addParticipant(pluginId, participant);
}
public void beginOperation(boolean createNewTree) throws CoreException {
WorkManager workManager= getWorkManager();
workManager.incrementNestedOperations();
if (!workManager.isBalanced())
Assert.isTrue(false, "Operation was not prepared."); //$NON-NLS-1$
if (workManager.getPreparedOperationDepth() > 1) {
if (createNewTree && tree.isImmutable())
newWorkingTree();
return;
}
// stash the current tree as the basis for this operation.
operationTree= tree;
if (createNewTree && tree.isImmutable())
newWorkingTree();
}
public void broadcastBuildEvent(Object source, int type, int buildTrigger) {
ResourceChangeEvent event= new ResourceChangeEvent(source, type, buildTrigger, null);
notificationManager.broadcastChanges(tree, event, false);
}
/**
* Broadcasts an internal workspace lifecycle event to interested internal listeners.
*/
protected void broadcastEvent(LifecycleEvent event) throws CoreException {
for (Iterator it= lifecycleListeners.iterator(); it.hasNext();) {
ILifecycleListener listener= (ILifecycleListener)it.next();
listener.handleEvent(event);
}
}
public void broadcastPostChange() {
ResourceChangeEvent event= new ResourceChangeEvent(this, IResourceChangeEvent.POST_CHANGE, 0, null);
notificationManager.broadcastChanges(tree, event, true);
}
/* (non-Javadoc)
* @see IWorkspace#build(int, IProgressMonitor)
*/
public void build(int trigger, IProgressMonitor monitor) throws CoreException {
monitor= Policy.monitorFor(monitor);
final ISchedulingRule rule= getRuleFactory().buildRule();
try {
monitor.beginTask("", Policy.opWork); //$NON-NLS-1$
try {
prepareOperation(rule, monitor);
beginOperation(true);
aboutToBuild(this, trigger);
IStatus result;
try {
result= getBuildManager().build(trigger, Policy.subMonitorFor(monitor, Policy.opWork));
} finally {
//must fire POST_BUILD if PRE_BUILD has occurred
broadcastBuildEvent(this, IResourceChangeEvent.POST_BUILD, trigger);
}
if (!result.isOK())
throw new ResourceException(result);
} finally {
//building may close the tree, but we are still inside an operation so open it
if (tree.isImmutable())
newWorkingTree();
endOperation(rule, false, Policy.subMonitorFor(monitor, Policy.endOpWork));
}
} finally {
monitor.done();
}
}
/**
* Returns whether creating executable extensions is acceptable at this point in time. In
* particular, returns <code>false</code> when the system bundle is shutting down, which only
* occurs when the entire framework is exiting.
*/
private boolean canCreateExtensions() {
return Platform.getBundle("org.eclipse.osgi").getState() != Bundle.STOPPING; //$NON-NLS-1$
}
/* (non-Javadoc)
* @see IWorkspace#checkpoint(boolean)
*/
public void checkpoint(boolean build) {
try {
final ISchedulingRule rule= getWorkManager().getNotifyRule();
try {
prepareOperation(rule, null);
beginOperation(true);
broadcastPostChange();
} finally {
endOperation(rule, build, null);
}
} catch (CoreException e) {
Policy.log(e.getStatus());
}
}
/**
* Closes this workspace; ignored if this workspace is not open. The state of this workspace is
* not saved before the workspace is shut down.
* <p>
* If the workspace was saved immediately prior to closing, it will have the same set of
* projects (open or closed) when reopened for a subsequent session. Otherwise, closing a
* workspace may lose some or all of the changes made since the last save or snapshot.
* </p>
* <p>
* Note that session properties are discarded when a workspace is closed.
* </p>
* <p>
* This method is long-running; progress and cancellation are provided by the given progress
* monitor.
* </p>
*
* @param monitor a progress monitor, or <code>null</code> if progress reporting and
* cancellation are not desired
* @exception CoreException if the workspace could not be shutdown.
*/
public void close(IProgressMonitor monitor) throws CoreException {
//nothing to do if the workspace failed to open
if (!isOpen())
return;
monitor= Policy.monitorFor(monitor);
try {
String msg= Messages.resources_closing_0;
int rootCount= tree.getChildCount(Path.ROOT);
monitor.beginTask(msg, rootCount + 2);
monitor.subTask(msg);
//this operation will never end because the world is going away
try {
stringPoolJob.cancel();
//shutdown save manager now so a last snapshot can be taken before we close
//note: you can't call #save() from within a nested operation
saveManager.shutdown(null);
prepareOperation(getRoot(), monitor);
//shutdown notification first to avoid calling third parties during shutdown
notificationManager.shutdown(null);
beginOperation(true);
IProject[] projects= getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
for (int i= 0; i < projects.length; i++) {
//notify managers of closing so they can cleanup
broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_CLOSE, projects[i]));
monitor.worked(1);
}
//empty the workspace tree so we leave in a clean state
deleteResource(getRoot());
openFlag= false;
// endOperation not needed here
} finally {
// Shutdown needs to be executed regardless of failures
shutdown(Policy.subMonitorFor(monitor, 2, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL));
}
} finally {
//release the scheduling rule to be a good job citizen
Job.getJobManager().endRule(getRoot());
monitor.done();
}
}
/**
* Computes the global total ordering of all open projects in the workspace based on project
* references. If an existing and open project P references another existing and open project Q
* also included in the list, then Q should come before P in the resulting ordering. Closed and
* non- existent projects are ignored, and will not appear in the result. References to
* non-existent or closed projects are also ignored, as are any self- references.
* <p>
* When there are choices, the choice is made in a reasonably stable way. For example, given an
* arbitrary choice between two projects, the one with the lower collating project name is
* usually selected.
* </p>
* <p>
* When the project reference graph contains cyclic references, it is impossible to honor all of
* the relationships. In this case, the result ignores as few relationships as possible. For
* example, if P2 references P1, P4 references P3, and P2 and P3 reference each other, then
* exactly one of the relationships between P2 and P3 will have to be ignored. The outcome will
* be either [P1, P2, P3, P4] or [P1, P3, P2, P4]. The result also contains complete details of
* any cycles present.
* </p>
*
* @return result describing the global project order
* @since 2.1
*/
private ProjectOrder computeFullProjectOrder() {
// determine the full set of accessible projects in the workspace
// order the set in descending alphabetical order of project name
SortedSet allAccessibleProjects= new TreeSet(new Comparator() {
public int compare(Object x, Object y) {
IProject px= (IProject)x;
IProject py= (IProject)y;
return py.getName().compareTo(px.getName());
}
});
IProject[] allProjects= getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
// List<IProject[]> edges
List edges= new ArrayList(allProjects.length);
for (int i= 0; i < allProjects.length; i++) {
Project project= (Project)allProjects[i];
// ignore projects that are not accessible
if (!project.isAccessible())
continue;
ProjectDescription desc= project.internalGetDescription();
if (desc == null)
continue;
//obtain both static and dynamic project references
IProject[] refs= desc.getAllReferences(false);
allAccessibleProjects.add(project);
for (int j= 0; j < refs.length; j++) {
IProject ref= refs[j];
// ignore self references and references to projects that are not accessible
if (ref.isAccessible() && !ref.equals(project))
edges.add(new IProject[] { project, ref });
}
}
ProjectOrder fullProjectOrder= ComputeProjectOrder.computeProjectOrder(allAccessibleProjects, edges);
return fullProjectOrder;
}
/**
* Implementation of API method declared on IWorkspace.
*
* @see IWorkspace#computePrerequisiteOrder(IProject[])
* @deprecated Replaced by <code>IWorkspace.computeProjectOrder</code>, which produces a more
* usable result when there are cycles in project reference graph.
*/
public IProject[][] computePrerequisiteOrder(IProject[] targets) {
return computePrerequisiteOrder1(targets);
}
/*
* Compatible reimplementation of
* <code>IWorkspace.computePrerequisiteOrder</code> using
* <code>IWorkspace.computeProjectOrder</code>.
*
* @since 2.1
*/
private IProject[][] computePrerequisiteOrder1(IProject[] projects) {
IWorkspace.ProjectOrder r= computeProjectOrder(projects);
if (!r.hasCycles) {
return new IProject[][] { r.projects, new IProject[0] };
}
// when there are cycles, we need to remove all knotted projects from
// r.projects to form result[0] and merge all knots to form result[1]
// Set<IProject> bad
Set bad= new HashSet();
// Set<IProject> bad
Set keepers= new HashSet(Arrays.asList(r.projects));
for (int i= 0; i < r.knots.length; i++) {
IProject[] knot= r.knots[i];
for (int j= 0; j < knot.length; j++) {
IProject project= knot[j];
// keep only selected projects in knot
if (keepers.contains(project)) {
bad.add(project);
}
}
}
IProject[] result2= new IProject[bad.size()];
bad.toArray(result2);
// List<IProject> p
List p= new LinkedList();
p.addAll(Arrays.asList(r.projects));
for (Iterator it= p.listIterator(); it.hasNext();) {
IProject project= (IProject)it.next();
if (bad.contains(project)) {
// remove knotted projects from the main answer
it.remove();
}
}
IProject[] result1= new IProject[p.size()];
p.toArray(result1);
return new IProject[][] { result1, result2 };
}
/* (non-Javadoc)
* @see IWorkspace#computeProjectOrder(IProject[])
* @since 2.1
*/
public ProjectOrder computeProjectOrder(IProject[] projects) {
// compute the full project order for all accessible projects
ProjectOrder fullProjectOrder= computeFullProjectOrder();
// "fullProjectOrder.projects" contains no inaccessible projects
// but might contain accessible projects omitted from "projects"
// optimize common case where "projects" includes everything
int accessibleCount= 0;
for (int i= 0; i < projects.length; i++) {
if (projects[i].isAccessible()) {
accessibleCount++;
}
}
// no filtering required if the subset accounts for the full list
if (accessibleCount == fullProjectOrder.projects.length) {
return fullProjectOrder;
}
// otherwise we need to eliminate mention of other projects...
// ... from "fullProjectOrder.projects"...
// Set<IProject> keepers
Set keepers= new HashSet(Arrays.asList(projects));
// List<IProject> projects
List reducedProjects= new ArrayList(fullProjectOrder.projects.length);
for (int i= 0; i < fullProjectOrder.projects.length; i++) {
IProject project= fullProjectOrder.projects[i];
if (keepers.contains(project)) {
// remove projects not in the initial subset
reducedProjects.add(project);
}
}
IProject[] p1= new IProject[reducedProjects.size()];
reducedProjects.toArray(p1);
// ... and from "fullProjectOrder.knots"
// List<IProject[]> knots
List reducedKnots= new ArrayList(fullProjectOrder.knots.length);
for (int i= 0; i < fullProjectOrder.knots.length; i++) {
IProject[] knot= fullProjectOrder.knots[i];
List x= new ArrayList(knot.length);
for (int j= 0; j < knot.length; j++) {
IProject project= knot[j];
if (keepers.contains(project)) {
x.add(project);
}
}
// keep knots containing 2 or more projects in the specified subset
if (x.size() > 1) {
reducedKnots.add(x.toArray(new IProject[x.size()]));
}
}
IProject[][] k1= new IProject[reducedKnots.size()][];
// okay to use toArray here because reducedKnots elements are IProject[]
reducedKnots.toArray(k1);
return new ProjectOrder(p1, (k1.length > 0), k1);
}
/* (non-Javadoc)
* @see IWorkspace#copy(IResource[], IPath, boolean, IProgressMonitor)
*/
public IStatus copy(IResource[] resources, IPath destination, boolean force, IProgressMonitor monitor) throws CoreException {
int updateFlags= force ? IResource.FORCE : IResource.NONE;
return copy(resources, destination, updateFlags, monitor);
}
/* (non-Javadoc)
* @see IWorkspace#copy(IResource[], IPath, int, IProgressMonitor)
*/
public IStatus copy(IResource[] resources, IPath destination, int updateFlags, IProgressMonitor monitor) throws CoreException {
monitor= Policy.monitorFor(monitor);
try {
Assert.isLegal(resources != null);
int opWork= Math.max(resources.length, 1);
int totalWork= Policy.totalWork * opWork / Policy.opWork;
String message= Messages.resources_copying_0;
monitor.beginTask(message, totalWork);
if (resources.length == 0)
return Status.OK_STATUS;
// to avoid concurrent changes to this array
resources= (IResource[])resources.clone();
IPath parentPath= null;
message= Messages.resources_copyProblem;
MultiStatus status= new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, message, null);
try {
prepareOperation(getRoot(), monitor);
beginOperation(true);
for (int i= 0; i < resources.length; i++) {
Policy.checkCanceled(monitor);
IResource resource= resources[i];
if (resource == null || isDuplicate(resources, i)) {
monitor.worked(1);
continue;
}
// test siblings
if (parentPath == null)
parentPath= resource.getFullPath().removeLastSegments(1);
if (parentPath.equals(resource.getFullPath().removeLastSegments(1))) {
// test copy requirements
try {
IPath destinationPath= destination.append(resource.getName());
IStatus requirements= ((Resource)resource).checkCopyRequirements(destinationPath, resource.getType(), updateFlags);
if (requirements.isOK()) {
try {
resource.copy(destinationPath, updateFlags, Policy.subMonitorFor(monitor, 1));
} catch (CoreException e) {
status.merge(e.getStatus());
}
} else {
monitor.worked(1);
status.merge(requirements);
}
} catch (CoreException e) {
monitor.worked(1);
status.merge(e.getStatus());
}
} else {
monitor.worked(1);
message= NLS.bind(Messages.resources_notChild, resources[i].getFullPath(), parentPath);
status.merge(new ResourceStatus(IResourceStatus.OPERATION_FAILED, resources[i].getFullPath(), message));
}
}
} catch (OperationCanceledException e) {
getWorkManager().operationCanceled();
throw e;
} finally {
endOperation(getRoot(), true, Policy.subMonitorFor(monitor, totalWork - opWork));
}
if (status.matches(IStatus.ERROR))
throw new ResourceException(status);
return status.isOK() ? Status.OK_STATUS : (IStatus)status;
} finally {
monitor.done();
}
}
protected void copyTree(IResource source, IPath destination, int depth,
int updateFlags, boolean keepSyncInfo) throws CoreException {
copyTree(source, destination, depth, updateFlags, keepSyncInfo, false, source.getType() == IResource.PROJECT);
}
private void copyTree(IResource source, IPath destination, int depth,
int updateFlags, boolean keepSyncInfo, boolean moveResources, boolean movingProject)
throws CoreException {
// retrieve the resource at the destination if there is one (phantoms included).
// if there isn't one, then create a new handle based on the type that we are
// trying to copy
IResource destinationResource= getRoot().findMember(destination, true);
int destinationType;
if (destinationResource == null) {
if (source.getType() == IResource.FILE)
destinationType= IResource.FILE;
else if (destination.segmentCount() == 1)
destinationType= IResource.PROJECT;
else
destinationType= IResource.FOLDER;
destinationResource= newResource(destination, destinationType);
} else
destinationType= destinationResource.getType();
// create the resource at the destination
ResourceInfo sourceInfo= ((Resource)source).getResourceInfo(true, false);
if (destinationType != source.getType()) {
sourceInfo= (ResourceInfo)sourceInfo.clone();
sourceInfo.setType(destinationType);
}
ResourceInfo newInfo= createResource(destinationResource, sourceInfo, false, false, keepSyncInfo);
// get/set the node id from the source's resource info so we can later put it in the
// info for the destination resource. This will help us generate the proper deltas,
// indicating a move rather than a add/delete
newInfo.setNodeId(sourceInfo.getNodeId());
// preserve local sync info but not location info
newInfo.setFlags(newInfo.getFlags() | (sourceInfo.getFlags() & M_LOCAL_EXISTS));
newInfo.setFileStoreRoot(null);
// forget content-related caching flags
newInfo.clear(M_CONTENT_CACHE);
// update link locations in project descriptions
if (source.isLinked()) {
LinkDescription linkDescription;
URI sourceLocationURI= transferVariableDefinition(source, destinationResource, source.getLocationURI());
if (((updateFlags & IResource.SHALLOW) != 0) || ((Resource)source).isUnderVirtual()) {
//for shallow move the destination is a linked resource with the same location
newInfo.set(ICoreConstants.M_LINK);
linkDescription= new LinkDescription(destinationResource, sourceLocationURI);
} else {
//for deep move the destination is not a linked resource
newInfo.clear(ICoreConstants.M_LINK);
linkDescription= null;
}
if (moveResources && !movingProject) {
if (((Project)source.getProject()).internalGetDescription().setLinkLocation(source.getProjectRelativePath(), null))
((Project)source.getProject()).writeDescription(updateFlags);
}
Project project= (Project)destinationResource.getProject();
project.internalGetDescription().setLinkLocation(destinationResource.getProjectRelativePath(), linkDescription);
project.writeDescription(updateFlags);
newInfo.setFileStoreRoot(null);
}
// update filters in project descriptions
if (source.getProject().exists() && source instanceof Container && ((Container)source).hasFilters()) {
Project sourceProject= (Project)source.getProject();
LinkedList/*<FilterDescription>*/originalDescriptions= sourceProject.internalGetDescription().getFilter(source.getProjectRelativePath());
LinkedList/*<FilterDescription>*/filterDescriptions= FilterDescription.copy(originalDescriptions, destinationResource);
if (moveResources && !movingProject) {
if (((Project)source.getProject())
.internalGetDescription()
.setFilters(source.getProjectRelativePath(), null))
((Project)source.getProject()).writeDescription(updateFlags);
}
Project project= (Project)destinationResource.getProject();
project.internalGetDescription().setFilters(destinationResource.getProjectRelativePath(), filterDescriptions);
project.writeDescription(updateFlags);
}
// do the recursion. if we have a file then it has no members so return. otherwise
// recursively call this method on the container's members if the depth tells us to
if (depth == IResource.DEPTH_ZERO || source.getType() == IResource.FILE)
return;
if (depth == IResource.DEPTH_ONE)
depth= IResource.DEPTH_ZERO;
//copy .project file first if project is being copied, otherwise links won't be able to update description
boolean projectCopy= source.getType() == IResource.PROJECT && destinationType == IResource.PROJECT;
if (projectCopy) {
IResource dotProject= ((Project)source).findMember(IProjectDescription.DESCRIPTION_FILE_NAME);
if (dotProject != null)
copyTree(dotProject, destination.append(dotProject.getName()), depth, updateFlags, keepSyncInfo, moveResources, movingProject);
}
IResource[] children= ((IContainer)source).members(IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN);
for (int i= 0, imax= children.length; i < imax; i++) {
String childName= children[i].getName();
if (!projectCopy || !childName.equals(IProjectDescription.DESCRIPTION_FILE_NAME)) {
IPath childPath= destination.append(childName);
copyTree(children[i], childPath, depth, updateFlags, keepSyncInfo, moveResources, movingProject);
}
}
}
public URI transferVariableDefinition(IResource source, IResource dest,
URI sourceURI) throws CoreException {
IPath srcLoc= source.getLocation();
IPath srcRawLoc= source.getRawLocation();
if ((srcLoc != null) && !srcRawLoc.equals(srcLoc)) {
// the location is variable relative
if (!source.getProject().equals(dest.getProject())) {
String variable= srcRawLoc.segment(0);
variable= copyVariable(source, dest,
variable);
IPath newLocation= Path.fromPortableString(variable).append(
srcRawLoc.removeFirstSegments(1));
sourceURI= toURI(newLocation);
} else {
sourceURI= toURI(srcRawLoc);
}
}
return sourceURI;
}
URI toURI(IPath path) {
if (path.isAbsolute())
return org.eclipse.core.filesystem.URIUtil.toURI(path);
try {
return new URI(null, null, path.toPortableString(), null);
} catch (URISyntaxException e) {
return org.eclipse.core.filesystem.URIUtil.toURI(path);
}
}
String copyVariable(IResource source, IResource dest, String variable)
throws CoreException {
IPathVariableManager destPathVariableManager= dest.getPathVariableManager();
IPathVariableManager srcPathVariableManager= source.getPathVariableManager();
IPath srcValue= URIUtil.toPath(srcPathVariableManager.getURIValue(variable));
if (srcValue == null) // if the variable doesn't exist, return another
// variable that doesn't exist either
return PathVariableUtil.getUniqueVariableName(variable, dest);
IPath resolvedSrcValue= URIUtil.toPath(srcPathVariableManager.resolveURI(URIUtil.toURI(srcValue)));
boolean variableExisted= false;
// look if the exact same variable exists
if (destPathVariableManager.isDefined(variable)) {
variableExisted= true;
IPath destValue=
URIUtil.toPath(destPathVariableManager.getURIValue(variable));
if (destValue != null && URIUtil.toPath(destPathVariableManager.resolveURI(URIUtil.toURI(destValue))).equals(
resolvedSrcValue))
return variable;
}
// look if one variable in the destination project matches
String[] variables= destPathVariableManager.getPathVariableNames();
for (int i= 0; i < variables.length; i++) {
if (!PathVariableUtil.isPreferred(variables[i]))
continue;
IPath resolveDestVariable= URIUtil.toPath(destPathVariableManager
.resolveURI(destPathVariableManager.getURIValue(variables[i])));
if (resolveDestVariable != null && resolveDestVariable.equals(resolvedSrcValue)) {
return variables[i];
}
}
// if the variable doesn't exist in the dest project, or
// if the value is different than the source project, we have to create
// an equivalent.
String destVariable= PathVariableUtil.getUniqueVariableName(variable, dest);
boolean shouldConvertToRelative= true;
if (!srcValue.equals(resolvedSrcValue) && !variableExisted) {
// the variable content contains references to more variables
String[] referencedVariables= PathVariableUtil
.splitVariableNames(srcValue.toPortableString());
shouldConvertToRelative= false;
// If the variable value is of type ${PARENT-COUNT-VAR},
// we can avoid generating an intermediate variable and convert it directly.
if (referencedVariables.length == 1) {
if (PathVariableUtil.isParentVariable(referencedVariables[0]))
shouldConvertToRelative= true;
}
if (!shouldConvertToRelative) {
String[] segments= PathVariableUtil
.splitVariablesAndContent(srcValue.toPortableString());
StringBuffer result= new StringBuffer();
for (int i= 0; i < segments.length; i++) {
String var= PathVariableUtil
.extractVariable(segments[i]);
if (var.length() > 0) {
String copiedVariable= copyVariable(source, dest, var);
int index= segments[i].indexOf(var);
if (index != -1) {
result.append(segments[i].substring(0, index));
result.append(copiedVariable);
int start= index + var.length();
int end= segments[i].length();
result.append(segments[i].substring(start, end));
}
} else
result.append(segments[i]);
}
srcValue= Path.fromPortableString(result.toString());
}
}
if (shouldConvertToRelative) {
IPath relativeSrcValue= PathVariableUtil.convertToPathRelativeMacro(destPathVariableManager, resolvedSrcValue, dest, true, null);
if (relativeSrcValue != null)
srcValue= relativeSrcValue;
}
destPathVariableManager.setURIValue(destVariable, URIUtil.toURI(srcValue));
return destVariable;
}
/**
* Returns the number of resources in a subtree of the resource tree.
*
* @param root The subtree to count resources for
* @param depth The depth of the subtree to count
* @param phantom If true, phantoms are included, otherwise they are ignored.
*/
public int countResources(IPath root, int depth, final boolean phantom) {
if (!tree.includes(root))
return 0;
switch (depth) {
case IResource.DEPTH_ZERO:
return 1;
case IResource.DEPTH_ONE:
return 1 + tree.getChildCount(root);
case IResource.DEPTH_INFINITE:
final int[] count= new int[1];
IElementContentVisitor visitor= new IElementContentVisitor() {
public boolean visitElement(ElementTree aTree, IPathRequestor requestor, Object elementContents) {
if (phantom || !((ResourceInfo)elementContents).isSet(M_PHANTOM))
count[0]++;
return true;
}
};
new ElementTreeIterator(tree, root).iterate(visitor);
return count[0];
}
return 0;
}
/*
* Creates the given resource in the tree and returns the new resource info object.
* If phantom is true, the created element is marked as a phantom.
* If there is already be an element in the tree for the given resource
* in the given state (i.e., phantom), a CoreException is thrown.
* If there is already a phantom in the tree and the phantom flag is false,
* the element is overwritten with the new element. (but the synchronization
* information is preserved)
*/
public ResourceInfo createResource(IResource resource, boolean phantom) throws CoreException {
return createResource(resource, null, phantom, false, false);
}
/**
* Creates a resource, honoring update flags requesting that the resource be immediately made
* derived, hidden and/or team private
*/
public ResourceInfo createResource(IResource resource, int updateFlags) throws CoreException {
ResourceInfo info= createResource(resource, null, false, false, false);
if ((updateFlags & IResource.DERIVED) != 0)
info.set(M_DERIVED);
if ((updateFlags & IResource.TEAM_PRIVATE) != 0)
info.set(M_TEAM_PRIVATE_MEMBER);
if ((updateFlags & IResource.HIDDEN) != 0)
info.set(M_HIDDEN);
// if ((updateFlags & IResource.VIRTUAL) != 0)
// info.set(M_VIRTUAL);
return info;
}
/*
* Creates the given resource in the tree and returns the new resource info object.
* If phantom is true, the created element is marked as a phantom.
* If there is already be an element in the tree for the given resource
* in the given state (i.e., phantom), a CoreException is thrown.
* If there is already a phantom in the tree and the phantom flag is false,
* the element is overwritten with the new element. (but the synchronization
* information is preserved) If the specified resource info is null, then create
* a new one.
*
* If keepSyncInfo is set to be true, the sync info in the given ResourceInfo is NOT
* cleared before being created and thus any sync info already existing at that namespace
* (as indicated by an already existing phantom resource) will be lost.
*/
public ResourceInfo createResource(IResource resource, ResourceInfo info, boolean phantom, boolean overwrite, boolean keepSyncInfo) throws CoreException {
info= info == null ? newElement(resource.getType()) : (ResourceInfo)info.clone();
ResourceInfo original= getResourceInfo(resource.getFullPath(), true, false);
if (phantom) {
info.set(M_PHANTOM);
info.clearModificationStamp();
}
// if nothing existed at the destination then just create the resource in the tree
if (original == null) {
// we got here from a copy/move. we don't want to copy over any sync info
// from the source so clear it.
if (!keepSyncInfo)
info.setSyncInfo(null);
tree.createElement(resource.getFullPath(), info);
} else {
// if overwrite==true then slam the new info into the tree even if one existed before
if (overwrite || (!phantom && original.isSet(M_PHANTOM))) {
// copy over the sync info and flags from the old resource info
// since we are replacing a phantom with a real resource
// DO NOT set the sync info dirty flag because we want to
// preserve the old sync info so its not dirty
// XXX: must copy over the generic sync info from the old info to the new
// XXX: do we really need to clone the sync info here?
if (!keepSyncInfo)
info.setSyncInfo(original.getSyncInfo(true));
// mark the markers bit as dirty so we snapshot an empty marker set for
// the new resource
info.set(ICoreConstants.M_MARKERS_SNAP_DIRTY);
tree.setElementData(resource.getFullPath(), info);
} else {
String message= NLS.bind(Messages.resources_mustNotExist, resource.getFullPath());
throw new ResourceException(IResourceStatus.RESOURCE_EXISTS, resource.getFullPath(), message, null);
}
}
return info;
}
/* (non-Javadoc)
* @see IWorkspace#delete(IResource[], boolean, IProgressMonitor)
*/
public IStatus delete(IResource[] resources, boolean force, IProgressMonitor monitor) throws CoreException {
int updateFlags= force ? IResource.FORCE : IResource.NONE;
updateFlags|= IResource.KEEP_HISTORY;
return delete(resources, updateFlags, monitor);
}
/* (non-Javadoc)
* @see IWorkspace#delete(IResource[], int, IProgressMonitor)
*/
public IStatus delete(IResource[] resources, int updateFlags, IProgressMonitor monitor) throws CoreException {
monitor= Policy.monitorFor(monitor);
try {
int opWork= Math.max(resources.length, 1);
int totalWork= Policy.totalWork * opWork / Policy.opWork;
String message= Messages.resources_deleting_0;
monitor.beginTask(message, totalWork);
message= Messages.resources_deleteProblem;
MultiStatus result= new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, message, null);
if (resources.length == 0)
return result;
resources= (IResource[])resources.clone(); // to avoid concurrent changes to this array
try {
prepareOperation(getRoot(), monitor);
beginOperation(true);
for (int i= 0; i < resources.length; i++) {
Policy.checkCanceled(monitor);
Resource resource= (Resource)resources[i];
if (resource == null) {
monitor.worked(1);
continue;
}
try {
resource.delete(updateFlags, Policy.subMonitorFor(monitor, 1));
} catch (CoreException e) {
// Don't really care about the exception unless the resource is still around.
ResourceInfo info= resource.getResourceInfo(false, false);
if (resource.exists(resource.getFlags(info), false)) {
message= NLS.bind(Messages.resources_couldnotDelete, resource.getFullPath());
result.merge(new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, resource.getFullPath(), message));
result.merge(e.getStatus());
}
}
}
if (result.matches(IStatus.ERROR))
throw new ResourceException(result);
return result;
} catch (OperationCanceledException e) {
getWorkManager().operationCanceled();
throw e;
} finally {
endOperation(getRoot(), true, Policy.subMonitorFor(monitor, totalWork - opWork));
}
} finally {
monitor.done();
}
}
/* (non-Javadoc)
* @see IWorkspace#deleteMarkers(IMarker[])
*/
public void deleteMarkers(IMarker[] markers) throws CoreException {
Assert.isNotNull(markers);
if (markers.length == 0)
return;
// clone to avoid outside changes
markers= (IMarker[])markers.clone();
try {
prepareOperation(null, null);
beginOperation(true);
for (int i= 0; i < markers.length; ++i)
if (markers[i] != null && markers[i].getResource() != null)
markerManager.removeMarker(markers[i].getResource(), markers[i].getId());
} finally {
endOperation(null, false, null);
}
}
/**
* Delete the given resource from the current tree of the receiver. This method simply removes
* the resource from the tree. No cleanup or other management is done. Use IResource.delete for
* proper deletion. If the given resource is the root, all of its children (i.e., all projects)
* are deleted but the root is left.
*/
void deleteResource(IResource resource) {
IPath path= resource.getFullPath();
if (path.equals(Path.ROOT)) {
IProject[] children= getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
for (int i= 0; i < children.length; i++)
tree.deleteElement(children[i].getFullPath());
} else
tree.deleteElement(path);
}
/**
* End an operation (group of resource changes). Notify interested parties that resource changes
* have taken place. All registered resource change listeners are notified. If autobuilding is
* enabled, a build is run.
*/
public void endOperation(ISchedulingRule rule, boolean build, IProgressMonitor monitor) throws CoreException {
WorkManager workManager= getWorkManager();
//don't do any end operation work if we failed to check in
if (workManager.checkInFailed(rule))
return;
// This is done in a try finally to ensure that we always decrement the operation count
// and release the workspace lock. This must be done at the end because snapshot
// and "hasChanges" comparison have to happen without interference from other threads.
boolean hasTreeChanges= false;
boolean depthOne= false;
try {
workManager.setBuild(build);
// if we are not exiting a top level operation then just decrement the count and return
depthOne= workManager.getPreparedOperationDepth() == 1;
if (!(notificationManager.shouldNotify() || depthOne)) {
notificationManager.requestNotify();
return;
}
// do the following in a try/finally to ensure that the operation tree is nulled at the end
// as we are completing a top level operation.
try {
notificationManager.beginNotify();
// check for a programming error on using beginOperation/endOperation
Assert.isTrue(workManager.getPreparedOperationDepth() > 0, "Mismatched begin/endOperation"); //$NON-NLS-1$
// At this time we need to re-balance the nested operations. It is necessary because
// build() and snapshot() should not fail if they are called.
workManager.rebalanceNestedOperations();
//find out if any operation has potentially modified the tree
hasTreeChanges= workManager.shouldBuild();
//double check if the tree has actually changed
if (hasTreeChanges)
hasTreeChanges= operationTree != null && ElementTree.hasChanges(tree, operationTree, ResourceComparator.getBuildComparator(), true);
broadcastPostChange();
// Request a snapshot if we are sufficiently out of date.
saveManager.snapshotIfNeeded(hasTreeChanges);
} finally {
// make sure the tree is immutable if we are ending a top-level operation.
if (depthOne) {
tree.immutable();
operationTree= null;
} else
newWorkingTree();
}
} finally {
workManager.checkOut(rule);
}
if (depthOne)
buildManager.endTopLevel(hasTreeChanges);
}
/**
* Flush the build order cache for the workspace. Only needed if the description does not
* already have a build order. That is, if this is really a cache.
*/
protected void flushBuildOrder() {
if (description.getBuildOrder(false) == null)
buildOrder= null;
}
/* (non-Javadoc)
* @see IWorkspace#forgetSavedTree(String)
*/
public void forgetSavedTree(String pluginId) {
saveManager.forgetSavedTree(pluginId);
}
public AliasManager getAliasManager() {
return aliasManager;
}
/**
* Returns this workspace's build manager
*/
public BuildManager getBuildManager() {
return buildManager;
}
/**
* Returns the order in which open projects in this workspace will be built.
* <p>
* The project build order is based on information specified in the workspace description. The
* projects are built in the order specified by <code>IWorkspaceDescription.getBuildOrder</code>
* ; closed or non-existent projects are ignored and not included in the result. If
* <code>IWorkspaceDescription.getBuildOrder</code> is non-null, the default build order is
* used; again, only open projects are included in the result.
* </p>
* <p>
* The returned value is cached in the <code>buildOrder</code> field.
* </p>
*
* @return the list of currently open projects in the workspace in the order in which they would
* be built by <code>IWorkspace.build</code>.
* @see IWorkspace#build(int, IProgressMonitor)
* @see IWorkspaceDescription#getBuildOrder()
* @since 2.1
*/
public IProject[] getBuildOrder() {
if (buildOrder != null) {
// return previously-computed and cached project build order
return buildOrder;
}
// see if a particular build order is specified
String[] order= description.getBuildOrder(false);
if (order != null) {
// convert from project names to project handles
// and eliminate non-existent and closed projects
List projectList= new ArrayList(order.length);
for (int i= 0; i < order.length; i++) {
IProject project= getRoot().getProject(order[i]);
if (project.isAccessible()) {
projectList.add(project);
}
}
buildOrder= new IProject[projectList.size()];
projectList.toArray(buildOrder);
} else {
// use default project build order
// computed for all accessible projects in workspace
buildOrder= computeFullProjectOrder().projects;
}
return buildOrder;
}
public CharsetManager getCharsetManager() {
return charsetManager;
}
public ContentDescriptionManager getContentDescriptionManager() {
return contentDescriptionManager;
}
/* (non-Javadoc)
* @see IWorkspace#getDanglingReferences()
*/
public Map getDanglingReferences() {
IProject[] projects= getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
Map result= new HashMap(projects.length);
for (int i= 0; i < projects.length; i++) {
Project project= (Project)projects[i];
if (!project.isAccessible())
continue;
IProject[] refs= project.internalGetDescription().getReferencedProjects(false);
List dangling= new ArrayList(refs.length);
for (int j= 0; j < refs.length; j++)
if (!refs[i].exists())
dangling.add(refs[i]);
if (!dangling.isEmpty())
result.put(projects[i], dangling.toArray(new IProject[dangling.size()]));
}
return result;
}
/* (non-Javadoc)
* @see IWorkspace#getDescription()
*/
public IWorkspaceDescription getDescription() {
WorkspaceDescription workingCopy= defaultWorkspaceDescription();
description.copyTo(workingCopy);
return workingCopy;
}
/**
* Returns the current element tree for this workspace
*/
public ElementTree getElementTree() {
return tree;
}
public FileSystemResourceManager getFileSystemManager() {
return fileSystemManager;
}
/**
* Returns the marker manager for this workspace
*/
public MarkerManager getMarkerManager() {
return markerManager;
}
public LocalMetaArea getMetaArea() {
return localMetaArea;
}
protected IMoveDeleteHook getMoveDeleteHook() {
if (moveDeleteHook == null)
initializeMoveDeleteHook();
return moveDeleteHook;
}
public IFilterMatcherDescriptor getFilterMatcherDescriptor(String filterMAtcherId) {
return filterManager.getFilterDescriptor(filterMAtcherId);
}
public IFilterMatcherDescriptor[] getFilterMatcherDescriptors() {
return filterManager.getFilterDescriptors();
}
/* (non-Javadoc)
* @see IWorkspace#getNatureDescriptor(String)
*/
public IProjectNatureDescriptor getNatureDescriptor(String natureId) {
return natureManager.getNatureDescriptor(natureId);
}
/* (non-Javadoc)
* @see IWorkspace#getNatureDescriptors()
*/
public IProjectNatureDescriptor[] getNatureDescriptors() {
return natureManager.getNatureDescriptors();
}
/**
* Returns the nature manager for this workspace.
*/
public NatureManager getNatureManager() {
return natureManager;
}
public NotificationManager getNotificationManager() {
return notificationManager;
}
/* (non-Javadoc)
* @see IWorkspace#getPathVariableManager()
*/
public IPathVariableManager getPathVariableManager() {
return pathVariableManager;
}
public IPropertyManager getPropertyManager() {
return propertyManager;
}
/**
* Returns the refresh manager for this workspace
*/
public RefreshManager getRefreshManager() {
return refreshManager;
}
/**
* Returns the resource info for the identified resource. null is returned if no such resource
* can be found. If the phantom flag is true, phantom resources are considered. If the mutable
* flag is true, the info is opened for change.
*
* This method DOES NOT throw an exception if the resource is not found.
*/
public ResourceInfo getResourceInfo(IPath path, boolean phantom, boolean mutable) {
try {
if (path.segmentCount() == 0) {
ResourceInfo info= (ResourceInfo)tree.getTreeData();
Assert.isNotNull(info, "Tree root info must never be null"); //$NON-NLS-1$
return info;
}
ResourceInfo result= null;
if (!tree.includes(path))
return null;
if (mutable)
result= (ResourceInfo)tree.openElementData(path);
else
result= (ResourceInfo)tree.getElementData(path);
if (result != null && (!phantom && result.isSet(M_PHANTOM)))
return null;
return result;
} catch (IllegalArgumentException e) {
return null;
}
}
/* (non-Javadoc)
* @see IWorkspace#getRoot()
*/
public IWorkspaceRoot getRoot() {
return defaultRoot;
}
/* (non-Javadoc)
* @see IWorkspace#getRuleFactory()
*/
public IResourceRuleFactory getRuleFactory() {
//note that the rule factory is created lazily because it
//requires loading the teamHook extension
if (ruleFactory == null)
ruleFactory= new Rules(this);
return ruleFactory;
}
public SaveManager getSaveManager() {
return saveManager;
}
/* (non-Javadoc)
* @see IWorkspace#getSynchronizer()
*/
public ISynchronizer getSynchronizer() {
return synchronizer;
}
/**
* Returns the installed team hook. Never returns null.
*/
protected TeamHook getTeamHook() {
if (teamHook == null)
initializeTeamHook();
return teamHook;
}
/**
* We should not have direct references to this field. All references should go through this
* method.
*/
public WorkManager getWorkManager() throws CoreException {
if (_workManager == null) {
String message= Messages.resources_shutdown;
throw new ResourceException(new ResourceStatus(IResourceStatus.INTERNAL_ERROR, null, message));
}
return _workManager;
}
/**
* A move/delete hook hasn't been initialized. Check the extension point and try to create a new
* hook if a user has one defined as an extension. Otherwise use the Core's implementation as
* the default.
*/
protected void initializeMoveDeleteHook() {
try {
if (!canCreateExtensions())
return;
IConfigurationElement[] configs= Platform.getExtensionRegistry().getConfigurationElementsFor(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_MOVE_DELETE_HOOK);
// no-one is plugged into the extension point so disable validation
if (configs == null || configs.length == 0) {
return;
}
// can only have one defined at a time. log a warning
if (configs.length > 1) {
//XXX: should provide a meaningful status code
IStatus status= new ResourceStatus(IStatus.ERROR, 1, null, Messages.resources_oneHook, null);
Policy.log(status);
return;
}
// otherwise we have exactly one hook extension. Try to create a new instance
// from the user-specified class.
try {
IConfigurationElement config= configs[0];
moveDeleteHook= (IMoveDeleteHook)config.createExecutableExtension("class"); //$NON-NLS-1$
} catch (CoreException e) {
//ignore the failure if we are shutting down (expected since extension
//provider plugin has probably already shut down
if (canCreateExtensions()) {
IStatus status= new ResourceStatus(IStatus.ERROR, 1, null, Messages.resources_initHook, e);
Policy.log(status);
}
}
} finally {
// for now just use Core's implementation
if (moveDeleteHook == null)
moveDeleteHook= new MoveDeleteHook();
}
}
/**
* A team hook hasn't been initialized. Check the extension point and try to create a new hook
* if a user has one defined as an extension. Otherwise use the Core's implementation as the
* default.
*/
protected void initializeTeamHook() {
try {
if (!canCreateExtensions())
return;
IConfigurationElement[] configs= Platform.getExtensionRegistry().getConfigurationElementsFor(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_TEAM_HOOK);
// no-one is plugged into the extension point so disable validation
if (configs == null || configs.length == 0) {
return;
}
// can only have one defined at a time. log a warning
if (configs.length > 1) {
//XXX: should provide a meaningful status code
IStatus status= new ResourceStatus(IStatus.ERROR, 1, null, Messages.resources_oneTeamHook, null);
Policy.log(status);
return;
}
// otherwise we have exactly one hook extension. Try to create a new instance
// from the user-specified class.
try {
IConfigurationElement config= configs[0];
teamHook= (TeamHook)config.createExecutableExtension("class"); //$NON-NLS-1$
} catch (CoreException e) {
//ignore the failure if we are shutting down (expected since extension
//provider plugin has probably already shut down
if (canCreateExtensions()) {
IStatus status= new ResourceStatus(IStatus.ERROR, 1, null, Messages.resources_initTeamHook, e);
Policy.log(status);
}
}
} finally {
// default to use Core's implementation
//create anonymous subclass because TeamHook is abstract
if (teamHook == null)
teamHook= new TeamHook() {
// empty
};
}
}
/**
* A file modification validator hasn't been initialized. Check the extension point and try to
* create a new validator if a user has one defined as an extension.
*/
protected void initializeValidator() {
shouldValidate= false;
if (!canCreateExtensions())
return;
IConfigurationElement[] configs= Platform.getExtensionRegistry().getConfigurationElementsFor(ResourcesPlugin.PI_RESOURCES, ResourcesPlugin.PT_FILE_MODIFICATION_VALIDATOR);
// no-one is plugged into the extension point so disable validation
if (configs == null || configs.length == 0) {
return;
}
// can only have one defined at a time. log a warning, disable validation, but continue with
// the #setContents (e.g. don't throw an exception)
if (configs.length > 1) {
//XXX: should provide a meaningful status code
IStatus status= new ResourceStatus(IStatus.ERROR, 1, null, Messages.resources_oneValidator, null);
Policy.log(status);
return;
}
// otherwise we have exactly one validator extension. Try to create a new instance
// from the user-specified class.
try {
IConfigurationElement config= configs[0];
validator= (IFileModificationValidator)config.createExecutableExtension("class"); //$NON-NLS-1$
shouldValidate= true;
} catch (CoreException e) {
//ignore the failure if we are shutting down (expected since extension
//provider plugin has probably already shut down
if (canCreateExtensions()) {
IStatus status= new ResourceStatus(IStatus.ERROR, 1, null, Messages.resources_initValidator, e);
Policy.log(status);
}
}
}
public WorkspaceDescription internalGetDescription() {
return description;
}
/* (non-Javadoc)
* @see IWorkspace#isAutoBuilding()
*/
public boolean isAutoBuilding() {
return description.isAutoBuilding();
}
public boolean isOpen() {
return openFlag;
}
/* (non-Javadoc)
* @see IWorkspace#isTreeLocked()
*/
public boolean isTreeLocked() {
return treeLocked == Thread.currentThread();
}
/**
* Link the given tree into the receiver's tree at the specified resource.
*/
protected void linkTrees(IPath path, ElementTree[] newTrees) {
tree= tree.mergeDeltaChain(path, newTrees);
}
/* (non-Javadoc)
* @see IWorkspace#loadProjectDescription(InputStream)
* @since 3.1
*/
public IProjectDescription loadProjectDescription(InputStream stream) throws CoreException {
IProjectDescription result= null;
result= new ProjectDescriptionReader().read(new InputSource(stream));
if (result == null) {
String message= NLS.bind(Messages.resources_errorReadProject, stream.toString());
IStatus status= new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_READ_METADATA, message, null);
throw new ResourceException(status);
}
return result;
}
/* (non-Javadoc)
* @see IWorkspace#loadProjectDescription(IPath)
* @since 2.0
*/
public IProjectDescription loadProjectDescription(IPath path) throws CoreException {
IProjectDescription result= null;
IOException e= null;
try {
result= new ProjectDescriptionReader().read(path);
if (result != null) {
// check to see if we are using in the default area or not. use java.io.File for
// testing equality because it knows better w.r.t. drives and case sensitivity
IPath user= path.removeLastSegments(1);
IPath platform= getRoot().getLocation().append(result.getName());
if (!user.toFile().equals(platform.toFile()))
result.setLocation(user);
}
} catch (IOException ex) {
e= ex;
}
if (result == null || e != null) {
String message= NLS.bind(Messages.resources_errorReadProject, path.toOSString());
IStatus status= new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_READ_METADATA, message, e);
throw new ResourceException(status);
}
return result;
}
/* (non-Javadoc)
* @see IWorkspace#move(IResource[], IPath, boolean, IProgressMonitor)
*/
public IStatus move(IResource[] resources, IPath destination, boolean force, IProgressMonitor monitor) throws CoreException {
int updateFlags= force ? IResource.FORCE : IResource.NONE;
updateFlags|= IResource.KEEP_HISTORY;
return move(resources, destination, updateFlags, monitor);
}
/* (non-Javadoc)
* @see IWorkspace#move(IResource[], IPath, int, IProgressMonitor)
*/
public IStatus move(IResource[] resources, IPath destination, int updateFlags, IProgressMonitor monitor) throws CoreException {
monitor= Policy.monitorFor(monitor);
try {
Assert.isLegal(resources != null);
int opWork= Math.max(resources.length, 1);
int totalWork= Policy.totalWork * opWork / Policy.opWork;
String message= Messages.resources_moving_0;
monitor.beginTask(message, totalWork);
if (resources.length == 0)
return Status.OK_STATUS;
resources= (IResource[])resources.clone(); // to avoid concurrent changes to this array
IPath parentPath= null;
message= Messages.resources_moveProblem;
MultiStatus status= new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, message, null);
try {
prepareOperation(getRoot(), monitor);
beginOperation(true);
for (int i= 0; i < resources.length; i++) {
Policy.checkCanceled(monitor);
Resource resource= (Resource)resources[i];
if (resource == null || isDuplicate(resources, i)) {
monitor.worked(1);
continue;
}
// test siblings
if (parentPath == null)
parentPath= resource.getFullPath().removeLastSegments(1);
if (parentPath.equals(resource.getFullPath().removeLastSegments(1))) {
// test move requirements
try {
IStatus requirements= resource.checkMoveRequirements(destination.append(resource.getName()), resource.getType(), updateFlags);
if (requirements.isOK()) {
try {
resource.move(destination.append(resource.getName()), updateFlags, Policy.subMonitorFor(monitor, 1));
} catch (CoreException e) {
status.merge(e.getStatus());
}
} else {
monitor.worked(1);
status.merge(requirements);
}
} catch (CoreException e) {
monitor.worked(1);
status.merge(e.getStatus());
}
} else {
monitor.worked(1);
message= NLS.bind(Messages.resources_notChild, resource.getFullPath(), parentPath);
status.merge(new ResourceStatus(IResourceStatus.OPERATION_FAILED, resource.getFullPath(), message));
}
}
} catch (OperationCanceledException e) {
getWorkManager().operationCanceled();
throw e;
} finally {
endOperation(getRoot(), true, Policy.subMonitorFor(monitor, totalWork - opWork));
}
if (status.matches(IStatus.ERROR))
throw new ResourceException(status);
return status.isOK() ? (IStatus)Status.OK_STATUS : (IStatus)status;
} finally {
monitor.done();
}
}
/**
* Moves this resource's subtree to the destination. This operation should only be used by move
* methods. Destination must be a valid destination for this resource. The keepSyncInfo boolean
* is used to indicated whether or not the sync info should be moved from the source to the
* destination.
*/
/* package */
void move(Resource source, IPath destination, int depth, int updateFlags, boolean keepSyncInfo) throws CoreException {
// overlay the tree at the destination path, preserving any important info
// in any already existing resource information
copyTree(source, destination, depth, updateFlags, keepSyncInfo, true, source.getType() == IResource.PROJECT);
source.fixupAfterMoveSource();
}
/**
* Create and return a new tree element of the given type.
*/
protected ResourceInfo newElement(int type) {
ResourceInfo result= null;
switch (type) {
case IResource.FILE:
case IResource.FOLDER:
result= new ResourceInfo();
break;
case IResource.PROJECT:
result= new ProjectInfo();
break;
case IResource.ROOT:
result= new RootInfo();
break;
}
result.setNodeId(nextNodeId());
updateModificationStamp(result);
result.setType(type);
return result;
}
/* (non-Javadoc)
* @see IWorkspace#newProjectDescription(String)
*/
public IProjectDescription newProjectDescription(String projectName) {
IProjectDescription result= new ProjectDescription();
result.setName(projectName);
return result;
}
public Resource newResource(IPath path, int type) {
String message;
switch (type) {
case IResource.FOLDER:
if (path.segmentCount() < ICoreConstants.MINIMUM_FOLDER_SEGMENT_LENGTH) {
message= "Path must include project and resource name: " + path.toString(); //$NON-NLS-1$
Assert.isLegal(false, message);
}
return new Folder(path.makeAbsolute(), this);
case IResource.FILE:
if (path.segmentCount() < ICoreConstants.MINIMUM_FILE_SEGMENT_LENGTH) {
message= "Path must include project and resource name: " + path.toString(); //$NON-NLS-1$
Assert.isLegal(false, message);
}
return new File(path.makeAbsolute(), this);
case IResource.PROJECT:
return (Resource)getRoot().getProject(path.lastSegment());
case IResource.ROOT:
return (Resource)getRoot();
}
Assert.isLegal(false);
// will never get here because of assertion.
return null;
}
/**
* Opens a new mutable element tree layer, thus allowing modifications to the tree.
*/
public ElementTree newWorkingTree() {
tree= tree.newEmptyDelta();
return tree;
}
/**
* Returns the next, previously unassigned, marker id.
*/
protected long nextMarkerId() {
return nextMarkerId++;
}
protected long nextNodeId() {
return nextNodeId++;
}
/**
* Opens this workspace using the data at its location in the local file system. This workspace
* must not be open. If the operation succeeds, the result will detail any serious (but
* non-fatal) problems encountered while opening the workspace. The status code will be
* <code>OK</code> if there were no problems. An exception is thrown if there are fatal problems
* opening the workspace, in which case the workspace is left closed.
* <p>
* This method is long-running; progress and cancellation are provided by the given progress
* monitor.
* </p>
*
* @param monitor a progress monitor, or <code>null</code> if progress reporting and
* cancellation are not desired
* @return status with code <code>OK</code> if no problems; otherwise status describing any
* serious but non-fatal problems.
*
* @exception CoreException if the workspace could not be opened. Reasons include:
* <ul>
* <li>There is no valid workspace structure at the given location in the local
* file system.</li>
* <li>The workspace structure on disk appears to be hopelessly corrupt.</li>
* </ul>
* @see ResourcesPlugin#getWorkspace()
*/
public IStatus open(IProgressMonitor monitor) throws CoreException {
// This method is not inside an operation because it is the one responsible for
// creating the WorkManager object (who takes care of operations).
String message= Messages.resources_workspaceOpen;
Assert.isTrue(!isOpen(), message);
if (!getMetaArea().hasSavedWorkspace()) {
message= Messages.resources_readWorkspaceMeta;
throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, Platform.getLocation(), message, null);
}
description= new WorkspacePreferences();
// if we have an old description file, read it (getting rid of it)
WorkspaceDescription oldDescription= getMetaArea().readOldWorkspace();
if (oldDescription != null) {
description.copyFrom(oldDescription);
ResourcesPlugin.getPlugin().savePluginPreferences();
}
// create root location
localMetaArea.locationFor(getRoot()).toFile().mkdirs();
IProgressMonitor nullMonitor= Policy.monitorFor(null);
startup(nullMonitor);
//restart the notification manager so it is initialized with the right tree
notificationManager.startup(null);
openFlag= true;
if (crashed || refreshRequested()) {
try {
refreshManager.refresh(getRoot());
} catch (RuntimeException e) {
//don't fail entire open if refresh failed, just report as warning
return new ResourceStatus(IResourceStatus.INTERNAL_ERROR, Path.ROOT, Messages.resources_errorMultiRefresh, e);
}
}
//finally register a string pool participant
stringPoolJob= new StringPoolJob();
stringPoolJob.addStringPoolParticipant(saveManager, getRoot());
return Status.OK_STATUS;
}
/**
* Called before checking the pre-conditions of an operation. Optionally supply a scheduling
* rule to determine when the operation is safe to run. If a scheduling rule is supplied, this
* method will block until it is safe to run.
*
* @param rule the scheduling rule that describes what this operation intends to modify.
*/
public void prepareOperation(ISchedulingRule rule, IProgressMonitor monitor) throws CoreException {
try {
//make sure autobuild is not running if it conflicts with this operation
if (rule != null && rule.isConflicting(getRuleFactory().buildRule()))
buildManager.interrupt();
} finally {
getWorkManager().checkIn(rule, monitor);
}
if (!isOpen()) {
String message= Messages.resources_workspaceClosed;
throw new ResourceException(IResourceStatus.OPERATION_FAILED, null, message, null);
}
}
protected boolean refreshRequested() {
String[] args= Platform.getCommandLineArgs();
for (int i= 0; i < args.length; i++)
if (args[i].equalsIgnoreCase(REFRESH_ON_STARTUP))
return true;
return false;
}
/* (non-Javadoc)
* @see IWorkspace#removeResourceChangeListener(IResourceChangeListener)
*/
public void removeResourceChangeListener(IResourceChangeListener listener) {
notificationManager.removeListener(listener);
}
/* (non-Javadoc)
* @see IWorkspace#removeSaveParticipant(Plugin)
*/
public void removeSaveParticipant(Plugin plugin) {
Assert.isNotNull(plugin, "Plugin must not be null"); //$NON-NLS-1$
saveManager.removeParticipant(plugin.getBundle().getSymbolicName());
}
/* (non-Javadoc)
* @see IWorkspace#removeSaveParticipant(String)
*/
public void removeSaveParticipant(String pluginId) {
Assert.isNotNull(pluginId, "Plugin id must not be null"); //$NON-NLS-1$
saveManager.removeParticipant(pluginId);
}
/* (non-Javadoc)
* @see IWorkspace#run(IWorkspaceRunnable, IProgressMonitor)
*/
public void run(IWorkspaceRunnable action, IProgressMonitor monitor) throws CoreException {
run(action, defaultRoot, IWorkspace.AVOID_UPDATE, monitor);
}
/* (non-Javadoc)
* @see IWorkspace#run(IWorkspaceRunnable, ISchedulingRule, int, IProgressMonitor)
*/
public void run(IWorkspaceRunnable action, ISchedulingRule rule, int options, IProgressMonitor monitor) throws CoreException {
monitor= Policy.monitorFor(monitor);
try {
monitor.beginTask("", Policy.totalWork); //$NON-NLS-1$
int depth= -1;
boolean avoidNotification= (options & IWorkspace.AVOID_UPDATE) != 0;
try {
prepareOperation(rule, monitor);
beginOperation(true);
if (avoidNotification)
avoidNotification= notificationManager.beginAvoidNotify();
depth= getWorkManager().beginUnprotected();
action.run(Policy.subMonitorFor(monitor, Policy.opWork, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK));
} catch (OperationCanceledException e) {
getWorkManager().operationCanceled();
throw e;
} finally {
if (avoidNotification)
notificationManager.endAvoidNotify();
if (depth >= 0)
getWorkManager().endUnprotected(depth);
endOperation(rule, false, Policy.subMonitorFor(monitor, Policy.endOpWork));
}
} finally {
monitor.done();
}
}
/* (non-Javadoc)
* @see IWorkspace#save(boolean, IProgressMonitor)
*/
public IStatus save(boolean full, IProgressMonitor monitor) throws CoreException {
return this.save(full, false, monitor);
}
public IStatus save(boolean full, boolean keepConsistencyWhenCanceled, IProgressMonitor monitor) throws CoreException {
String message;
if (full) {
//according to spec it is illegal to start a full save inside another operation
if (getWorkManager().isLockAlreadyAcquired()) {
message= Messages.resources_saveOp;
throw new ResourceException(IResourceStatus.OPERATION_FAILED, null, message, new IllegalStateException());
}
return saveManager.save(ISaveContext.FULL_SAVE, keepConsistencyWhenCanceled, null, monitor);
}
// A snapshot was requested. Start an operation (if not already started) and
// signal that a snapshot should be done at the end.
try {
prepareOperation(getRoot(), monitor);
beginOperation(false);
saveManager.requestSnapshot();
message= Messages.resources_snapRequest;
return new ResourceStatus(IStatus.OK, message);
} finally {
endOperation(getRoot(), false, null);
}
}
public void setCrashed(boolean value) {
crashed= value;
if (crashed) {
String msg= "The workspace exited with unsaved changes in the previous session; refreshing workspace to recover changes."; //$NON-NLS-1$
Policy.log(new ResourceStatus(ICoreConstants.CRASH_DETECTED, msg));
if (Policy.DEBUG)
System.out.println(msg);
}
}
/* (non-Javadoc)
* @see IWorkspace#setDescription(IWorkspaceDescription)
*/
public void setDescription(IWorkspaceDescription value) {
// if both the old and new description's build orders are null, leave the
// workspace's build order slot because it is caching the computed order.
// Otherwise, set the slot to null to force recomputing or building from the description.
WorkspaceDescription newDescription= (WorkspaceDescription)value;
String[] newOrder= newDescription.getBuildOrder(false);
if (description.getBuildOrder(false) != null || newOrder != null)
buildOrder= null;
description.copyFrom(newDescription);
ResourcesPlugin.getPlugin().savePluginPreferences();
}
public void setTreeLocked(boolean locked) {
Assert.isTrue(!locked || treeLocked == null, "The workspace tree is already locked"); //$NON-NLS-1$
treeLocked= locked ? Thread.currentThread() : null;
}
/**
* @deprecated
*/
public void setWorkspaceLock(WorkspaceLock lock) {
// do nothing
}
/**
* Shuts down the workspace managers.
*/
protected void shutdown(IProgressMonitor monitor) throws CoreException {
monitor= Policy.monitorFor(monitor);
try {
IManager[] managers= { buildManager, propertyManager, pathVariableManager, charsetManager, fileSystemManager, markerManager, _workManager, aliasManager, refreshManager,
contentDescriptionManager, natureManager, filterManager };
monitor.beginTask("", managers.length); //$NON-NLS-1$
String message= Messages.resources_shutdownProblems;
MultiStatus status= new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, message, null);
// best effort to shutdown every object and free resources
for (int i= 0; i < managers.length; i++) {
IManager manager= managers[i];
if (manager == null)
monitor.worked(1);
else {
try {
manager.shutdown(Policy.subMonitorFor(monitor, 1));
} catch (Exception e) {
message= Messages.resources_shutdownProblems;
status.add(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, message, e));
}
}
}
buildManager= null;
notificationManager= null;
propertyManager= null;
pathVariableManager= null;
fileSystemManager= null;
markerManager= null;
synchronizer= null;
saveManager= null;
_workManager= null;
aliasManager= null;
refreshManager= null;
charsetManager= null;
contentDescriptionManager= null;
if (!status.isOK())
throw new CoreException(status);
} finally {
monitor.done();
}
}
/* (non-Javadoc)
* @see IWorkspace#sortNatureSet(String[])
*/
public String[] sortNatureSet(String[] natureIds) {
return natureManager.sortNatureSet(natureIds);
}
/**
* Starts all the workspace manager classes.
*/
protected void startup(IProgressMonitor monitor) throws CoreException {
// ensure the tree is locked during the startup notification
try {
_workManager= new WorkManager(this);
_workManager.startup(null);
fileSystemManager= new FileSystemResourceManager(this);
fileSystemManager.startup(monitor);
pathVariableManager= new PathVariableManager();
pathVariableManager.startup(null);
natureManager= new NatureManager();
natureManager.startup(null);
filterManager= new FilterTypeManager();
filterManager.startup(null);
buildManager= new BuildManager(this, getWorkManager().getLock());
buildManager.startup(null);
notificationManager= new NotificationManager(this);
notificationManager.startup(null);
markerManager= new MarkerManager(this);
markerManager.startup(null);
synchronizer= new Synchronizer(this);
refreshManager= new RefreshManager(this);
saveManager= new SaveManager(this);
saveManager.startup(null);
//must start after save manager, because (read) access to tree is needed
refreshManager.startup(null);
aliasManager= new AliasManager(this);
aliasManager.startup(null);
propertyManager= ResourcesCompatibilityHelper.createPropertyManager();
propertyManager.startup(monitor);
charsetManager= new CharsetManager(this);
charsetManager.startup(null);
contentDescriptionManager= new ContentDescriptionManager();
contentDescriptionManager.startup(null);
} finally {
//unlock tree even in case of failure, otherwise shutdown will also fail
treeLocked= null;
_workManager.postWorkspaceStartup();
}
}
/**
* Returns a string representation of this working state's structure suitable for debug
* purposes.
*/
public String toDebugString() {
final StringBuffer buffer= new StringBuffer("\nDump of " + toString() + ":\n"); //$NON-NLS-1$ //$NON-NLS-2$
buffer.append(" parent: " + tree.getParent()); //$NON-NLS-1$
IElementContentVisitor visitor= new IElementContentVisitor() {
public boolean visitElement(ElementTree aTree, IPathRequestor requestor, Object elementContents) {
buffer.append("\n " + requestor.requestPath() + ": " + elementContents); //$NON-NLS-1$ //$NON-NLS-2$
return true;
}
};
new ElementTreeIterator(tree, Path.ROOT).iterate(visitor);
return buffer.toString();
}
public void updateModificationStamp(ResourceInfo info) {
info.incrementModificationStamp();
}
/* (non-javadoc)
* @see IWorkspace#validateEdit(IFile[], Object)
*/
public IStatus validateEdit(final IFile[] files, final Object context) {
// if validation is turned off then just return
if (!shouldValidate) {
String message= Messages.resources_readOnly2;
MultiStatus result= new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.READ_ONLY_LOCAL, message, null);
for (int i= 0; i < files.length; i++) {
if (files[i].isReadOnly()) {
IPath filePath= files[i].getFullPath();
message= NLS.bind(Messages.resources_readOnly, filePath);
result.add(new ResourceStatus(IResourceStatus.READ_ONLY_LOCAL, filePath, message));
}
}
return result.getChildren().length == 0 ? Status.OK_STATUS : (IStatus)result;
}
// first time through the validator hasn't been initialized so try and create it
if (validator == null)
initializeValidator();
// we were unable to initialize the validator. Validation has been turned off and
// a warning has already been logged so just return.
if (validator == null)
return Status.OK_STATUS;
// otherwise call the API and throw an exception if appropriate
final IStatus[] status= new IStatus[1];
ISafeRunnable body= new ISafeRunnable() {
public void handleException(Throwable exception) {
status[0]= new ResourceStatus(IStatus.ERROR, null, Messages.resources_errorValidator, exception);
}
public void run() throws Exception {
Object c= context;
//must null any reference to FileModificationValidationContext for backwards compatibility
if (!(validator instanceof FileModificationValidator))
if (c instanceof FileModificationValidationContext)
c= null;
status[0]= validator.validateEdit(files, c);
}
};
SafeRunner.run(body);
return status[0];
}
/* (non-Javadoc)
* @see IWorkspace#validateLinkLocation(IResource, IPath)
*/
public IStatus validateLinkLocation(IResource resource, IPath unresolvedLocation) {
return locationValidator.validateLinkLocation(resource, unresolvedLocation);
}
/* (non-Javadoc)
* @see IWorkspace#validateLinkLocation(IResource, IPath)
*/
public IStatus validateLinkLocationURI(IResource resource, URI unresolvedLocation) {
return locationValidator.validateLinkLocationURI(resource, unresolvedLocation);
}
/* (non-Javadoc)
* @see IWorkspace#validateName(String, int)
*/
public IStatus validateName(String segment, int type) {
return locationValidator.validateName(segment, type);
}
/* (non-Javadoc)
* @see IWorkspace#validateNatureSet(String[])
*/
public IStatus validateNatureSet(String[] natureIds) {
return natureManager.validateNatureSet(natureIds);
}
/* (non-Javadoc)
* @see IWorkspace#validatePath(String, int)
*/
public IStatus validatePath(String path, int type) {
return locationValidator.validatePath(path, type);
}
/* (non-Javadoc)
* @see IWorkspace#validateProjectLocation(IProject, IPath)
*/
public IStatus validateProjectLocation(IProject context, IPath location) {
return locationValidator.validateProjectLocation(context, location);
}
/*
* (non-Javadoc)
* @see org.eclipse.core.resources.IWorkspace#validateProjectLocation(org.eclipse.core.resources.IProject, java.net.URI)
*/
public IStatus validateProjectLocationURI(IProject project, URI location) {
return locationValidator.validateProjectLocationURI(project, location);
}
/**
* Internal method. To be called only from the following methods:
* <ul>
* <li><code>IFile#appendContents</code></li>
* <li><code>IFile#setContents(InputStream, boolean, boolean, IProgressMonitor)</code></li>
* <li><code>IFile#setContents(IFileState, boolean, boolean, IProgressMonitor)</code></li>
* </ul>
*
* @see IFileModificationValidator#validateSave(IFile)
*/
protected void validateSave(final IFile file) throws CoreException {
// if validation is turned off then just return
if (!shouldValidate)
return;
// first time through the validator hasn't been initialized so try and create it
if (validator == null)
initializeValidator();
// we were unable to initialize the validator. Validation has been turned off and
// a warning has already been logged so just return.
if (validator == null)
return;
// otherwise call the API and throw an exception if appropriate
final IStatus[] status= new IStatus[1];
ISafeRunnable body= new ISafeRunnable() {
public void handleException(Throwable exception) {
status[0]= new ResourceStatus(IStatus.ERROR, null, Messages.resources_errorValidator, exception);
}
public void run() throws Exception {
status[0]= validator.validateSave(file);
}
};
SafeRunner.run(body);
if (!status[0].isOK())
throw new ResourceException(status[0]);
}
/* (non-Javadoc)
* @see IWorkspace#validateFiltered(IResource)
*/
public IStatus validateFiltered(IResource resource) {
try {
if (((Resource)resource).isFilteredWithException(true))
return new ResourceStatus(IStatus.ERROR, Messages.resources_errorResourceIsFiltered);
} catch (CoreException e) {
// if we can't validate it, we return OK
}
return Status.OK_STATUS;
}
}