/*****************************************************************************
* Copyright (c) 2011 Atos.
*
*
* 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:
* Mathieu Velten (Atos) - Initial API and implementation
*
*****************************************************************************/
package org.eclipse.papyrus.commands;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.operations.ICompositeOperation;
import org.eclipse.core.commands.operations.IOperationApprover;
import org.eclipse.core.commands.operations.IOperationApprover2;
import org.eclipse.core.commands.operations.IOperationHistory;
import org.eclipse.core.commands.operations.IOperationHistoryListener;
import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.core.commands.operations.IUndoableOperation;
import org.eclipse.core.commands.operations.OperationHistoryFactory;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.workspace.EMFCommandOperation;
import org.eclipse.gmf.runtime.common.core.command.ICommand;
import org.eclipse.papyrus.commands.wrappers.GMFtoEMFCommandWrapper;
public class CheckedOperationHistory implements IOperationHistory {
private static class CheckedOperationHistoryHolder {
public static final CheckedOperationHistory instance = new CheckedOperationHistory();
}
public static CheckedOperationHistory getInstance() {
return CheckedOperationHistoryHolder.instance;
}
protected static final IOperationApprover2[] approversArray;
protected IOperationHistory history;
private static class ApproverPriorityPair implements Comparable<ApproverPriorityPair> {
public IOperationApprover2 approver;
public int priority;
public int compareTo(ApproverPriorityPair o) {
if(o.priority > priority) {
return 1;
} else if(o.priority < priority) {
return -1;
} else {
return 0;
}
}
}
static {
IConfigurationElement[] configElements = Platform.getExtensionRegistry().getConfigurationElementsFor("org.eclipse.papyrus.commands", "operationApprover");
List<ApproverPriorityPair> approverPriorityPairs = new LinkedList<ApproverPriorityPair>();
for(IConfigurationElement elem : configElements) {
if("operationApprover".equals(elem.getName())) {
try {
ApproverPriorityPair approverPriorityPair = new ApproverPriorityPair();
approverPriorityPair.approver = (IOperationApprover2)elem.createExecutableExtension("class");
approverPriorityPair.priority = Integer.parseInt(elem.getAttribute("priority"));
approverPriorityPairs.add(approverPriorityPair);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Collections.sort(approverPriorityPairs);
approversArray = new IOperationApprover2[approverPriorityPairs.size()];
for(int i = 0; i < approversArray.length; i++) {
approversArray[i] = approverPriorityPairs.get(i).approver;
}
}
private CheckedOperationHistory() {
history = OperationHistoryFactory.getOperationHistory();
}
/*
* Consult the IOperationApprovers to see if the proposed redo should be
* allowed.
*/
protected IStatus getRedoApproval(IUndoableOperation operation, IAdaptable info) {
operation = unwrap(operation);
for(int i = 0; i < approversArray.length; i++) {
IStatus approval = approversArray[i].proceedRedoing(operation, this, info);
if(!approval.isOK()) {
return approval;
}
}
return Status.OK_STATUS;
}
/*
* Consult the IOperationApprovers to see if the proposed undo should be
* allowed.
*/
protected IStatus getUndoApproval(IUndoableOperation operation, IAdaptable info) {
operation = unwrap(operation);
for(int i = 0; i < approversArray.length; i++) {
IStatus approval = approversArray[i].proceedUndoing(operation, this, info);
if(!approval.isOK()) {
return approval;
}
}
return Status.OK_STATUS;
}
/*
* Consult the IOperationApprovers to see if the proposed execution should
* be allowed.
*
* @since 3.2
*/
protected IStatus getExecuteApproval(IUndoableOperation operation, IAdaptable info) {
operation = unwrap(operation);
for(int i = 0; i < approversArray.length; i++) {
IStatus approval = approversArray[i].proceedExecuting(operation, this, info);
if(!approval.isOK()) {
return approval;
}
}
return Status.OK_STATUS;
}
/**
* the unified command stack wraps ICommand GMFtoEMFCommandWrapper
* which are wrapped in EMFCommandOperation,
* unwrap it before validation
*
* @param operation
* @return
*/
protected IUndoableOperation unwrap(IUndoableOperation operation) {
if(operation instanceof EMFCommandOperation) {
Command emfCommand = ((EMFCommandOperation)operation).getCommand();
if(emfCommand instanceof GMFtoEMFCommandWrapper) {
ICommand gmfCommand = ((GMFtoEMFCommandWrapper)emfCommand).getGMFCommand();
if(gmfCommand != null) {
return gmfCommand;
}
}
}
return operation;
}
public IStatus execute(IUndoableOperation operation, IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
// check with the operation approvers
IStatus status = getExecuteApproval(operation, info);
if(!status.isOK()) {
// not approved. No notifications are sent, just return the status.
return status;
}
return history.execute(operation, monitor, info);
}
public IStatus undo(IUndoContext context, IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
Assert.isNotNull(context);
IUndoableOperation operation = getUndoOperation(context);
// info if there is no operation
if(operation == null) {
return IOperationHistory.NOTHING_TO_UNDO_STATUS;
}
// check with the operation approvers
IStatus status = getUndoApproval(operation, info);
if(!status.isOK()) {
// not approved. No notifications are sent, just return the status.
return status;
}
return history.undo(context, monitor, info);
}
public IStatus redo(IUndoContext context, IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
Assert.isNotNull(context);
IUndoableOperation operation = getRedoOperation(context);
// info if there is no operation
if(operation == null) {
return IOperationHistory.NOTHING_TO_REDO_STATUS;
}
// check with the operation approvers
IStatus status = getRedoApproval(operation, info);
if(!status.isOK()) {
// not approved. No notifications are sent, just return the status.
return status;
}
return history.redo(context, monitor, info);
}
// all the following methods are pure delegation
public IStatus undoOperation(IUndoableOperation operation, IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
return history.undoOperation(operation, monitor, info);
}
public void setLimit(IUndoContext context, int limit) {
history.setLimit(context, limit);
}
public void replaceOperation(IUndoableOperation operation, IUndoableOperation[] replacements) {
history.replaceOperation(operation, replacements);
}
public void removeOperationHistoryListener(IOperationHistoryListener listener) {
history.removeOperationHistoryListener(listener);
}
public void removeOperationApprover(IOperationApprover approver) {
history.removeOperationApprover(approver);
}
public IStatus redoOperation(IUndoableOperation operation, IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
return redoOperation(operation, monitor, info);
}
public void operationChanged(IUndoableOperation operation) {
history.operationChanged(operation);
}
public void openOperation(ICompositeOperation operation, int mode) {
history.openOperation(operation, mode);
}
public IUndoableOperation getUndoOperation(IUndoContext context) {
return history.getUndoOperation(context);
}
public IUndoableOperation[] getUndoHistory(IUndoContext context) {
return history.getUndoHistory(context);
}
public IUndoableOperation getRedoOperation(IUndoContext context) {
return history.getRedoOperation(context);
}
public IUndoableOperation[] getRedoHistory(IUndoContext context) {
return history.getRedoHistory(context);
}
public int getLimit(IUndoContext context) {
return history.getLimit(context);
}
public void dispose(IUndoContext context, boolean flushUndo, boolean flushRedo, boolean flushContext) {
history.dispose(context, flushUndo, flushRedo, flushContext);
}
public void closeOperation(boolean operationOK, boolean addToHistory, int mode) {
history.closeOperation(operationOK, addToHistory, mode);
}
public boolean canUndo(IUndoContext context) {
return history.canUndo(context);
}
public boolean canRedo(IUndoContext context) {
return history.canRedo(context);
}
public void addOperationHistoryListener(IOperationHistoryListener listener) {
history.addOperationHistoryListener(listener);
}
public void addOperationApprover(IOperationApprover approver) {
history.addOperationApprover(approver);
}
public void add(IUndoableOperation operation) {
history.add(operation);
}
}