blob: 43747c030adddb9921887e726f4394dec22c8138 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ui.actions;
import java.lang.reflect.InvocationTargetException;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.operation.IThreadListener;
import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
/**
* An operation which potentially makes changes to the workspace. All resource
* modification should be performed using this operation. The primary
* consequence of using this operation is that events which typically occur as a
* result of workspace changes (such as the firing of resource deltas,
* performance of autobuilds, etc.) are deferred until the outermost operation
* has successfully completed.
* <p>
* If a scheduling rule is provided, the operation will obtain that scheduling
* rule for the duration of its <code>execute</code> method. If no scheduling
* rule is provided, the operation will obtain a scheduling rule that locks
* the entire workspace for the duration of the operation.
* </p>
* <p>
* Subclasses must implement <code>execute</code> to do the work of the
* operation.
* </p>
* @see ISchedulingRule
* @see org.eclipse.core.resources.IWorkspace#run(IWorkspaceRunnable, IProgressMonitor)
* */
public abstract class WorkspaceModifyOperation implements IRunnableWithProgress, IThreadListener {
private ISchedulingRule rule;
/**
* Creates a new operation.
*/
protected WorkspaceModifyOperation() {
this(IDEWorkbenchPlugin.getPluginWorkspace().getRoot());
}
/**
* Creates a new operation that will run using the provided
* scheduling rule.
* @param rule The ISchedulingRule to use or <code>null</code>.
* @since 3.0
*/
protected WorkspaceModifyOperation(ISchedulingRule rule) {
this.rule = rule;
}
/**
* Performs the steps that are to be treated as a single logical workspace
* change.
* <p>
* Subclasses must implement this method.
* </p>
*
* @param monitor the progress monitor to use to display progress and field
* user requests to cancel
* @exception CoreException if the operation fails due to a CoreException
* @exception InvocationTargetException if the operation fails due to an exception other than CoreException
* @exception InterruptedException if the operation detects a request to cancel,
* using <code>IProgressMonitor.isCanceled()</code>, it should exit by throwing
* <code>InterruptedException</code>. It is also possible to throw
* <code>OperationCanceledException</code>, which gets mapped to <code>InterruptedException</code>
* by the <code>run</code> method.
*/
protected abstract void execute(IProgressMonitor monitor)
throws CoreException, InvocationTargetException,
InterruptedException;
/**
* The <code>WorkspaceModifyOperation</code> implementation of this
* <code>IRunnableWithProgress</code> method initiates a batch of changes by
* invoking the <code>execute</code> method as a workspace runnable
* (<code>IWorkspaceRunnable</code>).
*/
public synchronized final void run(IProgressMonitor monitor)
throws InvocationTargetException, InterruptedException {
final InvocationTargetException[] iteHolder = new InvocationTargetException[1];
try {
IWorkspaceRunnable workspaceRunnable = new IWorkspaceRunnable() {
public void run(IProgressMonitor pm) throws CoreException {
try {
execute(pm);
} catch (InvocationTargetException e) {
// Pass it outside the workspace runnable
iteHolder[0] = e;
} catch (InterruptedException e) {
// Re-throw as OperationCanceledException, which will be
// caught and re-thrown as InterruptedException below.
throw new OperationCanceledException(e.getMessage());
}
// CoreException and OperationCanceledException are propagated
}
};
IDEWorkbenchPlugin.getPluginWorkspace().run(workspaceRunnable,
rule, IResource.NONE, monitor);
} catch (CoreException e) {
throw new InvocationTargetException(e);
} catch (OperationCanceledException e) {
throw new InterruptedException(e.getMessage());
}
// Re-throw the InvocationTargetException, if any occurred
if (iteHolder[0] != null) {
throw iteHolder[0];
}
}
/* (non-Javadoc)
* @see IThreadListener#threadChange(Thread);
*/
public void threadChange(Thread thread) {
//we must make sure we aren't transferring control away from a thread that
//already owns a scheduling rule because this is deadlock prone (bug 105491)
if (rule == null)
return;
Job currentJob = Platform.getJobManager().currentJob();
if (currentJob == null)
return;
ISchedulingRule currentRule = currentJob.getRule();
if (currentRule == null)
return;
throw new IllegalStateException("Cannot fork a thread from a thread owning a rule"); //$NON-NLS-1$
}
}