/*
* 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.service.entity.internal;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.operations.AbstractOperation;
import org.eclipse.core.commands.operations.ICompositeOperation;
import org.eclipse.core.commands.operations.IOperationHistory;
import org.eclipse.core.commands.operations.IUndoableOperation;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.operations.IWorkbenchOperationSupport;
import de.fhg.igd.slf4jplus.ALogger;
import de.fhg.igd.slf4jplus.ALoggerFactory;
import eu.esdihumboldt.hale.common.align.model.AlignmentUtil;
import eu.esdihumboldt.hale.common.align.model.Condition;
import eu.esdihumboldt.hale.common.align.model.EntityDefinition;
import eu.esdihumboldt.hale.common.instance.model.Filter;
import eu.esdihumboldt.hale.ui.internal.HALEUIPlugin;
import eu.esdihumboldt.hale.ui.service.entity.EntityDefinitionService;
/**
* Decorator that adds undo/redo support to an entity definition service.
*
* @author Kai Schwierczek
*/
public class EntityDefinitionServiceUndoSupport extends EntityDefinitionServiceDecorator {
private static final ALogger log = ALoggerFactory
.getLogger(EntityDefinitionServiceUndoSupport.class);
/**
* Create undo/redo support for the given entity definition service
*
* @param entityDefinitionService the entity definition service
*/
public EntityDefinitionServiceUndoSupport(EntityDefinitionService entityDefinitionService) {
super(entityDefinitionService);
}
/**
* @see eu.esdihumboldt.hale.ui.service.entity.internal.EntityDefinitionServiceDecorator#addConditionContext(eu.esdihumboldt.hale.common.align.model.EntityDefinition,
* eu.esdihumboldt.hale.common.instance.model.Filter)
*/
@Override
public EntityDefinition addConditionContext(EntityDefinition sibling, Filter filter) {
AddConditionContextOperation operation = new AddConditionContextOperation(sibling, filter);
executeOperation(operation);
return operation.getResult();
}
/**
* @see eu.esdihumboldt.hale.ui.service.entity.internal.EntityDefinitionServiceDecorator#addIndexContext(eu.esdihumboldt.hale.common.align.model.EntityDefinition,
* java.lang.Integer)
*/
@Override
public EntityDefinition addIndexContext(EntityDefinition sibling, Integer index) {
AddIndexContextOperation operation = new AddIndexContextOperation(sibling, index);
executeOperation(operation);
return operation.getResult();
}
/**
* @see eu.esdihumboldt.hale.ui.service.entity.internal.EntityDefinitionServiceDecorator#addNamedContext(eu.esdihumboldt.hale.common.align.model.EntityDefinition)
*/
@Override
public EntityDefinition addNamedContext(EntityDefinition sibling) {
AddNamedContextOperation operation = new AddNamedContextOperation(sibling);
executeOperation(operation);
return operation.getResult();
}
/**
* @see eu.esdihumboldt.hale.ui.service.entity.internal.EntityDefinitionServiceDecorator#removeContext(eu.esdihumboldt.hale.common.align.model.EntityDefinition)
*/
@Override
public void removeContext(EntityDefinition entity) {
RemoveContextOperation operation = new RemoveContextOperation(entity);
executeOperation(operation);
}
/**
* @see eu.esdihumboldt.hale.ui.service.entity.internal.EntityDefinitionServiceDecorator#editConditionContext(eu.esdihumboldt.hale.common.align.model.EntityDefinition,
* eu.esdihumboldt.hale.common.instance.model.Filter)
*/
@Override
public EntityDefinition editConditionContext(EntityDefinition sibling, Filter filter) {
ICompositeOperation operation = new CompositeOperation("Edit condition context");
IWorkbenchOperationSupport operationSupport = PlatformUI.getWorkbench()
.getOperationSupport();
operation.addContext(operationSupport.getUndoContext());
operationSupport.getOperationHistory().openOperation(operation, IOperationHistory.EXECUTE);
EntityDefinition result = super.editConditionContext(sibling, filter);
operationSupport.getOperationHistory().closeOperation(result != null, true,
IOperationHistory.EXECUTE);
return result;
}
/**
* Execute an operation.
*
* @param operation the operation to execute
*/
protected void executeOperation(IUndoableOperation operation) {
IWorkbenchOperationSupport operationSupport = PlatformUI.getWorkbench()
.getOperationSupport();
// service is workbench wide, so the operation should also be workbench
// wide
operation.addContext(operationSupport.getUndoContext());
try {
operationSupport.getOperationHistory().execute(operation, null, null);
} catch (ExecutionException e) {
log.error("Error executing operation on entity definition service", e);
}
}
/**
* A simple composite operation which gathers operations and
* execute/undos/redos them in order.
*/
private static class CompositeOperation extends AbstractOperation implements
ICompositeOperation {
// XXX also rewrite add/has/remove/get-Context(s)?
// In our case it currently is not necessary!?
private final Deque<IUndoableOperation> operations = new ArrayDeque<IUndoableOperation>();
/**
* Construct an operation that has the specified label.
*
* @param label the label to be used for the operation. Should never be
* <code>null</code>.
*/
public CompositeOperation(String label) {
super(label);
}
/**
* @see org.eclipse.core.commands.operations.AbstractOperation#canExecute()
*/
@Override
public boolean canExecute() {
for (IUndoableOperation operation : operations)
if (!operation.canExecute())
return false;
return true;
}
/**
* @see org.eclipse.core.commands.operations.AbstractOperation#canRedo()
*/
@Override
public boolean canRedo() {
for (IUndoableOperation operation : operations)
if (!operation.canRedo())
return false;
return true;
}
/**
* @see org.eclipse.core.commands.operations.AbstractOperation#canUndo()
*/
@Override
public boolean canUndo() {
for (IUndoableOperation operation : operations)
if (!operation.canUndo())
return false;
return true;
}
/**
* @see org.eclipse.core.commands.operations.ICompositeOperation#add(org.eclipse.core.commands.operations.IUndoableOperation)
*/
@Override
public void add(IUndoableOperation operation) {
// "If the operation instance has already been added, this method will have no effect."
if (!operations.contains(operation))
operations.add(operation);
}
/**
* @see org.eclipse.core.commands.operations.ICompositeOperation#remove(org.eclipse.core.commands.operations.IUndoableOperation)
*/
@Override
public void remove(IUndoableOperation operation) {
operations.remove(operation);
operation.dispose();
}
/**
* @see org.eclipse.core.commands.operations.AbstractOperation#execute(org.eclipse.core.runtime.IProgressMonitor,
* org.eclipse.core.runtime.IAdaptable)
*/
@Override
public IStatus execute(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
IStatus[] children = new IStatus[operations.size()];
Iterator<IUndoableOperation> iter = operations.iterator();
int i = 0;
while (iter.hasNext())
children[i++] = iter.next().execute(monitor, info);
return new MultiStatus(HALEUIPlugin.PLUGIN_ID, 1, children, "", null);
}
/**
* @see org.eclipse.core.commands.operations.AbstractOperation#redo(org.eclipse.core.runtime.IProgressMonitor,
* org.eclipse.core.runtime.IAdaptable)
*/
@Override
public IStatus redo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
IStatus[] children = new IStatus[operations.size()];
Iterator<IUndoableOperation> iter = operations.iterator();
int i = 0;
while (iter.hasNext())
children[i++] = iter.next().redo(monitor, info);
return new MultiStatus(HALEUIPlugin.PLUGIN_ID, 1, children, "", null);
}
/**
* @see org.eclipse.core.commands.operations.AbstractOperation#undo(org.eclipse.core.runtime.IProgressMonitor,
* org.eclipse.core.runtime.IAdaptable)
*/
@Override
public IStatus undo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
IStatus[] children = new IStatus[operations.size()];
Iterator<IUndoableOperation> iter = operations.descendingIterator();
int i = 0;
while (iter.hasNext())
children[i++] = iter.next().undo(monitor, info);
return new MultiStatus(HALEUIPlugin.PLUGIN_ID, 1, children, "", null);
}
}
/**
* Abstract operation that supports an entity definition result and maps
* redo to execute.
*/
private static abstract class AbstractResultOperation extends AbstractOperation {
private EntityDefinition result;
/**
* Construct an operation that has the specified label.
*
* @param label the label to be used for the operation. Should never be
* <code>null</code>.
*/
public AbstractResultOperation(String label) {
super(label);
}
/**
* Sets the resulting entity definition.
*
* @param result the result
*/
protected void setResult(EntityDefinition result) {
this.result = result;
}
/**
* Returns the resulting entity definition. Operation must be executed
* once, otherwise null is returned.
*
* @return the resulting entity definition or <code>null</code> if the
* operation wasn't executed yet.
*/
public EntityDefinition getResult() {
return result;
}
/**
* @see org.eclipse.core.commands.operations.AbstractOperation#redo(org.eclipse.core.runtime.IProgressMonitor,
* org.eclipse.core.runtime.IAdaptable)
*/
@Override
public IStatus redo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
return execute(monitor, info);
}
}
/**
* Operation that adds a condition context to an entity definition.
*/
private class AddConditionContextOperation extends AbstractResultOperation {
private final EntityDefinition sibling;
private final Filter filter;
/**
* Create an operation that adds a condition context to an entity
* definition.
*
* @param sibling the entity definition which is a sibling of the entity
* definition to create
* @param filter the condition filter
*/
private AddConditionContextOperation(EntityDefinition sibling, Filter filter) {
super("Add condition context");
this.sibling = sibling;
this.filter = filter;
}
/**
* @see org.eclipse.core.commands.operations.AbstractOperation#execute(org.eclipse.core.runtime.IProgressMonitor,
* org.eclipse.core.runtime.IAdaptable)
*/
@Override
public IStatus execute(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
setResult(entityDefinitionService.addConditionContext(sibling, filter));
return Status.OK_STATUS;
}
/**
* @see org.eclipse.core.commands.operations.AbstractOperation#undo(org.eclipse.core.runtime.IProgressMonitor,
* org.eclipse.core.runtime.IAdaptable)
*/
@Override
public IStatus undo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
entityDefinitionService.removeContext(getResult());
return Status.OK_STATUS;
}
}
/**
* Operation that adds an index context to an entity definition.
*/
private class AddIndexContextOperation extends AbstractResultOperation {
private final EntityDefinition sibling;
private final Integer index;
/**
* Create an operation that adds an index context to an entity
* definition.
*
* @param sibling the entity definition which is a sibling of the entity
* definition to create
* @param index the condition filter
*/
private AddIndexContextOperation(EntityDefinition sibling, Integer index) {
super("Add index context");
this.sibling = sibling;
this.index = index;
}
/**
* @see org.eclipse.core.commands.operations.AbstractOperation#execute(org.eclipse.core.runtime.IProgressMonitor,
* org.eclipse.core.runtime.IAdaptable)
*/
@Override
public IStatus execute(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
setResult(entityDefinitionService.addIndexContext(sibling, index));
return Status.OK_STATUS;
}
/**
* @see org.eclipse.core.commands.operations.AbstractOperation#undo(org.eclipse.core.runtime.IProgressMonitor,
* org.eclipse.core.runtime.IAdaptable)
*/
@Override
public IStatus undo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
entityDefinitionService.removeContext(getResult());
return Status.OK_STATUS;
}
}
/**
* Operation that adds a named context to an entity definition.
*/
private class AddNamedContextOperation extends AbstractResultOperation {
private final EntityDefinition sibling;
/**
* Create an operation that adds a named context to an entity
* definition.
*
* @param sibling the entity definition which is a sibling of the entity
* definition to create
*/
private AddNamedContextOperation(EntityDefinition sibling) {
super("Add named context");
this.sibling = sibling;
}
/**
* @see org.eclipse.core.commands.operations.AbstractOperation#execute(org.eclipse.core.runtime.IProgressMonitor,
* org.eclipse.core.runtime.IAdaptable)
*/
@Override
public IStatus execute(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
setResult(entityDefinitionService.addNamedContext(sibling));
return Status.OK_STATUS;
}
/**
* @see org.eclipse.core.commands.operations.AbstractOperation#undo(org.eclipse.core.runtime.IProgressMonitor,
* org.eclipse.core.runtime.IAdaptable)
*/
@Override
public IStatus undo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
entityDefinitionService.removeContext(getResult());
return Status.OK_STATUS;
}
}
/**
* Operation that removes a context from an entity definition.
*/
private class RemoveContextOperation extends AbstractOperation {
private final Filter filter;
private final Integer index;
private final Integer name;
// entity with context
private final EntityDefinition entity;
// entity without context
private final EntityDefinition baseEntity;
/**
* Create an operation that removes a context from an entity definition.
*
* @param entity the entity definition
*/
public RemoveContextOperation(EntityDefinition entity) {
super("Remove " + getTypeText(entity) + " context");
Condition condition = AlignmentUtil.getContextCondition(entity);
if (condition != null)
filter = condition.getFilter();
else
filter = null;
index = AlignmentUtil.getContextIndex(entity);
name = AlignmentUtil.getContextName(entity);
this.entity = entity;
this.baseEntity = AlignmentUtil.getDefaultEntity(entity);
}
/**
* @see org.eclipse.core.commands.operations.AbstractOperation#execute(org.eclipse.core.runtime.IProgressMonitor,
* org.eclipse.core.runtime.IAdaptable)
*/
@Override
public IStatus execute(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
entityDefinitionService.removeContext(entity);
return Status.OK_STATUS;
}
/**
* @see org.eclipse.core.commands.operations.AbstractOperation#redo(org.eclipse.core.runtime.IProgressMonitor,
* org.eclipse.core.runtime.IAdaptable)
*/
@Override
public IStatus redo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
return execute(monitor, info);
}
/**
* @see org.eclipse.core.commands.operations.AbstractOperation#undo(org.eclipse.core.runtime.IProgressMonitor,
* org.eclipse.core.runtime.IAdaptable)
*/
@Override
public IStatus undo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
// resulting entity is equal to original entity
if (filter != null)
entityDefinitionService.addConditionContext(baseEntity, filter);
else if (index != null)
entityDefinitionService.addIndexContext(baseEntity, index);
else if (name != null)
entityDefinitionService.addNamedContext(baseEntity);
return Status.OK_STATUS;
}
}
private static String getTypeText(EntityDefinition entity) {
if (AlignmentUtil.getContextCondition(entity) != null)
return "condition";
else if (AlignmentUtil.getContextIndex(entity) != null)
return "index";
else if (AlignmentUtil.getContextName(entity) != null)
return "named";
throw new IllegalArgumentException("Cannot remove context from entity without context.");
}
}