package org.marketcetera.photon.commons.ui.workbench;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.operation.ModalContext;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.handlers.HandlerUtil;
import org.marketcetera.photon.commons.ui.JFaceUtils;
import org.marketcetera.photon.commons.ui.JFaceUtils.IUnsafeRunnableWithProgress;
import org.marketcetera.util.log.I18NBoundMessage;
import org.marketcetera.util.misc.ClassVersion;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Lists;
/* $License$ */
/**
* Abstract handler that computes the selection (with an optional filter) and
* calls the abstract {@link #process(T, SubMonitor)} for each item in the
* selection.
* <p>
* If an exception is thrown, an error dialog will be presented to the user, see
* {@link ProgressUtils#runModalWithErrorDialog(org.eclipse.jface.window.IShellProvider, IRunnableWithProgress, I18NBoundMessage)}.
* <p>
* This should be used when you want to process a homogeneous subset of the
* selection, e.g. start all selected items that are not already started.
*
* @author <a href="mailto:will@marketcetera.com">Will Horn</a>
* @version $Id: AbstractSelectionHandler.java 16154 2012-07-14 16:34:05Z colin $
* @since 2.0.0
*/
@ClassVersion("$Id: AbstractSelectionHandler.java 16154 2012-07-14 16:34:05Z colin $")
public abstract class AbstractSelectionHandler<T> extends SafeHandler {
private final Class<T> mClazz;
private final Predicate<T> mPredicate;
private final I18NBoundMessage mFailureMessage;
/**
* Constructor. The class parameter will be used to filter items of the
* correct type.
*
* @param clazz
* the type of items to be processed
* @param failureMessage
* the message to log if the operation fails.
* @throws IllegalArgumentException
* if any parameter is null
*/
public AbstractSelectionHandler(Class<T> clazz,
I18NBoundMessage failureMessage) {
this(clazz, failureMessage, Predicates.<T> alwaysTrue());
}
/**
* Constructor providing a predicate for additional filtering.
*
* @param clazz
* the type of items to be processed
* @param failureMessage
* the message to log if the operation fails.
* @param predicate
* the predicate for filtering
* @throws IllegalArgumentException
* if any parameter is null
*/
public AbstractSelectionHandler(Class<T> clazz,
I18NBoundMessage failureMessage, Predicate<T> predicate) {
mClazz = clazz;
mFailureMessage = failureMessage;
mPredicate = predicate;
}
@Override
public void executeSafely(ExecutionEvent event) throws ExecutionException {
IStructuredSelection selection = (IStructuredSelection) HandlerUtil
.getCurrentSelectionChecked(event);
final List<T> selected = Collections.synchronizedList(Lists
.<T> newArrayList());
for (Object item : selection.toList()) {
if (mClazz.isInstance(item)) {
T cast = mClazz.cast(item);
if (mPredicate.apply(cast)) {
selected.add(cast);
}
}
}
if (selected.isEmpty()) {
return;
}
final IRunnableWithProgress operation = JFaceUtils
.safeRunnableWithProgress(new IUnsafeRunnableWithProgress() {
@Override
public void run(IProgressMonitor monitor) throws Exception {
SubMonitor progress = SubMonitor.convert(monitor,
selected.size());
for (T item : selected) {
ModalContext.checkCanceled(progress);
process(item, progress.newChild(1));
}
}
});
ProgressUtils.runModalWithErrorDialog(HandlerUtil
.getActiveWorkbenchWindowChecked(event), operation,
mFailureMessage);
}
/**
* Processes a single item. Implementations should use the monitor to set
* the current task name, but should not call
* {@link IProgressMonitor#worked} or {@link IProgressMonitor#done()}.
*
* @param item
* the current item
* @param monitor
* the progress monitor to use for reporting progress to the
* user. It is the caller's responsibility to call done() on the
* given monitor. Will not be null.
* @throws InterruptedException
* if the operation detects a request to cancel, using
* <code>IProgressMonitor.isCanceled()</code>, it should exit by
* throwing <code>InterruptedException</code>
* @throws Exception
* if an exception occurs during the operation
*/
protected abstract void process(T item, IProgressMonitor monitor)
throws Exception;
}