Refactored trace creation into an undoable operation
The creation of a trace link is now an undoable operation. This means
that all views that want to create a trace link can reuse the operation
from the org.eclipse.capra.ui.operations package and get automatic undo
ability. To facilitate this, a new getTraces() method has also been
implemented in TraceHelper. In addition, the API in this class has been
simplified.
At the same time, creating a trace link with the SelectionView can now
be undone via the Edit menu as well as using the platform-specific keys.
Change-Id: I5042ba23fce8eccceca3ebe8fdfa11080ff0b8c7
diff --git a/bundles/org.eclipse.capra.core/src/org/eclipse/capra/core/helpers/TraceHelper.java b/bundles/org.eclipse.capra.core/src/org/eclipse/capra/core/helpers/TraceHelper.java
index 03a8068..dba21af 100644
--- a/bundles/org.eclipse.capra.core/src/org/eclipse/capra/core/helpers/TraceHelper.java
+++ b/bundles/org.eclipse.capra.core/src/org/eclipse/capra/core/helpers/TraceHelper.java
@@ -17,6 +17,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.stream.Collectors;
import org.eclipse.capra.core.adapters.ArtifactMetaModelAdapter;
import org.eclipse.capra.core.adapters.Connection;
@@ -56,6 +57,15 @@
}
/**
+ * Deletes the given traces from the trace model
+ *
+ * @param toDelete the traces to delete from the trace model
+ */
+ public void deleteTraces(List<Connection> toDelete) {
+ traceAdapter.deleteTrace(toDelete, traceModel);
+ }
+
+ /**
* Annotate artifacts represented by wrappers
*
* @param wrappers
@@ -98,29 +108,36 @@
}
}
}
-
+
public List<EObject> getTracedElements(Connection connection) {
List<EObject> tracedElements = new ArrayList<>();
tracedElements.add(connection.getOrigin());
tracedElements.addAll(connection.getTargets());
return tracedElements;
-
+
}
/**
* Checks if a trace link of a certain type containing a certain selection
- * already exists in the trace model.
+ * already exists in the trace model for the instance of this class.
*
- * @param selection
- * the selected elements
- * @param traceType
- * the type of trace link
- * @param traceModel
- * the trace model to be checked
+ * @param selection the selected elements
+ * @param traceType the type of trace link
* @return true if the link exists
*/
+ public boolean traceExists(List<EObject> selection, EClass traceType) {
+ return !getTraces(selection, traceType).isEmpty();
+ }
- public boolean traceExists(List<EObject> selection, EClass traceType, EObject traceModel) {
+ /**
+ * Returns all trace link of the given type containing the given selection that
+ * exist in the trace model set in the instance of this class.
+ *
+ * @param selection the selected elements
+ * @param traceType the type of trace link
+ * @return a list of trace links that fit the criteria
+ */
+ public List<Connection> getTraces(List<EObject> selection, EClass traceType) {
TraceMetaModelAdapter traceAdapter = ExtensionPointHelper.getTraceMetamodelAdapter().get();
// create a connection
List<EObject> allElements = new ArrayList<>(selection);
@@ -135,7 +152,8 @@
Connection connection = new Connection(source, targets, tempTlink);
- return traceAdapter.getAllTraceLinks(traceModel).stream().anyMatch(c -> c.equals(connection));
+ return traceAdapter.getAllTraceLinks(this.traceModel).stream().filter(c -> c.equals(connection))
+ .collect(Collectors.toCollection(ArrayList::new));
}
}
diff --git a/bundles/org.eclipse.capra.ui/META-INF/MANIFEST.MF b/bundles/org.eclipse.capra.ui/META-INF/MANIFEST.MF
index 4537245..9ef4cad 100644
--- a/bundles/org.eclipse.capra.ui/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.capra.ui/META-INF/MANIFEST.MF
@@ -21,9 +21,11 @@
org.eclipse.ui.ide,
org.eclipse.ui,
org.eclipse.core.resources,
+ org.eclipse.core.commands,
org.eclipse.capra.core
Bundle-ActivationPolicy: lazy
Export-Package: org.eclipse.capra.ui.handlers,
org.eclipse.capra.ui.helpers,
- org.eclipse.capra.ui.views
+ org.eclipse.capra.ui.views,
+ org.eclipse.capra.ui.operations
diff --git a/bundles/org.eclipse.capra.ui/src/org/eclipse/capra/ui/handlers/TraceCreationHandler.java b/bundles/org.eclipse.capra.ui/src/org/eclipse/capra/ui/handlers/TraceCreationHandler.java
index 0baa5f2..cf70018 100644
--- a/bundles/org.eclipse.capra.ui/src/org/eclipse/capra/ui/handlers/TraceCreationHandler.java
+++ b/bundles/org.eclipse.capra.ui/src/org/eclipse/capra/ui/handlers/TraceCreationHandler.java
@@ -13,130 +13,35 @@
*******************************************************************************/
package org.eclipse.capra.ui.handlers;
-import java.util.Collection;
-import java.util.List;
-import java.util.Optional;
-import java.util.function.BiFunction;
-import java.util.stream.Collectors;
-
-import org.eclipse.capra.core.adapters.TraceMetaModelAdapter;
-import org.eclipse.capra.core.adapters.TracePersistenceAdapter;
-import org.eclipse.capra.core.handlers.IArtifactHandler;
-import org.eclipse.capra.core.helpers.ArtifactHelper;
-import org.eclipse.capra.core.helpers.EMFHelper;
-import org.eclipse.capra.core.helpers.ExtensionPointHelper;
-import org.eclipse.capra.core.helpers.TraceHelper;
-import org.eclipse.capra.ui.preferences.CapraPreferences;
+import org.eclipse.capra.ui.operations.CreateTraceOperation;
import org.eclipse.capra.ui.views.SelectionView;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
-import org.eclipse.emf.ecore.EClass;
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.emf.ecore.resource.ResourceSet;
-import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.jface.dialogs.MessageDialogWithToggle;
-import org.eclipse.jface.preference.IPreferenceStore;
-import org.eclipse.jface.viewers.LabelProvider;
-import org.eclipse.jface.window.Window;
-import org.eclipse.swt.SWT;
-import org.eclipse.ui.IWorkbenchWindow;
-import org.eclipse.ui.dialogs.ElementListSelectionDialog;
-import org.eclipse.ui.handlers.HandlerUtil;
+import org.eclipse.core.commands.operations.IOperationHistory;
+import org.eclipse.core.commands.operations.IUndoContext;
+import org.eclipse.core.commands.operations.OperationHistoryFactory;
+import org.eclipse.core.runtime.IAdaptable;
public class TraceCreationHandler extends AbstractHandler {
- private static final String CAPRA_INFORMATION = "Capra Information";
- private static final String TRACE_LINK_EXISTS = "The trace link you want to create already exists and will therefore not be created";
- private static final String TRACE_LINK_SUCCESSFULLY_CREATED = "Trace link has been successfully created";
- private static final String DO_NOT_SHOW_DIALOG_AGAIN = "Do not show this dialog again";
- private static final String SELECT_TRACE_LINK_TYPE = "Select the trace type you want to create";
- private static final String SELECTION = "Selection";
+ private IUndoContext undoContext = IOperationHistory.GLOBAL_UNDO_CONTEXT;
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
- IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindowChecked(event);
- createTrace(window, (traceTypes, selection) -> getTraceTypeToCreate(window, traceTypes, selection));
+ IAdaptable adapter = SelectionView.getOpenedView().getSite();
+
+ IOperationHistory operationHistory = OperationHistoryFactory.getOperationHistory();
+
+ CreateTraceOperation createTraceOperation = new CreateTraceOperation("Create trace link",
+ SelectionView.getOpenedView().getSelection());
+ createTraceOperation.addContext(undoContext);
+ operationHistory.execute(createTraceOperation, null, adapter);
return null;
}
- public void createTrace(IWorkbenchWindow window,
- BiFunction<Collection<EClass>, List<EObject>, Optional<EClass>> chooseTraceType) {
- List<?> artifacts = SelectionView.getOpenedView().getSelection();
-
- TraceMetaModelAdapter traceAdapter = ExtensionPointHelper.getTraceMetamodelAdapter().get();
- TracePersistenceAdapter persistenceAdapter = ExtensionPointHelper.getTracePersistenceAdapter().get();
-
- ResourceSet resourceSet = new ResourceSetImpl();
- // add trace model to resource set
- EObject traceModel = persistenceAdapter.getTraceModel(resourceSet);
- // add artifact model to resource set
- EObject artifactModel = persistenceAdapter.getArtifactWrappers(resourceSet);
-
- ArtifactHelper artifactHelper = new ArtifactHelper(artifactModel);
- TraceHelper traceHelper = new TraceHelper(traceModel);
-
- // Create the artifact wrappers
- List<EObject> wrappers = artifactHelper.createWrappers(artifacts);
-
- // Get the type of trace to be created
- Collection<EClass> traceTypes = traceAdapter.getAvailableTraceTypes(wrappers);
- Optional<EClass> chosenType = chooseTraceType.apply(traceTypes, wrappers);
-
- // Create trace
- if (chosenType.isPresent()) {
- // check if the connection already exists
- if (traceHelper.traceExists(wrappers, chosenType.get(), traceModel)) {
- MessageDialog.openInformation(window.getShell(), CAPRA_INFORMATION, TRACE_LINK_EXISTS);
- } else {
- traceHelper.createTrace(wrappers, chosenType.get());
- persistenceAdapter.saveTracesAndArtifacts(traceModel, artifactModel);
- traceHelper.annotateTrace(wrappers);
-
- // check from preferences if user wants to see the "trace
- // successfully created dialog"
- IPreferenceStore store = CapraPreferences.getPreferences();
- if (store.getBoolean(CapraPreferences.SHOW_TRACE_CREATED_CONFIRMATION_DIALOG)) {
- MessageDialogWithToggle.open(MessageDialog.INFORMATION, window.getShell(), CAPRA_INFORMATION,
- TRACE_LINK_SUCCESSFULLY_CREATED, DO_NOT_SHOW_DIALOG_AGAIN, false, store,
- CapraPreferences.SHOW_TRACE_CREATED_CONFIRMATION_DIALOG, SWT.NONE);
- }
- }
- }
- }
-
- private Optional<EClass> getTraceTypeToCreate(IWorkbenchWindow window, Collection<EClass> traceTypes,
- List<EObject> wrappers) {
- ElementListSelectionDialog dialog = new ElementListSelectionDialog(window.getShell(), new LabelProvider() {
- @Override
- public String getText(Object element) {
- EClass eclass = (EClass) element;
- return eclass.getName();
- }
- });
- dialog.setTitle(SELECT_TRACE_LINK_TYPE);
- dialog.setElements(traceTypes.toArray());
-
- dialog.setMessage(
- SELECTION + " : " + wrappers.stream().map(this::getSelectionDisplayName).collect(Collectors.toList()));
-
- if (dialog.open() == Window.OK) {
- return Optional.of((EClass) dialog.getFirstResult());
- }
-
- return Optional.empty();
- }
-
- private String getSelectionDisplayName(EObject element) {
- TracePersistenceAdapter persistenceAdapter = ExtensionPointHelper.getTracePersistenceAdapter().get();
- EObject artifactModel = persistenceAdapter.getArtifactWrappers(new ResourceSetImpl());
- ArtifactHelper artifactHelper = new ArtifactHelper(artifactModel);
- IArtifactHandler<?> handler = artifactHelper.getHandler(artifactHelper.unwrapWrapper(element)).get();
-
- return handler.withCastedHandler(artifactHelper.unwrapWrapper(element), (h, o) -> h.getDisplayName(o))
- .orElse(EMFHelper.getIdentifier(element));
-
+ public void setUndoContext(IUndoContext undoContext) {
+ this.undoContext = undoContext;
}
}
diff --git a/bundles/org.eclipse.capra.ui/src/org/eclipse/capra/ui/operations/CreateTraceOperation.java b/bundles/org.eclipse.capra.ui/src/org/eclipse/capra/ui/operations/CreateTraceOperation.java
new file mode 100644
index 0000000..19f34cf
--- /dev/null
+++ b/bundles/org.eclipse.capra.ui/src/org/eclipse/capra/ui/operations/CreateTraceOperation.java
@@ -0,0 +1,216 @@
+/*******************************************************************************
+ * Copyright (c) 2016, 2019 Chalmers | University of Gothenburg, rt-labs and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v20.html
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Chalmers | University of Gothenburg and rt-labs - initial API and implementation and/or initial documentation
+ * Chalmers | University of Gothenburg - additional features, updated API
+ *******************************************************************************/
+package org.eclipse.capra.ui.operations;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.BiFunction;
+import java.util.stream.Collectors;
+
+import org.eclipse.capra.core.adapters.Connection;
+import org.eclipse.capra.core.adapters.TraceMetaModelAdapter;
+import org.eclipse.capra.core.adapters.TracePersistenceAdapter;
+import org.eclipse.capra.core.handlers.IArtifactHandler;
+import org.eclipse.capra.core.helpers.ArtifactHelper;
+import org.eclipse.capra.core.helpers.EMFHelper;
+import org.eclipse.capra.core.helpers.ExtensionPointHelper;
+import org.eclipse.capra.core.helpers.TraceHelper;
+import org.eclipse.capra.ui.preferences.CapraPreferences;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.operations.AbstractOperation;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.MessageDialogWithToggle;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.dialogs.ElementListSelectionDialog;
+
+/**
+ * Operation to create trace links, undo and redo.
+ *
+ * @author Jan-Philipp Steghöfer
+ *
+ */
+public class CreateTraceOperation extends AbstractOperation {
+
+ private static final String CAPRA_INFORMATION = "Capra Information";
+ private static final String TRACE_LINK_EXISTS = "The trace link you want to create already exists and will therefore not be created";
+ private static final String TRACE_LINK_SUCCESSFULLY_CREATED = "Trace link has been successfully created";
+ private static final String DO_NOT_SHOW_DIALOG_AGAIN = "Do not show this dialog again";
+ private static final String SELECT_TRACE_LINK_TYPE = "Select the trace type you want to create";
+ private static final String SELECTION = "Selection";
+
+ private Optional<EClass> chosenType;
+ private List<EObject> wrappers;
+ private EObject traceModel;
+
+ private List<?> artifacts = null;
+
+ private BiFunction<Collection<EClass>, List<EObject>, Optional<EClass>> chooseTraceType = null;
+
+ private CreateTraceOperation(String label) {
+ super(label);
+ }
+
+ /**
+ * Creates a new operation to create links.
+ *
+ * @param label the label used for the operation. Should never be
+ * <code>null</code>.
+ * @param artifacts the artifacts on which this operation is performed. Should
+ * never be <code>null</code>.
+ */
+ public CreateTraceOperation(String label, List<?> artifacts) {
+ super(label);
+ Assert.isNotNull(artifacts);
+ this.artifacts = artifacts;
+ }
+
+ @Override
+ public IStatus execute(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
+ if (artifacts == null || artifacts.isEmpty()) {
+ return Status.CANCEL_STATUS;
+ }
+ Shell shell = info.getAdapter(Shell.class);
+ if (chooseTraceType == null) {
+ chooseTraceType = (traceTypes, selection) -> getTraceTypeToCreate(shell, traceTypes, selection);
+ }
+ createTrace(shell, chooseTraceType);
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public IStatus redo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
+ return execute(monitor, info);
+ }
+
+ @Override
+ public IStatus undo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
+ TraceHelper traceHelper = new TraceHelper(traceModel);
+ if (this.wrappers != null && this.chosenType != null && this.traceModel != null) {
+ List<Connection> connections = traceHelper.getTraces(this.wrappers, this.chosenType.get());
+ if (!connections.isEmpty()) {
+ traceHelper.deleteTraces(connections);
+ return Status.OK_STATUS;
+ }
+ }
+ return Status.CANCEL_STATUS;
+ }
+
+ /**
+ * Create a trace link after eliciting the trace type via the provided
+ * bi-function.
+ *
+ * @param shell a shell instance to allow opening message windows
+ * @param chooseTraceType a bi-function used to select a suitable trace link
+ * type
+ */
+ public void createTrace(Shell shell,
+ BiFunction<Collection<EClass>, List<EObject>, Optional<EClass>> chooseTraceType) {
+ TraceMetaModelAdapter traceAdapter = ExtensionPointHelper.getTraceMetamodelAdapter().get();
+ TracePersistenceAdapter persistenceAdapter = ExtensionPointHelper.getTracePersistenceAdapter().get();
+
+ ResourceSet resourceSet = new ResourceSetImpl();
+ // add trace model to resource set
+ this.traceModel = persistenceAdapter.getTraceModel(resourceSet);
+ // add artifact model to resource set
+ EObject artifactModel = persistenceAdapter.getArtifactWrappers(resourceSet);
+
+ ArtifactHelper artifactHelper = new ArtifactHelper(artifactModel);
+ TraceHelper traceHelper = new TraceHelper(this.traceModel);
+
+ // Create the artifact wrappers
+ this.wrappers = artifactHelper.createWrappers(this.artifacts);
+
+ // Get the type of trace to be created
+ Collection<EClass> traceTypes = traceAdapter.getAvailableTraceTypes(this.wrappers);
+ this.chosenType = chooseTraceType.apply(traceTypes, this.wrappers);
+
+ // Create trace
+ if (this.chosenType.isPresent()) {
+ // check if the connection already exists
+ if (traceHelper.traceExists(this.wrappers, this.chosenType.get())) {
+ MessageDialog.openInformation(shell, CAPRA_INFORMATION, TRACE_LINK_EXISTS);
+ } else {
+ traceHelper.createTrace(this.wrappers, this.chosenType.get());
+ persistenceAdapter.saveTracesAndArtifacts(traceModel, artifactModel);
+ traceHelper.annotateTrace(this.wrappers);
+
+ // check from preferences if user wants to see the "trace
+ // successfully created dialog"
+ IPreferenceStore store = CapraPreferences.getPreferences();
+ if (store.getBoolean(CapraPreferences.SHOW_TRACE_CREATED_CONFIRMATION_DIALOG)) {
+ MessageDialogWithToggle.open(MessageDialog.INFORMATION, shell, CAPRA_INFORMATION,
+ TRACE_LINK_SUCCESSFULLY_CREATED, DO_NOT_SHOW_DIALOG_AGAIN, false, store,
+ CapraPreferences.SHOW_TRACE_CREATED_CONFIRMATION_DIALOG, SWT.NONE);
+ }
+ }
+ }
+ }
+
+ /**
+ * Sets the bi-function to select a suitable trace link type.
+ *
+ * @param chooseTraceType the bi-function to use to select a suitable trace link
+ * type
+ */
+ public void setChooseTraceType(BiFunction<Collection<EClass>, List<EObject>, Optional<EClass>> chooseTraceType) {
+ this.chooseTraceType = chooseTraceType;
+ }
+
+ private Optional<EClass> getTraceTypeToCreate(Shell shell, Collection<EClass> traceTypes, List<EObject> wrappers) {
+ ElementListSelectionDialog dialog = new ElementListSelectionDialog(shell, new LabelProvider() {
+ @Override
+ public String getText(Object element) {
+ EClass eclass = (EClass) element;
+ return eclass.getName();
+ }
+ });
+ dialog.setTitle(SELECT_TRACE_LINK_TYPE);
+ dialog.setElements(traceTypes.toArray());
+
+ dialog.setMessage(
+ SELECTION + " : " + wrappers.stream().map(this::getSelectionDisplayName).collect(Collectors.toList()));
+
+ if (dialog.open() == Window.OK) {
+ return Optional.of((EClass) dialog.getFirstResult());
+ }
+
+ return Optional.empty();
+ }
+
+ private String getSelectionDisplayName(EObject element) {
+ TracePersistenceAdapter persistenceAdapter = ExtensionPointHelper.getTracePersistenceAdapter().get();
+ EObject artifactModel = persistenceAdapter.getArtifactWrappers(new ResourceSetImpl());
+ ArtifactHelper artifactHelper = new ArtifactHelper(artifactModel);
+ IArtifactHandler<?> handler = artifactHelper.getHandler(artifactHelper.unwrapWrapper(element)).get();
+
+ return handler.withCastedHandler(artifactHelper.unwrapWrapper(element), (h, o) -> h.getDisplayName(o))
+ .orElse(EMFHelper.getIdentifier(element));
+
+ }
+
+}
diff --git a/bundles/org.eclipse.capra.ui/src/org/eclipse/capra/ui/views/SelectionView.java b/bundles/org.eclipse.capra.ui/src/org/eclipse/capra/ui/views/SelectionView.java
index 5228867..45d3aa0 100644
--- a/bundles/org.eclipse.capra.ui/src/org/eclipse/capra/ui/views/SelectionView.java
+++ b/bundles/org.eclipse.capra.ui/src/org/eclipse/capra/ui/views/SelectionView.java
@@ -29,6 +29,8 @@
import org.eclipse.capra.core.handlers.PriorityHandler;
import org.eclipse.capra.core.helpers.ArtifactHelper;
import org.eclipse.capra.core.helpers.ExtensionPointHelper;
+import org.eclipse.core.commands.operations.IOperationHistory;
+import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.jface.action.IMenuListener;
@@ -51,9 +53,13 @@
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
+import org.eclipse.ui.IActionBars;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.actions.ActionFactory;
+import org.eclipse.ui.operations.RedoActionHandler;
+import org.eclipse.ui.operations.UndoActionHandler;
import org.eclipse.ui.part.ViewPart;
public class SelectionView extends ViewPart {
@@ -82,6 +88,22 @@
/** The maintained selection of EObjects */
private Set<Object> selection = new LinkedHashSet<>();
+ /**
+ * The appropriate undo context. We are using the global context to ensure that
+ * trace creation can be undone in all viewers and editors.
+ */
+ private IUndoContext undoContext = IOperationHistory.GLOBAL_UNDO_CONTEXT;
+
+ /**
+ * Action handler to undo the creation of a trace.
+ */
+ private UndoActionHandler undoAction;
+
+ /**
+ * Action handler to redo the creation of a trace.
+ */
+ private RedoActionHandler redoAction;
+
class ViewContentProvider implements IStructuredContentProvider {
@Override
public void inputChanged(Viewer v, Object oldInput, Object newInput) {
@@ -125,8 +147,8 @@
}
/**
- * Leaves the order of objects unchanged by returning 0 for all combinations
- * of objects.
+ * Leaves the order of objects unchanged by returning 0 for all combinations of
+ * objects.
*
* @see ViewerComparator#compare(Viewer, Object, Object)
*/
@@ -181,6 +203,8 @@
.map(Transfer.class::cast).collect(Collectors.toList()));
viewer.addDropSupport(ops, transfers.toArray(DEFAULT_TRANSFERS), new SelectionDropAdapter(viewer));
+
+ createGlobalActionHandlers();
}
private void hookContextMenu() {
@@ -265,4 +289,14 @@
selection.removeAll(currentselection);
viewer.refresh();
}
+
+ private void createGlobalActionHandlers() {
+ // set up action handlers that operate on the current context
+ undoAction = new UndoActionHandler(this.getSite(), undoContext);
+ redoAction = new RedoActionHandler(this.getSite(), undoContext);
+ IActionBars actionBars = getViewSite().getActionBars();
+ actionBars.setGlobalActionHandler(ActionFactory.UNDO.getId(), undoAction);
+ actionBars.setGlobalActionHandler(ActionFactory.REDO.getId(), redoAction);
+ actionBars.updateActionBars();
+ }
}
diff --git a/tests/org.eclipse.capra.testsuite/src/org/eclipse/capra/testsuite/TestCreateTraceOperation.java b/tests/org.eclipse.capra.testsuite/src/org/eclipse/capra/testsuite/TestCreateTraceOperation.java
new file mode 100644
index 0000000..9d1800f
--- /dev/null
+++ b/tests/org.eclipse.capra.testsuite/src/org/eclipse/capra/testsuite/TestCreateTraceOperation.java
@@ -0,0 +1,149 @@
+/*******************************************************************************
+ * Copyright (c) 2016, 2019 Chalmers | University of Gothenburg, rt-labs and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v20.html
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Chalmers | University of Gothenburg and rt-labs - initial API and implementation and/or initial documentation
+ * Chalmers | University of Gothenburg - additional features, updated API
+ *******************************************************************************/
+package org.eclipse.capra.testsuite;
+
+import static org.eclipse.capra.testsuite.TestHelper.clearWorkspace;
+import static org.eclipse.capra.testsuite.TestHelper.createEClassInEPackage;
+import static org.eclipse.capra.testsuite.TestHelper.createEcoreModel;
+import static org.eclipse.capra.testsuite.TestHelper.createSimpleProject;
+import static org.eclipse.capra.testsuite.TestHelper.getProject;
+import static org.eclipse.capra.testsuite.TestHelper.load;
+import static org.eclipse.capra.testsuite.TestHelper.projectExists;
+import static org.eclipse.capra.testsuite.TestHelper.resetSelectionView;
+import static org.eclipse.capra.testsuite.TestHelper.save;
+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 java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.capra.core.adapters.TracePersistenceAdapter;
+import org.eclipse.capra.core.helpers.ArtifactHelper;
+import org.eclipse.capra.core.helpers.ExtensionPointHelper;
+import org.eclipse.capra.core.helpers.TraceHelper;
+import org.eclipse.capra.generic.tracemodel.TracemodelPackage;
+import org.eclipse.capra.ui.operations.CreateTraceOperation;
+import org.eclipse.capra.ui.views.SelectionView;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.operations.IOperationHistory;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.PlatformUI;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestCreateTraceOperation {
+
+ private static final String CLASS_A_NAME = "A";
+ private static final String CLASS_B_NAME = "B";
+
+ private static final String MODEL_A_FILENAME = "modelA.ecore";
+ private static final String MODEL_B_FILENAME = "modelB.ecore";
+ private static final String MODEL_A_NAME = "modelA";
+ private static final String MODEL_B_NAME = "modelB";
+
+ private static final String TEST_PROJECT_NAME = "TestProject";
+
+ @Before
+ public void init() throws CoreException {
+ clearWorkspace();
+ resetSelectionView();
+ }
+
+ @Test
+ public void testUndoTraceLinkCreation() throws CoreException, IOException {
+ // Create a project
+ createSimpleProject(TEST_PROJECT_NAME);
+ assertTrue(projectExists(TEST_PROJECT_NAME));
+
+ // Create two models and persist them
+ IProject testProject = getProject(TEST_PROJECT_NAME);
+ EPackage a = TestHelper.createEcoreModel(MODEL_A_NAME);
+ createEClassInEPackage(a, CLASS_A_NAME);
+ save(testProject, a);
+
+ EPackage b = createEcoreModel(MODEL_B_NAME);
+ createEClassInEPackage(b, CLASS_B_NAME);
+ save(testProject, b);
+
+ // Load them, choose two elements
+ ResourceSet rs = new ResourceSetImpl();
+
+ EPackage _a = load(testProject, MODEL_A_FILENAME, rs);
+ assertEquals(_a.getName(), MODEL_A_NAME);
+ EClass _A = (EClass) _a.getEClassifier(CLASS_A_NAME);
+
+ EPackage _b = load(testProject, MODEL_B_FILENAME, rs);
+ assertEquals(_b.getName(), MODEL_B_NAME);
+ EClass _B = (EClass) _b.getEClassifier(CLASS_B_NAME);
+
+ // Add them to the selection view
+ SelectionView.getOpenedView().dropToSelection(_A);
+ SelectionView.getOpenedView().dropToSelection(_B);
+
+ EClass traceType = TracemodelPackage.eINSTANCE.getRelatedTo();
+
+ IAdaptable adapter = SelectionView.getOpenedView().getSite();
+
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IOperationHistory operationHistory = workbench.getOperationSupport().getOperationHistory();
+
+ CreateTraceOperation createTraceOperation = new CreateTraceOperation("Create trace link",
+ SelectionView.getOpenedView().getSelection());
+ createTraceOperation.setChooseTraceType((traceTypes, selection) -> {
+ if (traceTypes.contains(traceType)) {
+ return Optional.of(traceType);
+ } else {
+ return Optional.empty();
+ }
+ });
+ try {
+ assertEquals(operationHistory.execute(createTraceOperation, null, adapter), Status.OK_STATUS);
+ } catch (ExecutionException e) {
+ fail("Could not create trace: ExecutionException in operation");
+ }
+
+ TracePersistenceAdapter persistenceAdapter = ExtensionPointHelper.getTracePersistenceAdapter().get();
+ TraceHelper traceHelper = new TraceHelper(persistenceAdapter.getTraceModel(new ResourceSetImpl()));
+ EObject artifactModel = persistenceAdapter.getArtifactWrappers(_A.eResource().getResourceSet());
+ ArtifactHelper artifactHelper = new ArtifactHelper(artifactModel);
+ List<EObject> selection = artifactHelper.createWrappers(SelectionView.getOpenedView().getSelection());
+
+ // Check that the trace exists
+ selection = artifactHelper.createWrappers(SelectionView.getOpenedView().getSelection());
+ assertTrue(traceHelper.traceExists(selection, traceType));
+
+ try {
+ assertEquals(operationHistory.undoOperation(createTraceOperation, null, adapter), Status.OK_STATUS);
+ } catch (ExecutionException e) {
+ fail("Could not undo trace creation: ExecutionException in operation");
+ }
+
+ // Check that the trace does not exist
+ traceHelper = new TraceHelper(persistenceAdapter.getTraceModel(new ResourceSetImpl()));
+ selection = artifactHelper.createWrappers(SelectionView.getOpenedView().getSelection());
+ assertFalse(traceHelper.traceExists(selection, traceType));
+ }
+}
diff --git a/tests/org.eclipse.capra.testsuite/src/org/eclipse/capra/testsuite/TestDuplicateLinks.java b/tests/org.eclipse.capra.testsuite/src/org/eclipse/capra/testsuite/TestDuplicateLinks.java
index adb5b47..d407a8b 100644
--- a/tests/org.eclipse.capra.testsuite/src/org/eclipse/capra/testsuite/TestDuplicateLinks.java
+++ b/tests/org.eclipse.capra.testsuite/src/org/eclipse/capra/testsuite/TestDuplicateLinks.java
@@ -186,20 +186,18 @@
// Test the trace exists method
TracePersistenceAdapter persistenceAdapter = ExtensionPointHelper.getTracePersistenceAdapter().get();
- EObject traceModel = persistenceAdapter.getTraceModel(_A.eResource().getResourceSet());
- TraceHelper traceHelper = new TraceHelper(traceModel);
+ TraceHelper traceHelper = new TraceHelper(persistenceAdapter.getTraceModel(_A.eResource().getResourceSet()));
EObject artifactModel = persistenceAdapter.getArtifactWrappers(_A.eResource().getResourceSet());
ArtifactHelper artifactHelper = new ArtifactHelper(artifactModel);
EClass traceType = TracemodelPackage.eINSTANCE.getRelatedTo();
List<EObject> selection = artifactHelper.createWrappers(SelectionView.getOpenedView().getSelection());
- assertFalse(traceHelper.traceExists(selection, traceType,
- persistenceAdapter.getTraceModel(_A.eResource().getResourceSet())));
+ assertFalse(traceHelper.traceExists(selection, traceType));
createTraceForCurrentSelectionOfType(TracemodelPackage.eINSTANCE.getRelatedTo());
- assertTrue(traceHelper.traceExists(selection, traceType,
- persistenceAdapter.getTraceModel(_A.eResource().getResourceSet())));
+ traceHelper = new TraceHelper(persistenceAdapter.getTraceModel(_A.eResource().getResourceSet()));
+ assertTrue(traceHelper.traceExists(selection, traceType));
// Change the order of the selection in the selection view
SelectionView.getOpenedView().clearSelection();
@@ -210,18 +208,16 @@
// Check that the trace exists
selection = artifactHelper.createWrappers(SelectionView.getOpenedView().getSelection());
- assertTrue(traceHelper.traceExists(selection, traceType,
- persistenceAdapter.getTraceModel(_A.eResource().getResourceSet())));
+ traceHelper = new TraceHelper(persistenceAdapter.getTraceModel(_A.eResource().getResourceSet()));
+ assertTrue(traceHelper.traceExists(selection, traceType));
// Add another class to the selection view
SelectionView.getOpenedView().dropToSelection(_C);
// Check that the trace does not exist
selection = artifactHelper.createWrappers(SelectionView.getOpenedView().getSelection());
- assertFalse(traceHelper.traceExists(selection, traceType,
- persistenceAdapter.getTraceModel(_A.eResource().getResourceSet())));
-
-
+ traceHelper = new TraceHelper(persistenceAdapter.getTraceModel(_A.eResource().getResourceSet()));
+ assertFalse(traceHelper.traceExists(selection, traceType));
}
}
diff --git a/tests/org.eclipse.capra.testsuite/src/org/eclipse/capra/testsuite/TestHelper.java b/tests/org.eclipse.capra.testsuite/src/org/eclipse/capra/testsuite/TestHelper.java
index c68ca18..d875914 100644
--- a/tests/org.eclipse.capra.testsuite/src/org/eclipse/capra/testsuite/TestHelper.java
+++ b/tests/org.eclipse.capra.testsuite/src/org/eclipse/capra/testsuite/TestHelper.java
@@ -25,7 +25,7 @@
import org.eclipse.capra.core.adapters.TracePersistenceAdapter;
import org.eclipse.capra.core.helpers.ArtifactHelper;
import org.eclipse.capra.core.helpers.ExtensionPointHelper;
-import org.eclipse.capra.ui.handlers.TraceCreationHandler;
+import org.eclipse.capra.ui.operations.CreateTraceOperation;
import org.eclipse.capra.ui.plantuml.ToggleTransitivityHandler;
import org.eclipse.capra.ui.views.SelectionView;
import org.eclipse.cdt.core.CCorePlugin;
@@ -64,7 +64,7 @@
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
-import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
/**
@@ -85,8 +85,7 @@
/**
* Creates an empty project
*
- * @param projectName
- * the name of the project
+ * @param projectName the name of the project
* @throws CoreException
*/
public static IProject createSimpleProject(String projectName) throws CoreException {
@@ -101,8 +100,7 @@
/**
* Creates a Java project and a Java class declaration inside it.
*
- * @param projectName
- * the name of the project
+ * @param projectName the name of the project
* @return the created Java class
* @throws CoreException
*/
@@ -165,10 +163,8 @@
/**
* Checks if the project with the provided name exists.
*
- * @param projectName
- * the name of the project
- * @return true if the project exists in the active workspace, false
- * otherwise
+ * @param projectName the name of the project
+ * @return true if the project exists in the active workspace, false otherwise
*/
public static boolean projectExists(String projectName) {
return getProject(projectName).exists();
@@ -177,8 +173,7 @@
/**
* Returns a handle to the project resource with the given name.
*
- * @param projectName
- * the name of the project
+ * @param projectName the name of the project
* @return a handle to the project resource
*/
public static IProject getProject(String projectName) {
@@ -189,8 +184,7 @@
/**
* Creates an empty Ecore model.
*
- * @param name
- * the name of the model
+ * @param name the name of the model
* @return
*/
public static EPackage createEcoreModel(String name) {
@@ -202,10 +196,8 @@
/**
* Creates an EClass entity in the provided model.
*
- * @param p
- * an Ecore model
- * @param name
- * the name of the created EClass entity
+ * @param p an Ecore model
+ * @param name the name of the created EClass entity
*/
public static void createEClassInEPackage(EPackage p, String name) {
EClass c = EcoreFactory.eINSTANCE.createEClass();
@@ -216,10 +208,8 @@
/**
* Nests a new EPackage inside the provided EPackage.
*
- * @param p
- * a new EPackage
- * @param name
- * the name of the created EPackage
+ * @param p a new EPackage
+ * @param name the name of the created EPackage
*/
public static void createEPackageInEPackage(EPackage p, String name) {
EPackage pkg = EcoreFactory.eINSTANCE.createEPackage();
@@ -230,10 +220,8 @@
/**
* Persists (saves) the provided Ecore model in the specified project.
*
- * @param project
- * a handle to the project in which the model is to be persisted
- * @param pack
- * the Ecore model to be persisted
+ * @param project a handle to the project in which the model is to be persisted
+ * @param pack the Ecore model to be persisted
* @throws IOException
*/
public static void save(IProject project, EPackage pack) throws IOException {
@@ -247,12 +235,9 @@
/**
* Returns an Ecore model entity from the specified project.
*
- * @param project
- * the project containing the model
- * @param p
- * the name of the model
- * @param rs
- * the provided ResourceSet instance
+ * @param project the project containing the model
+ * @param p the name of the model
+ * @param rs the provided ResourceSet instance
* @return an Ecore model entity
* @throws IOException
*/
@@ -264,13 +249,13 @@
/**
* Creates a trace between the objects that are in the Selection view.
*
- * @param traceType
- * the type of the trace that is to connect the objects
+ * @param traceType the type of the trace that is to connect the objects
*/
public static void createTraceForCurrentSelectionOfType(EClass traceType) {
- IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
- TraceCreationHandler handler = new TraceCreationHandler();
- handler.createTrace(window, (traceTypes, selection) -> {
+ Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+ CreateTraceOperation operation = new CreateTraceOperation("Create trace link",
+ SelectionView.getOpenedView().getSelection());
+ operation.createTrace(shell, (traceTypes, selection) -> {
if (traceTypes.contains(traceType)) {
return Optional.of(traceType);
} else {
@@ -282,10 +267,8 @@
/**
* Checks if there is a trace between the provided Objects.
*
- * @param a
- * first EObject
- * @param b
- * second EObject
+ * @param a first EObject
+ * @param b second EObject
* @return true if a trace exists between the two objects, false otherwise
*/
public static boolean thereIsATraceBetween(Object firstObject, Object secondObject) {
@@ -331,8 +314,7 @@
/**
* Creates an empty C or C++ project.
*
- * @param projectName
- * the name of the project to be created
+ * @param projectName the name of the project to be created
* @return a handle to the created project
* @throws CoreException
* @throws BuildException
@@ -360,10 +342,8 @@
/**
* Creates a C source file in the provided C project.
*
- * @param fileName
- * the name of the C source file to be created in the project
- * @param cProject
- * the project in which the file is to be created
+ * @param fileName the name of the C source file to be created in the project
+ * @param cProject the project in which the file is to be created
* @return the created TranslationUnit
* @throws CoreException
*/
@@ -386,10 +366,8 @@
/**
* Creates an empty file in the project with the provided name.
*
- * @param fileName
- * the name of the created file
- * @param projectName
- * the name of the project in which the file is to be created
+ * @param fileName the name of the created file
+ * @param projectName the name of the project in which the file is to be created
* @return a handle to the created file
* @throws CoreException
*/