blob: 178046c73118a864e5bedf3b023fdf2873a43087 [file] [log] [blame]
/*
* <copyright>
*
* Copyright (c) 2014, IETR/INSA of Rennes
* 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:
* IETR/INSA of Rennes - initial API, implementation and documentation
*
* </copyright>
*
*/
package org.eclipse.graphiti.features.custom;
import java.util.Collection;
import java.util.Collections;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.emf.transaction.RecordingCommand;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.features.context.IContext;
import org.eclipse.graphiti.features.context.ICustomContext;
/**
* <p>
* This class should be used if for some reason a CustomFeature can be long to
* execute. It runs {@link #execute(ICustomContext, IProgressMonitor)} in a
* Command on top of current TransactionalEditingDomain. This command itself is
* run in a Job, and can use the associated IProgressMonitor
* </p>
* <p>
* This is useful to indicate to users that the job is running, but eclipse has
* not crashed.
* </p>
*
* @author Antoine Lorence
* @since 0.12
*/
public abstract class AbstractAsynchronousCustomFeature extends AbstractCustomFeature {
public AbstractAsynchronousCustomFeature(IFeatureProvider fp) {
super(fp);
}
/**
* Concrete code to execute. Sub-classes should use the given monitor
* correctly: create tasks (and eventually sub-tasks), notify for worked and
* done tasks and check if user cancelled the task.
*
* @param context
* The CustomFeature context
* @param monitor
* The monitor used to manage progress bar and Job cancellation
*/
protected abstract void execute(ICustomContext context, IProgressMonitor monitor);
/**
* Callback executed just before job scheduling; called in the feature
* execution thread. Default implementation is empty.
*/
protected void beforeJobExecution() {
}
/**
* Callback executed immediately after job execution in the background job
* execution thread. Default implementation is empty.
*/
protected void afterJobExecution() {
}
/**
* Initialize the Job.
*
* @param context
* The CustomContext that will be given to
* {@link #execute(ICustomContext, IProgressMonitor)}.
* @return The Job instance
*/
protected Job initializeJob(final ICustomContext context) {
return new Job(getName()) {
@Override
protected IStatus run(final IProgressMonitor monitor) {
final TransactionalEditingDomain editingDomain = getDiagramBehavior().getEditingDomain();
final RecordingCommand command = new RecordingCommand(editingDomain, getName()) {
private IStatus result = null;
@Override
protected void doExecute() {
try {
AbstractAsynchronousCustomFeature.this.execute(context, monitor);
result = Status.OK_STATUS;
} catch (OperationCanceledException e) {
result = Status.CANCEL_STATUS;
}
}
@Override
public Collection<?> getResult() {
return result == null ? Collections.EMPTY_LIST : Collections.singletonList(result);
}
};
// Execute (synchronously) the defined command in a proper EMF
// transaction
editingDomain.getCommandStack().execute(command);
// Update the dirty state of the diagram
getDiagramBehavior().getDiagramContainer().updateDirtyState();
// Callback
afterJobExecution();
return (IStatus) command.getResult().iterator().next();
}
};
}
/**
* Initialize parameters of the given Job
*
* @param job
* The Job instance to configure
*/
protected void configureJob(Job job) {
job.setUser(true);
job.setPriority(Job.LONG);
}
/**
* Must not be overridden in order to guarantee correct delegation to the
* background job, see {@link #execute(ICustomContext)}.
*/
@Override
final public void execute(IContext context) {
super.execute(context);
}
/**
* The implementation of this method will during the execution of the
* feature trigger the creation of a background job, configure it and
* execute it.<br>
* Clients should implement their feature functionality in
* {@link #execute(ICustomContext, IProgressMonitor)}, potentially also in
* the {@link #beforeJobExecution()} and {@link #afterJobExecution()} hooks.
* The background job and its behavior can be changed in the methods
* {@link #initializeJob(ICustomContext)} and {@link #configureJob(Job)}.
*
* @param context
* The CustomFeature context
*
*/
@Override
final public void execute(ICustomContext context) {
final Job job = initializeJob(context);
configureJob(job);
// Callback
beforeJobExecution();
// Job is run
job.schedule();
}
/**
* The default implementation returns <code>false</code> in any case. This
* is usually the desired return value, because this method will be queried
* already before the asynchronous feature will be executed (
* {@link #execute(ICustomContext, IProgressMonitor)} is called). Returning
* <code>true</code> here would mean that users could already undo the
* feature while it is still being executed; this would mean also canceling
* the a running job. Because of potentially strange effects and complexity
* this is not possible.
*
* @return Always <code>false</code>
*/
@Override
final public boolean hasDoneChanges() {
return false;
}
}