/*******************************************************************************
* Copyright (c) 2013 Google, Inc 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:
* Sergey Prigogin (Google) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.ui;
import java.lang.reflect.InvocationTargetException;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.IJobManager;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.progress.IProgressService;
import org.eclipse.cdt.ui.CUIPlugin;
/**
* Synchronously executes a {@link Job} while allowing user to cancel it if it takes too long.
*/
public final class BusyCursorJobRunner {
/**
* Adapts a {@link Job} to be an {@link IRunnableWithProgress}.
*/
private static class JobRunnableWithProgressAdapter implements IRunnableWithProgress {
private final Job job;
/**
* Creates the {@link IRunnableWithProgress} from the {@link Job}.
*/
public JobRunnableWithProgressAdapter(Job job) {
this.job = job;
}
@Override
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
IStatus result;
try {
monitor.beginTask(job.getName(), IProgressMonitor.UNKNOWN);
result = executeAndWait(job, monitor);
} catch (RuntimeException e) {
throw new InvocationTargetException(e);
}
switch (result.getSeverity()) {
case IStatus.CANCEL:
throw new InterruptedException();
case IStatus.ERROR:
if (result.getException() instanceof OperationCanceledException) {
throw new InterruptedException();
}
throw new InvocationTargetException(new CoreException(result));
}
}
}
/**
* Runs the given job and waits for it to finish. If executing in the UI thread, sets the cursor
* to busy while the job is being executed.
*
* @param job the job to execute
* @return the status reflecting the result of the job execution
*/
public static IStatus execute(Job job) {
boolean inUiThread = Thread.currentThread() == Display.getDefault().getThread();
if (inUiThread) {
return busyCursorWhile(job);
}
return executeAndWait(job, new NullProgressMonitor());
}
private static IStatus busyCursorWhile(Job job) {
try {
IProgressService progressService = PlatformUI.getWorkbench().getProgressService();
progressService.busyCursorWhile(new JobRunnableWithProgressAdapter(job));
} catch (InterruptedException e) {
return Status.CANCEL_STATUS; // Operation was cancelled.
} catch (InvocationTargetException e) {
Throwable targetException = e.getTargetException();
if (targetException instanceof CoreException) {
return ((CoreException) targetException).getStatus();
}
return new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID, e.getMessage(), e);
}
return Status.OK_STATUS;
}
private static IStatus executeAndWait(final Job job, IProgressMonitor monitor) {
final IStatus[] statusHolder = new IStatus[1];
IJobManager jobManager = Job.getJobManager();
JobChangeAdapter listener = new JobChangeAdapter() {
@Override
public void done(IJobChangeEvent event) {
super.done(event);
if (event.getJob() == job) {
synchronized (statusHolder) {
statusHolder[0] = event.getResult();
statusHolder.notifyAll();
}
}
}
};
jobManager.addJobChangeListener(listener);
job.schedule();
try {
synchronized (statusHolder) {
while (statusHolder[0] == null) {
try {
statusHolder.wait(100);
if (monitor.isCanceled()) {
job.cancel();
}
} catch (InterruptedException e) {
job.cancel();
return Status.CANCEL_STATUS;
}
}
return statusHolder[0];
}
} finally {
monitor.done();
jobManager.removeJobChangeListener(listener);
}
}
private BusyCursorJobRunner() {}
}