/******************************************************************************* * Copyright (c) 2014, 2016 itemis AG 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: * Alexander Nyßen (itemis AG) - initial API and implementation * *******************************************************************************/ package org.eclipse.gef.mvc.fx.operations; import java.util.ArrayList; import java.util.List; 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.IUndoContext; 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.gef.mvc.fx.MvcFxBundle; /** * The {@link AbstractCompositeOperation} is an abstract implementation of the * {@link ICompositeOperation} interface. The individual operations are stored * in a {@link List}. They are executed/redone/undone in forward order. * * @author anyssen * @author mwienand * */ public abstract class AbstractCompositeOperation extends AbstractOperation implements ICompositeOperation, ITransactionalOperation { /** * The list containing the {@link ITransactionalOperation}s which are * combined in this composite operation. */ List<ITransactionalOperation> operations = new ArrayList<>(); /** * Creates a new {@link AbstractCompositeOperation} with the given label. * * @param label * The label for this {@link AbstractCompositeOperation}. */ public AbstractCompositeOperation(String label) { super(label); } @Override public void add(IUndoableOperation operation) { if (operation instanceof ITransactionalOperation) { operations.add((ITransactionalOperation) operation); } else { throw new IllegalArgumentException( "The given operation may not be null and must implement ITransactionalOperation."); } } /** * Adds the given {@link IUndoableOperation}s to this composite operation. * * @param operations * The {@link IUndoableOperation}s which are added to this * composite operation. */ public void addAll(List<ITransactionalOperation> operations) { /* * Do not use <code>operations.addAll()</code> because we need to check * for <code>null</code> operations. */ for (ITransactionalOperation op : operations) { add(op); } } @Override public void addContext(IUndoContext context) { super.addContext(context); } @Override public boolean canExecute() { for (ITransactionalOperation operation : operations) { if (!operation.canExecute()) { return false; } } return true; } @Override public boolean canRedo() { for (ITransactionalOperation operation : operations) { if (!operation.canRedo()) { return false; } } return true; } @Override public boolean canUndo() { for (ITransactionalOperation operation : operations) { if (!operation.canUndo()) { return false; } } return true; } /** * Return an {@link IStatus} representing the merge of the given first and * second {@link IStatus}s. * * @param s1 * The first {@link IStatus}. * @param s2 * The second {@link IStatus}. * @return The merge of the first and second {@link IStatus}. */ protected IStatus combine(IStatus s1, IStatus s2) { MultiStatus status = new MultiStatus(MvcFxBundle.PLUGIN_ID, IStatus.OK, null, null); status.merge(s1); status.merge(s2); return status; } @Override public IStatus execute(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { IStatus status = Status.OK_STATUS; for (ITransactionalOperation operation : operations) { combine(status, operation.execute(monitor, info)); } return status; } /** * Returns the list of {@link ITransactionalOperation}s which are combined * in this composite operation. * * @return The list of {@link ITransactionalOperation}s which are combined * in this composite operation. */ public List<ITransactionalOperation> getOperations() { return operations; } @Override public boolean isContentRelevant() { for (ITransactionalOperation op : operations) { if (op.isContentRelevant()) { return true; } } return false; } /** * Returns <code>true</code> if no operations are currently combined in this * composite operation. Otherwise returns <code>false</code>. * * @return <code>true</code> if no operations are currently combined in this * composite operation, otherwise <code>false</code>. */ public boolean isEmpty() { return operations.isEmpty(); } @Override public boolean isNoOp() { for (ITransactionalOperation op : operations) { if (!op.isNoOp()) { return false; } } return true; } @Override public IStatus redo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { IStatus status = Status.OK_STATUS; for (ITransactionalOperation operation : operations) { combine(status, operation.redo(monitor, info)); } return status; } @Override public void remove(IUndoableOperation operation) { operations.remove(operation); } @Override public IStatus undo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { IStatus status = Status.OK_STATUS; for (ITransactionalOperation operation : operations) { combine(status, operation.undo(monitor, info)); } return status; } /** * Simplifies this composite operation if possible and returns the * simplified operation. When this composite operation does not contain any * operations, <code>null</code> is returned. When this composite operation * contains exactly one operation, that one operation is returned. * Otherwise, this composite operation is returned. * * @param filterNoOps * <code>true</code> if no-ops (see * {@link ITransactionalOperation#isNoOp()}) should be removed * from the list of operations, otherwise <code>false</code>. * @return <code>null</code> when no operations are contained, the one * operation when only one operation is contained, this composite * when multiple operations are contained. */ public ITransactionalOperation unwrap(boolean filterNoOps) { if (filterNoOps) { // remove no-op operations for (int i = operations.size() - 1; i >= 0; i--) { ITransactionalOperation op = operations.get(i); if (op.isNoOp()) { operations.remove(i); } else if (op instanceof AbstractCompositeOperation) { // unwrap recursively ITransactionalOperation unwrapped = ((AbstractCompositeOperation) operations .get(i)).unwrap(filterNoOps); operations.remove(i); operations.add(i, unwrapped); } } } // reduce operation if (operations.size() == 0) { return null; } else if (operations.size() == 1) { return operations.get(0); } else { return this; } } }