/****************************************************************************** * Copyright (c) 2006 IBM Corporation 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: * IBM Corporation - initial API and implementation ****************************************************************************/ package org.eclipse.gmf.runtime.emf.ui.action; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.commands.operations.ICompositeOperation; import org.eclipse.core.commands.operations.IOperationHistory; 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.emf.transaction.TransactionalEditingDomain; import org.eclipse.gmf.runtime.common.core.command.CommandResult; import org.eclipse.gmf.runtime.common.core.command.ICommand; import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand; import org.eclipse.gmf.runtime.emf.commands.core.command.CompositeTransactionalCommand; /** * Command used to allow subclasses of the {@link AbstractModelActionDelegate} * to read and write to the model from their #doRun implementation. * <P> * This is a kind of {@link ICompositeOperation} that opens itself on the * operation history. It runs a {@link Runnable} supplied at the time of * instantiation and any operations executed through the history in that * runnable are considered to be part of the composite, and can be undone and * redone in a single step. This allows file modification validation to be done * on each command executed by a subclass of {@link AbstractModelActionDelegate}. * * @author ldamus */ abstract class WriteCommand extends AbstractTransactionalCommand implements ICompositeOperation { private final CompositeTransactionalCommand compositeDelegate; private final Runnable runnable; private final IOperationHistory history; /** * Initializes me. * * @param domain * my editing domain * @param label * my user-readable label * @param history * the operation history * @param runnable * the runnable to run when I execute */ public WriteCommand(TransactionalEditingDomain domain, String label, IOperationHistory history, Runnable runnable) { super(domain, label, null); this.compositeDelegate = new CompositeTransactionalCommand(domain, label); this.runnable = runnable; this.history = history; } /** * Opens a composite operation on the operation history and runs the * runnable. */ protected CommandResult doExecuteWithResult( IProgressMonitor progressMonitor, IAdaptable info) throws ExecutionException { if (runnable != null) { history.openOperation(this, IOperationHistory.EXECUTE); try { runnable.run(); history.closeOperation(getStatus().isOK(), false, IOperationHistory.EXECUTE); } catch (RuntimeException e) { history.closeOperation(false, false, IOperationHistory.EXECUTE); throw e; } } // have to compute the result because we didn't actually execute the // compositeDelegate return computeResult(); } /** * Redoes the commands that were accumulated when the runnable was run. */ protected CommandResult doRedoWithResult(IProgressMonitor progressMonitor, IAdaptable info) throws ExecutionException { compositeDelegate.redo(progressMonitor, info); return compositeDelegate.getCommandResult(); } /** * Undoes the commands that were accumulated when the runnable was run. */ protected CommandResult doUndoWithResult(IProgressMonitor progressMonitor, IAdaptable info) throws ExecutionException { compositeDelegate.undo(progressMonitor, info); return compositeDelegate.getCommandResult(); } /** * Adds to my {@link CompositeTransactionalCommand} delegate. */ public void add(IUndoableOperation operation) { compositeDelegate.add(operation); refreshContexts(); } /** * Removes from my {@link CompositeTransactionalCommand} delegate. */ public void remove(IUndoableOperation operation) { compositeDelegate.remove(operation); refreshContexts(); } private void refreshContexts() { IUndoContext[] currentContexts = getContexts(); for (int i = 0; i < currentContexts.length; i++) { if (!compositeDelegate.hasContext(currentContexts[i])) { removeContext(currentContexts[i]); } } IUndoContext[] newContexts = compositeDelegate.getContexts(); for (int i = 0; i < newContexts.length; i++) { if (!hasContext(newContexts[i])) { addContext(newContexts[i]); } } } /** * Gets the affected files from my {@link CompositeTransactionalCommand} * delegate. */ public List getAffectedFiles() { return compositeDelegate.getAffectedFiles(); } /** * Computes a command result based on the contents of my * {@link CompositeTransactionalCommand} delegate. * * @return the command result */ private CommandResult computeResult() { if (compositeDelegate.size() == 0) { return null; } else if (compositeDelegate.size() == 1) { IUndoableOperation operation = (IUndoableOperation) compositeDelegate .iterator().next(); if (operation instanceof ICommand) { return ((ICommand) operation).getCommandResult(); } return new CommandResult(Status.OK_STATUS); } IStatus worst = Status.OK_STATUS; List statuses = new ArrayList(); List returnValues = new ArrayList(); for (Iterator i = compositeDelegate.iterator(); i.hasNext();) { IUndoableOperation operation = (IUndoableOperation) i.next(); if (operation instanceof ICommand) { ICommand command = (ICommand) operation; CommandResult result = command.getCommandResult(); if (result != null) { IStatus nextStatus = result.getStatus(); if (nextStatus.getSeverity() > worst.getSeverity()) { worst = nextStatus; } statuses.add(nextStatus); Object nextValue = result.getReturnValue(); if (nextValue != null) { if (getClass().isInstance(command)) { // unwrap the values from other composites if (nextValue != null && nextValue instanceof Collection) { returnValues.addAll((Collection) nextValue); } else { returnValues.add(nextValue); } } else { returnValues.add(nextValue); } } } } } IStatus status = new MultiStatus(worst.getPlugin(), worst.getCode(), (IStatus[]) statuses.toArray(new IStatus[statuses.size()]), worst.getMessage(), null); return new CommandResult(status, returnValues); } public abstract IStatus getStatus(); }