/*
* Copyright (c) 2012 Data Harmonisation Panel
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* HUMBOLDT EU Integrated Project #030962
* Data Harmonisation Panel <http://www.dhpanel.eu>
*/
package eu.esdihumboldt.hale.ui.util.io;
import java.lang.reflect.InvocationTargetException;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
import eu.esdihumboldt.hale.ui.util.io.internal.StatesIfDoneProgressMonitor;
/**
* Stores current {@link IProgressMonitor}s used in a thread. Allows subtasking
* w/o knowing of the parent task.
*
* @author Simon Templer
*/
public class ThreadProgressMonitor {
private static final ThreadLocal<LinkedList<IProgressMonitor>> threadMonitors = new ThreadLocal<LinkedList<IProgressMonitor>>();
/**
* Get the monitor currently associated with the thread.
*
* @return the progress monitor or <code>null</code>
*/
public static IProgressMonitor getCurrent() {
LinkedList<IProgressMonitor> mons = threadMonitors.get();
if (mons == null || mons.isEmpty()) {
return null;
}
return mons.getLast();
}
/**
* Register a progress monitor with the current thread. It must be removed
* using {@link #remove(IProgressMonitor)}.
*
* @param monitor the progress monitor
*/
public static void register(IProgressMonitor monitor) {
LinkedList<IProgressMonitor> mons = threadMonitors.get();
if (mons == null) {
mons = new LinkedList<IProgressMonitor>();
threadMonitors.set(mons);
}
mons.add(monitor);
}
/**
* Remove a progress monitor that was previously registered. Also removes
* monitors that have been added after the given one.
*
* @param monitor the progress monitor to remove
*/
public static void remove(IProgressMonitor monitor) {
LinkedList<IProgressMonitor> mons = threadMonitors.get();
if (mons == null || mons.isEmpty()) {
return;
}
if (mons.contains(monitor)) {
while (!mons.isEmpty() && !mons.getLast().equals(monitor)) {
// remove all monitors that have been added after the given
// monitor (and should have been removed)
mons.removeLast();
}
// remove given monitor
mons.removeLast();
}
}
/**
* Run the given operation in a forked thread with a progress monitor dialog
* or in the current thread with a sub progress monitor if possible.
*
* @param op the operation to execute
* @param isCancelable if the operation can be canceled
* @throws Exception if any error occurs executing the operation
*/
public static void runWithProgressDialog(final IRunnableWithProgress op,
final boolean isCancelable) throws Exception {
IProgressMonitor pm = getCurrent();
if (pm == null) {
// no current progress monitor associated to thread, so
// in display thread, launch a new progress monitor dialog
final Display display = PlatformUI.getWorkbench().getDisplay();
final AtomicReference<Exception> error = new AtomicReference<Exception>();
final IRunnableWithProgress progressOp = new IRunnableWithProgress() {
@Override
public void run(IProgressMonitor monitor) throws InvocationTargetException,
InterruptedException {
// create a custom progress monitor to be able to decide
// whether the progress is done
StatesIfDoneProgressMonitor cpm = new StatesIfDoneProgressMonitor(monitor);
// register the progress monitor
register(cpm);
try {
op.run(cpm);
} finally {
// deregister the progress monitor
remove(cpm);
}
}
};
display.syncExec(new Runnable() {
@Override
public void run() {
try {
new ProgressMonitorDialog(display.getActiveShell()).run(true, isCancelable,
progressOp);
} catch (Exception e) {
error.set(e);
}
}
});
if (error.get() != null) {
throw error.get();
}
}
else {
// progress monitor associated to this thread, so
// run the operation in the same thread
boolean useOriginalMonitor = false;
if (pm instanceof StatesIfDoneProgressMonitor) {
useOriginalMonitor = ((StatesIfDoneProgressMonitor) pm).isDone();
}
if (useOriginalMonitor) {
// use the original monitor
pm.subTask(""); // reset subtask name
op.run(pm);
}
else {
// use a sub progress monitor
IProgressMonitor sm = new StatesIfDoneProgressMonitor(new SubProgressMonitor(pm, 0,
SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK));
register(sm);
try {
op.run(sm);
} finally {
remove(sm);
}
}
}
}
}