Copyright (c) 2000, 2015 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 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 Broadcom Corporation - ongoing development Lars Vogel - Bug 473427
/******************************************************************************* * Copyright (c) 2000, 2015 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * 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 * Broadcom Corporation - ongoing development * Lars Vogel <Lars.Vogel@vogella.com> - Bug 473427 *******************************************************************************/
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 java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Predicate; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileStore; 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.properties.PropertyManager2; import org.eclipse.core.internal.refresh.RefreshManager; import org.eclipse.core.internal.resources.ComputeProjectOrder.Digraph; import org.eclipse.core.internal.resources.ComputeProjectOrder.VertexOrder; 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.*; 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.

The lifecycle of the resources plugin is encapsulated by the open(IProgressMonitor) and close(IProgressMonitor) methods. A closed workspace is completely unusable - any attempt to access or modify interesting workspace state on a closed workspace will fail.

All modifications to the workspace occur within the context of a workspace operation. A workspace operation is implemented using the following sequence:

	try {
	prepareOperation(...);
	//check preconditions
	beginOperation(...);
	//perform changes
} finally {
	endOperation(...);
}
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 prepareOperation(ISchedulingRule, IProgressMonitor), beginOperation(boolean), and endOperation(ISchedulingRule, boolean) for more details.

Major areas of functionality are farmed off to various manager classes. Open a type hierarchy on IManager to see all the different managers. Each manager is typically referenced three times in this class: Once in startup(IProgressMonitor) when it is instantiated, once in shutdown(IProgressMonitor) when it is destroyed, and once in a manager accessor method.

/** * The workspace class is the monolithic nerve center of the resources plugin. * All interesting functionality stems from this class. * <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: * </p> * <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. * <p> * See the javadoc of {@link #prepareOperation(ISchedulingRule, IProgressMonitor)}, * {@link #beginOperation(boolean)}, and {@link #endOperation(ISchedulingRule, boolean)} * 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$
Work manager should never be accessed directly because accessor asserts that workspace is still open.
/** * 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 volatile IBuildConfiguration[] buildOrder = null; protected volatile Digraph<IBuildConfiguration> buildOrderGraph; protected JobGroup buildJobGroup; protected CharsetManager charsetManager; protected ContentDescriptionManager contentDescriptionManager;
indicates if the workspace crashed in a previous session
/** 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 CopyOnWriteArrayList<ILifecycleListener> lifecycleListeners = new CopyOnWriteArrayList<>(); protected LocalMetaArea localMetaArea;
Helper class for performing validation of resource names and locations.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * Job that performs periodic string pool canonicalization. */
private StringPoolJob stringPoolJob;
The synchronizer
/** * The synchronizer */
protected Synchronizer synchronizer;
The currently installed team hook.
/** * 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 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 WorkManager.
/** * 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 volatile 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.
/** * 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 volatile Thread treeLocked = null;
The currently installed file modification validator.
/** * The currently installed file modification validator. */
protected IFileModificationValidator validator = null;
Data structure for holding the multi-part outcome of IWorkspace.computeProjectBuildConfigOrder.

This class is not intended to be instantiated by clients.

See Also:
  • computeProjectBuildConfigOrder.computeProjectBuildConfigOrder(IBuildConfiguration[])
Since:3.7
/** * Data structure for holding the multi-part outcome of * <code>IWorkspace.computeProjectBuildConfigOrder</code>. * <p> * This class is not intended to be instantiated by clients. * </p> * * @see Workspace#computeProjectBuildConfigOrder(IBuildConfiguration[]) * @since 3.7 */
public static final class ProjectBuildConfigOrder {
Creates an instance with the given values.

This class is not intended to be instantiated by clients.

Params:
  • buildConfigurations – initial value of buildConfigurations field
  • hasCycles – initial value of hasCycles field
  • knots – initial value of knots field
/** * Creates an instance with the given values. * <p> * This class is not intended to be instantiated by clients. * </p> * * @param buildConfigurations initial value of <code>buildConfigurations</code> field * @param hasCycles initial value of <code>hasCycles</code> field * @param knots initial value of <code>knots</code> field */
public ProjectBuildConfigOrder(IBuildConfiguration[] buildConfigurations, boolean hasCycles, IBuildConfiguration[][] knots) { this.buildConfigurations = buildConfigurations; this.hasCycles = hasCycles; this.knots = knots; }
A list of project buildConfigs ordered so as to honor the build configuration reference relationships between these project buildConfigs wherever possible. The elements are a subset of the ones passed as the buildConfigurations parameter to IWorkspace.computeProjectOrder, where inaccessible (closed or non-existent) projects have been omitted.
/** * A list of project buildConfigs ordered so as to honor the build configuration reference * relationships between these project buildConfigs wherever possible. The elements * are a subset of the ones passed as the <code>buildConfigurations</code> * parameter to <code>IWorkspace.computeProjectOrder</code>, where * inaccessible (closed or non-existent) projects have been omitted. */
public IBuildConfiguration[] buildConfigurations;
Indicates whether any of the accessible project buildConfigs in buildConfigurations are involved in non-trivial cycles. true if the reference graph contains at least one cycle involving two or more of the project buildConfigs in buildConfigurations, and false if none of the project buildConfigs in buildConfigurations are involved in cycles.
/** * Indicates whether any of the accessible project buildConfigs in * <code>buildConfigurations</code> are involved in non-trivial cycles. * <code>true</code> if the reference graph contains at least * one cycle involving two or more of the project buildConfigs in * <code>buildConfigurations</code>, and <code>false</code> if none of the * project buildConfigs in <code>buildConfigurations</code> are involved in cycles. */
public boolean hasCycles;
A list of knots in the reference graph. This list is empty if the reference graph does not contain cycles. If the reference graph contains cycles, each element is a knot of two or more accessible project buildConfigs from buildConfigurations that are involved in a cycle of mutually dependent references.
/** * A list of knots in the reference graph. This list is empty if * the reference graph does not contain cycles. If the * reference graph contains cycles, each element is a knot of two or * more accessible project buildConfigs from <code>buildConfigurations</code> that are * involved in a cycle of mutually dependent references. */
public IBuildConfiguration[][] knots; } // Comparator used to provide a stable ordering of project buildConfigs private static class BuildConfigurationComparator implements Comparator<IBuildConfiguration> { public BuildConfigurationComparator() { } @Override public int compare(IBuildConfiguration px, IBuildConfiguration py) { int cmp = py.getProject().getName().compareTo(px.getProject().getName()); if (cmp == 0) cmp = py.getName().compareTo(px.getName()); return cmp; } }
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.
/** * 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) { IFileStore fileStore = EFS.getLocalFileSystem().fromLocalFile(root); try { fileStore.delete(EFS.NONE, new NullProgressMonitor()); } catch (CoreException e) { return false; } return true; } 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.
/** * 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.
/** * 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.
/** * Adds a listener for internal workspace lifecycle events. There is no way to * remove lifecycle listeners. */
public void addLifecycleListener(ILifecycleListener listener) { lifecycleListeners.addIfAbsent(listener); } @Override public void addResourceChangeListener(IResourceChangeListener listener) { notificationManager.addListener(listener, IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_DELETE | IResourceChangeEvent.POST_CHANGE); } @Override public void addResourceChangeListener(IResourceChangeListener listener, int eventMask) { notificationManager.addListener(listener, eventMask); }
Deprecated:Use addSaveParticipant(String, ISaveParticipant) instead
/** * @deprecated Use {@link #addSaveParticipant(String, ISaveParticipant)} instead */
@Deprecated @Override 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); } @Override 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.
/** * Broadcasts an internal workspace lifecycle event to interested * internal listeners. */
protected void broadcastEvent(LifecycleEvent event) throws CoreException { for (ILifecycleListener listener : lifecycleListeners) listener.handleEvent(event); } public void broadcastPostChange() { ResourceChangeEvent event = new ResourceChangeEvent(this, IResourceChangeEvent.POST_CHANGE, 0, null); notificationManager.broadcastChanges(tree, event, true); }
Add all IBuildConfigurations reachable from config to the configs collection.
Params:
  • configs – collection of configurations to extend
  • config – config to find reachable configurations to.
/** * Add all IBuildConfigurations reachable from config to the configs collection. * @param configs collection of configurations to extend * @param config config to find reachable configurations to. */
private void recursivelyAddBuildConfigs(Collection/*<IBuildConfiguration>*/<IBuildConfiguration> configs, IBuildConfiguration config) { try { IBuildConfiguration[] referenced = config.getProject().getReferencedBuildConfigs(config.getName(), false); for (IBuildConfiguration element : referenced) { if (configs.contains(element)) continue; configs.add(element); recursivelyAddBuildConfigs(configs, element); } } catch (CoreException e) { // Not possible, we've checked that the project + configuration are accessible. Assert.isTrue(false); } } @Override public void build(int trigger, IProgressMonitor monitor) throws CoreException { buildInternal(EMPTY_BUILD_CONFIG_ARRAY, trigger, true, monitor); } @Override public void build(IBuildConfiguration[] configs, int trigger, boolean buildReferences, IProgressMonitor monitor) throws CoreException { if (configs.length == 0) return; buildInternal(configs, trigger, buildReferences, monitor); }
Build the passed in configurations or the whole workspace.
Params:
  • requestedConfigs – to build or EMPTY_BUILD_CONFIG_ARRAY for the whole workspace
  • trigger – build trigger
  • buildReferences – transitively build referenced build configurations
/** * Build the passed in configurations or the whole workspace. * @param requestedConfigs to build or EMPTY_BUILD_CONFIG_ARRAY for the whole workspace * @param trigger build trigger * @param buildReferences transitively build referenced build configurations */
private void buildInternal(IBuildConfiguration[] requestedConfigs, int trigger, boolean buildReferences, IProgressMonitor monitor) throws CoreException { // Bug 343256 use a relaxed scheduling rule if the config we're building uses a relaxed rule. // Otherwise fall-back to WR. // PRE + POST_BUILD, and the build itself are allowed to modify resources, so require the current thread's scheduling rule // to either contain the WR or be null. Therefore, if not null, ensure it contains the WR rule... final boolean noEnclosingRule = Job.getJobManager().currentRule() == null; boolean relaxed = noEnclosingRule && requestedConfigs.length > 0 && allRelaxed(requestedConfigs, trigger); final ISchedulingRule notificationRule = getRuleFactory().buildRule(); ISchedulingRule currentRule = null; boolean buildParallel = noEnclosingRule && getDescription().getMaxConcurrentBuilds() > 1 && getDescription().getBuildOrder() == null; SubMonitor subMonitor = SubMonitor.convert(monitor, 100); try { try { // Must run the PRE_BUILD with the WRule held before acquiring WS lock // Can remove this if we run notifications without the WS lock held: bug 249951 prepareOperation(notificationRule, subMonitor.newChild(1)); currentRule = notificationRule; beginOperation(true); aboutToBuild(this, trigger); } finally { if (relaxed) { endOperation(currentRule, false); prepareOperation(null, subMonitor.newChild(1)); currentRule = null; beginOperation(false); } } IStatus result; try { // Calculate the build-order having called the pre-build notification (which may // change build order) // If configs == EMPTY_BUILD_CONFIG_ARRAY => This is a full workspace build. IBuildConfiguration[] allConfigs = requestedConfigs; Digraph<IBuildConfiguration> buildGraph = null; if (allConfigs == EMPTY_BUILD_CONFIG_ARRAY) { if (trigger != IncrementalProjectBuilder.CLEAN_BUILD) { if (getDescription().getBuildOrder() != null) { allConfigs = getBuildOrder(); } else { buildGraph = getBuildGraph(); } } else { // clean all accessible configurations List<IBuildConfiguration> configArr = new ArrayList<>(); IProject[] prjs = getRoot().getProjects(); for (IProject prj : prjs) if (prj.isAccessible()) configArr.addAll(Arrays.asList(prj.getBuildConfigs())); allConfigs = configArr.toArray(new IBuildConfiguration[configArr.size()]); } } else { // Order the passed in build configurations + resolve references if requested Set<IBuildConfiguration> refsList = new HashSet<>(); for (IBuildConfiguration config : allConfigs) { // Check project + build configuration are accessible. if (!config.getProject().isAccessible() || !config.getProject().hasBuildConfig(config.getName())) continue; refsList.add(config); // Find transitive closure of referenced project buildConfigs if (buildReferences) recursivelyAddBuildConfigs(refsList, config); } // Order the referenced project buildConfigs buildGraph = computeProjectBuildConfigOrderGraph(refsList); } if (buildGraph != null) { buildGraph.freeze(); if (Policy.DEBUG_BUILD_CYCLE && buildGraph.containsCycles()) { List<IBuildConfiguration[]> nonTrivialComponents = buildGraph.nonTrivialComponents(); for (IBuildConfiguration[] iBuildConfigurations : nonTrivialComponents) { Policy.debug("Cycle: " + Arrays.toString(iBuildConfigurations)); //$NON-NLS-1$ } } allConfigs = ComputeProjectOrder.computeVertexOrder(buildGraph, IBuildConfiguration.class).vertexes; } buildParallel &= (buildGraph != null && buildGraph.vertexList.size() > 1); if (buildParallel) { relaxed = noEnclosingRule && allRelaxed(allConfigs, trigger); buildParallel &= relaxed; } if (buildParallel) { endOperation(currentRule, false); // operation needs to be ended to allow concurrent edits currentRule = null; result = getBuildManager().buildParallel(buildGraph, requestedConfigs, trigger, getBuildJobGroup(), subMonitor.newChild(97)); } else { result = getBuildManager().build(allConfigs, requestedConfigs, trigger, subMonitor.newChild(97)); } } finally { // Run the POST_BUILD with the WRule held if (relaxed) { if (!buildParallel) { endOperation(currentRule, false); } prepareOperation(notificationRule, subMonitor.newChild(1)); currentRule = notificationRule; beginOperation(false); } // must fire POST_BUILD if PRE_BUILD has occurred broadcastBuildEvent(this, IResourceChangeEvent.POST_BUILD, trigger); } if (!result.isOK()) throw new ResourceException(result); } finally { subMonitor.done(); // building may close the tree, but we are still inside an operation so open it if (tree.isImmutable()) newWorkingTree(); // Rule will be the build-rule from the POST_BUILD refresh endOperation(currentRule, false); } } private JobGroup getBuildJobGroup() { if (buildJobGroup == null || buildJobGroup.getMaxThreads() != description.getMaxConcurrentBuilds()) { buildJobGroup = new JobGroup(getClass().getName(), description.getMaxConcurrentBuilds(), 0); } return buildJobGroup; } private boolean allRelaxed(IBuildConfiguration[] requestedConfigs, int trigger) { return Arrays.stream(requestedConfigs).map(cfg -> getBuildManager().getRule(cfg, trigger, null, null)).allMatch(this::isRelaxedRule); } boolean isRelaxedRule(ISchedulingRule rule) { return rule == null || !rule.contains(getRoot()); }
Returns whether creating executable extensions is acceptable at this point in time. In particular, returns false when the system bundle is shutting down, which only occurs when the entire framework is exiting.
/** * 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$ } @Override public void checkpoint(boolean build) { try { final ISchedulingRule rule = getWorkManager().getNotifyRule(); try { prepareOperation(rule, null); beginOperation(true); broadcastPostChange(); } finally { endOperation(rule, build); } } catch (CoreException e) { Policy.log(e.getStatus().getSeverity(), e.getMessage(), e); } }
Closes this workspace; ignored if this workspace is not open. The state of this workspace is not saved before the workspace is shut down.

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.

Note that session properties are discarded when a workspace is closed.

This method is long-running; progress and cancellation are provided by the given progress monitor.

Params:
  • monitor – a progress monitor, or null if progress reporting and cancellation are not desired
Throws:
/** * 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; try { String msg = Messages.resources_closing_0; SubMonitor subMonitor = SubMonitor.convert(monitor, msg, 20); subMonitor.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); saveManager.reportSnapshotRequestor(); prepareOperation(getRoot(), subMonitor.newChild(1)); //shutdown notification first to avoid calling third parties during shutdown notificationManager.shutdown(null); beginOperation(true); IProject[] projects = getRoot().getProjects(IContainer.INCLUDE_HIDDEN); subMonitor.setWorkRemaining(projects.length + 2); for (IProject project : projects) { //notify managers of closing so they can cleanup broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_CLOSE, project)); subMonitor.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(subMonitor.newChild(2, SubMonitor.SUPPRESS_SUBTASK)); } } finally { //release the scheduling rule to be a good job citizen Job.getJobManager().endRule(getRoot()); } }
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.

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.

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.

Returns:result describing the global project order
Since:2.1
/** * 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 VertexOrder<IProject> computeFullProjectOrder() { // determine the full set of accessible projects in the workspace // order the set in descending alphabetical order of project name SortedSet<IProject> allAccessibleProjects = new TreeSet<>((px, py) -> py.getName().compareTo(px.getName())); IProject[] allProjects = getRoot().getProjects(IContainer.INCLUDE_HIDDEN); // List<IProject[]> edges List<IProject[]> edges = new ArrayList<>(allProjects.length); for (IProject p : allProjects) { Project project = (Project) p; // 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(project, false); allAccessibleProjects.add(project); for (IProject ref : refs) { // ignore self references and references to projects that are not accessible if (ref.isAccessible() && !ref.equals(project)) edges.add(new IProject[] {project, ref}); } } return ComputeProjectOrder.computeVertexOrder(allAccessibleProjects, edges, IProject.class); }
Computes the global total ordering of all open projects' active buildConfigs in the workspace based on build configuration references. If an existing and open project's build config P references another existing and open project's build config Q, then Q should come before P in the resulting ordering. If a build config references a non-active build config it is added to the resulting ordered list. Closed and non-existent projects/buildConfigs are ignored, and will not appear in the result. References to non-existent or closed projects/buildConfigs are also ignored, as are any self-references.

When there are choices, the choice is made in a reasonably stable way. For example, given an arbitrary choice between two project buildConfigs, the one with the lower collating project name and build config name will appear earlier in the list.

When the build configuration 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.

Returns:result describing the global active build configuration order
/** * Computes the global total ordering of all open projects' active buildConfigs in the * workspace based on build configuration references. If an existing and open project's build config P * references another existing and open project's build config Q, then Q should come before P * in the resulting ordering. If a build config references a non-active build config it is * added to the resulting ordered list. Closed and non-existent projects/buildConfigs are * ignored, and will not appear in the result. References to non-existent or closed * projects/buildConfigs 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 project buildConfigs, the one with the * lower collating project name and build config name will appear earlier in the list. * </p> * <p> * When the build configuration 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 active build configuration order */
private VertexOrder<IBuildConfiguration> computeActiveBuildConfigOrder() { Digraph<IBuildConfiguration> activeBuildConfigurationGraph = computeActiveBuildConfigGraph(); return ComputeProjectOrder.computeVertexOrder(activeBuildConfigurationGraph, IBuildConfiguration.class); } private Digraph<IBuildConfiguration> computeActiveBuildConfigGraph() { // Determine the full set of accessible active project buildConfigs in the workspace, // and all the accessible project buildConfigs that they reference. This forms a set // of all the project buildConfigs that will be returned. // Order the set in descending alphabetical order of project name then build config name, // as a secondary sort applied after sorting based on references, to achieve a stable // ordering. SortedSet<IBuildConfiguration> allAccessibleBuildConfigs = new TreeSet<>(new BuildConfigurationComparator()); // For each project's active build config, perform a depth first search in the reference graph // rooted at that build config. // This generates the required subset of the reference graph that is required to order all // the dependencies of the active project buildConfigs. IProject[] allProjects = getRoot().getProjects(IContainer.INCLUDE_HIDDEN); List<IBuildConfiguration[]> edges = new ArrayList<>(allProjects.length); for (IProject allProject : allProjects) { Project project = (Project) allProject; // Ignore projects that are not accessible if (!project.isAccessible()) continue; // If the active build configuration hasn't already been explored // perform a depth first search rooted at it if (!allAccessibleBuildConfigs.contains(project.internalGetActiveBuildConfig())) { allAccessibleBuildConfigs.add(project.internalGetActiveBuildConfig()); Stack<IBuildConfiguration> stack = new Stack<>(); stack.push(project.internalGetActiveBuildConfig()); while (!stack.isEmpty()) { IBuildConfiguration buildConfiguration = stack.pop(); // Add all referenced buildConfigs from the current configuration // (it is guaranteed to be accessible as it was pushed onto the stack) Project subProject = (Project) buildConfiguration.getProject(); IBuildConfiguration[] refs = subProject.internalGetReferencedBuildConfigs(buildConfiguration.getName(), false); for (IBuildConfiguration ref : refs) { // Ignore self references and references to projects that are not accessible if (ref.equals(buildConfiguration)) continue; // Add the referenced accessible configuration edges.add(new IBuildConfiguration[] {buildConfiguration, ref}); // If we have already explored the referenced configuration, don't explore it again if (allAccessibleBuildConfigs.contains(ref)) continue; allAccessibleBuildConfigs.add(ref); // Push the referenced configuration onto the stack so that it is explored by the depth first search stack.push(ref); } } } } return ComputeProjectOrder.computeGraph(allAccessibleBuildConfigs, edges, IBuildConfiguration.class); }
Computes the global total ordering of all project buildConfigs in the workspace based on build config references. If an existing and open build config P references another existing and open project build config Q, then Q should come before P in the resulting ordering. Closed and non-existent projects/buildConfigs are ignored, and will not appear in the result. References to non-existent or closed projects/buildConfigs are also ignored, as are any self-references.

When there are choices, the choice is made in a reasonably stable way. For example, given an arbitrary choice between two project buildConfigs, the one with the lower collating project name and build config name will appear earlier in the list.

When the build config 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.

Returns:result describing the global project build configuration order
Deprecated:Use computeFullBuildConfigGraph() instead
/** * Computes the global total ordering of all project buildConfigs in the workspace based * on build config references. If an existing and open build config P * references another existing and open project build config Q, then Q should come before P * in the resulting ordering. Closed and non-existent projects/buildConfigs are * ignored, and will not appear in the result. References to non-existent or closed * projects/buildConfigs 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 project buildConfigs, the one with the * lower collating project name and build config name will appear earlier in the list. * </p> * <p> * When the build config 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 build configuration order * @deprecated Use {@link #computeFullBuildConfigGraph()} instead */
@Deprecated private VertexOrder<IBuildConfiguration> computeFullBuildConfigOrder() { Digraph<IBuildConfiguration> graph = computeFullBuildConfigGraph(); return ComputeProjectOrder.computeVertexOrder(graph, IBuildConfiguration.class); } private Digraph<IBuildConfiguration> computeFullBuildConfigGraph() { // Compute the order for all accessible project buildConfigs SortedSet<IBuildConfiguration> allAccessibleBuildConfigurations = new TreeSet<>(new BuildConfigurationComparator()); IProject[] allProjects = getRoot().getProjects(IContainer.INCLUDE_HIDDEN); List<IBuildConfiguration[]> edges = new ArrayList<>(allProjects.length); for (IProject p : allProjects) { Project project = (Project) p; // Ignore projects that are not accessible if (!project.isAccessible()) continue; IBuildConfiguration[] configs = project.internalGetBuildConfigs(false); for (IBuildConfiguration config : configs) { allAccessibleBuildConfigurations.add(config); IBuildConfiguration[] refs = project.internalGetReferencedBuildConfigs(config.getName(), false); for (IBuildConfiguration ref : refs) { // Ignore self references if (ref.equals(config)) continue; // Add the reference to the set of reachable configs + add an edge allAccessibleBuildConfigurations.add(ref); edges.add(new IBuildConfiguration[] {config, ref}); } } } return ComputeProjectOrder.computeGraph(allAccessibleBuildConfigurations, edges, IBuildConfiguration.class); } private static ProjectOrder vertexOrderToProjectOrder(VertexOrder<IProject> order) { IProject[] projects = new IProject[order.vertexes.length]; System.arraycopy(order.vertexes, 0, projects, 0, order.vertexes.length); IProject[][] knots = new IProject[order.knots.length][]; for (int i = 0; i < order.knots.length; i++) { knots[i] = new IProject[order.knots[i].length]; System.arraycopy(order.knots[i], 0, knots[i], 0, order.knots[i].length); } return new ProjectOrder(projects, order.hasCycles, knots); } private static ProjectBuildConfigOrder vertexOrderToProjectBuildConfigOrder(VertexOrder<IBuildConfiguration> order) { IBuildConfiguration[] buildConfigs = new IBuildConfiguration[order.vertexes.length]; System.arraycopy(order.vertexes, 0, buildConfigs, 0, order.vertexes.length); IBuildConfiguration[][] knots = new IBuildConfiguration[order.knots.length][]; for (int i = 0; i < order.knots.length; i++) { knots[i] = new IBuildConfiguration[order.knots[i].length]; System.arraycopy(order.knots[i], 0, knots[i], 0, order.knots[i].length); } return new ProjectBuildConfigOrder(buildConfigs, order.hasCycles, knots); } @Deprecated @Override 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<IProject> bad = new HashSet<>(); // Set<IProject> bad Set<IProject> keepers = new HashSet<>(Arrays.asList(r.projects)); for (IProject[] knot : r.knots) { for (IProject project : knot) { // 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<IProject> p = new LinkedList<>(); p.addAll(Arrays.asList(r.projects)); for (Iterator<IProject> it = p.listIterator(); it.hasNext();) { IProject project = 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}; } @Override public ProjectOrder computeProjectOrder(IProject[] projects) { // Compute the full project order for all accessible projects VertexOrder<IProject> fullProjectOrder = computeFullProjectOrder(); // Create a filter to remove all projects that are not in the list asked for final Set<IProject> projectSet = new HashSet<>(projects.length); projectSet.addAll(Arrays.asList(projects)); Predicate<IProject> filter = vertex -> !projectSet.contains(vertex); // Filter the order and return it return vertexOrderToProjectOrder(ComputeProjectOrder.filterVertexOrder(fullProjectOrder, filter, IProject.class)); }
Computes a total ordering of the given projects buildConfigs based on both static and dynamic project references. If an existing and open project's build configuratioin P references another existing and open project's configuration 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/buildConfigs are also ignored, as are any self-references. The total ordering is always consistent with the global total ordering of all open projects' buildConfigs in the workspace.

When there are choices, the choice is made in a reasonably stable way. For example, given an arbitrary choice between two project buildConfigs, the one with the lower collating configuration name is usually selected.

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.

This method is time-consuming and should not be called unnecessarily. There are a very limited set of changes to a workspace that could affect the outcome: creating, renaming, or deleting a project; opening or closing a project; deleting a build configuration; adding or removing a build configuration reference.

Params:
  • buildConfigs – the build configurations to order
Returns:result describing the build configuration order
Since:3.7
/** * Computes a total ordering of the given projects buildConfigs based on both static and * dynamic project references. If an existing and open project's build configuratioin P references * another existing and open project's configuration 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/buildConfigs are also ignored, as are any * self-references. The total ordering is always consistent with the global * total ordering of all open projects' buildConfigs in the workspace. * <p> * When there are choices, the choice is made in a reasonably stable way. * For example, given an arbitrary choice between two project buildConfigs, the one with * the lower collating configuration 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> * <p> * This method is time-consuming and should not be called unnecessarily. * There are a very limited set of changes to a workspace that could affect * the outcome: creating, renaming, or deleting a project; opening or * closing a project; deleting a build configuration; adding or removing a build configuration reference. * </p> * * @param buildConfigs the build configurations to order * @return result describing the build configuration order * @since 3.7 */
public ProjectBuildConfigOrder computeProjectBuildConfigOrder(IBuildConfiguration[] buildConfigs) { // Compute the full project order for all accessible projects VertexOrder<IBuildConfiguration> fullBuildConfigOrder = computeFullBuildConfigOrder(); // Create a filter to remove all project buildConfigs that are not in the list asked for final Set<IBuildConfiguration> projectConfigSet = new HashSet<>(buildConfigs.length); projectConfigSet.addAll(Arrays.asList(buildConfigs)); Predicate<IBuildConfiguration> filter = vertex -> !projectConfigSet.contains(vertex); // Filter the order and return it return vertexOrderToProjectBuildConfigOrder(ComputeProjectOrder.filterVertexOrder(fullBuildConfigOrder, filter, IBuildConfiguration.class)); } private Digraph<IBuildConfiguration> computeProjectBuildConfigOrderGraph(Collection<IBuildConfiguration> buildConfigs) { Digraph<IBuildConfiguration> fullBuildConfigOrder = computeFullBuildConfigGraph(); // Create a filter to remove all project buildConfigs that are not in the list asked for final Set<IBuildConfiguration> projectConfigSet = new HashSet<>(buildConfigs); Predicate<IBuildConfiguration> filter = vertex -> !projectConfigSet.contains(vertex); // Filter the order and return it return ComputeProjectOrder.buildFilteredDigraph(fullBuildConfigOrder, filter, IBuildConfiguration.class); } @Override 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); } @Override public IStatus copy(IResource[] resources, IPath destination, int updateFlags, IProgressMonitor monitor) throws CoreException { Assert.isLegal(resources != null); if (resources.length == 0) { return Status.OK_STATUS; } // to avoid concurrent changes to this array resources = resources.clone(); SubMonitor subMonitor = SubMonitor.convert(monitor, Messages.resources_copying_0, resources.length); IPath parentPath = null; String message = Messages.resources_copyProblem; MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, message, null); try { prepareOperation(getRoot(), subMonitor); beginOperation(true); for (int i = 0; i < resources.length; i++) { IResource resource = resources[i]; if (resource == null || isDuplicate(resources, i)) { subMonitor.split(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 { status.merge(requirements); } } catch (CoreException e) { status.merge(e.getStatus()); } } else { message = NLS.bind(Messages.resources_notChild, resources[i].getFullPath(), parentPath); status.merge(new ResourceStatus(IResourceStatus.OPERATION_FAILED, resources[i].getFullPath(), message)); } subMonitor.worked(1); } } catch (OperationCanceledException e) { getWorkManager().operationCanceled(); throw e; } finally { subMonitor.done(); endOperation(getRoot(), true); } if (status.matches(IStatus.ERROR)) throw new ResourceException(status); return status.isOK() ? Status.OK_STATUS : (IStatus) status; } 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, true, 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 (IResource element : children) { String childName = element.getName(); if (!projectCopy || !childName.equals(IProjectDescription.DESCRIPTION_FILE_NAME)) { IPath childPath = destination.append(childName); copyTree(element, 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 != null) && !srcLoc.equals(srcRawLoc)) { // 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 (String other : variables) { if (!PathVariableUtil.isPreferred(other)) continue; IPath resolveDestVariable = URIUtil.toPath(destPathVariableManager.resolveURI(destPathVariableManager.getURIValue(other))); if (resolveDestVariable != null && resolveDestVariable.equals(resolvedSrcValue)) { return other; } } // 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()); StringBuilder result = new StringBuilder(); for (String segment : segments) { String var = PathVariableUtil.extractVariable(segment); if (var.length() > 0) { String copiedVariable = copyVariable(source, dest, var); int index = segment.indexOf(var); if (index != -1) { result.append(segment.substring(0, index)); result.append(copiedVariable); int start = index + var.length(); int end = segment.length(); result.append(segment.substring(start, end)); } } else result.append(segment); } 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.
Params:
  • root – The subtree to count resources for
  • depth – The depth of the subtree to count
  • phantom – If true, phantoms are included, otherwise they are ignored.
/** * 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 = (aTree, requestor, 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
/** * 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; } @Override 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); } @Override public IStatus delete(IResource[] resources, int updateFlags, IProgressMonitor monitor) throws CoreException { String message = Messages.resources_deleteProblem; MultiStatus result = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, message, null); if (resources.length == 0) { return result; } resources = resources.clone(); // to avoid concurrent changes to this array SubMonitor subMonitor = SubMonitor.convert(monitor, message, resources.length); try { prepareOperation(getRoot(), subMonitor); beginOperation(true); for (IResource r : resources) { Resource resource = (Resource) r; if (resource == null) { subMonitor.split(1); continue; } try { resource.delete(updateFlags, subMonitor.newChild(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()); } } subMonitor.worked(1); } if (result.matches(IStatus.ERROR)) throw new ResourceException(result); return result; } catch (OperationCanceledException e) { getWorkManager().operationCanceled(); throw e; } finally { subMonitor.done(); endOperation(getRoot(), true); } } @Override public void deleteMarkers(IMarker[] markers) throws CoreException { Assert.isNotNull(markers); if (markers.length == 0) return; // clone to avoid outside changes markers = markers.clone(); try { prepareOperation(null, null); beginOperation(true); for (IMarker marker : markers) if (marker != null && marker.getResource() != null) markerManager.removeMarker(marker.getResource(), marker.getId()); } finally { endOperation(null, false); } }
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.
/** * 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 (IProject element : children) tree.deleteElement(element.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.
/** * 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) 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. The buildOrder cache contains the total order of the build configurations in the workspace, including projects not mentioned in the workspace description.
/** * Flush the build order cache for the workspace. The buildOrder cache contains the total * order of the build configurations in the workspace, including projects not mentioned in * the workspace description. */
protected void flushBuildOrder() { buildOrder = null; buildOrderGraph = null; } @Override public void forgetSavedTree(String pluginId) { saveManager.forgetSavedTree(pluginId); } public AliasManager getAliasManager() { return aliasManager; }
Returns this workspace's build manager
/** * Returns this workspace's build manager */
public BuildManager getBuildManager() { return buildManager; }
Returns the order in which open projects in this workspace will be built. The result returned is a list of project buildConfigs, that need to be built in order to successfully build the active config of every project in this workspace.

The build configuration order is based on information specified in the workspace description. The project build configs are built in the order specified by IWorkspaceDescription.getBuildOrder; closed or non-existent projects are ignored and not included in the result. If any open projects are not specified in this order, they are appended to the end of the build order sorted by project name (to provide a stable ordering).

If IWorkspaceDescription.getBuildOrder is non-null, the default build order is used (calculated based on references); again, only open projects' buildConfigs are included in the result.

The returned value is cached in the buildOrder field.

See Also:
Returns:the list of currently open projects active buildConfigs (and the project buildConfigs they depend on) in the workspace in the order in which they would be built by IWorkspace.build.
/** * Returns the order in which open projects in this workspace will be built. * The result returned is a list of project buildConfigs, that need to be built * in order to successfully build the active config of every project in this * workspace. * <p> * The build configuration order is based on information specified in the workspace * description. The project build configs 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 any open projects are * not specified in this order, they are appended to the end of the build order * sorted by project name (to provide a stable ordering). * </p> * <p> * If <code>IWorkspaceDescription.getBuildOrder</code> is non-null, the default * build order is used (calculated based on references); again, only open projects' * buildConfigs 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 active buildConfigs (and the project buildConfigs * they depend on) 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() */
public IBuildConfiguration[] getBuildOrder() { // Return the build order cache. if (buildOrder != null) return buildOrder; // see if a particular build order is specified String[] order = description.getBuildOrder(false); if (order != null) { LinkedHashSet<IBuildConfiguration> configs = new LinkedHashSet<>(); // convert from project names to active project buildConfigs // and eliminate non-existent and closed projects for (String element : order) { IProject project = getRoot().getProject(element); if (project.isAccessible()) configs.add(((Project) project).internalGetActiveBuildConfig()); } // Add projects not mentioned in the build order to the end, in a sensible reference order configs.addAll(Arrays.asList(vertexOrderToProjectBuildConfigOrder(computeActiveBuildConfigOrder()).buildConfigurations)); // Update the cache - Java 5 volatile memory barrier semantics IBuildConfiguration[] bo = new IBuildConfiguration[configs.size()]; configs.toArray(bo); this.buildOrder = bo; } else { // use default project build order // computed for all accessible projects in workspace Digraph<IBuildConfiguration> buildGraph = getBuildGraph(); buildOrder = vertexOrderToProjectBuildConfigOrder(ComputeProjectOrder.computeVertexOrder(buildGraph, IBuildConfiguration.class)).buildConfigurations; } return buildOrder; }
Returns:the build graph, or null if a build order has been specified (in which case, use getBuildOrder() directly
/** * * @return the build graph, or null if a build order has been specified (in which case, use {@link #getBuildOrder()} directly */
private Digraph<IBuildConfiguration> getBuildGraph() { if (buildOrderGraph != null) { return buildOrderGraph; } String[] order = description.getBuildOrder(false); if (order != null) { return null; } buildOrderGraph = computeActiveBuildConfigGraph(); return buildOrderGraph; } public CharsetManager getCharsetManager() { return charsetManager; } public ContentDescriptionManager getContentDescriptionManager() { return contentDescriptionManager; } @Override public Map<IProject, IProject[]> getDanglingReferences() { IProject[] projects = getRoot().getProjects(IContainer.INCLUDE_HIDDEN); Map<IProject, IProject[]> result = new HashMap<>(projects.length); for (IProject project : projects) { if (!project.isAccessible()) { continue; } IProject[] refs = ((Project)project).internalGetDescription().getReferencedProjects(false); List<IProject> dangling = new ArrayList<>(refs.length); for (IProject ref : refs) { if (!ref.exists()) { dangling.add(ref); } } if (!dangling.isEmpty()) { result.put(project, dangling.toArray(new IProject[dangling.size()])); } } return result; } @Override public IWorkspaceDescription getDescription() { WorkspaceDescription workingCopy = defaultWorkspaceDescription(); description.copyTo(workingCopy); return workingCopy; }
Returns the current element tree for this workspace
/** * 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
/** * 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; } @Override public IFilterMatcherDescriptor getFilterMatcherDescriptor(String filterMatcherId) { return filterManager.getFilterDescriptor(filterMatcherId); } @Override public IFilterMatcherDescriptor[] getFilterMatcherDescriptors() { return filterManager.getFilterDescriptors(); } @Override public IProjectNatureDescriptor getNatureDescriptor(String natureId) { return natureManager.getNatureDescriptor(natureId); } @Override public IProjectNatureDescriptor[] getNatureDescriptors() { return natureManager.getNatureDescriptors(); }
Returns the nature manager for this workspace.
/** * Returns the nature manager for this workspace. */
public NatureManager getNatureManager() { return natureManager; } public NotificationManager getNotificationManager() { return notificationManager; } @Override public IPathVariableManager getPathVariableManager() { return pathVariableManager; } public IPropertyManager getPropertyManager() { return propertyManager; }
Returns the refresh manager for this workspace
/** * 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.
/** * 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; } } @Override public IWorkspaceRoot getRoot() { return defaultRoot; } @Override 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; } @Override public ISynchronizer getSynchronizer() { return synchronizer; }
Returns the installed team hook. Never returns null.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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; } @Override public boolean isAutoBuilding() { return description.isAutoBuilding(); } public boolean isOpen() { return openFlag; } @Override public boolean isTreeLocked() { return treeLocked == Thread.currentThread(); }
Link the given tree into the receiver's tree at the specified resource.
/** * 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); } @Override 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; } @Override 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; } @Override 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); } @Override public IStatus move(IResource[] resources, IPath destination, int updateFlags, IProgressMonitor monitor) throws CoreException { Assert.isLegal(resources != null); if (resources.length == 0) { return Status.OK_STATUS; } resources = resources.clone(); // to avoid concurrent changes to this array String message = Messages.resources_moving_0; SubMonitor subMonitor = SubMonitor.convert(monitor, message, resources.length); IPath parentPath = null; message = Messages.resources_moveProblem; MultiStatus status = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, message, null); try { prepareOperation(getRoot(), subMonitor); beginOperation(true); for (int i = 0; i < resources.length; i++) { Resource resource = (Resource) resources[i]; if (resource == null || isDuplicate(resources, i)) { subMonitor.split(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, subMonitor.newChild(1)); } catch (CoreException e) { status.merge(e.getStatus()); } } else { subMonitor.worked(1); status.merge(requirements); } } catch (CoreException e) { subMonitor.worked(1); status.merge(e.getStatus()); } } else { subMonitor.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 { subMonitor.done(); endOperation(getRoot(), true); } if (status.matches(IStatus.ERROR)) throw new ResourceException(status); return status.isOK() ? (IStatus) Status.OK_STATUS : (IStatus) status; }
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.
/** * 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.
/** * 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; } @Override public IBuildConfiguration newBuildConfig(String projectName, String configName) { return new BuildConfiguration(getRoot().getProject(projectName), configName); } @Override 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.
/** * 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.
/** * 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 OK 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.

This method is long-running; progress and cancellation are provided by the given progress monitor.

Params:
  • monitor – a progress monitor, or null if progress reporting and cancellation are not desired
Throws:
  • CoreException – if the workspace could not be opened. Reasons include:
    • There is no valid workspace structure at the given location in the local file system.
    • The workspace structure on disk appears to be hopelessly corrupt.
See Also:
Returns:status with code OK if no problems; otherwise status describing any serious but non-fatal problems.
/** * 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(); SubMonitor subMonitor = SubMonitor.convert(null); startup(subMonitor); // 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.
Params:
  • rule – the scheduling rule that describes what this operation intends to modify.
/** * 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 ISchedulingRule buildRule = getRuleFactory().buildRule(); if (rule != null && buildRule != null && (rule.isConflicting(buildRule) || buildRule.isConflicting(rule))) 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 (String arg : args) if (arg.equalsIgnoreCase(REFRESH_ON_STARTUP)) return true; return false; } @Override public void removeResourceChangeListener(IResourceChangeListener listener) { notificationManager.removeListener(listener); } @Deprecated @Override public void removeSaveParticipant(Plugin plugin) { Assert.isNotNull(plugin, "Plugin must not be null"); //$NON-NLS-1$ saveManager.removeParticipant(plugin.getBundle().getSymbolicName()); } @Override public void removeSaveParticipant(String pluginId) { Assert.isNotNull(pluginId, "Plugin id must not be null"); //$NON-NLS-1$ saveManager.removeParticipant(pluginId); } @Override public void run(ICoreRunnable action, IProgressMonitor monitor) throws CoreException { run(action, defaultRoot, IWorkspace.AVOID_UPDATE, monitor); } @Override public void run(ICoreRunnable action, ISchedulingRule rule, int options, IProgressMonitor monitor) throws CoreException { SubMonitor subMonitor = SubMonitor.convert(monitor, Policy.totalWork); // $NON-NLS-1$ int depth = -1; boolean avoidNotification = (options & IWorkspace.AVOID_UPDATE) != 0; try { prepareOperation(rule, subMonitor); beginOperation(true); if (avoidNotification) avoidNotification = notificationManager.beginAvoidNotify(); depth = getWorkManager().beginUnprotected(); action.run(subMonitor.newChild(Policy.opWork)); } catch (OperationCanceledException e) { getWorkManager().operationCanceled(); throw e; } catch (CoreException e) { if (e.getStatus().getSeverity() == IStatus.CANCEL) getWorkManager().operationCanceled(); throw e; } finally { subMonitor.done(); if (avoidNotification) notificationManager.endAvoidNotify(); if (depth >= 0) getWorkManager().endUnprotected(depth); endOperation(rule, false); } } @Override public void run(IWorkspaceRunnable action, IProgressMonitor monitor) throws CoreException { run((ICoreRunnable) action, defaultRoot, IWorkspace.AVOID_UPDATE, monitor); } @Override public void run(IWorkspaceRunnable action, ISchedulingRule rule, int options, IProgressMonitor monitor) throws CoreException { run((ICoreRunnable) action, rule, options, monitor); } @Override 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); } } 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) Policy.debug(msg); } } @Override 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) { flushBuildOrder(); } 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; }
Shuts down the workspace managers.
/** * Shuts down the workspace managers. */
protected void shutdown(IProgressMonitor monitor) throws CoreException { IManager[] managers = { buildManager, propertyManager, pathVariableManager, charsetManager, fileSystemManager, markerManager, _workManager, aliasManager, refreshManager, contentDescriptionManager, natureManager, filterManager }; SubMonitor subMonitor = SubMonitor.convert(monitor, managers.length); // $NON-NLS-1$ try { 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 (IManager manager : managers) { if (manager == null) subMonitor.worked(1); else { try { manager.shutdown(subMonitor.newChild(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 { subMonitor.done(); } } @Override public String[] sortNatureSet(String[] natureIds) { return natureManager.sortNatureSet(natureIds); }
Starts all the workspace manager classes.
/** * 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); saveManager = new SaveManager(this); saveManager.startup(null); propertyManager = new PropertyManager2((Workspace) ResourcesPlugin.getWorkspace()); propertyManager.startup(monitor); charsetManager = new CharsetManager(this); charsetManager.startup(null); contentDescriptionManager = new ContentDescriptionManager(); contentDescriptionManager.startup(null); //must start after save manager, because (read) access to tree is needed //must start after other managers to avoid potential cyclic dependency on uninitialized managers (see bug 316182) //must start before alias manager (see bug 94829) refreshManager = new RefreshManager(this); refreshManager.startup(null); //must start at the end to avoid potential cyclic dependency on other uninitialized managers (see bug 369177) aliasManager = new AliasManager(this); aliasManager.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.
/** * Returns a string representation of this working state's * structure suitable for debug purposes. */
public String toDebugString() { final StringBuilder buffer = new StringBuilder("\nDump of " + this + ":\n"); //$NON-NLS-1$ //$NON-NLS-2$ buffer.append(" parent: " + tree.getParent()); //$NON-NLS-1$ IElementContentVisitor visitor = (aTree, requestor, 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(); } @Override 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 (IFile file : files) { if (file.isReadOnly()) { IPath filePath = file.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() { @Override public void handleException(Throwable exception) { status[0] = new ResourceStatus(IStatus.ERROR, null, Messages.resources_errorValidator, exception); } @Override 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]; } @Override public IStatus validateLinkLocation(IResource resource, IPath unresolvedLocation) { return locationValidator.validateLinkLocation(resource, unresolvedLocation); } @Override public IStatus validateLinkLocationURI(IResource resource, URI unresolvedLocation) { return locationValidator.validateLinkLocationURI(resource, unresolvedLocation); } @Override public IStatus validateName(String segment, int type) { return locationValidator.validateName(segment, type); } @Override public IStatus validateNatureSet(String[] natureIds) { return natureManager.validateNatureSet(natureIds); } @Override public IStatus validatePath(String path, int type) { return locationValidator.validatePath(path, type); } @Override public IStatus validateProjectLocation(IProject context, IPath location) { return locationValidator.validateProjectLocation(context, location); } @Override public IStatus validateProjectLocationURI(IProject project, URI location) { return locationValidator.validateProjectLocationURI(project, location); }
Internal method. To be called only from the following methods:
  • IFile#appendContents
  • IFile#setContents(InputStream, boolean, boolean, IProgressMonitor)
  • IFile#setContents(IFileState, boolean, boolean, IProgressMonitor)
See Also:
  • validateSave.validateSave(IFile)
/** * 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() { @Override public void handleException(Throwable exception) { status[0] = new ResourceStatus(IStatus.ERROR, null, Messages.resources_errorValidator, exception); } @Override public void run() throws Exception { status[0] = validator.validateSave(file); } }; SafeRunner.run(body); if (!status[0].isOK()) throw new ResourceException(status[0]); } @Override 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; } }