package org.eclipse.core.commands.operations;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
public final class TriggeredOperations extends AbstractOperation implements
ICompositeOperation, IAdvancedUndoableOperation, IAdvancedUndoableOperation2,
IContextReplacingOperation {
private IUndoableOperation triggeringOperation;
private IOperationHistory history;
private List<IUndoableOperation> children = new ArrayList<>();
public TriggeredOperations(IUndoableOperation operation, IOperationHistory history) {
super(operation.getLabel());
triggeringOperation = operation;
recomputeContexts();
this.history = history;
}
@Override
public void add(IUndoableOperation operation) {
children.add(operation);
recomputeContexts();
}
@Override
public void remove(IUndoableOperation operation) {
if (operation == triggeringOperation) {
triggeringOperation = null;
List<IUndoableOperation> childrenToRestore = new ArrayList<>(children);
if (childrenToRestore.size() > 1) {
Set<IUndoableOperation> undoHistory = new HashSet<>();
for (IUndoContext context : this.getContexts()) {
if (context != null) {
undoHistory.addAll(Arrays.asList(history.getUndoHistory(context)));
}
}
if (undoHistory.contains(this)) {
Collections.reverse(childrenToRestore);
}
}
children = new ArrayList<>(0);
recomputeContexts();
operation.dispose();
history.replaceOperation(this, childrenToRestore.toArray(new IUndoableOperation[childrenToRestore.size()]));
} else {
children.remove(operation);
operation.dispose();
recomputeContexts();
}
}
@Override
public void removeContext(IUndoContext context) {
boolean recompute = false;
if (triggeringOperation != null
&& triggeringOperation.hasContext(context)) {
if (triggeringOperation.getContexts().length == 1) {
remove(triggeringOperation);
return;
}
triggeringOperation.removeContext(context);
recompute = true;
}
ArrayList<IUndoableOperation> toBeRemoved = new ArrayList<>();
for (int i = 0; i < children.size(); i++) {
IUndoableOperation child = children.get(i);
if (child.hasContext(context)) {
if (child.getContexts().length == 1) {
toBeRemoved.add(child);
} else {
child.removeContext(context);
}
recompute = true;
}
}
for (int i = 0; i < toBeRemoved.size(); i++) {
remove(toBeRemoved.get(i));
}
if (recompute) {
recomputeContexts();
}
}
@Override
public IStatus execute(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
if (triggeringOperation != null) {
history.openOperation(this, IOperationHistory.EXECUTE);
try {
IStatus status = triggeringOperation.execute(monitor, info);
history.closeOperation(status.isOK(), false, IOperationHistory.EXECUTE);
return status;
} catch (ExecutionException | RuntimeException e) {
history.closeOperation(false, false, IOperationHistory.EXECUTE);
throw e;
}
}
return IOperationHistory.OPERATION_INVALID_STATUS;
}
@Override
public IStatus redo(IProgressMonitor monitor, IAdaptable info)
throws ExecutionException {
if (triggeringOperation != null) {
history.openOperation(this, IOperationHistory.REDO);
List<IUndoableOperation> childrenToRestore = new ArrayList<>(children);
try {
removeAllChildren();
IStatus status = triggeringOperation.redo(monitor, info);
if (!status.isOK()) {
children = childrenToRestore;
}
history.closeOperation(status.isOK(), false, IOperationHistory.REDO);
return status;
} catch (ExecutionException | RuntimeException e) {
children = childrenToRestore;
history.closeOperation(false, false, IOperationHistory.REDO);
throw e;
}
}
return IOperationHistory.OPERATION_INVALID_STATUS;
}
@Override
public IStatus undo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
if (triggeringOperation != null) {
history.openOperation(this, IOperationHistory.UNDO);
List<IUndoableOperation> childrenToRestore = new ArrayList<>(children);
try {
removeAllChildren();
IStatus status = triggeringOperation.undo(monitor, info);
if (!status.isOK()) {
children = childrenToRestore;
}
history.closeOperation(status.isOK(), false, IOperationHistory.UNDO);
return status;
} catch (ExecutionException | RuntimeException e) {
children = childrenToRestore;
history.closeOperation(false, false, IOperationHistory.UNDO);
throw e;
}
}
return IOperationHistory.OPERATION_INVALID_STATUS;
}
@Override
public boolean canUndo() {
if (triggeringOperation != null) {
return triggeringOperation.canUndo();
}
return false;
}
@Override
public boolean canExecute() {
if (triggeringOperation != null) {
return triggeringOperation.canExecute();
}
return false;
}
@Override
public boolean canRedo() {
if (triggeringOperation != null) {
return triggeringOperation.canRedo();
}
return false;
}
@Override
public void dispose() {
for (int i = 0; i < children.size(); i++) {
(children.get(i)).dispose();
}
if (triggeringOperation != null) {
triggeringOperation.dispose();
}
}
private void recomputeContexts() {
ArrayList<IUndoContext> allContexts = new ArrayList<>();
if (triggeringOperation != null) {
IUndoContext[] contexts = triggeringOperation.getContexts();
allContexts.addAll(Arrays.asList(contexts));
}
for (int i = 0; i < children.size(); i++) {
IUndoContext[] contexts = children.get(i).getContexts();
for (IUndoContext context : contexts) {
if (!allContexts.contains(context)) {
allContexts.add(context);
}
}
}
contexts = allContexts;
}
private void removeAllChildren() {
IUndoableOperation[] nonTriggers = children.toArray(new IUndoableOperation[children.size()]);
for (IUndoableOperation nonTrigger : nonTriggers) {
children.remove(nonTrigger);
nonTrigger.dispose();
}
}
public IUndoableOperation getTriggeringOperation() {
return triggeringOperation;
}
@Override
public Object[] getAffectedObjects() {
if (triggeringOperation instanceof IAdvancedUndoableOperation) {
return ((IAdvancedUndoableOperation) triggeringOperation).getAffectedObjects();
}
return null;
}
@Override
public void aboutToNotify(OperationHistoryEvent event) {
if (triggeringOperation instanceof IAdvancedUndoableOperation) {
((IAdvancedUndoableOperation) triggeringOperation).aboutToNotify(event);
}
}
@Override
public IStatus computeUndoableStatus(IProgressMonitor monitor) throws ExecutionException {
if (triggeringOperation instanceof IAdvancedUndoableOperation) {
try {
return ((IAdvancedUndoableOperation) triggeringOperation).computeUndoableStatus(monitor);
} catch (OperationCanceledException e) {
return Status.CANCEL_STATUS;
}
}
return Status.OK_STATUS;
}
@Override
public IStatus computeRedoableStatus(IProgressMonitor monitor) throws ExecutionException {
if (triggeringOperation instanceof IAdvancedUndoableOperation) {
try {
return ((IAdvancedUndoableOperation) triggeringOperation).computeRedoableStatus(monitor);
} catch (OperationCanceledException e) {
return Status.CANCEL_STATUS;
}
}
return Status.OK_STATUS;
}
@Override
public void replaceContext(IUndoContext original, IUndoContext replacement) {
if (triggeringOperation != null
&& triggeringOperation.hasContext(original)) {
if (triggeringOperation instanceof IContextReplacingOperation) {
((IContextReplacingOperation) triggeringOperation).replaceContext(original, replacement);
} else {
triggeringOperation.removeContext(original);
triggeringOperation.addContext(replacement);
}
}
for (int i = 0; i < children.size(); i++) {
IUndoableOperation child = children.get(i);
if (child.hasContext(original)) {
if (child instanceof IContextReplacingOperation) {
((IContextReplacingOperation) child).replaceContext(
original, replacement);
} else {
child.removeContext(original);
child.addContext(replacement);
}
}
}
recomputeContexts();
}
@Override
public void addContext(IUndoContext context) {
if (triggeringOperation != null) {
triggeringOperation.addContext(context);
recomputeContexts();
}
}
@Override
public IStatus computeExecutionStatus(IProgressMonitor monitor) throws ExecutionException {
if (triggeringOperation instanceof IAdvancedUndoableOperation2) {
try {
return ((IAdvancedUndoableOperation2) triggeringOperation).computeExecutionStatus(monitor);
} catch (OperationCanceledException e) {
return Status.CANCEL_STATUS;
}
}
return Status.OK_STATUS;
}
@Override
public void setQuietCompute(boolean quiet) {
if (triggeringOperation instanceof IAdvancedUndoableOperation2) {
((IAdvancedUndoableOperation2) triggeringOperation).setQuietCompute(quiet);
}
}
@Override
public boolean runInBackground() {
if (triggeringOperation instanceof IAdvancedUndoableOperation2) {
return ((IAdvancedUndoableOperation2) triggeringOperation).runInBackground();
}
return false;
}
}