Bug 443304 - Improve undo/redo handling in Graphiti features
* Introduce ICustomUndoRedoFeature, ICustomUndoRedoPattern holding
** both pre/post hooks for both undo/redo
* Deprecate ICustomUndoableFeature, ICustomUndoablePattern
* Call both feature interfaces in feature undo/redo of command stack
* Introduce ICustomAbortableUndoRedoFeature,
ICustomAbortableUndoRedoPattern holding isAbort() hook
** Check isAbort after calling pre hooks
** Delegate in pattern features to pattern
* Docu adapted
Change-Id: I0e22d4f9182dab9f1adccf29daaac675c7d17680
diff --git a/examples/org.eclipse.graphiti.doc/resources/docu/gfw/Non-EMF domain objects.htm b/examples/org.eclipse.graphiti.doc/resources/docu/gfw/Non-EMF domain objects.htm
index 5538dcc..b0422bb 100644
--- a/examples/org.eclipse.graphiti.doc/resources/docu/gfw/Non-EMF domain objects.htm
+++ b/examples/org.eclipse.graphiti.doc/resources/docu/gfw/Non-EMF domain objects.htm
@@ -49,26 +49,28 @@
(The changes done to the graphical representation (Graphiti<em> PictogramElements</em>
and <em>GraphicsAlgorithms)</em> are still handled automatically.)</p>
<p>In order to provide undo/redo for non-EMF domain models users can implement the
-new interface <span class="inlinecode">ICustomUndoableFeature</span> within their
+interface <span class="inlinecode">ICustomUndoRedoFeature</span> within their
features. In case a feature implements this interface the Graphiti command stack
will care about the EMF undo/redo and additionally to the standard EMF-undo/redo
call the appropriate methods (<span class="inlinecode">canUndo</span> and
-<span class="inlinecode">undo</span> resp. <span class="inlinecode">canRedo</span>
-and <span class="inlinecode">redo</span>) within the feature.</p>
+<span class="inlinecode">preUndo</span>/<span class="inlinecode">postUndo</span> resp.
+<span class="inlinecode">canRedo</span> and <span class="inlinecode">preRedo</span>/
+<span class="inlinecode">postRedo</span>) within the feature.</p>
<p>Inside the feature coding for those methods users can use the information passed
(the executed feature instance will be called with the instance of its context)
to undo the operations done while executing the feature. Within the features
<span class="inlinecode">execute</span> method users might add additional information
needed to perform the undo to the context object.</p>
-<p>The decision to implement <span class="inlinecode">ICustomUndoableFeature</span>
+<p>The decision to implement <span class="inlinecode">ICustomUndoRedoFeature</span>
can be taken individually for each feature.</p>
<p>For the pattern approach a similar interface has been introduced:
-<span class="inlinecode">ICustomUndoablePattern</span>, for which the before mentioned
+<span class="inlinecode">ICustomUndoRedoPattern</span>, for which the before mentioned
also applies accordingly.</p>
<p>By introducing this functionality it is now possible for users of Graphiti to
implement undo and redo functionalities also for non-EMF domain changes; nevertheless
this functionality might also by used for EMF domain models in case they need to
-implement additional undo/redo functionality.</p>
+implement additional undo/redo functionality that is executed either before or after the
+EMF undo/redo.</p>
<p>Still there is one thing to be aware of: All changes done inside the Graphiti
diagram editor or viewer (no matter if EMF changes or non-EMF changes) will write an
<span class="inlinecode">IExecutionInfo</span> entry to the stack that will be available
diff --git a/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/AddFeatureForPattern.java b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/AddFeatureForPattern.java
index 1b57590..4f7af00 100644
--- a/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/AddFeatureForPattern.java
+++ b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/AddFeatureForPattern.java
@@ -1,7 +1,7 @@
/*******************************************************************************
* <copyright>
*
- * Copyright (c) 2005, 2012 SAP AG.
+ * Copyright (c) 2005, 2014 SAP AG.
* 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
@@ -11,12 +11,14 @@
* SAP AG - initial API, implementation and documentation
* mwenz - Bug 324859 - Need Undo/Redo support for Non-EMF based domain objects
* mwenz - Bug 325084 - Provide documentation for Patterns
+ * mwenz - Bug 443304 - Improve undo/redo handling in Graphiti features
*
* </copyright>
*
*******************************************************************************/
package org.eclipse.graphiti.pattern;
+import org.eclipse.graphiti.features.ICustomAbortableUndoRedoFeature;
import org.eclipse.graphiti.features.ICustomUndoableFeature;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.features.context.IAddContext;
@@ -31,8 +33,10 @@
*
* @noextend This class is not intended to be subclassed by clients.
* @noinstantiate This class is not intended to be instantiated by clients.
+ * @since 0.12
*/
-public class AddFeatureForPattern extends AbstractAddFeature implements ICustomUndoableFeature {
+public class AddFeatureForPattern extends AbstractAddFeature implements ICustomUndoableFeature,
+ ICustomAbortableUndoRedoFeature {
private IAdd pattern;
/**
@@ -56,6 +60,17 @@
return pattern.add(context);
}
+ /**
+ * @since 0.12
+ */
+ @Override
+ public boolean isAbort() {
+ if (pattern instanceof ICustomAbortableUndoRedoPattern) {
+ return ((ICustomAbortableUndoRedoPattern) pattern).isAbort();
+ }
+ return false;
+ }
+
@Override
public boolean canUndo(IContext context) {
if (pattern instanceof ICustomUndoablePattern) {
@@ -65,7 +80,25 @@
}
/**
+ * @since 0.12
+ */
+ @Override
+ public void preUndo(IContext context) {
+ }
+
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void postUndo(IContext context) {
+ if (pattern instanceof ICustomUndoRedoPattern) {
+ ((ICustomUndoRedoPattern) pattern).postUndo(this, context);
+ }
+ }
+
+ /**
* @since 0.8
+ * @deprecated use {@link #postUndo(IContext)} instead
*/
public void undo(IContext context) {
if (pattern instanceof ICustomUndoablePattern) {
@@ -84,11 +117,26 @@
}
/**
- * @since 0.8
+ * @since 0.12
*/
- public void redo(IContext context) {
+ @Override
+ public void preRedo(IContext context) {
+ }
+
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void postRedo(IContext context) {
if (pattern instanceof ICustomUndoablePattern) {
((ICustomUndoablePattern) pattern).redo(this, context);
}
}
+
+ /**
+ * @since 0.8
+ * @deprecated use {@link #postRedo(IContext)} instead
+ */
+ public void redo(IContext context) {
+ }
}
diff --git a/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/CreateConnectionFeatureForPattern.java b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/CreateConnectionFeatureForPattern.java
index ffbd26a..0f41afb 100644
--- a/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/CreateConnectionFeatureForPattern.java
+++ b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/CreateConnectionFeatureForPattern.java
@@ -1,7 +1,7 @@
/*******************************************************************************
* <copyright>
*
- * Copyright (c) 2005, 2012 SAP AG.
+ * Copyright (c) 2005, 2014 SAP AG.
* 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
@@ -12,12 +12,14 @@
* mwenz - Bug 324859 - Need Undo/Redo support for Non-EMF based domain objects
* mgorning - Bug 329517 - state call backs during creation of a connection
* mwenz - Bug 325084 - Provide documentation for Patterns
+ * mwenz - Bug 443304 - Improve undo/redo handling in Graphiti features
*
* </copyright>
*
*******************************************************************************/
package org.eclipse.graphiti.pattern;
+import org.eclipse.graphiti.features.ICustomAbortableUndoRedoFeature;
import org.eclipse.graphiti.features.ICustomUndoableFeature;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.features.context.IContext;
@@ -33,7 +35,7 @@
* @noinstantiate This class is not intended to be instantiated by clients.
*/
public class CreateConnectionFeatureForPattern extends AbstractCreateConnectionFeature implements
- ICustomUndoableFeature {
+ ICustomUndoableFeature, ICustomAbortableUndoRedoFeature {
private IConnectionPattern delegate;
/**
@@ -72,6 +74,17 @@
return delegate.getCreateLargeImageId();
}
+ /**
+ * @since 0.12
+ */
+ @Override
+ public boolean isAbort() {
+ if (delegate instanceof ICustomAbortableUndoRedoPattern) {
+ return ((ICustomAbortableUndoRedoPattern) delegate).isAbort();
+ }
+ return false;
+ }
+
@Override
public boolean canUndo(IContext context) {
if (delegate instanceof ICustomUndoablePattern) {
@@ -81,7 +94,25 @@
}
/**
+ * @since 0.12
+ */
+ @Override
+ public void preUndo(IContext context) {
+ }
+
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void postUndo(IContext context) {
+ if (delegate instanceof ICustomAbortableUndoRedoPattern) {
+ ((ICustomAbortableUndoRedoPattern) delegate).postUndo(this, context);
+ }
+ }
+
+ /**
* @since 0.8
+ * @deprecated use {@link #postUndo(IContext)} instead
*/
public void undo(IContext context) {
if (delegate instanceof ICustomUndoablePattern) {
@@ -100,14 +131,29 @@
}
/**
- * @since 0.8
+ * @since 0.12
*/
- public void redo(IContext context) {
+ @Override
+ public void preRedo(IContext context) {
+ }
+
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void postRedo(IContext context) {
if (delegate instanceof ICustomUndoablePattern) {
((ICustomUndoablePattern) delegate).redo(this, context);
}
}
+ /**
+ * @since 0.8
+ * @deprecated use {@link #postRedo(IContext)} instead
+ */
+ public void redo(IContext context) {
+ }
+
@Override
public void startConnecting() {
delegate.startConnecting();
diff --git a/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/CreateFeatureForPattern.java b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/CreateFeatureForPattern.java
index 3137335..9baaf8d 100644
--- a/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/CreateFeatureForPattern.java
+++ b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/CreateFeatureForPattern.java
@@ -1,7 +1,7 @@
/*******************************************************************************
* <copyright>
*
- * Copyright (c) 2005, 2012 SAP AG.
+ * Copyright (c) 2005, 2014 SAP AG.
* 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
@@ -11,12 +11,14 @@
* SAP AG - initial API, implementation and documentation
* mwenz - Bug 324859 - Need Undo/Redo support for Non-EMF based domain objects
* mwenz - Bug 325084 - Provide documentation for Patterns
+ * mwenz - Bug 443304 - Improve undo/redo handling in Graphiti features
*
* </copyright>
*
*******************************************************************************/
package org.eclipse.graphiti.pattern;
+import org.eclipse.graphiti.features.ICustomAbortableUndoRedoFeature;
import org.eclipse.graphiti.features.ICustomUndoableFeature;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.features.context.IContext;
@@ -30,7 +32,8 @@
* @noextend This class is not intended to be subclassed by clients.
* @noinstantiate This class is not intended to be instantiated by clients.
*/
-public class CreateFeatureForPattern extends AbstractCreateFeature implements ICustomUndoableFeature {
+public class CreateFeatureForPattern extends AbstractCreateFeature implements ICustomUndoableFeature,
+ ICustomAbortableUndoRedoFeature {
private IFeatureForPattern delegate;
/**
@@ -75,6 +78,18 @@
return delegate.getPattern();
}
+ /**
+ * @since 0.12
+ */
+ @Override
+ public boolean isAbort() {
+ IPattern pattern = delegate.getPattern();
+ if (pattern instanceof ICustomAbortableUndoRedoPattern) {
+ return ((ICustomAbortableUndoRedoPattern) pattern).isAbort();
+ }
+ return false;
+ }
+
@Override
public boolean canUndo(IContext context) {
IPattern pattern = delegate.getPattern();
@@ -85,7 +100,26 @@
}
/**
+ * @since 0.12
+ */
+ @Override
+ public void preUndo(IContext context) {
+ }
+
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void postUndo(IContext context) {
+ IPattern pattern = delegate.getPattern();
+ if (pattern instanceof ICustomUndoRedoPattern) {
+ ((ICustomUndoRedoPattern) pattern).postUndo(this, context);
+ }
+ }
+
+ /**
* @since 0.8
+ * @deprecated use {@link #postUndo(IContext)} instead
*/
public void undo(IContext context) {
IPattern pattern = delegate.getPattern();
@@ -106,12 +140,27 @@
}
/**
- * @since 0.8
+ * @since 0.12
*/
- public void redo(IContext context) {
+ @Override
+ public void preRedo(IContext context) {
+ }
+
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void postRedo(IContext context) {
IPattern pattern = delegate.getPattern();
if (pattern instanceof ICustomUndoablePattern) {
((ICustomUndoablePattern) pattern).redo(this, context);
}
}
+
+ /**
+ * @since 0.8
+ * @deprecated use {@link #postRedo(IContext)} instead
+ */
+ public void redo(IContext context) {
+ }
}
diff --git a/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/DeleteFeatureForPattern.java b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/DeleteFeatureForPattern.java
index f6ceb88..b58aa92 100644
--- a/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/DeleteFeatureForPattern.java
+++ b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/DeleteFeatureForPattern.java
@@ -1,7 +1,7 @@
/*******************************************************************************
* <copyright>
*
- * Copyright (c) 2012 Volker Wegert and others.
+ * Copyright (c) 2012, 2014 Volker Wegert 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
@@ -14,12 +14,14 @@
* creation entry
* mwenz - Bug 324859 - Need Undo/Redo support for Non-EMF based domain objects
* mwenz - Bug 325084 - Provide documentation for Patterns
+ * mwenz - Bug 443304 - Improve undo/redo handling in Graphiti features
*
* </copyright>
*
*******************************************************************************/
package org.eclipse.graphiti.pattern;
+import org.eclipse.graphiti.features.ICustomAbortableUndoRedoFeature;
import org.eclipse.graphiti.features.ICustomUndoableFeature;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.features.context.IContext;
@@ -36,7 +38,8 @@
*
* @since 0.8.0
*/
-public class DeleteFeatureForPattern extends DefaultDeleteFeature implements ICustomUndoableFeature {
+public class DeleteFeatureForPattern extends DefaultDeleteFeature implements ICustomUndoableFeature,
+ ICustomAbortableUndoRedoFeature {
private IFeatureForPattern delegate;
@@ -73,6 +76,18 @@
delegate.getPattern().postDelete(context);
}
+ /**
+ * @since 0.12
+ */
+ @Override
+ public boolean isAbort() {
+ IPattern pattern = delegate.getPattern();
+ if (pattern instanceof ICustomAbortableUndoRedoPattern) {
+ return ((ICustomAbortableUndoRedoPattern) pattern).isAbort();
+ }
+ return false;
+ }
+
@Override
public boolean canUndo(IContext context) {
IPattern pattern = delegate.getPattern();
@@ -82,6 +97,27 @@
return super.canUndo(context);
}
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void preUndo(IContext context) {
+ }
+
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void postUndo(IContext context) {
+ IPattern pattern = delegate.getPattern();
+ if (pattern instanceof ICustomUndoRedoPattern) {
+ ((ICustomUndoRedoPattern) pattern).postUndo(this, context);
+ }
+ }
+
+ /**
+ * @deprecated use {@link #postUndo(IContext)} instead
+ */
public void undo(IContext context) {
IPattern pattern = delegate.getPattern();
if (pattern instanceof ICustomUndoablePattern) {
@@ -97,6 +133,27 @@
return true;
}
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void preRedo(IContext context) {
+ }
+
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void postRedo(IContext context) {
+ IPattern pattern = delegate.getPattern();
+ if (pattern instanceof ICustomUndoRedoPattern) {
+ ((ICustomUndoRedoPattern) pattern).postRedo(this, context);
+ }
+ }
+
+ /**
+ * @deprecated use {@link #postRedo(IContext)} instead
+ */
public void redo(IContext context) {
IPattern pattern = delegate.getPattern();
if (pattern instanceof ICustomUndoablePattern) {
diff --git a/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/DirectEditingFeatureForPattern.java b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/DirectEditingFeatureForPattern.java
index 38b77ba..2be778a 100644
--- a/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/DirectEditingFeatureForPattern.java
+++ b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/DirectEditingFeatureForPattern.java
@@ -1,7 +1,7 @@
/*******************************************************************************
* <copyright>
*
- * Copyright (c) 2005, 2012 SAP AG.
+ * Copyright (c) 2005, 2014 SAP AG.
* 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
@@ -12,12 +12,14 @@
* Volker Wegert - Bug 332363 - Direct Editing: enable automatic resizing for combo boxes
* mwenz - Bug 324859 - Need Undo/Redo support for Non-EMF based domain objects
* mwenz - Bug 325084 - Provide documentation for Patterns
+ * mwenz - Bug 443304 - Improve undo/redo handling in Graphiti features
*
* </copyright>
*
*******************************************************************************/
package org.eclipse.graphiti.pattern;
+import org.eclipse.graphiti.features.ICustomAbortableUndoRedoFeature;
import org.eclipse.graphiti.features.ICustomUndoableFeature;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.features.context.IContext;
@@ -33,7 +35,8 @@
* @noextend This class is not intended to be subclassed by clients.
* @noinstantiate This class is not intended to be instantiated by clients.
*/
-public class DirectEditingFeatureForPattern extends AbstractDirectEditingFeature implements ICustomUndoableFeature {
+public class DirectEditingFeatureForPattern extends AbstractDirectEditingFeature implements ICustomUndoableFeature,
+ ICustomAbortableUndoRedoFeature {
private IDirectEditing delegate;
/**
@@ -106,6 +109,17 @@
return delegate.getProposalSupport();
}
+ /**
+ * @since 0.12
+ */
+ @Override
+ public boolean isAbort() {
+ if (delegate instanceof ICustomAbortableUndoRedoPattern) {
+ return ((ICustomAbortableUndoRedoPattern) delegate).isAbort();
+ }
+ return false;
+ }
+
@Override
public boolean canUndo(IContext context) {
if (delegate instanceof ICustomUndoablePattern) {
@@ -115,7 +129,25 @@
}
/**
+ * @since 0.12
+ */
+ @Override
+ public void preUndo(IContext context) {
+ }
+
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void postUndo(IContext context) {
+ if (delegate instanceof ICustomUndoRedoPattern) {
+ ((ICustomUndoRedoPattern) delegate).postUndo(this, context);
+ }
+ }
+
+ /**
* @since 0.8
+ * @deprecated use {@link #postUndo(IContext)} instead
*/
public void undo(IContext context) {
if (delegate instanceof ICustomUndoablePattern) {
@@ -134,7 +166,25 @@
}
/**
+ * @since 0.12
+ */
+ @Override
+ public void preRedo(IContext context) {
+ }
+
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void postRedo(IContext context) {
+ if (delegate instanceof ICustomUndoRedoPattern) {
+ ((ICustomUndoRedoPattern) delegate).postRedo(this, context);
+ }
+ }
+
+ /**
* @since 0.8
+ * @deprecated use {@link #postRedo(IContext)} instead
*/
public void redo(IContext context) {
if (delegate instanceof ICustomUndoablePattern) {
diff --git a/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/ICustomAbortableUndoRedoPattern.java b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/ICustomAbortableUndoRedoPattern.java
new file mode 100644
index 0000000..2faf33b
--- /dev/null
+++ b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/ICustomAbortableUndoRedoPattern.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * <copyright>
+ *
+ * Copyright (c) 2014, 2014 SAP AG.
+ * 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:
+ * mwenz - Bug 443304 - Improve undo/redo handling in Graphiti features
+ *
+ * </copyright>
+ *
+ *******************************************************************************/
+package org.eclipse.graphiti.pattern;
+
+
+
+/**
+ * This interface can by used and implemented by customers within any pattern to
+ * signal the need for additional work that needs to be done before or after
+ * undo and redo. In addition to {@link ICustomUndoRedoPattern} this interface
+ * supports cancellation of undo/redo operations.
+ *
+ * @see ICustomUndoRedoPattern
+ * @since 0.12
+ */
+public interface ICustomAbortableUndoRedoPattern extends ICustomUndoRedoPattern {
+
+ /**
+ * The Graphiti framework will call this method after
+ * {@link #preUndo(org.eclipse.graphiti.features.context.IContext)}/
+ * {@link #preRedo(org.eclipse.graphiti.features.context.IContext)} have
+ * been called and before the actual undo/redo operation is triggered. In
+ * case this method returns <code>true</code>, the operation will be
+ * cancelled by the Graphiti framework by throwing an
+ * {@link OperationCanceledException} that causes am EMF revert of the
+ * operation.
+ * <p>
+ * Implementing classes might e.g. set a flag in
+ * {@link #preUndo(org.eclipse.graphiti.features.context.IContext)}/
+ * {@link #preRedo(org.eclipse.graphiti.features.context.IContext)} as
+ * cancellation indication and check that that flag here.
+ *
+ * @return <code>true</code> in case you want to cancel the current
+ * operation, <code>false</code> otherwise.
+ */
+ boolean isAbort();
+}
diff --git a/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/ICustomUndoRedoPattern.java b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/ICustomUndoRedoPattern.java
new file mode 100644
index 0000000..b3d034f
--- /dev/null
+++ b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/ICustomUndoRedoPattern.java
@@ -0,0 +1,146 @@
+/*******************************************************************************
+ * <copyright>
+ *
+ * Copyright (c) 2014, 2014 SAP AG.
+ * 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:
+ * mwenz - Bug 443304 - Improve undo/redo handling in Graphiti features
+ *
+ * </copyright>
+ *
+ *******************************************************************************/
+package org.eclipse.graphiti.pattern;
+
+import org.eclipse.graphiti.features.IFeature;
+import org.eclipse.graphiti.features.context.IContext;
+import org.eclipse.graphiti.mm.algorithms.GraphicsAlgorithm;
+import org.eclipse.graphiti.mm.pictograms.PictogramElement;
+
+/**
+ * This interface can by used and implemented by customers within any pattern to
+ * signal the need for additional work that needs to be done before or after
+ * undo and redo. When a pattern implements this interface, and the framework
+ * performs an undo or a redo, the framework will call the contained methods.
+ * <p>
+ * Implementing this interface is especially helpful if customers want to
+ * implement undo/redo functionality for non-EMF changes, e.g. for non-EMF
+ * domain models. Note that any EMF-model change (including the changes done to
+ * the graphical representation (Graphiti {@link PictogramElement}s and
+ * {@link GraphicsAlgorithm}s will by handled automatically by the Graphiti
+ * framework no matter if this interface is implemented by a pattern or not. The
+ * pattern may use the context and feature objects (e.g. the contained
+ * properties set) passed to the contained methods while executing the pattern
+ * in order to collect any information needed for undo.
+ * <p>
+ * In case you want to cancel undo/redo operations in {@link #preUndo(IContext)}/{@link #preRedo(IContext)}, you need to implement
+ * {@link ICustomAbortableUndoRedoPattern} which offers an
+ * {@link ICustomAbortableUndoRedoPattern#isAbort()} method that causes the
+ * cancellation of undo/redo operation in case <code>true</code> is returned.
+ *
+ * @see ICustomAbortableUndoRedoPattern
+ * @since 0.12
+ */
+public interface ICustomUndoRedoPattern {
+
+ /**
+ * Decides if the changes done by a processed pattern functionality can be
+ * undone. This method is called once by the Graphiti framework just before
+ * any undo work is started, e.g. before {@link #preUndo(IContext)}.
+ * <p>
+ * Note that as soon as any pattern reports <code>false</code> here, also
+ * all previous entries in the command stack are no longer reachable for
+ * undo.
+ *
+ * @param feature
+ * this is the instance of the {@link IFeature} object that was
+ * in use when executing the pattern functionality
+ * @param context
+ * this is the instance of the {@link IContext} object that was
+ * in use when executing the feature.
+ *
+ * @return true if the feature can be undone, false if not
+ */
+ boolean canUndo(IFeature feature, IContext context);
+
+ /**
+ * This method will be called by the Graphiti framework before the EMF undo
+ * is triggered. Customers may revert their non-EMF changes done by the
+ * pattern functionality here or in {@link #postUndo(IContext)}.
+ *
+ * @param feature
+ * this is the instance of the {@link IFeature} object that was
+ * in use when executing the pattern functionality
+ * @param context
+ * this is the instance of the {@link IContext} object that was
+ * in use when executing the feature
+ */
+ void preUndo(IFeature feature, IContext context);
+
+ /**
+ * This method will be called by the Graphiti framework after the EMF undo
+ * is finished. Customers may revert their non-EMF changes done by the
+ * pattern functionality here or in {@link #preUndo(IContext)}.
+ *
+ * @param feature
+ * this is the instance of the {@link IFeature} object that was
+ * in use when executing the pattern functionality
+ * @param context
+ * this is the instance of the {@link IContext} object that was
+ * in use when executing the feature
+ */
+ void postUndo(IFeature feature, IContext context);
+
+ /**
+ * Decides if the processed feature can be re-done. This method is called
+ * once by the Graphiti framework just before any redo work is started, e.g.
+ * before {@link #preRedo(IContext)}.
+ * <p>
+ * Note that as soon as any pattern reports <code>false</code> here, also
+ * all consecutive entries in the command stack are no longer reachable for
+ * redo.
+ *
+ * @param feature
+ * this is the instance of the {@link IFeature} object that was
+ * in use when executing the pattern functionality
+ * @param context
+ * this is the instance of the {@link IContext} object that was
+ * in use when executing the feature
+ *
+ * @return true if the feature can be re-done, false if not
+ */
+ boolean canRedo(IFeature feature, IContext context);
+
+ /**
+ * This method will be called by the Graphiti framework before the EMF undo
+ * has triggered. Customers may re-apply their non-EMF changes done by the
+ * pattern functionality here or in {@link #postRedo(IContext)}. (Usually it
+ * might be sufficient to delegate to the execution method of the feature.)
+ *
+ * @param feature
+ * this is the instance of the {@link IFeature} object that was
+ * in use when executing the pattern functionality
+ * @param context
+ * this is the instance of the {@link IContext} object that was
+ * in use when executing the feature
+ */
+ void preRedo(IFeature feature, IContext context);
+
+ /**
+ * This method will be called by the Graphiti framework after the EMF undo
+ * has finished. Customers may re-apply their non-EMF changes done by the
+ * pattern functionality here or in {@link #preRedo(IContext)}. (Usually it
+ * might be sufficient to delegate to the execution method of the feature.)
+ *
+ * @param feature
+ * this is the instance of the {@link IFeature} object that was
+ * in use when executing the pattern functionality
+ * @param context
+ * this is the instance of the {@link IContext} object that was
+ * in use when executing the feature
+ */
+ void postRedo(IFeature feature, IContext context);
+}
diff --git a/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/LayoutFeatureForPattern.java b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/LayoutFeatureForPattern.java
index dbf19cf..9452982 100644
--- a/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/LayoutFeatureForPattern.java
+++ b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/LayoutFeatureForPattern.java
@@ -1,7 +1,7 @@
/*******************************************************************************
* <copyright>
*
- * Copyright (c) 2005, 2012 SAP AG.
+ * Copyright (c) 2005, 2014 SAP AG.
* 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
@@ -11,12 +11,14 @@
* SAP AG - initial API, implementation and documentation
* mwenz - Bug 324859 - Need Undo/Redo support for Non-EMF based domain objects
* mwenz - Bug 325084 - Provide documentation for Patterns
+ * mwenz - Bug 443304 - Improve undo/redo handling in Graphiti features
*
* </copyright>
*
*******************************************************************************/
package org.eclipse.graphiti.pattern;
+import org.eclipse.graphiti.features.ICustomAbortableUndoRedoFeature;
import org.eclipse.graphiti.features.ICustomUndoableFeature;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.features.context.IContext;
@@ -30,7 +32,8 @@
* @noextend This class is not intended to be subclassed by clients.
* @noinstantiate This class is not intended to be instantiated by clients.
*/
-public class LayoutFeatureForPattern extends AbstractLayoutFeature implements ICustomUndoableFeature {
+public class LayoutFeatureForPattern extends AbstractLayoutFeature implements ICustomUndoableFeature,
+ ICustomAbortableUndoRedoFeature {
private IFeatureForPattern delegate;
/**
@@ -55,6 +58,18 @@
return delegate.getPattern().layout(context);
}
+ /**
+ * @since 0.12
+ */
+ @Override
+ public boolean isAbort() {
+ IPattern pattern = delegate.getPattern();
+ if (pattern instanceof ICustomAbortableUndoRedoPattern) {
+ return ((ICustomAbortableUndoRedoPattern) pattern).isAbort();
+ }
+ return false;
+ }
+
@Override
public boolean canUndo(IContext context) {
IPattern pattern = delegate.getPattern();
@@ -65,7 +80,26 @@
}
/**
+ * @since 0.12
+ */
+ @Override
+ public void preUndo(IContext context) {
+ }
+
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void postUndo(IContext context) {
+ IPattern pattern = delegate.getPattern();
+ if (pattern instanceof ICustomUndoRedoPattern) {
+ ((ICustomUndoRedoPattern) pattern).postUndo(this, context);
+ }
+ }
+
+ /**
* @since 0.8
+ * @deprecated use {@link #postUndo(IContext)} instead
*/
public void undo(IContext context) {
IPattern pattern = delegate.getPattern();
@@ -86,7 +120,26 @@
}
/**
+ * @since 0.12
+ */
+ @Override
+ public void preRedo(IContext context) {
+ }
+
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void postRedo(IContext context) {
+ IPattern pattern = delegate.getPattern();
+ if (pattern instanceof ICustomUndoRedoPattern) {
+ ((ICustomUndoRedoPattern) pattern).postRedo(this, context);
+ }
+ }
+
+ /**
* @since 0.8
+ * @deprecated use {@link #postRedo(IContext)} instead
*/
public void redo(IContext context) {
IPattern pattern = delegate.getPattern();
diff --git a/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/MoveShapeFeatureForPattern.java b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/MoveShapeFeatureForPattern.java
index e91265b..77f3eab 100644
--- a/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/MoveShapeFeatureForPattern.java
+++ b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/MoveShapeFeatureForPattern.java
@@ -1,7 +1,7 @@
/*******************************************************************************
* <copyright>
*
- * Copyright (c) 2005, 2012 SAP AG.
+ * Copyright (c) 2005, 2014 SAP AG.
* 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
@@ -11,12 +11,14 @@
* SAP AG - initial API, implementation and documentation
* mwenz - Bug 324859 - Need Undo/Redo support for Non-EMF based domain objects
* mwenz - Bug 325084 - Provide documentation for Patterns
+ * mwenz - Bug 443304 - Improve undo/redo handling in Graphiti features
*
* </copyright>
*
*******************************************************************************/
package org.eclipse.graphiti.pattern;
+import org.eclipse.graphiti.features.ICustomAbortableUndoRedoFeature;
import org.eclipse.graphiti.features.ICustomUndoableFeature;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.features.context.IContext;
@@ -30,7 +32,8 @@
* @noextend This class is not intended to be subclassed by clients.
* @noinstantiate This class is not intended to be instantiated by clients.
*/
-public class MoveShapeFeatureForPattern extends AbstractMoveShapeFeature implements ICustomUndoableFeature {
+public class MoveShapeFeatureForPattern extends AbstractMoveShapeFeature implements ICustomUndoableFeature,
+ ICustomAbortableUndoRedoFeature {
private IFeatureForPattern delegate;
/**
@@ -55,6 +58,18 @@
delegate.getPattern().moveShape(context);
}
+ /**
+ * @since 0.12
+ */
+ @Override
+ public boolean isAbort() {
+ IPattern pattern = delegate.getPattern();
+ if (pattern instanceof ICustomAbortableUndoRedoPattern) {
+ return ((ICustomAbortableUndoRedoPattern) pattern).isAbort();
+ }
+ return false;
+ }
+
@Override
public boolean canUndo(IContext context) {
IPattern pattern = delegate.getPattern();
@@ -65,7 +80,26 @@
}
/**
+ * @since 0.12
+ */
+ @Override
+ public void preUndo(IContext context) {
+ }
+
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void postUndo(IContext context) {
+ IPattern pattern = delegate.getPattern();
+ if (pattern instanceof ICustomUndoRedoPattern) {
+ ((ICustomUndoRedoPattern) pattern).postUndo(this, context);
+ }
+ }
+
+ /**
* @since 0.8
+ * @deprecated use {@link #postUndo(IContext)} instead
*/
public void undo(IContext context) {
IPattern pattern = delegate.getPattern();
@@ -86,7 +120,26 @@
}
/**
+ * @since 0.12
+ */
+ @Override
+ public void preRedo(IContext context) {
+ }
+
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void postRedo(IContext context) {
+ IPattern pattern = delegate.getPattern();
+ if (pattern instanceof ICustomUndoRedoPattern) {
+ ((ICustomUndoRedoPattern) pattern).postRedo(this, context);
+ }
+ }
+
+ /**
* @since 0.8
+ * @deprecated use {@link #postRedo(IContext)} instead
*/
public void redo(IContext context) {
IPattern pattern = delegate.getPattern();
diff --git a/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/ReconnectionFeatureForPattern.java b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/ReconnectionFeatureForPattern.java
index 8124213..3a14d66 100644
--- a/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/ReconnectionFeatureForPattern.java
+++ b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/ReconnectionFeatureForPattern.java
@@ -1,7 +1,7 @@
/*******************************************************************************
* <copyright>
*
- * Copyright (c) 2005, 2012 SAP AG.
+ * Copyright (c) 2005, 2014 SAP AG.
* 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
@@ -13,12 +13,14 @@
* mgorning - Bug 343983 - Notification for Cancelled Reconnection Events
* mwenz - Bug 325084 - Provide documentation for Patterns
* mlypik - Bug 401792 - Disable starting reconnection
+ * mwenz - Bug 443304 - Improve undo/redo handling in Graphiti features
*
* </copyright>
*
*******************************************************************************/
package org.eclipse.graphiti.pattern;
+import org.eclipse.graphiti.features.ICustomAbortableUndoRedoFeature;
import org.eclipse.graphiti.features.ICustomUndoableFeature;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.features.IReconnectionFeature;
@@ -36,7 +38,7 @@
* @noinstantiate This class is not intended to be instantiated by clients.
*/
public class ReconnectionFeatureForPattern extends AbstractFeature implements IReconnectionFeature,
- ICustomUndoableFeature {
+ ICustomUndoableFeature, ICustomAbortableUndoRedoFeature {
private static final String NAME = Messages.ReconnectionFeatureForPattern_0_xfld;
private IReconnection delegate;
@@ -96,6 +98,17 @@
return NAME;
}
+ /**
+ * @since 0.12
+ */
+ @Override
+ public boolean isAbort() {
+ if (delegate instanceof ICustomAbortableUndoRedoPattern) {
+ return ((ICustomAbortableUndoRedoPattern) delegate).isAbort();
+ }
+ return false;
+ }
+
@Override
public boolean canUndo(IContext context) {
if (delegate instanceof ICustomUndoablePattern) {
@@ -105,7 +118,25 @@
}
/**
+ * @since 0.12
+ */
+ @Override
+ public void preUndo(IContext context) {
+ }
+
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void postUndo(IContext context) {
+ if (delegate instanceof ICustomUndoRedoPattern) {
+ ((ICustomUndoRedoPattern) delegate).postUndo(this, context);
+ }
+ }
+
+ /**
* @since 0.8
+ * @deprecated use {@link #postUndo(IContext)} instead
*/
public void undo(IContext context) {
if (delegate instanceof ICustomUndoablePattern) {
@@ -124,7 +155,25 @@
}
/**
+ * @since 0.12
+ */
+ @Override
+ public void preRedo(IContext context) {
+ }
+
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void postRedo(IContext context) {
+ if (delegate instanceof ICustomUndoRedoPattern) {
+ ((ICustomUndoRedoPattern) delegate).postRedo(this, context);
+ }
+ }
+
+ /**
* @since 0.8
+ * @deprecated use {@link #postRedo(IContext)} instead
*/
public void redo(IContext context) {
if (delegate instanceof ICustomUndoablePattern) {
diff --git a/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/RemoveFeatureForPattern.java b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/RemoveFeatureForPattern.java
index 1544cff..f8dc522 100644
--- a/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/RemoveFeatureForPattern.java
+++ b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/RemoveFeatureForPattern.java
@@ -1,7 +1,7 @@
/*******************************************************************************
* <copyright>
*
- * Copyright (c) 2013 Volker Wegert and others.
+ * Copyright (c) 2013, 2014 Volker Wegert 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
@@ -15,12 +15,14 @@
* mwenz - Bug 324859 - Need Undo/Redo support for Non-EMF based domain objects
* mwenz - Bug 325084 - Provide documentation for Patterns
* mwenz - Bug 390331 - preDelete and postDelete not called for Patterns
+ * mwenz - Bug 443304 - Improve undo/redo handling in Graphiti features
*
* </copyright>
*
*******************************************************************************/
package org.eclipse.graphiti.pattern;
+import org.eclipse.graphiti.features.ICustomAbortableUndoRedoFeature;
import org.eclipse.graphiti.features.ICustomUndoableFeature;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.features.context.IContext;
@@ -37,7 +39,8 @@
*
* @since 0.8.0
*/
-public class RemoveFeatureForPattern extends DefaultRemoveFeature implements ICustomUndoableFeature {
+public class RemoveFeatureForPattern extends DefaultRemoveFeature implements ICustomUndoableFeature,
+ ICustomAbortableUndoRedoFeature {
private IFeatureForPattern delegate;
@@ -74,6 +77,18 @@
delegate.getPattern().postRemove(context);
}
+ /**
+ * @since 0.12
+ */
+ @Override
+ public boolean isAbort() {
+ IPattern pattern = delegate.getPattern();
+ if (pattern instanceof ICustomAbortableUndoRedoPattern) {
+ return ((ICustomAbortableUndoRedoPattern) pattern).isAbort();
+ }
+ return false;
+ }
+
@Override
public boolean canUndo(IContext context) {
IPattern pattern = delegate.getPattern();
@@ -83,6 +98,27 @@
return super.canUndo(context);
}
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void preUndo(IContext context) {
+ }
+
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void postUndo(IContext context) {
+ IPattern pattern = delegate.getPattern();
+ if (pattern instanceof ICustomUndoRedoPattern) {
+ ((ICustomUndoRedoPattern) pattern).postUndo(this, context);
+ }
+ }
+
+ /**
+ * @deprecated use {@link #postUndo(IContext)} instead
+ */
public void undo(IContext context) {
IPattern pattern = delegate.getPattern();
if (pattern instanceof ICustomUndoablePattern) {
@@ -98,6 +134,27 @@
return true;
}
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void preRedo(IContext context) {
+ }
+
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void postRedo(IContext context) {
+ IPattern pattern = delegate.getPattern();
+ if (pattern instanceof ICustomUndoRedoPattern) {
+ ((ICustomUndoRedoPattern) pattern).postRedo(this, context);
+ }
+ }
+
+ /**
+ * @deprecated use {@link #postRedo(IContext)} instead
+ */
public void redo(IContext context) {
IPattern pattern = delegate.getPattern();
if (pattern instanceof ICustomUndoablePattern) {
diff --git a/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/ResizeShapeFeatureForPattern.java b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/ResizeShapeFeatureForPattern.java
index c1a61d1..f8b3d0f 100644
--- a/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/ResizeShapeFeatureForPattern.java
+++ b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/ResizeShapeFeatureForPattern.java
@@ -1,7 +1,7 @@
/*******************************************************************************
* <copyright>
*
- * Copyright (c) 2005, 2012 SAP AG.
+ * Copyright (c) 2005, 2014 SAP AG.
* 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
@@ -12,12 +12,14 @@
* Patch 185019 from Bug 332360 contributed by Volker Wegert
* mwenz - Bug 324859 - Need Undo/Redo support for Non-EMF based domain objects
* mwenz - Bug 325084 - Provide documentation for Patterns
+ * mwenz - Bug 443304 - Improve undo/redo handling in Graphiti features
*
* </copyright>
*
*******************************************************************************/
package org.eclipse.graphiti.pattern;
+import org.eclipse.graphiti.features.ICustomAbortableUndoRedoFeature;
import org.eclipse.graphiti.features.ICustomUndoableFeature;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.features.IResizeConfiguration;
@@ -32,7 +34,8 @@
* @noextend This class is not intended to be subclassed by clients.
* @noinstantiate This class is not intended to be instantiated by clients.
*/
-public class ResizeShapeFeatureForPattern extends DefaultResizeShapeFeature implements ICustomUndoableFeature {
+public class ResizeShapeFeatureForPattern extends DefaultResizeShapeFeature implements ICustomUndoableFeature,
+ ICustomAbortableUndoRedoFeature {
private IFeatureForPattern delegate;
@@ -70,6 +73,18 @@
return super.getResizeConfiguration(context);
}
+ /**
+ * @since 0.12
+ */
+ @Override
+ public boolean isAbort() {
+ IPattern pattern = delegate.getPattern();
+ if (pattern instanceof ICustomAbortableUndoRedoPattern) {
+ return ((ICustomAbortableUndoRedoPattern) pattern).isAbort();
+ }
+ return false;
+ }
+
@Override
public boolean canUndo(IContext context) {
IPattern pattern = delegate.getPattern();
@@ -80,7 +95,26 @@
}
/**
+ * @since 0.12
+ */
+ @Override
+ public void preUndo(IContext context) {
+ }
+
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void postUndo(IContext context) {
+ IPattern pattern = delegate.getPattern();
+ if (pattern instanceof ICustomUndoRedoPattern) {
+ ((ICustomUndoRedoPattern) pattern).postUndo(this, context);
+ }
+ }
+
+ /**
* @since 0.8
+ * @deprecated use {@link #postUndo(IContext)} instead
*/
public void undo(IContext context) {
IPattern pattern = delegate.getPattern();
@@ -101,7 +135,26 @@
}
/**
+ * @since 0.12
+ */
+ @Override
+ public void preRedo(IContext context) {
+ }
+
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void postRedo(IContext context) {
+ IPattern pattern = delegate.getPattern();
+ if (pattern instanceof ICustomUndoRedoPattern) {
+ ((ICustomUndoRedoPattern) pattern).postRedo(this, context);
+ }
+ }
+
+ /**
* @since 0.8
+ * @deprecated use {@link #postRedo(IContext)} instead
*/
public void redo(IContext context) {
IPattern pattern = delegate.getPattern();
diff --git a/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/UpdateFeatureForPattern.java b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/UpdateFeatureForPattern.java
index ce8367a..3846ead 100644
--- a/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/UpdateFeatureForPattern.java
+++ b/plugins/org.eclipse.graphiti.pattern/src/org/eclipse/graphiti/pattern/UpdateFeatureForPattern.java
@@ -1,7 +1,7 @@
/*******************************************************************************
* <copyright>
*
- * Copyright (c) 2005, 2012 SAP AG.
+ * Copyright (c) 2005, 2014 SAP AG.
* 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
@@ -11,12 +11,14 @@
* SAP AG - initial API, implementation and documentation
* mwenz - Bug 324859 - Need Undo/Redo support for Non-EMF based domain objects
* mwenz - Bug 325084 - Provide documentation for Patterns
+ * mwenz - Bug 443304 - Improve undo/redo handling in Graphiti features
*
* </copyright>
*
*******************************************************************************/
package org.eclipse.graphiti.pattern;
+import org.eclipse.graphiti.features.ICustomAbortableUndoRedoFeature;
import org.eclipse.graphiti.features.ICustomUndoableFeature;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.features.IReason;
@@ -33,7 +35,8 @@
* @noextend This class is not intended to be subclassed by clients.
* @noinstantiate This class is not intended to be instantiated by clients.
*/
-public class UpdateFeatureForPattern extends AbstractUpdateFeature implements ICustomUndoableFeature {
+public class UpdateFeatureForPattern extends AbstractUpdateFeature implements ICustomUndoableFeature,
+ ICustomAbortableUndoRedoFeature {
private IUpdate pattern;
@@ -70,6 +73,17 @@
return pattern.update(context);
}
+ /**
+ * @since 0.12
+ */
+ @Override
+ public boolean isAbort() {
+ if (pattern instanceof ICustomAbortableUndoRedoPattern) {
+ return ((ICustomAbortableUndoRedoPattern) pattern).isAbort();
+ }
+ return false;
+ }
+
@Override
public boolean canUndo(IContext context) {
if (pattern instanceof ICustomUndoablePattern) {
@@ -79,7 +93,25 @@
}
/**
+ * @since 0.12
+ */
+ @Override
+ public void preUndo(IContext context) {
+ }
+
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void postUndo(IContext context) {
+ if (pattern instanceof ICustomUndoRedoPattern) {
+ ((ICustomUndoRedoPattern) pattern).postUndo(this, context);
+ }
+ }
+
+ /**
* @since 0.8
+ * @deprecated use {@link #postUndo(IContext)} instead
*/
public void undo(IContext context) {
if (pattern instanceof ICustomUndoablePattern) {
@@ -98,7 +130,25 @@
}
/**
+ * @since 0.12
+ */
+ @Override
+ public void preRedo(IContext context) {
+ }
+
+ /**
+ * @since 0.12
+ */
+ @Override
+ public void postRedo(IContext context) {
+ if (pattern instanceof ICustomUndoRedoPattern) {
+ ((ICustomUndoRedoPattern) pattern).postRedo(this, context);
+ }
+ }
+
+ /**
* @since 0.8
+ * @deprecated use {@link #postRedo(IContext)} instead
*/
public void redo(IContext context) {
if (pattern instanceof ICustomUndoablePattern) {
diff --git a/plugins/org.eclipse.graphiti.ui/src/org/eclipse/graphiti/ui/internal/editor/GFWorkspaceCommandStackImpl.java b/plugins/org.eclipse.graphiti.ui/src/org/eclipse/graphiti/ui/internal/editor/GFWorkspaceCommandStackImpl.java
index a657476..c4b56c8 100644
--- a/plugins/org.eclipse.graphiti.ui/src/org/eclipse/graphiti/ui/internal/editor/GFWorkspaceCommandStackImpl.java
+++ b/plugins/org.eclipse.graphiti.ui/src/org/eclipse/graphiti/ui/internal/editor/GFWorkspaceCommandStackImpl.java
@@ -15,6 +15,7 @@
* mwenz - Bug 371717 - IllegalStateException When updating cells on Diagram
* mwenz - Bug 389380 - Undo/Redo handling wrong Command executed by undo action
* mwenz - Bug 430609 - Re-entrance in diagram update causes transaction error
+ * mwenz - Bug 443304 - Improve undo/redo handling in Graphiti features
*
* </copyright>
*
@@ -25,10 +26,13 @@
import java.util.Stack;
import org.eclipse.core.commands.operations.IOperationHistory;
+import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.transaction.RollbackException;
import org.eclipse.emf.workspace.impl.WorkspaceCommandStackImpl;
import org.eclipse.graphiti.IExecutionInfo;
+import org.eclipse.graphiti.features.ICustomAbortableUndoRedoFeature;
+import org.eclipse.graphiti.features.ICustomUndoRedoFeature;
import org.eclipse.graphiti.features.ICustomUndoableFeature;
import org.eclipse.graphiti.features.IFeature;
import org.eclipse.graphiti.features.IFeatureAndContext;
@@ -137,14 +141,49 @@
@Override
public void redo() {
- // Care about EMF redo
- super.redo();
-
- // Check if non-EMF redo is needed and care about it
+ IFeatureAndContext[] executionList = null;
+ boolean[] canRedo = null;
if (!redoStackForExecutionInfo.isEmpty()) {
IExecutionInfo ei = redoStackForExecutionInfo.pop();
undoStackForExecutionInfo.push(ei);
- IFeatureAndContext[] executionList = ei.getExecutionList();
+ executionList = ei.getExecutionList();
+ canRedo = new boolean[executionList.length];
+ for (int i = 0; i < executionList.length; i++) {
+ IFeature feature = executionList[i].getFeature();
+ IContext context = executionList[i].getContext();
+ if (feature instanceof ICustomUndoRedoFeature) {
+ canRedo[i] = feature.canUndo(context);
+ } else {
+ canRedo[i] = false;
+ }
+ }
+ }
+
+ // Care about non-EMF pre-redo
+ if (executionList != null) {
+ // Traverse operation forwards
+ for (int i = executionList.length - 1; i >= 0; i--) {
+ IFeature feature = executionList[i].getFeature();
+ IContext context = executionList[i].getContext();
+ if (feature instanceof ICustomUndoRedoFeature) {
+ ICustomUndoRedoFeature undoableFeature = (ICustomUndoRedoFeature) feature;
+ if (canRedo[i]) {
+ undoableFeature.preRedo(context);
+ if (undoableFeature instanceof ICustomAbortableUndoRedoFeature) {
+ if (((ICustomAbortableUndoRedoFeature) undoableFeature).isAbort()) {
+ throw new OperationCanceledException();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Trigger EMF redo
+ super.redo();
+
+ // Check if non-EMF redo is needed and care about it
+ if (executionList != null) {
// Traverse operation forwards
for (int i = 0; i < executionList.length; i++) {
IFeature feature = executionList[i].getFeature();
@@ -156,23 +195,69 @@
}
}
}
+ for (int i = 0; i < executionList.length; i++) {
+ IFeature feature = executionList[i].getFeature();
+ IContext context = executionList[i].getContext();
+ if (feature instanceof ICustomUndoRedoFeature) {
+ ICustomUndoRedoFeature undoableFeature = (ICustomUndoRedoFeature) feature;
+ if (canRedo[i]) {
+ undoableFeature.postRedo(context);
+ }
+ }
+ }
}
}
@Override
public void undo() {
- // Care about EMF undo
- super.undo();
-
- // Check if non-EMF undo is needed and care about it
+ IFeatureAndContext[] executionList = null;
+ boolean[] canUndo = null;
if (!undoStackForExecutionInfo.isEmpty()) {
IExecutionInfo ei = undoStackForExecutionInfo.pop();
redoStackForExecutionInfo.push(ei);
- IFeatureAndContext[] executionList = ei.getExecutionList();
+ executionList = ei.getExecutionList();
+ canUndo = new boolean[executionList.length];
+ for (int i = 0; i < executionList.length; i++) {
+ IFeature feature = executionList[i].getFeature();
+ IContext context = executionList[i].getContext();
+ if (feature instanceof ICustomUndoRedoFeature) {
+ canUndo[i] = feature.canUndo(context);
+ } else {
+ canUndo[i] = false;
+ }
+ }
+ }
+
+ // Care about non-EMF pre-undo
+ if (executionList != null) {
// Traverse operations backwards
for (int i = executionList.length - 1; i >= 0; i--) {
IFeature feature = executionList[i].getFeature();
IContext context = executionList[i].getContext();
+ if (feature instanceof ICustomUndoRedoFeature) {
+ ICustomUndoRedoFeature undoableFeature = (ICustomUndoRedoFeature) feature;
+ if (canUndo[i]) {
+ undoableFeature.preUndo(context);
+ if (undoableFeature instanceof ICustomAbortableUndoRedoFeature) {
+ if (((ICustomAbortableUndoRedoFeature) undoableFeature).isAbort()) {
+ throw new OperationCanceledException();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Trigger EMF undo
+ super.undo();
+
+ // Care about non-EMF undo and post-undo
+ if (executionList != null) {
+ // Traverse operations backwards (individually for undo and
+ // post-undo)
+ for (int i = executionList.length - 1; i >= 0; i--) {
+ IFeature feature = executionList[i].getFeature();
+ IContext context = executionList[i].getContext();
if (feature instanceof ICustomUndoableFeature) {
ICustomUndoableFeature undoableFeature = (ICustomUndoableFeature) feature;
if (undoableFeature.canUndo(context)) {
@@ -180,6 +265,16 @@
}
}
}
+ for (int i = executionList.length - 1; i >= 0; i--) {
+ IFeature feature = executionList[i].getFeature();
+ IContext context = executionList[i].getContext();
+ if (feature instanceof ICustomUndoRedoFeature) {
+ ICustomUndoRedoFeature undoableFeature = (ICustomUndoRedoFeature) feature;
+ if (canUndo[i]) {
+ undoableFeature.postUndo(context);
+ }
+ }
+ }
}
}
}
diff --git a/plugins/org.eclipse.graphiti/src/org/eclipse/graphiti/features/ICustomAbortableUndoRedoFeature.java b/plugins/org.eclipse.graphiti/src/org/eclipse/graphiti/features/ICustomAbortableUndoRedoFeature.java
new file mode 100644
index 0000000..c945bb9
--- /dev/null
+++ b/plugins/org.eclipse.graphiti/src/org/eclipse/graphiti/features/ICustomAbortableUndoRedoFeature.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * <copyright>
+ *
+ * Copyright (c) 2014, 2014 SAP AG.
+ * 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:
+ * mwenz - Bug 443304 - Improve undo/redo handling in Graphiti features
+ *
+ * </copyright>
+ *
+ *******************************************************************************/
+package org.eclipse.graphiti.features;
+
+import org.eclipse.core.runtime.OperationCanceledException;
+
+
+/**
+ * This interface can by used and implemented by customers within any feature to
+ * signal the need for additional work that needs to be done before or after
+ * undo and redo. In addition to {@link ICustomUndoRedoFeature} this interface
+ * supports cancellation of undo/redo operations.
+ *
+ * @see ICustomUndoRedoFeature
+ * @since 0.12
+ */
+public interface ICustomAbortableUndoRedoFeature extends ICustomUndoRedoFeature {
+
+ /**
+ * The Graphiti framework will call this method after
+ * {@link #preUndo(org.eclipse.graphiti.features.context.IContext)}/
+ * {@link #preRedo(org.eclipse.graphiti.features.context.IContext)} have
+ * been called and before the actual undo/redo operation is triggered. In
+ * case this method returns <code>true</code>, the operation will be
+ * cancelled by the Graphiti framework by throwing an
+ * {@link OperationCanceledException} that causes am EMF revert of the
+ * operation.
+ * <p>
+ * Implementing classes might e.g. set a flag in
+ * {@link #preUndo(org.eclipse.graphiti.features.context.IContext)}/
+ * {@link #preRedo(org.eclipse.graphiti.features.context.IContext)} as
+ * cancellation indication and check that that flag here.
+ *
+ * @return <code>true</code> in case you want to cancel the current
+ * operation, <code>false</code> otherwise.
+ */
+ boolean isAbort();
+}
diff --git a/plugins/org.eclipse.graphiti/src/org/eclipse/graphiti/features/ICustomUndoRedoFeature.java b/plugins/org.eclipse.graphiti/src/org/eclipse/graphiti/features/ICustomUndoRedoFeature.java
new file mode 100644
index 0000000..ad490c0
--- /dev/null
+++ b/plugins/org.eclipse.graphiti/src/org/eclipse/graphiti/features/ICustomUndoRedoFeature.java
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ * <copyright>
+ *
+ * Copyright (c) 2014, 2014 SAP AG.
+ * 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:
+ * mwenz - Bug 443304 - Improve undo/redo handling in Graphiti features
+ *
+ * </copyright>
+ *
+ *******************************************************************************/
+package org.eclipse.graphiti.features;
+
+import org.eclipse.graphiti.features.context.IContext;
+import org.eclipse.graphiti.mm.algorithms.GraphicsAlgorithm;
+import org.eclipse.graphiti.mm.pictograms.PictogramElement;
+
+/**
+ * This interface can by used and implemented by customers within any feature to
+ * signal the need for additional work that needs to be done before or after
+ * undo and redo. When a feature implements this interface, and the framework
+ * performs an undo or a redo, the framework will call the contained methods.
+ * <p>
+ * Implementing this interface is especially helpful if customers want to
+ * implement undo/redo functionality for non-EMF changes, e.g. for non-EMF
+ * domain models. Note that any EMF-model change (including the changes done to
+ * the graphical representation (Graphiti {@link PictogramElement}s and
+ * {@link GraphicsAlgorithm}s will by handled automatically by the Graphiti
+ * framework no matter if this interface is implemented by a feature or not. The
+ * feature may use the context object (e.g. the contained properties set) passed
+ * to the contained methods while executing the feature in order to collect any
+ * information needed for undo.
+ * <p>
+ * In case you want to cancel undo/redo operations in {@link #preUndo(IContext)}/{@link #preRedo(IContext)}, you need to implement
+ * {@link ICustomAbortableUndoRedoFeature} which offers an
+ * {@link ICustomAbortableUndoRedoFeature#isAbort()} method that causes the
+ * cancellation of undo/redo operation in case <code>true</code> is returned.
+ *
+ * @see ICustomAbortableUndoRedoFeature
+ * @since 0.12
+ */
+public interface ICustomUndoRedoFeature {
+
+ /**
+ * Decides if the changes done by a processed feature can be undone. This
+ * method is called once by the Graphiti framework just before any undo work
+ * is started, e.g. before {@link #preUndo(IContext)}.
+ * <p>
+ * Note that as soon as any feature reports <code>false</code> here, also
+ * all previous entries in the command stack are no longer reachable for
+ * undo.
+ * <p>
+ * Note: this method with exactly the same signature is also already part of
+ * the {@link IFeature} contract. It is repeated here for transparency
+ * purposes only.
+ *
+ * @param context
+ * this is the instance of the {@link IContext} object that was
+ * in use when executing the feature.
+ *
+ * @return true if the feature can be undone, false if not
+ */
+ boolean canUndo(IContext context);
+
+ /**
+ * This method will be called by the Graphiti framework before the EMF undo
+ * is triggered. Customers may revert their non-EMF changes done by the
+ * feature here or in {@link #postUndo(IContext)}.
+ *
+ * @param context
+ * this is the instance of the {@link IContext} object that was
+ * in use when executing the feature
+ */
+ void preUndo(IContext context);
+
+ /**
+ * This method will be called by the Graphiti framework after the EMF undo
+ * is finished. Customers may revert their non-EMF changes done by the
+ * feature here or in {@link #preUndo(IContext)}.
+ *
+ * @param context
+ * this is the instance of the {@link IContext} object that was
+ * in use when executing the feature
+ */
+ void postUndo(IContext context);
+
+ /**
+ * Decides if the processed feature can be re-done. This method is called
+ * once by the Graphiti framework just before any redo work is started, e.g.
+ * before {@link #preRedo(IContext)}.
+ * <p>
+ * Note that as soon as any feature reports <code>false</code> here, also
+ * all consecutive entries in the command stack are no longer reachable for
+ * redo.
+ *
+ * @param context
+ * this is the instance of the {@link IContext} object that was
+ * in use when executing the feature
+ *
+ * @return true if the feature can be re-done, false if not
+ */
+ boolean canRedo(IContext context);
+
+ /**
+ * This method will be called by the Graphiti framework before the EMF undo
+ * has triggered. Customers may re-apply their non-EMF changes done by the
+ * feature here or in {@link #postRedo(IContext)}. (Usually it might be
+ * sufficient to delegate to the execution method of the feature.)
+ *
+ * @param context
+ * this is the instance of the {@link IContext} object that was
+ * in use when executing the feature
+ */
+ void preRedo(IContext context);
+
+ /**
+ * This method will be called by the Graphiti framework after the EMF undo
+ * has finished. Customers may re-apply their non-EMF changes done by the
+ * feature here or in {@link #preRedo(IContext)}. (Usually it might be
+ * sufficient to delegate to the execution method of the feature.)
+ *
+ * @param context
+ * this is the instance of the {@link IContext} object that was
+ * in use when executing the feature
+ */
+ void postRedo(IContext context);
+}
diff --git a/plugins/org.eclipse.graphiti/src/org/eclipse/graphiti/features/ICustomUndoableFeature.java b/plugins/org.eclipse.graphiti/src/org/eclipse/graphiti/features/ICustomUndoableFeature.java
index 5c52a5f..5ad74df 100644
--- a/plugins/org.eclipse.graphiti/src/org/eclipse/graphiti/features/ICustomUndoableFeature.java
+++ b/plugins/org.eclipse.graphiti/src/org/eclipse/graphiti/features/ICustomUndoableFeature.java
@@ -1,7 +1,7 @@
/*******************************************************************************
* <copyright>
*
- * Copyright (c) 2005, 2011 SAP AG.
+ * Copyright (c) 2005, 2014 SAP AG.
* 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
@@ -9,6 +9,7 @@
*
* Contributors:
* mwenz - Bug 324859 - initial API, implementation and documentation
+ * mwenz - Bug 443304 - Improve undo/redo handling in Graphiti features
*
* </copyright>
*
@@ -36,6 +37,7 @@
* information needed for undo.
*
* @since 0.8.0
+ * @deprecated Use {@link ICustomUndoRedoFeature} instead
*/
public interface ICustomUndoableFeature {
diff --git a/tests/org.eclipse.graphiti.ui.tests/src/org/eclipse/graphiti/ui/tests/AllTests.java b/tests/org.eclipse.graphiti.ui.tests/src/org/eclipse/graphiti/ui/tests/AllTests.java
index bdd9185..2145f8f 100644
--- a/tests/org.eclipse.graphiti.ui.tests/src/org/eclipse/graphiti/ui/tests/AllTests.java
+++ b/tests/org.eclipse.graphiti.ui.tests/src/org/eclipse/graphiti/ui/tests/AllTests.java
@@ -13,6 +13,7 @@
* mwenz - Bug 415884 - Cannot query size of a multi-line text
* mwenz - Bug 423573 - Angles should never be integer
* mwenz - Bug 416039 - TextStyle rendering does not fall back to abstract text font
+ * mwenz - Bug 443304 - Improve undo/redo handling in Graphiti features
*
* </copyright>
*
@@ -27,6 +28,6 @@
@RunWith(Suite.class)
@Suite.SuiteClasses({ PackageTest.class, CommandStackTest.class, MigrationServiceTest.class,
CustomUndoableFeatureTest.class, RollbackTest.class, LayoutServiceTest.class, CommandTest.class,
- CompatibilityTests.class, GFFigureUtilTest.class })
+ CompatibilityTests.class, GFFigureUtilTest.class, CustomUndoRedoFeatureTest.class })
public class AllTests {
}
diff --git a/tests/org.eclipse.graphiti.ui.tests/src/org/eclipse/graphiti/ui/tests/CustomUndoRedoFeatureTest.java b/tests/org.eclipse.graphiti.ui.tests/src/org/eclipse/graphiti/ui/tests/CustomUndoRedoFeatureTest.java
new file mode 100644
index 0000000..edb897d
--- /dev/null
+++ b/tests/org.eclipse.graphiti.ui.tests/src/org/eclipse/graphiti/ui/tests/CustomUndoRedoFeatureTest.java
@@ -0,0 +1,415 @@
+/*******************************************************************************
+ * <copyright>
+ *
+ * Copyright (c) 2014, 2014 SAP AG.
+ * 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:
+ * mwenz - Bug 443304 - Improve undo/redo handling in Graphiti features
+ *
+ * </copyright>
+ *
+ *******************************************************************************/
+package org.eclipse.graphiti.ui.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.easymock.EasyMock;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.emf.transaction.TransactionalEditingDomain;
+import org.eclipse.graphiti.dt.IDiagramTypeProvider;
+import org.eclipse.graphiti.features.ICustomAbortableUndoRedoFeature;
+import org.eclipse.graphiti.features.IFeatureProvider;
+import org.eclipse.graphiti.features.context.IContext;
+import org.eclipse.graphiti.features.context.ICustomContext;
+import org.eclipse.graphiti.features.context.IReconnectionContext;
+import org.eclipse.graphiti.features.context.impl.ReconnectionContext;
+import org.eclipse.graphiti.features.custom.AbstractCustomFeature;
+import org.eclipse.graphiti.features.impl.DefaultReconnectionFeature;
+import org.eclipse.graphiti.internal.command.CommandContainer;
+import org.eclipse.graphiti.internal.command.GenericFeatureCommandWithContext;
+import org.eclipse.graphiti.mm.pictograms.Anchor;
+import org.eclipse.graphiti.mm.pictograms.Connection;
+import org.eclipse.graphiti.mm.pictograms.PictogramElement;
+import org.eclipse.graphiti.tb.IToolBehaviorProvider;
+import org.eclipse.graphiti.tests.reuse.GFAbstractTestCase;
+import org.eclipse.graphiti.ui.editor.DiagramBehavior;
+import org.eclipse.graphiti.ui.editor.IDiagramContainerUI;
+import org.eclipse.graphiti.ui.internal.command.GefCommandWrapper;
+import org.eclipse.graphiti.ui.internal.command.ReconnectCommand;
+import org.eclipse.graphiti.ui.internal.config.IConfigurationProviderInternal;
+import org.eclipse.graphiti.ui.internal.editor.GFCommandStack;
+import org.eclipse.graphiti.ui.platform.IConfigurationProvider;
+import org.eclipse.graphiti.ui.services.GraphitiUi;
+import org.junit.Test;
+
+/**
+ *
+ */
+@SuppressWarnings("restriction")
+public class CustomUndoRedoFeatureTest extends GFAbstractTestCase {
+
+ @Test
+ public void testPositive() {
+ TransactionalEditingDomain editingDomain = GraphitiUi.getEmfService().createResourceSetAndEditingDomain();
+ IToolBehaviorProvider toolBehaviorProvider = EasyMock.createNiceMock(IToolBehaviorProvider.class);
+ EasyMock.replay(toolBehaviorProvider);
+
+ IDiagramTypeProvider diagramTypeProvider = EasyMock.createNiceMock(IDiagramTypeProvider.class);
+ IFeatureProvider featureProvider = EasyMock.createNiceMock(IFeatureProvider.class);
+ EasyMock.replay(featureProvider);
+
+ EasyMock.expect(diagramTypeProvider.getCurrentToolBehaviorProvider()).andReturn(toolBehaviorProvider).anyTimes();
+ EasyMock.replay(diagramTypeProvider);
+
+ IConfigurationProvider configurationProvider = EasyMock.createNiceMock(IConfigurationProviderInternal.class);
+ EasyMock.expect(configurationProvider.getDiagramTypeProvider()).andReturn(diagramTypeProvider).anyTimes();
+ EasyMock.replay(configurationProvider);
+ GFCommandStack commandStack = new GFCommandStack(configurationProvider, editingDomain);
+
+ ICustomContext context = EasyMock.createNiceMock(ICustomContext.class);
+ EasyMock.replay(context);
+
+ // Do not abort after pre hook
+ TestCustomFeature feature = new TestCustomFeature(featureProvider, false);
+ GenericFeatureCommandWithContext featureCommand = new GenericFeatureCommandWithContext(feature, context);
+
+ CommandContainer commandContainer = new CommandContainer(featureProvider);
+ commandContainer.add(featureCommand);
+
+ GefCommandWrapper commandWrapper = new GefCommandWrapper(commandContainer, editingDomain);
+ commandStack.execute(commandWrapper);
+
+ // Check that feature can be undone
+ assertTrue("Executed command must be undoable", commandStack.canUndo());
+
+ // Check that undo is called
+ commandStack.undo();
+ assertTrue("preUndo() must have been called", feature.preUndoCalled);
+ assertTrue("postUndo() must have been called", feature.postUndoCalled);
+
+ // Check that feature can be redone
+ assertTrue("Executed command must be redoable", commandStack.canRedo());
+
+ // Check that redo is called
+ commandStack.redo();
+ assertTrue("preRedo() must have been called", feature.preRedoCalled);
+ assertTrue("postRedo() must have been called", feature.postRedoCalled);
+ }
+
+ @Test
+ public void testAbort() {
+ TransactionalEditingDomain editingDomain = GraphitiUi.getEmfService().createResourceSetAndEditingDomain();
+ IToolBehaviorProvider toolBehaviorProvider = EasyMock.createNiceMock(IToolBehaviorProvider.class);
+ EasyMock.replay(toolBehaviorProvider);
+
+ IDiagramTypeProvider diagramTypeProvider = EasyMock.createNiceMock(IDiagramTypeProvider.class);
+ IFeatureProvider featureProvider = EasyMock.createNiceMock(IFeatureProvider.class);
+ EasyMock.replay(featureProvider);
+
+ EasyMock.expect(diagramTypeProvider.getCurrentToolBehaviorProvider()).andReturn(toolBehaviorProvider)
+ .anyTimes();
+ EasyMock.replay(diagramTypeProvider);
+
+ IConfigurationProvider configurationProvider = EasyMock.createNiceMock(IConfigurationProviderInternal.class);
+ EasyMock.expect(configurationProvider.getDiagramTypeProvider()).andReturn(diagramTypeProvider).anyTimes();
+ EasyMock.replay(configurationProvider);
+ GFCommandStack commandStack = new GFCommandStack(configurationProvider, editingDomain);
+
+ ICustomContext context = EasyMock.createNiceMock(ICustomContext.class);
+ EasyMock.replay(context);
+
+ // Do abort after pre hook
+ TestCustomFeature feature = new TestCustomFeature(featureProvider, true);
+ GenericFeatureCommandWithContext featureCommand = new GenericFeatureCommandWithContext(feature, context);
+
+ CommandContainer commandContainer = new CommandContainer(featureProvider);
+ commandContainer.add(featureCommand);
+
+ GefCommandWrapper commandWrapper = new GefCommandWrapper(commandContainer, editingDomain);
+ commandStack.execute(commandWrapper);
+
+ // Check that feature can be undone
+ assertTrue("Executed command must be undoable", commandStack.canUndo());
+
+ // Check that undo is called
+ try {
+ commandStack.undo();
+ fail("Abort expected");
+ } catch (OperationCanceledException e) {
+ // Expected
+ }
+ assertTrue("preUndo() must have been called", feature.preUndoCalled);
+ assertFalse("postUndo() must not have been called", feature.postUndoCalled);
+ }
+
+ @Test
+ public void testReconnect() {
+ TransactionalEditingDomain editingDomain = GraphitiUi.getEmfService().createResourceSetAndEditingDomain();
+ IToolBehaviorProvider toolBehaviorProvider = EasyMock.createNiceMock(IToolBehaviorProvider.class);
+ EasyMock.replay(toolBehaviorProvider);
+
+ IDiagramTypeProvider diagramTypeProvider = EasyMock.createNiceMock(IDiagramTypeProvider.class);
+ IFeatureProvider featureProvider = EasyMock.createNiceMock(IFeatureProvider.class);
+ // Do not abort after pre hook
+ TestReconnectionFeature feature = new TestReconnectionFeature(featureProvider, false);
+ EasyMock.expect(featureProvider.getReconnectionFeature(EasyMock.<IReconnectionContext> anyObject()))
+ .andReturn(feature).anyTimes();
+ EasyMock.replay(featureProvider);
+
+ EasyMock.expect(diagramTypeProvider.getCurrentToolBehaviorProvider()).andReturn(toolBehaviorProvider).anyTimes();
+ EasyMock.expect(diagramTypeProvider.getFeatureProvider()).andReturn(featureProvider).anyTimes();
+ EasyMock.replay(diagramTypeProvider);
+
+ IDiagramContainerUI diagramContainer = EasyMock.createNiceMock(IDiagramContainerUI.class);
+
+ DiagramBehavior diagramBehavior = new MockDiagramBehavior(diagramContainer, editingDomain);
+
+ IConfigurationProvider configurationProvider = EasyMock.createNiceMock(IConfigurationProviderInternal.class);
+ EasyMock.expect(configurationProvider.getDiagramTypeProvider()).andReturn(diagramTypeProvider).anyTimes();
+ EasyMock.expect(configurationProvider.getDiagramBehavior()).andReturn(diagramBehavior).anyTimes();
+ EasyMock.replay(configurationProvider);
+ GFCommandStack commandStack = new GFCommandStack(configurationProvider, editingDomain);
+
+ ICustomContext context = EasyMock.createNiceMock(ICustomContext.class);
+ EasyMock.replay(context);
+
+ Connection connection = EasyMock.createNiceMock(Connection.class);
+ Anchor oldAnchor = EasyMock.createNiceMock(Anchor.class);
+ Anchor newAnchor = EasyMock.createNiceMock(Anchor.class);
+ PictogramElement newTargetPictogramElement = EasyMock.createNiceMock(PictogramElement.class);
+
+ ReconnectCommand reconnectCommand = new ReconnectCommand(configurationProvider, connection, oldAnchor,
+ newAnchor, newTargetPictogramElement, ReconnectionContext.RECONNECT_SOURCE, null);
+ commandStack.execute(reconnectCommand);
+
+ // Check that feature can be undone
+ assertTrue("Executed command must be undoable", commandStack.canUndo());
+
+ // Check that undo is called
+ commandStack.undo();
+ assertTrue("preUndo() must have been called", feature.preUndoCalled);
+ assertTrue("postUndo() must have been called", feature.postUndoCalled);
+
+ // Check that feature can be redone
+ assertTrue("Executed command must be redoable", commandStack.canRedo());
+
+ // Check that redo is called
+ commandStack.redo();
+ assertTrue("preRedo() must have been called", feature.preRedoCalled);
+ assertTrue("postRedo() must have been called", feature.postRedoCalled);
+ }
+
+ @Test
+ public void testReconnectAbort() {
+ TransactionalEditingDomain editingDomain = GraphitiUi.getEmfService().createResourceSetAndEditingDomain();
+ IToolBehaviorProvider toolBehaviorProvider = EasyMock.createNiceMock(IToolBehaviorProvider.class);
+ EasyMock.replay(toolBehaviorProvider);
+
+ IDiagramTypeProvider diagramTypeProvider = EasyMock.createNiceMock(IDiagramTypeProvider.class);
+ IFeatureProvider featureProvider = EasyMock.createNiceMock(IFeatureProvider.class);
+ // Do abort after pre hook
+ TestReconnectionFeature feature = new TestReconnectionFeature(featureProvider, true);
+ EasyMock.expect(featureProvider.getReconnectionFeature(EasyMock.<IReconnectionContext> anyObject()))
+ .andReturn(feature).anyTimes();
+ EasyMock.replay(featureProvider);
+
+ EasyMock.expect(diagramTypeProvider.getCurrentToolBehaviorProvider()).andReturn(toolBehaviorProvider)
+ .anyTimes();
+ EasyMock.expect(diagramTypeProvider.getFeatureProvider()).andReturn(featureProvider).anyTimes();
+ EasyMock.replay(diagramTypeProvider);
+
+ IDiagramContainerUI diagramContainer = EasyMock.createNiceMock(IDiagramContainerUI.class);
+
+ DiagramBehavior diagramBehavior = new MockDiagramBehavior(diagramContainer, editingDomain);
+
+ IConfigurationProvider configurationProvider = EasyMock.createNiceMock(IConfigurationProviderInternal.class);
+ EasyMock.expect(configurationProvider.getDiagramTypeProvider()).andReturn(diagramTypeProvider).anyTimes();
+ EasyMock.expect(configurationProvider.getDiagramBehavior()).andReturn(diagramBehavior).anyTimes();
+ EasyMock.replay(configurationProvider);
+ GFCommandStack commandStack = new GFCommandStack(configurationProvider, editingDomain);
+
+ ICustomContext context = EasyMock.createNiceMock(ICustomContext.class);
+ EasyMock.replay(context);
+
+ Connection connection = EasyMock.createNiceMock(Connection.class);
+ Anchor oldAnchor = EasyMock.createNiceMock(Anchor.class);
+ Anchor newAnchor = EasyMock.createNiceMock(Anchor.class);
+ PictogramElement newTargetPictogramElement = EasyMock.createNiceMock(PictogramElement.class);
+
+ ReconnectCommand reconnectCommand = new ReconnectCommand(configurationProvider, connection, oldAnchor,
+ newAnchor, newTargetPictogramElement, ReconnectionContext.RECONNECT_SOURCE, null);
+ commandStack.execute(reconnectCommand);
+
+ // Check that feature can be undone
+ assertTrue("Executed command must be undoable", commandStack.canUndo());
+
+ // Check that undo is called
+ try {
+ commandStack.undo();
+ fail("Abort expected");
+ } catch (OperationCanceledException e) {
+ // Expected
+ }
+ assertTrue("preUndo() must have been called", feature.preUndoCalled);
+ assertFalse("postUndo() must not have been called", feature.postUndoCalled);
+ }
+
+ private class TestCustomFeature extends AbstractCustomFeature implements ICustomAbortableUndoRedoFeature {
+
+ public boolean preUndoCalled = false;
+ public boolean postUndoCalled = false;
+
+ public boolean preRedoCalled = false;
+ public boolean postRedoCalled = false;
+
+ private ICustomContext context = null;
+ private boolean abortAfterPreHook;
+
+ public TestCustomFeature(IFeatureProvider fp, boolean abortAfterPreHook) {
+ super(fp);
+ this.abortAfterPreHook = abortAfterPreHook;
+ }
+
+ @Override
+ public boolean canExecute(ICustomContext context) {
+ return true;
+ }
+
+ @Override
+ public boolean isAbort() {
+ return abortAfterPreHook;
+ }
+
+ public void execute(ICustomContext context) {
+ // Do nothing
+ this.context = context;
+ }
+
+ @Override
+ public boolean canUndo(IContext context) {
+ return true;
+ }
+
+ @Override
+ public void preUndo(IContext context) {
+ preUndoCalled = true;
+ assertEquals("Context object must be the same as in execute", this.context, context);
+ }
+
+ @Override
+ public void postUndo(IContext context) {
+ postUndoCalled = true;
+ assertEquals("Context object must be the same as in execute", this.context, context);
+ }
+
+ public boolean canRedo(IContext context) {
+ return true;
+ }
+
+ @Override
+ public void preRedo(IContext context) {
+ preRedoCalled = true;
+ assertEquals("Context object must be the same as in execute", this.context, context);
+ }
+
+ @Override
+ public void postRedo(IContext context) {
+ postRedoCalled = true;
+ assertEquals("Context object must be the same as in execute", this.context, context);
+ }
+ }
+
+ private class TestReconnectionFeature extends DefaultReconnectionFeature implements ICustomAbortableUndoRedoFeature {
+
+ public boolean preUndoCalled = false;
+ public boolean postUndoCalled = false;
+
+ public boolean preRedoCalled = false;
+ public boolean postRedoCalled = false;
+
+ private IReconnectionContext context = null;
+ private boolean abortAfterPreHook;
+
+ public TestReconnectionFeature(IFeatureProvider fp, boolean abortAfterPreHook) {
+ super(fp);
+ this.abortAfterPreHook = abortAfterPreHook;
+ }
+
+ @Override
+ public boolean canReconnect(IReconnectionContext context) {
+ return true;
+ }
+
+ @Override
+ public boolean isAbort() {
+ return abortAfterPreHook;
+ }
+
+ @Override
+ public boolean canUndo(IContext context) {
+ return true;
+ }
+
+ @Override
+ public void preUndo(IContext context) {
+ preUndoCalled = true;
+ assertEquals("Context object must be the same as in execute", this.context, context);
+ }
+
+ @Override
+ public void postUndo(IContext context) {
+ postUndoCalled = true;
+ assertEquals("Context object must be the same as in execute", this.context, context);
+ }
+
+ public boolean canRedo(IContext context) {
+ return true;
+ }
+
+ @Override
+ public void preRedo(IContext context) {
+ preRedoCalled = true;
+ assertEquals("Context object must be the same as in execute", this.context, context);
+ }
+
+ @Override
+ public void postRedo(IContext context) {
+ postRedoCalled = true;
+ assertEquals("Context object must be the same as in execute", this.context, context);
+ }
+
+ public void preReconnect(IReconnectionContext context) {
+ // Do nothing
+ this.context = context;
+ }
+
+ public void postReconnect(IReconnectionContext context) {
+ }
+
+ public void canceledReconnect(IReconnectionContext context) {
+ }
+ }
+
+ public class MockDiagramBehavior extends DiagramBehavior {
+
+ private TransactionalEditingDomain editingDomain;
+
+ public MockDiagramBehavior(IDiagramContainerUI diagramContainer, TransactionalEditingDomain editingDomain) {
+ super(diagramContainer);
+ this.editingDomain = editingDomain;
+ }
+
+ @Override
+ public TransactionalEditingDomain getEditingDomain() {
+ return editingDomain;
+ }
+ }
+
+}