Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/OneShotExecutor.java79
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/TransactionHelper.java25
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/TransactionPrecommitExecutor.java23
-rw-r--r--plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/command/AsynchronousCommand.java59
-rw-r--r--plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/referencialgrilling/ComputeOwnerHelper.java73
-rw-r--r--plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/referencialgrilling/GridManagementEditPolicy.java39
-rw-r--r--plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/referencialgrilling/ResizeOperandEditPolicy.java1
-rw-r--r--plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/ExecutionSpecificationUtil.java28
-rw-r--r--plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/RetryingDeferredAction.java87
-rw-r--r--plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/validation/AsyncValidateCommand.java11
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/PapyrusEditorFixture.java107
-rw-r--r--tests/junit/plugins/infra/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/utils/OneShotExecutorTest.java90
-rw-r--r--tests/junit/plugins/infra/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/utils/TransactionHelperTest.java94
-rw-r--r--tests/junit/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence.tests/resource/bugs/bug533679.di2
-rw-r--r--tests/junit/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence.tests/resource/bugs/bug533679.notation129
-rw-r--r--tests/junit/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence.tests/resource/bugs/bug533679.uml29
-rw-r--r--tests/junit/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence.tests/src/org/eclipse/papyrus/uml/diagram/sequence/tests/bug/CombinedFragmentRegressionTest.java96
17 files changed, 909 insertions, 63 deletions
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/OneShotExecutor.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/OneShotExecutor.java
new file mode 100644
index 00000000000..3e69e6d6799
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/OneShotExecutor.java
@@ -0,0 +1,79 @@
+/*****************************************************************************
+ * Copyright (c) 2018 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.core.utils;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * A wrapper for an {@link Executor} that permits only a single
+ * {@link Runnable} to be pending execution at any time. If while
+ * a task is pending additional tasks are requested for execution,
+ * the last requested task will supersede the pending task so that
+ * when some task eventually is executed, it will only be one
+ * task. So, it is best to use this for asynchronous execution of
+ * runnables that all do the same thing, where the aim is just to
+ * avoid redundant execution.
+ *
+ * @since 3.0
+ */
+public final class OneShotExecutor implements Executor {
+ private final AtomicReference<Runnable> pending = new AtomicReference<>();
+ private final Executor delegate;
+
+ /**
+ * Initializes me with the {@code executor} on which to submit
+ * tasks.
+ *
+ * @param executor
+ * the executor to which I delegate execution of tasks
+ */
+ public OneShotExecutor(Executor executor) {
+ super();
+
+ this.delegate = executor;
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ delegate.execute(new Task(command));
+ }
+
+ //
+ // Nested types
+ //
+
+ /**
+ * A one-shot task that replaces any pending task and executes
+ * its delegate if and only if it is still the pending task
+ * when its time to execute arrives.
+ */
+ private final class Task implements Runnable {
+ private final Runnable delegate;
+
+ Task(Runnable command) {
+ super();
+
+ this.delegate = command;
+ pending.set(this);
+ }
+
+ @Override
+ public void run() {
+ if (pending.compareAndSet(this, null)) {
+ delegate.run();
+ }
+ }
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/TransactionHelper.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/TransactionHelper.java
index 90315f531b5..42e4a220ae8 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/TransactionHelper.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/TransactionHelper.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2014, 2016 CEA LIST, Christian W. Damus, and others.
+ * Copyright (c) 2014, 2018 CEA LIST, Christian W. Damus, and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -9,13 +9,14 @@
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
* Christian W. Damus (CEA) - bugs 429826, 408491, 433320
- * Christian W. Damus - bugs 451557, 457560, 461629, 463564, 466997, 465416, 485220, 498140
+ * Christian W. Damus - bugs 451557, 457560, 461629, 463564, 466997, 465416, 485220, 498140, 533679
*
*****************************************************************************/
package org.eclipse.papyrus.infra.core.utils;
import java.util.Collections;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
@@ -474,6 +475,26 @@ public class TransactionHelper {
}
/**
+ * Disposes of a {@linkplain #createTransactionExecutor(TransactionalEditingDomain, Executor) transaction executor}
+ * that is no longer needed on an editing domain that is still in use.
+ *
+ * @param executor
+ * a transaction executor to dispose
+ *
+ * @throws IllegalArgumentException
+ * if the {@code executor} is not a transaction executor
+ * @throws NullPointerException
+ * if the {@code executor} is {@code null}
+ * @since 3.0
+ */
+ public static void disposeTransactionExecutor(Executor executor) {
+ if (!(Objects.requireNonNull(executor) instanceof TransactionPrecommitExecutor)) {
+ throw new IllegalArgumentException("executor"); //$NON-NLS-1$
+ }
+ ((TransactionPrecommitExecutor) executor).dispose();
+ }
+
+ /**
* <p>
* Create a privileged progress runnable, which is like a regular {@linkplain TransactionalEditingDomain#createPrivilegedRunnable(Runnable)
* privileged runnable} except that it is given a progress monitor for progress reporting.
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/TransactionPrecommitExecutor.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/TransactionPrecommitExecutor.java
index 75df6652d6c..bbb1592ed88 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/TransactionPrecommitExecutor.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/TransactionPrecommitExecutor.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2015 Christian W. Damus and others.
+ * Copyright (c) 2015, 2018 Christian W. Damus and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -39,6 +39,7 @@ import com.google.common.collect.Maps;
* write transaction is active.
*/
class TransactionPrecommitExecutor implements Executor, TransactionalEditingDomainListener {
+ private final TransactionalEditingDomain editingDomain;
private final Executor fallback;
private final AtomicBoolean writeActive = new AtomicBoolean();
@@ -46,9 +47,12 @@ class TransactionPrecommitExecutor implements Executor, TransactionalEditingDoma
private final IExecutorPolicy policy;
private final Map<?, ?> options;
+ private final AtomicBoolean disposed = new AtomicBoolean();
+
TransactionPrecommitExecutor(TransactionalEditingDomain domain, Executor fallback, IExecutorPolicy policy, Map<?, ?> options) {
super();
+ this.editingDomain = domain;
this.fallback = fallback;
this.policy = (policy == null) ? IExecutorPolicy.NULL : policy;
this.options = ((options != null) && options.isEmpty()) ? null : options;
@@ -56,8 +60,25 @@ class TransactionPrecommitExecutor implements Executor, TransactionalEditingDoma
TransactionUtil.getAdapter(domain, TransactionalEditingDomain.Lifecycle.class).addTransactionalEditingDomainListener(this);
}
+ /**
+ * Disposes me. This entails at least desisting in listening to lifecycle changes in
+ * my editing domain.
+ */
+ public void dispose() {
+ if (disposed.compareAndSet(false, true)) {
+ TransactionalEditingDomain.Lifecycle lifecycle = TransactionUtil.getAdapter(editingDomain, TransactionalEditingDomain.Lifecycle.class);
+ if (lifecycle != null) {
+ lifecycle.removeTransactionalEditingDomainListener(this);
+ }
+ }
+ }
+
@Override
public void execute(Runnable command) {
+ if (disposed.get()) {
+ return;
+ }
+
if (writeActive.get() && selectSelf(command)) {
queue.offer(command);
} else {
diff --git a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/command/AsynchronousCommand.java b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/command/AsynchronousCommand.java
index a82671e62f1..91ba0cb0aba 100644
--- a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/command/AsynchronousCommand.java
+++ b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/command/AsynchronousCommand.java
@@ -14,6 +14,7 @@
package org.eclipse.papyrus.uml.diagram.sequence.command;
import java.util.List;
+import java.util.concurrent.Executor;
import java.util.function.Supplier;
import org.eclipse.core.commands.ExecutionException;
@@ -28,6 +29,7 @@ import org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand;
import org.eclipse.papyrus.infra.emf.gmf.util.GMFUnsafe;
import org.eclipse.papyrus.uml.diagram.sequence.part.UMLDiagramEditorPlugin;
import org.eclipse.papyrus.uml.diagram.sequence.util.RetryingDeferredAction;
+import org.eclipse.swt.widgets.Display;
/**
* A command that posts its execution asynchronously on the UI thread
@@ -39,11 +41,12 @@ import org.eclipse.papyrus.uml.diagram.sequence.util.RetryingDeferredAction;
public class AsynchronousCommand extends AbstractCommand {
private final TransactionalEditingDomain editingDomain;
+ private final Executor executor;
private Supplier<ICommand> futureCommand;
private ICommand actualCommand;
/**
- * Initializes me with my label and an asynchronous supplier of the command
+ * Initializes me with my {@code label} and an asynchronous supplier of the command
* to be executed.
*
* @param label
@@ -58,15 +61,58 @@ public class AsynchronousCommand extends AbstractCommand {
* up to three times
*/
public AsynchronousCommand(String label, TransactionalEditingDomain editingDomain, Supplier<ICommand> futureCommand) {
+ this(label, editingDomain, futureCommand, Display.getDefault()::asyncExec);
+ }
+
+ /**
+ * Initializes me with my {@code label}, affected files, and an asynchronous supplier of the command
+ * to be executed.
+ *
+ * @param label
+ * my label
+ * @param affectedFiles
+ * my affected files
+ * @param editingDomain
+ * my contextual editing domain
+ * @param futureCommand
+ * a supplier of the command. It will be invoked later on
+ * the UI thread to compute the command to be executed then and captured
+ * back into the original context of this GMF command. If the supplied
+ * command is {@code null}, then it will be asynchronously re-tried
+ * up to three times
+ */
+ public AsynchronousCommand(String label, @SuppressWarnings("rawtypes") List affectedFiles, TransactionalEditingDomain editingDomain, Supplier<ICommand> futureCommand) {
+ this(label, affectedFiles, editingDomain, futureCommand, Display.getDefault()::asyncExec);
+ }
+
+ /**
+ * Initializes me with my {@code label}, an asynchronous supplier of the command
+ * to be executed, and an {@code executor} on which to run.
+ *
+ * @param label
+ * my label
+ * @param editingDomain
+ * my contextual editing domain
+ * @param futureCommand
+ * a supplier of the command. It will be invoked later on
+ * the UI thread to compute the command to be executed then and captured
+ * back into the original context of this GMF command. If the supplied
+ * command is {@code null}, then it will be asynchronously re-tried
+ * up to three times
+ * @param executor
+ * the executor on which to run myself
+ */
+ public AsynchronousCommand(String label, TransactionalEditingDomain editingDomain, Supplier<ICommand> futureCommand, Executor executor) {
super(label);
this.editingDomain = editingDomain;
this.futureCommand = futureCommand;
+ this.executor = executor;
}
/**
- * Initializes me with my label, affected filesm, and an asynchronous supplier of the command
- * to be executed.
+ * Initializes me with my {@code label}, affected files, an asynchronous supplier of the command
+ * to be executed, and an {@code executor} on which to run.
*
* @param label
* my label
@@ -80,17 +126,20 @@ public class AsynchronousCommand extends AbstractCommand {
* back into the original context of this GMF command. If the supplied
* command is {@code null}, then it will be asynchronously re-tried
* up to three times
+ * @param executor
+ * the executor on which to run myself
*/
- public AsynchronousCommand(String label, @SuppressWarnings("rawtypes") List affectedFiles, TransactionalEditingDomain editingDomain, Supplier<ICommand> futureCommand) {
+ public AsynchronousCommand(String label, @SuppressWarnings("rawtypes") List affectedFiles, TransactionalEditingDomain editingDomain, Supplier<ICommand> futureCommand, Executor executor) {
super(label, affectedFiles);
this.editingDomain = editingDomain;
this.futureCommand = futureCommand;
+ this.executor = executor;
}
@Override
protected CommandResult doExecuteWithResult(IProgressMonitor progressMonitor, IAdaptable info) throws ExecutionException {
- RetryingDeferredAction.defer(this::captureCommand);
+ RetryingDeferredAction.defer(executor, this::captureCommand);
return CommandResult.newOKCommandResult();
}
diff --git a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/referencialgrilling/ComputeOwnerHelper.java b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/referencialgrilling/ComputeOwnerHelper.java
index 3614b761a51..e85d91dee02 100644
--- a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/referencialgrilling/ComputeOwnerHelper.java
+++ b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/referencialgrilling/ComputeOwnerHelper.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2017 CEA LIST and others.
+ * Copyright (c) 2017, 2018 CEA LIST, Christian W. Damus, and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -9,20 +9,26 @@
* Contributors:
* CEA LIST - Initial API and implementation
* Mickaƫl ADAM (ALL4TEC) mickael.adam@all4tec.net - Bug 525369
+ * Christian W. Damus - bug 533679
*****************************************************************************/
package org.eclipse.papyrus.uml.diagram.sequence.referencialgrilling;
+import static org.eclipse.papyrus.uml.diagram.sequence.util.ExecutionSpecificationUtil.getStartedExecution;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Optional;
+import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.edit.command.SetCommand;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.gmf.runtime.notation.DecorationNode;
import org.eclipse.papyrus.uml.diagram.sequence.part.UMLDiagramEditorPlugin;
import org.eclipse.papyrus.uml.diagram.sequence.util.LogOptions;
+import org.eclipse.papyrus.uml.diagram.sequence.validation.AsyncValidateCommand;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.ExecutionOccurrenceSpecification;
import org.eclipse.uml2.uml.ExecutionSpecification;
@@ -30,6 +36,7 @@ import org.eclipse.uml2.uml.Interaction;
import org.eclipse.uml2.uml.InteractionFragment;
import org.eclipse.uml2.uml.InteractionOperand;
import org.eclipse.uml2.uml.Lifeline;
+import org.eclipse.uml2.uml.OccurrenceSpecification;
import org.eclipse.uml2.uml.UMLPackage;
/**
@@ -51,7 +58,7 @@ public class ComputeOwnerHelper implements IComputeOwnerHelper {
}
}
if (column.getElement() instanceof Lifeline) {
- HorizontalLifeLinetoOperand.put((Lifeline) column.getElement(), (ArrayList<InteractionOperand>) interactionOperandStack.clone());
+ HorizontalLifeLinetoOperand.put((Lifeline) column.getElement(), new ArrayList<>(interactionOperandStack));
}
@@ -70,7 +77,7 @@ public class ComputeOwnerHelper implements IComputeOwnerHelper {
interactionOperandStack.add((InteractionOperand) row.getElement());
}
} else if (row.getElement() instanceof Element) {
- verticalElementToOperand.put((Element) row.getElement(), (ArrayList<InteractionOperand>) interactionOperandStack.clone());
+ verticalElementToOperand.put((Element) row.getElement(), new ArrayList<>(interactionOperandStack));
}
}
@@ -92,8 +99,8 @@ public class ComputeOwnerHelper implements IComputeOwnerHelper {
ArrayList<InteractionFragment> elementForInteraction = new ArrayList<>();
// list of element for the interactionOperand
- HashMap<InteractionOperand, ArrayList<InteractionFragment>> elementForIneractionOp = new HashMap<>();
- Iterator elementInteraction = interaction.eAllContents();
+ HashMap<InteractionOperand, ArrayList<InteractionFragment>> elementForInteractionOp = new HashMap<>();
+ Iterator<EObject> elementInteraction = interaction.eAllContents();
while (elementInteraction.hasNext()) {
Element element = (Element) elementInteraction.next();
if (element instanceof InteractionFragment) {
@@ -107,19 +114,21 @@ public class ComputeOwnerHelper implements IComputeOwnerHelper {
if (potentialoperand.size() >= 1) {
simplifyOwnerInteractionOperand(potentialoperand);
if (potentialoperand.size() == 1) {
- if (elementForIneractionOp.get(potentialoperand.get(0)) == null) {
- elementForIneractionOp.put(potentialoperand.get(0), new ArrayList<InteractionFragment>());
+ if (elementForInteractionOp.get(potentialoperand.get(0)) == null) {
+ elementForInteractionOp.put(potentialoperand.get(0), new ArrayList<InteractionFragment>());
}
- elementForIneractionOp.get(potentialoperand.get(0)).add(aFragment);
- if (aFragment instanceof ExecutionOccurrenceSpecification) {
- elementForIneractionOp.get(potentialoperand.get(0)).add(((ExecutionOccurrenceSpecification) aFragment).getExecution());
+ elementForInteractionOp.get(potentialoperand.get(0)).add(aFragment);
+ if (aFragment instanceof OccurrenceSpecification) {
+ Optional<ExecutionSpecification> exec = getStartedExecution((OccurrenceSpecification) aFragment);
+ exec.ifPresent(elementForInteractionOp.get(potentialoperand.get(0))::add);
}
}
} else {
if (!(aFragment instanceof InteractionOperand)) {
elementForInteraction.add(aFragment);
if (aFragment instanceof ExecutionOccurrenceSpecification) {
- elementForInteraction.add(((ExecutionOccurrenceSpecification) aFragment).getExecution());
+ Optional<ExecutionSpecification> exec = getStartedExecution((OccurrenceSpecification) aFragment);
+ exec.ifPresent(elementForInteraction::add);
}
}
}
@@ -133,10 +142,10 @@ public class ComputeOwnerHelper implements IComputeOwnerHelper {
}
// update fragments of interaction operrands
- Iterator<InteractionOperand> iterator = elementForIneractionOp.keySet().iterator();
+ Iterator<InteractionOperand> iterator = elementForInteractionOp.keySet().iterator();
while (iterator.hasNext()) {
InteractionOperand interactionOperand = iterator.next();
- ArrayList<InteractionFragment> elements = elementForIneractionOp.get(interactionOperand);
+ ArrayList<InteractionFragment> elements = elementForInteractionOp.get(interactionOperand);
if (elements.size() != 0) {
// sort list bu taking
ArrayList<InteractionFragment> existedFragments = new ArrayList<>();
@@ -144,6 +153,9 @@ public class ComputeOwnerHelper implements IComputeOwnerHelper {
existedFragments.addAll(sorted);
existedFragments.addAll(interactionOperand.getFragments());
grid.execute(new SetCommand(domain, interactionOperand, UMLPackage.eINSTANCE.getInteractionOperand_Fragment(), existedFragments));
+
+ AsyncValidateCommand.get(interactionOperand)
+ .ifPresent(grid::execute);
}
}
@@ -188,7 +200,18 @@ public class ComputeOwnerHelper implements IComputeOwnerHelper {
DecorationNode row = iteratorRow.next();
if (fragments.contains(row.getElement())) {
if (!sortedList.contains(row.getElement())) {
- sortedList.add((InteractionFragment) row.getElement());
+ InteractionFragment fragment = (InteractionFragment) row.getElement();
+ sortedList.add(fragment);
+
+ if (fragment instanceof OccurrenceSpecification) {
+ // These (often) aren't in the rows
+ Optional<ExecutionSpecification> execSpec = getStartedExecution((OccurrenceSpecification) fragment);
+ execSpec.ifPresent(exec -> {
+ if (!sortedList.contains(exec)) {
+ sortedList.add(exec);
+ }
+ });
+ }
}
}
@@ -203,17 +226,17 @@ public class ComputeOwnerHelper implements IComputeOwnerHelper {
* @param operandList
*/
protected static void simplifyOwnerInteractionOperand(ArrayList<InteractionOperand> operandList) {
-/*
- while (operandList.size() > 1) {
-
- InteractionOperand last = operandList.get(operandList.size() - 1);
- EObject parent = last.eContainer();
- while (parent != null) {
- operandList.remove(parent);
- parent = parent.eContainer();
- }
- }
-*/
+ /*
+ * while (operandList.size() > 1) {
+ *
+ * InteractionOperand last = operandList.get(operandList.size() - 1);
+ * EObject parent = last.eContainer();
+ * while (parent != null) {
+ * operandList.remove(parent);
+ * parent = parent.eContainer();
+ * }
+ * }
+ */
}
/**
diff --git a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/referencialgrilling/GridManagementEditPolicy.java b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/referencialgrilling/GridManagementEditPolicy.java
index 557f6a429a1..fffaf63c898 100644
--- a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/referencialgrilling/GridManagementEditPolicy.java
+++ b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/referencialgrilling/GridManagementEditPolicy.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2016, 2017 CEA LIST, ALL4TEC and others.
+ * Copyright (c) 2016, 2018 CEA LIST, ALL4TEC, Christian W. Damus, and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -10,6 +10,7 @@
* CEA LIST - Initial API and implementation
* Mickaƫl ADAM (ALL4TEC) mickael.adam@all4tec.net - Bug 519756
* Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Bug 531936
+ * Christian W. Damus - bug 533679
*****************************************************************************/
package org.eclipse.papyrus.uml.diagram.sequence.referencialgrilling;
@@ -21,6 +22,7 @@ import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.Executor;
import org.eclipse.core.commands.operations.OperationHistoryFactory;
import org.eclipse.draw2d.geometry.Point;
@@ -42,6 +44,8 @@ import org.eclipse.gmf.runtime.notation.Location;
import org.eclipse.gmf.runtime.notation.Node;
import org.eclipse.gmf.runtime.notation.NotationPackage;
import org.eclipse.gmf.runtime.notation.View;
+import org.eclipse.papyrus.infra.core.utils.OneShotExecutor;
+import org.eclipse.papyrus.infra.core.utils.TransactionHelper;
import org.eclipse.papyrus.infra.gmfdiag.common.editpolicies.AutomaticNotationEditPolicy;
import org.eclipse.papyrus.infra.gmfdiag.common.utils.DiagramEditPartsUtil;
import org.eclipse.papyrus.uml.diagram.sequence.command.CreateCoordinateCommand;
@@ -49,6 +53,7 @@ import org.eclipse.papyrus.uml.diagram.sequence.command.CreateGrillingStructureC
import org.eclipse.papyrus.uml.diagram.sequence.part.UMLDiagramEditorPlugin;
import org.eclipse.papyrus.uml.diagram.sequence.util.LogOptions;
import org.eclipse.papyrus.uml.diagram.sequence.util.RedirectionOperationListener;
+import org.eclipse.swt.widgets.Display;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.ExecutionOccurrenceSpecification;
import org.eclipse.uml2.uml.Interaction;
@@ -71,6 +76,9 @@ public class GridManagementEditPolicy extends GraphicalEditPolicyEx implements A
public static int threshold = 5;
+ private Executor transactionExecutor;
+ private Executor coveredUpdateExecutor;
+
/**
* @return the threshold
*/
@@ -183,6 +191,12 @@ public class GridManagementEditPolicy extends GraphicalEditPolicyEx implements A
@Override
public void activate() {
super.activate();
+
+ transactionExecutor = TransactionHelper.createTransactionExecutor(
+ ((IGraphicalEditPart) getHost()).getEditingDomain(),
+ Display.getCurrent()::asyncExec);
+ coveredUpdateExecutor = new OneShotExecutor(transactionExecutor);
+
getDiagramEventBroker().addNotificationListener(((EObject) getHost().getModel()), this);
// contentDiagramListener = new ContentDiagramListener(this);
@@ -224,8 +238,7 @@ public class GridManagementEditPolicy extends GraphicalEditPolicyEx implements A
i++;
}
// cleanUnusedRowAndColumn();
- updateRowsAndColumns();
- updateCoveredAndOwnerAfterUpdate();
+ postRowColumnCoverageUpdate();
}
/**
@@ -353,6 +366,10 @@ public class GridManagementEditPolicy extends GraphicalEditPolicyEx implements A
@Override
public void deactivate() {
getDiagramEventBroker().removeNotificationListener(((EObject) getHost().getModel()), this);
+ TransactionHelper.disposeTransactionExecutor(transactionExecutor);
+ transactionExecutor = null;
+ coveredUpdateExecutor = null;
+
if (null != this.operationHistoryListener) {
OperationHistoryFactory.getOperationHistory().removeOperationHistoryListener(this.operationHistoryListener);
}
@@ -366,11 +383,21 @@ public class GridManagementEditPolicy extends GraphicalEditPolicyEx implements A
*/
@Override
public void notifyChanged(Notification notification) {
- updateRowsAndColumns();
- updateCoveredAndOwnerAfterUpdate();
+ postRowColumnCoverageUpdate();
}
+ private void postRowColumnCoverageUpdate() {
+ Runnable update = () -> {
+ updateRowsAndColumns();
+ updateCoveredAndOwnerAfterUpdate();
+ };
+ if (coveredUpdateExecutor == null) {
+ update.run();
+ } else {
+ coveredUpdateExecutor.execute(update);
+ }
+ }
/**
* get the decoration node that represents a column from a position (absolute)
@@ -517,4 +544,4 @@ public class GridManagementEditPolicy extends GraphicalEditPolicyEx implements A
}
}
-} \ No newline at end of file
+}
diff --git a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/referencialgrilling/ResizeOperandEditPolicy.java b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/referencialgrilling/ResizeOperandEditPolicy.java
index b5a925b1ae0..8a87ae6ea46 100644
--- a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/referencialgrilling/ResizeOperandEditPolicy.java
+++ b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/referencialgrilling/ResizeOperandEditPolicy.java
@@ -31,7 +31,6 @@ import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gmf.runtime.common.core.command.CompositeCommand;
import org.eclipse.gmf.runtime.common.core.command.ICommand;
import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy;
-import org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.gmf.runtime.emf.core.util.EObjectAdapter;
import org.eclipse.gmf.runtime.notation.View;
diff --git a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/ExecutionSpecificationUtil.java b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/ExecutionSpecificationUtil.java
index 3830a3e4b67..7d9fb7130b2 100644
--- a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/ExecutionSpecificationUtil.java
+++ b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/ExecutionSpecificationUtil.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2017 CEA LIST and others.
+ * Copyright (c) 2017, 2018 CEA LIST, Christian W. Damus, and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,7 @@
*
* Contributors:
* Nicolas FAUVERGUE (CEA LIST) nicolas.fauvergue@cea.fr - Initial API and implementation
+ * Christian W. Damus - bug 533679
*
*****************************************************************************/
@@ -19,8 +20,11 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.function.Predicate;
import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy;
@@ -31,6 +35,10 @@ import org.eclipse.papyrus.uml.diagram.sequence.command.SetResizeAndLocationComm
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.AbstractExecutionSpecificationEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.LifelineEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.referencialgrilling.BoundForEditPart;
+import org.eclipse.uml2.common.util.CacheAdapter;
+import org.eclipse.uml2.uml.ExecutionSpecification;
+import org.eclipse.uml2.uml.OccurrenceSpecification;
+import org.eclipse.uml2.uml.UMLPackage;
/**
* This call allows to define needed methods for the exeuction specification objects.
@@ -301,4 +309,22 @@ public class ExecutionSpecificationUtil {
return result;
}
+
+ /**
+ * Query the execution, if any, that is started by an {@code occurrence}.
+ *
+ * @param occurrence
+ * an occurrence specification
+ * @return the execution specification that it starts
+ * @since 5.0
+ */
+ public static Optional<ExecutionSpecification> getStartedExecution(OccurrenceSpecification occurrence) {
+ CacheAdapter cache = CacheAdapter.getCacheAdapter(occurrence);
+ Predicate<EStructuralFeature.Setting> settingFilter = setting -> setting.getEStructuralFeature() == UMLPackage.Literals.EXECUTION_SPECIFICATION__START;
+
+ return cache.getInverseReferences(occurrence).stream()
+ .filter(settingFilter).findFirst()
+ .map(EStructuralFeature.Setting::getEObject)
+ .map(ExecutionSpecification.class::cast);
+ }
}
diff --git a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/RetryingDeferredAction.java b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/RetryingDeferredAction.java
index 6c38f53e55c..d2c5298a622 100644
--- a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/RetryingDeferredAction.java
+++ b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/RetryingDeferredAction.java
@@ -13,6 +13,7 @@
package org.eclipse.papyrus.uml.diagram.sequence.util;
+import java.util.concurrent.Executor;
import java.util.function.BooleanSupplier;
import org.eclipse.papyrus.uml.diagram.sequence.part.UMLDiagramEditorPlugin;
@@ -27,34 +28,61 @@ import org.eclipse.swt.widgets.Display;
public abstract class RetryingDeferredAction {
private static final int DEFAULT_RETRY_LIMIT = 3;
- private final Display display;
+ private final Executor executor;
private final int retryLimit;
private volatile int retries;
/**
- * Initializes me.
+ * Initializes me with the {@code executor} on which to run myself.
*
- * @param display
- * the display on which I post myself for delayed execution
+ * @param executor
+ * the executor on which to run myself
* @param retryLimit
* the number of times I may retry
*
* @throws IllegalArgumentException
* if the retry limit is non-positive
*/
- public RetryingDeferredAction(Display display, int retryLimit) {
+ public RetryingDeferredAction(Executor executor, int retryLimit) {
super();
if (retryLimit <= 0) {
throw new IllegalArgumentException("retry limit must be positive"); //$NON-NLS-1$
}
- this.display = display;
+ this.executor = executor;
this.retryLimit = retryLimit;
}
/**
- * Initializes me with the default number (three) of retries.
+ * Initializes me with the default number (three) of retries and
+ * an {@code executor} on which to run myself.
+ *
+ * @param executor
+ * the executor on which to run myself
+ */
+ public RetryingDeferredAction(Executor executor) {
+ this(executor, DEFAULT_RETRY_LIMIT);
+ }
+
+ /**
+ * Initializes me with a {@code display} on which to executor myself.
+ *
+ * @param display
+ * the display on which I post myself for delayed execution
+ * @param retryLimit
+ * the number of times I may retry
+ *
+ * @throws IllegalArgumentException
+ * if the retry limit is non-positive
+ */
+ public RetryingDeferredAction(Display display, int retryLimit) {
+ this(display::asyncExec, retryLimit);
+ }
+
+ /**
+ * Initializes me with the default number (three) of retries and
+ * a {@code display} on which to executor myself.
*
* @param display
* the display on which I post myself for delayed execution
@@ -64,7 +92,7 @@ public abstract class RetryingDeferredAction {
}
/**
- * Initializes me with the current display.
+ * Initializes me to execute myself asynchronously on the current display.
*
* @param retryLimit
* the number of times I may retry
@@ -77,7 +105,8 @@ public abstract class RetryingDeferredAction {
}
/**
- * Initializes me with the current display and the default number (three) of retries.
+ * Initializes me to execute myself asynchronously on the current display and
+ * with the default number (three) of retries.
*/
public RetryingDeferredAction() {
this(Display.getCurrent(), DEFAULT_RETRY_LIMIT);
@@ -100,7 +129,7 @@ public abstract class RetryingDeferredAction {
* if the retry limit is non-positive
*/
public static void defer(Display display, int retryLimit, BooleanSupplier action) {
- new Wrapper(display, retryLimit, action).post();
+ defer(display::asyncExec, retryLimit, action);
}
/**
@@ -116,6 +145,38 @@ public abstract class RetryingDeferredAction {
}
/**
+ * Try an {@code action} up to the given number of times, asynchronously on an {@code executor}.
+ * This is useful for the simple case where it is only necessary to attempt to perform the
+ * action and there is no need for an explicit preparation step.
+ *
+ * @param executor
+ * the executor on which to run myself
+ * @param retryLimit
+ * the maximal number of times to tr-try the {@code action}
+ * @param action
+ * the action to perform. If it returns {@code false}, then it will
+ * be re-tried (unless the limit is exceeded, of course)
+ *
+ * @throws IllegalArgumentException
+ * if the retry limit is non-positive
+ */
+ public static void defer(Executor executor, int retryLimit, BooleanSupplier action) {
+ new Wrapper(executor, retryLimit, action).post();
+ }
+
+ /**
+ * Try an {@code action} up to the default number (three) of times, asynchronously on an {@code executor}.
+ *
+ * @param executor
+ * the executor on which to run myself
+ * @param action
+ * the action to perform
+ */
+ public static void defer(Executor executor, BooleanSupplier action) {
+ defer(executor, DEFAULT_RETRY_LIMIT, action);
+ }
+
+ /**
* Try an {@code action} up to the given number of times, deferred on the current display thread.
*
* @param retryLimit
@@ -168,7 +229,7 @@ public abstract class RetryingDeferredAction {
*/
public void post() {
if (retries < retryLimit) {
- display.asyncExec(this::run);
+ executor.execute(this::run);
} else {
UMLDiagramEditorPlugin.log.warn("Retry limit exceeded for " + this); //$NON-NLS-1$
}
@@ -181,8 +242,8 @@ public abstract class RetryingDeferredAction {
private static final class Wrapper extends RetryingDeferredAction {
private final BooleanSupplier action;
- Wrapper(Display display, int retryLimit, BooleanSupplier action) {
- super(display, retryLimit);
+ Wrapper(Executor executor, int retryLimit, BooleanSupplier action) {
+ super(executor, retryLimit);
this.action = action;
}
diff --git a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/validation/AsyncValidateCommand.java b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/validation/AsyncValidateCommand.java
index 4f43cc45447..e85ba137ed2 100644
--- a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/validation/AsyncValidateCommand.java
+++ b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/validation/AsyncValidateCommand.java
@@ -16,6 +16,7 @@ package org.eclipse.papyrus.uml.diagram.sequence.validation;
import static org.eclipse.papyrus.uml.diagram.sequence.util.OccurrenceSpecificationHelper.findExecutionWith;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
@@ -29,11 +30,13 @@ import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.transaction.util.TransactionUtil;
import org.eclipse.gmf.runtime.common.core.command.ICommand;
import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.papyrus.infra.core.utils.OneShotExecutor;
import org.eclipse.papyrus.infra.emf.gmf.command.INonDirtying;
import org.eclipse.papyrus.infra.services.validation.ValidationTool;
import org.eclipse.papyrus.infra.services.validation.commands.ValidateSubtreeCommand;
import org.eclipse.papyrus.uml.diagram.sequence.command.AsynchronousCommand;
import org.eclipse.papyrus.uml.diagram.sequence.part.UMLDiagramEditorPlugin;
+import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.uml2.uml.ExecutionSpecification;
import org.eclipse.uml2.uml.InteractionOperand;
@@ -43,10 +46,13 @@ import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.OccurrenceSpecification;
import org.eclipse.uml2.uml.util.UMLSwitch;
+import com.google.common.collect.MapMaker;
+
/**
* An asynchronous validation command.
*/
public class AsyncValidateCommand extends AsynchronousCommand implements INonDirtying {
+ private static Map<EObject, OneShotExecutor> executors = new MapMaker().weakKeys().makeMap();
/**
* Initializes me with the {@code object to validate}.
@@ -55,7 +61,10 @@ public class AsyncValidateCommand extends AsynchronousCommand implements INonDir
* the object to validate later
*/
public AsyncValidateCommand(EObject object) {
- super("Validate", TransactionUtil.getEditingDomain(object), () -> validate(object));
+ super("Validate", TransactionUtil.getEditingDomain(object), () -> validate(object),
+ // Ensure that only one async validation of this object can be pending at any time
+ // and that subsequent requests just supersede any previous pending requests
+ executors.computeIfAbsent(object, __ -> new OneShotExecutor(Display.getDefault()::asyncExec)));
}
private static ICommand validate(EObject object) {
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/PapyrusEditorFixture.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/PapyrusEditorFixture.java
index 818ac24103b..21c36251496 100644
--- a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/PapyrusEditorFixture.java
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/PapyrusEditorFixture.java
@@ -8,7 +8,7 @@
*
* Contributors:
* Christian W. Damus (CEA) - Initial API and implementation
- * Christian W. Damus - bugs 433206, 465416, 434983, 483721, 469188, 485220, 491542, 497865, 533673, 533682, 533676
+ * Christian W. Damus - bugs 433206, 465416, 434983, 483721, 469188, 485220, 491542, 497865, 533673, 533682, 533676, 533679
* Thanh Liem PHAN (ALL4TEC) thanhliem.phan@all4tec.net - Bug 521550
*****************************************************************************/
package org.eclipse.papyrus.junit.utils.rules;
@@ -20,10 +20,12 @@ import static org.junit.Assert.fail;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.core.commands.operations.IOperationHistory;
@@ -61,7 +63,9 @@ import org.eclipse.emf.edit.provider.IItemLabelProvider;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.emf.workspace.IWorkspaceCommandStack;
import org.eclipse.gef.EditPart;
+import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.GraphicalEditPart;
+import org.eclipse.gef.Request;
import org.eclipse.gef.RequestConstants;
import org.eclipse.gef.RootEditPart;
import org.eclipse.gef.requests.ChangeBoundsRequest;
@@ -75,6 +79,7 @@ import org.eclipse.gmf.runtime.diagram.ui.parts.DiagramEditor;
import org.eclipse.gmf.runtime.diagram.ui.parts.DiagramEditorWithFlyOutPalette;
import org.eclipse.gmf.runtime.diagram.ui.parts.IDiagramWorkbenchPart;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest;
+import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest.ViewDescriptor;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequestFactory;
import org.eclipse.gmf.runtime.diagram.ui.requests.EditCommandRequestWrapper;
import org.eclipse.gmf.runtime.emf.type.core.IElementType;
@@ -93,6 +98,7 @@ import org.eclipse.papyrus.infra.core.services.ServiceException;
import org.eclipse.papyrus.infra.core.services.ServicesRegistry;
import org.eclipse.papyrus.infra.core.utils.ServiceUtils;
import org.eclipse.papyrus.infra.gmfdiag.common.model.NotationModel;
+import org.eclipse.papyrus.infra.gmfdiag.common.service.palette.AspectUnspecifiedTypeCreationTool;
import org.eclipse.papyrus.infra.gmfdiag.common.utils.DiagramEditPartsUtil;
import org.eclipse.papyrus.infra.nattable.common.editor.AbstractEMFNattableEditor;
import org.eclipse.papyrus.infra.nattable.common.modelresource.PapyrusNattableModel;
@@ -109,7 +115,10 @@ import org.eclipse.papyrus.uml.tools.model.UmlModel;
import org.eclipse.papyrus.views.modelexplorer.ModelExplorerPage;
import org.eclipse.papyrus.views.modelexplorer.ModelExplorerPageBookView;
import org.eclipse.papyrus.views.modelexplorer.ModelExplorerView;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IPartListener;
@@ -1498,7 +1507,8 @@ public class PapyrusEditorFixture extends AbstractModelFixture<TransactionalEdit
* @param location
* the location (mouse pointer) at which to create the shape
* @param size
- * the size of the shape to create
+ * the size of the shape to create, or {@code null} for the default size as
+ * would be created when just clicking in the diagram
* @return the newly created shape edit-part
*
* @since 2.2
@@ -1514,13 +1524,98 @@ public class PapyrusEditorFixture extends AbstractModelFixture<TransactionalEdit
org.eclipse.gef.commands.Command command = target.getCommand(request);
execute(command);
- // Find the new edit-part
- return request.getViewDescriptors().stream()
+ return getNewEditPart(parent, request.getViewDescriptors());
+ }
+
+ private EditPart getNewEditPart(EditPart context, Collection<? extends ViewDescriptor> viewDescriptors) {
+ return viewDescriptors.stream()
.map(desc -> desc.getAdapter(View.class)).map(View.class::cast)
.filter(Objects::nonNull)
- .map(view -> DiagramEditPartsUtil.getEditPartFromView(view, parent))
+ .map(view -> DiagramEditPartsUtil.getEditPartFromView(view, context))
.filter(Objects::nonNull)
- .findAny().orElseGet(failOnAbsence("Could not find new shape edit-part"));
+ .findAny().orElseGet(failOnAbsence("Could not find newly created edit-part"));
+ }
+
+ /**
+ * Create a new shape in the current diagram by automating the creation tool.
+ * Fails if the shape cannot be created or cannot be found in the diagram after creation.
+ *
+ * @param type
+ * the type of shape to create
+ * @param location
+ * the location (mouse pointer) at which to create the shape
+ * @param size
+ * the size of the shape to create, or {@code null} for the default size as
+ * would be created when just clicking in the diagram
+ * @return the newly created shape edit-part
+ *
+ * @since 2.2
+ */
+ public EditPart createShape(IElementType type, Point location, Dimension size) {
+ class MyTool extends AspectUnspecifiedTypeCreationTool {
+ private Collection<? extends ViewDescriptor> results = Collections.emptyList();
+
+ MyTool() {
+ super(Collections.singletonList(type));
+ }
+
+ @Override
+ protected Request getTargetRequest() {
+ return super.getTargetRequest();
+ }
+
+ @Override
+ protected void selectAddedObject(EditPartViewer viewer, @SuppressWarnings("rawtypes") Collection objects) {
+ super.selectAddedObject(viewer, objects);
+
+ results = ((Collection<?>) objects).stream()
+ .filter(ViewDescriptor.class::isInstance).map(ViewDescriptor.class::cast)
+ .collect(Collectors.toList());
+ }
+
+ Collection<? extends ViewDescriptor> getResults() {
+ return results;
+ }
+ }
+
+ EditPartViewer viewer = getActiveDiagram().getViewer();
+ MyTool tool = new MyTool();
+
+ Event mouse = new Event();
+ mouse.display = editor.getSite().getShell().getDisplay();
+ mouse.widget = viewer.getControl();
+ mouse.button = 1;
+ mouse.x = location.x();
+ mouse.y = location.y();
+
+ viewer.getEditDomain().setActiveTool(tool);
+ tool.setViewer(viewer);
+ mouse.type = SWT.MouseDown;
+ tool.mouseDown(new MouseEvent(mouse), viewer);
+
+ flushDisplayEvents();
+
+ if (size == null) {
+ // Just a click
+ mouse.type = SWT.MouseUp;
+ tool.mouseUp(new MouseEvent(mouse), viewer);
+ } else {
+ // Drag and release
+ mouse.type = SWT.MouseMove;
+ mouse.x = location.x() + size.width();
+ mouse.y = location.y() + size.height();
+ tool.mouseDrag(new MouseEvent(mouse), viewer);
+
+ flushDisplayEvents();
+
+ mouse.type = SWT.MouseUp;
+ tool.mouseUp(new MouseEvent(mouse), viewer);
+ }
+
+ flushDisplayEvents();
+
+ // Find the new edit-part
+ return getNewEditPart(getActiveDiagram(), tool.getResults());
}
/**
diff --git a/tests/junit/plugins/infra/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/utils/OneShotExecutorTest.java b/tests/junit/plugins/infra/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/utils/OneShotExecutorTest.java
new file mode 100644
index 00000000000..3b7c295451e
--- /dev/null
+++ b/tests/junit/plugins/infra/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/utils/OneShotExecutorTest.java
@@ -0,0 +1,90 @@
+/*****************************************************************************
+ * Copyright (c) 2018 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.core.utils;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.fail;
+
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.Test;
+
+/**
+ * Test cases for the {@link OneShotExecutor} class.
+ */
+public class OneShotExecutorTest {
+
+ private final TestExecutor executor = new TestExecutor();
+ private final Executor fixture = new OneShotExecutor(executor);
+
+ /**
+ * Initializes me.
+ */
+ public OneShotExecutorTest() {
+ super();
+ }
+
+ @Test
+ public void executionsPiledUp() {
+ final AtomicInteger count = new AtomicInteger();
+
+ for (int i = 0; i < 5; i++) {
+ fixture.execute(count::incrementAndGet);
+ }
+
+ executor.drain();
+
+ assertThat("Not a one-shot execution", count.get(), is(1));
+ }
+
+ @Test
+ public void executionsInSequence() {
+ final AtomicInteger count = new AtomicInteger();
+
+ for (int i = 0; i < 5; i++) {
+ fixture.execute(count::incrementAndGet);
+ executor.drain();
+ }
+
+ assertThat("Wrong number of executions", count.get(), is(5));
+ }
+
+ //
+ // Test framework
+ //
+
+ private static final class TestExecutor implements Executor {
+ Queue<Runnable> queue = new LinkedList<>();
+
+ @Override
+ public void execute(Runnable command) {
+ queue.add(command);
+ }
+
+ void drain() {
+ for (Runnable next = queue.poll(); next != null; next = queue.poll()) {
+ try {
+ next.run();
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail("Uncaught exception in test runnable: " + e.getMessage());
+ }
+ }
+ }
+ }
+}
diff --git a/tests/junit/plugins/infra/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/utils/TransactionHelperTest.java b/tests/junit/plugins/infra/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/utils/TransactionHelperTest.java
index 6499866a806..6cd7d07a866 100644
--- a/tests/junit/plugins/infra/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/utils/TransactionHelperTest.java
+++ b/tests/junit/plugins/infra/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/utils/TransactionHelperTest.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2016 Christian W. Damus and others.
+ * Copyright (c) 2016, 2018 Christian W. Damus and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -14,20 +14,31 @@
package org.eclipse.papyrus.infra.core.utils;
import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.fail;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
+import org.eclipse.emf.common.EMFPlugin;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EcoreFactory;
import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.xmi.impl.EcoreResourceFactoryImpl;
import org.eclipse.emf.transaction.RecordingCommand;
+import org.eclipse.emf.transaction.Transaction;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
+import org.eclipse.emf.transaction.impl.InternalTransactionalEditingDomain;
+import org.eclipse.emf.transaction.impl.TransactionImpl;
import org.eclipse.papyrus.infra.tools.util.IProgressCallable;
import org.eclipse.papyrus.infra.tools.util.IProgressRunnable;
import org.eclipse.papyrus.junit.utils.PrintingProgressMonitor;
@@ -133,6 +144,77 @@ public class TransactionHelperTest {
assertThat(eclass.getName(), is("Foo"));
}
+ @Test
+ public void testSimpleTransactionExecutor_readWrite() throws InterruptedException, ExecutionException {
+ Executor transactionExecutor = TransactionHelper.createTransactionExecutor(domain, exec);
+ AtomicReference<Transaction> t = new AtomicReference<>();
+
+ domain.getCommandStack().execute(new RecordingCommand(domain) {
+
+ @Override
+ protected void doExecute() {
+ transactionExecutor.execute(() -> {
+ InternalTransactionalEditingDomain iDomain = (InternalTransactionalEditingDomain) domain;
+ t.set(iDomain.getActiveTransaction());
+ });
+ }
+ });
+
+ // In case the runnable is posted to the thread
+ exec.submit(this::pass).get();
+
+ assertThat("Not executed in the transaction", t.get(), notNullValue());
+ assertThat("Not a read/write transaction", t.get().isReadOnly(), is(false));
+ assertThat("Not a trigger transaction",
+ t.get().getOptions().get(TransactionImpl.OPTION_IS_TRIGGER_TRANSACTION),
+ is(Boolean.TRUE));
+ }
+
+ @Test
+ public void testSimpleTransactionExecutor_readOnly() throws InterruptedException, ExecutionException {
+ Executor transactionExecutor = TransactionHelper.createTransactionExecutor(domain, exec);
+ AtomicReference<Transaction> t = new AtomicReference<>();
+ AtomicBoolean ran = new AtomicBoolean();
+
+ domain.runExclusive(() -> {
+ transactionExecutor.execute(() -> {
+ ran.set(true);
+
+ InternalTransactionalEditingDomain iDomain = (InternalTransactionalEditingDomain) domain;
+ t.set(iDomain.getActiveTransaction());
+ });
+ });
+
+ // In case the runnable is posted to the thread (which it should be)
+ exec.submit(this::pass).get();
+
+ assertThat("Was executed in the transaction", t.get(), nullValue());
+ assertThat("Was not executed", ran.get(), is(true));
+ }
+
+ @Test
+ public void testDisposeTransactionExecutor() throws InterruptedException, ExecutionException {
+ Executor transactionExecutor = TransactionHelper.createTransactionExecutor(domain, exec);
+ AtomicReference<Transaction> t = new AtomicReference<>();
+ AtomicBoolean ran = new AtomicBoolean();
+
+ TransactionHelper.disposeTransactionExecutor(transactionExecutor);
+ domain.runExclusive(() -> {
+ transactionExecutor.execute(() -> {
+ ran.set(true);
+
+ InternalTransactionalEditingDomain iDomain = (InternalTransactionalEditingDomain) domain;
+ t.set(iDomain.getActiveTransaction());
+ });
+ });
+
+ // In case the runnable is posted to the thread
+ exec.submit(this::pass).get();
+
+ assertThat("Was executed in the transaction", t.get(), nullValue());
+ assertThat("Was executed", ran.get(), is(false));
+ }
+
//
// Test framework
//
@@ -145,7 +227,11 @@ public class TransactionHelperTest {
@Before
public void createFixture() throws Exception {
domain = houseKeeper.createSimpleEditingDomain();
- Resource res = domain.createResource("file:bogus.ecore");
+ if (!EMFPlugin.IS_ECLIPSE_RUNNING) {
+ domain.getResourceSet().getResourceFactoryRegistry().getExtensionToFactoryMap()
+ .put("ecore", new EcoreResourceFactoryImpl());
+ }
+ Resource res = domain.createResource("file:/bogus.ecore");
TransactionHelper.run(domain, () -> {
EPackage epackage = EcoreFactory.eINSTANCE.createEPackage();
@@ -169,6 +255,10 @@ public class TransactionHelperTest {
return exec.submit(() -> runnable.run(new PrintingProgressMonitor()));
}
+ void pass() {
+ // Do nothing
+ }
+
//
// Nested types
//
diff --git a/tests/junit/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence.tests/resource/bugs/bug533679.di b/tests/junit/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence.tests/resource/bugs/bug533679.di
new file mode 100644
index 00000000000..8c549eecdc6
--- /dev/null
+++ b/tests/junit/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence.tests/resource/bugs/bug533679.di
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<architecture:ArchitectureDescription xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:architecture="http://www.eclipse.org/papyrus/infra/core/architecture" contextId="org.eclipse.papyrus.infra.services.edit.TypeContext"/>
diff --git a/tests/junit/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence.tests/resource/bugs/bug533679.notation b/tests/junit/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence.tests/resource/bugs/bug533679.notation
new file mode 100644
index 00000000000..0f786972ab3
--- /dev/null
+++ b/tests/junit/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence.tests/resource/bugs/bug533679.notation
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<notation:Diagram xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:notation="http://www.eclipse.org/gmf/runtime/1.0.2/notation" xmlns:style="http://www.eclipse.org/papyrus/infra/gmfdiag/style" xmlns:uml="http://www.eclipse.org/uml2/5.0.0/UML" xmi:id="_QKU0YEMwEeiEZ5Jtfh6-KA" type="PapyrusUMLSequenceDiagram" name="sequence" measurementUnit="Pixel">
+ <children xmi:type="notation:Shape" xmi:id="_QKU0YUMwEeiEZ5Jtfh6-KA" type="Interaction_Shape">
+ <children xmi:type="notation:DecorationNode" xmi:id="_QKU0YkMwEeiEZ5Jtfh6-KA" type="Interaction_NameLabel">
+ <element xmi:type="uml:Interaction" href="bug533679.uml#_POkWwEMwEeiEZ5Jtfh6-KA"/>
+ </children>
+ <children xmi:type="notation:BasicCompartment" xmi:id="_QKU0Y0MwEeiEZ5Jtfh6-KA" type="Interaction_SubfragmentCompartment">
+ <children xmi:type="notation:Shape" xmi:id="_RZ4XwEMwEeiEZ5Jtfh6-KA" type="Lifeline_Shape">
+ <children xmi:type="notation:DecorationNode" xmi:id="_RZ4-0EMwEeiEZ5Jtfh6-KA" type="Lifeline_NameLabel">
+ <element xmi:type="uml:Lifeline" href="bug533679.uml#_RZwb8EMwEeiEZ5Jtfh6-KA"/>
+ </children>
+ <children xmi:type="notation:BasicCompartment" xmi:id="_YQnmcFIgEeitkMuoPZuJ9g" type="compartment_shape_display">
+ <styles xmi:type="notation:TitleStyle" xmi:id="_YQnmcVIgEeitkMuoPZuJ9g"/>
+ <layoutConstraint xmi:type="notation:Bounds" xmi:id="_YQnmclIgEeitkMuoPZuJ9g"/>
+ </children>
+ <element xmi:type="uml:Lifeline" href="bug533679.uml#_RZwb8EMwEeiEZ5Jtfh6-KA"/>
+ <layoutConstraint xmi:type="notation:Bounds" xmi:id="_RZ4XwUMwEeiEZ5Jtfh6-KA" x="58" y="10"/>
+ </children>
+ <children xmi:type="notation:Shape" xmi:id="_R0X7YEMwEeiEZ5Jtfh6-KA" type="Lifeline_Shape">
+ <children xmi:type="notation:DecorationNode" xmi:id="_R0X7YkMwEeiEZ5Jtfh6-KA" type="Lifeline_NameLabel">
+ <element xmi:type="uml:Lifeline" href="bug533679.uml#_R0URAEMwEeiEZ5Jtfh6-KA"/>
+ </children>
+ <children xmi:type="notation:BasicCompartment" xmi:id="_R0cM0EMwEeiEZ5Jtfh6-KA" type="compartment_shape_display">
+ <styles xmi:type="notation:TitleStyle" xmi:id="_R0cM0UMwEeiEZ5Jtfh6-KA"/>
+ <element xmi:type="uml:Lifeline" href="bug533679.uml#_R0URAEMwEeiEZ5Jtfh6-KA"/>
+ <layoutConstraint xmi:type="notation:Bounds" xmi:id="_R0cM0kMwEeiEZ5Jtfh6-KA"/>
+ </children>
+ <children xmi:type="notation:Shape" xmi:id="_XgABcEMwEeiEZ5Jtfh6-KA" type="BehaviorExecutionSpecification_Shape">
+ <children xmi:type="notation:DecorationNode" xmi:id="_XgABckMwEeiEZ5Jtfh6-KA" type="BehaviorExecutionSpecification_Behavior">
+ <element xmi:type="uml:BehaviorExecutionSpecification" href="bug533679.uml#_Xf7wAEMwEeiEZ5Jtfh6-KA"/>
+ <layoutConstraint xmi:type="notation:Location" xmi:id="_XgAogEMwEeiEZ5Jtfh6-KA" x="18" y="18"/>
+ </children>
+ <element xmi:type="uml:BehaviorExecutionSpecification" href="bug533679.uml#_Xf7wAEMwEeiEZ5Jtfh6-KA"/>
+ <layoutConstraint xmi:type="notation:Bounds" xmi:id="_XgABcUMwEeiEZ5Jtfh6-KA" x="40" y="67" width="20" height="100"/>
+ </children>
+ <children xmi:type="notation:Shape" xmi:id="_vCa_wFIfEeitkMuoPZuJ9g" type="DestructionOccurrenceSpecification_Shape">
+ <element xmi:type="uml:DestructionOccurrenceSpecification" href="bug533679.uml#_vBfysFIfEeitkMuoPZuJ9g"/>
+ <layoutConstraint xmi:type="notation:Bounds" xmi:id="_vCa_wVIfEeitkMuoPZuJ9g" x="37" y="207"/>
+ </children>
+ <element xmi:type="uml:Lifeline" href="bug533679.uml#_R0URAEMwEeiEZ5Jtfh6-KA"/>
+ <layoutConstraint xmi:type="notation:Bounds" xmi:id="_R0X7YUMwEeiEZ5Jtfh6-KA" x="237" y="93" width="100" height="227"/>
+ </children>
+ <element xmi:type="uml:Interaction" href="bug533679.uml#_POkWwEMwEeiEZ5Jtfh6-KA"/>
+ <layoutConstraint xmi:type="notation:Bounds" xmi:id="_QKU0ZEMwEeiEZ5Jtfh6-KA"/>
+ </children>
+ <element xmi:type="uml:Interaction" href="bug533679.uml#_POkWwEMwEeiEZ5Jtfh6-KA"/>
+ <layoutConstraint xmi:type="notation:Bounds" xmi:id="_QKU0ZUMwEeiEZ5Jtfh6-KA"/>
+ </children>
+ <styles xmi:type="notation:StringValueStyle" xmi:id="_QKU0ZkMwEeiEZ5Jtfh6-KA" name="diagram_compatibility_version" stringValue="1.4.0"/>
+ <styles xmi:type="notation:DiagramStyle" xmi:id="_QKU0Z0MwEeiEZ5Jtfh6-KA"/>
+ <styles xmi:type="style:PapyrusDiagramStyle" xmi:id="_QKU0aEMwEeiEZ5Jtfh6-KA" diagramKindId="org.eclipse.papyrus.uml.diagram.sequence">
+ <owner xmi:type="uml:Class" href="bug533679.uml#_Jqu6wEMwEeiEZ5Jtfh6-KA"/>
+ </styles>
+ <element xmi:type="uml:Interaction" href="bug533679.uml#_POkWwEMwEeiEZ5Jtfh6-KA"/>
+ <edges xmi:type="notation:Connector" xmi:id="_Xfg5QEMwEeiEZ5Jtfh6-KA" type="Message_SynchEdge" source="_RZ4XwEMwEeiEZ5Jtfh6-KA" target="_R0X7YEMwEeiEZ5Jtfh6-KA">
+ <children xmi:type="notation:DecorationNode" xmi:id="_Xfg5Q0MwEeiEZ5Jtfh6-KA" type="Message_SynchNameLabel">
+ <element xmi:type="uml:Message" href="bug533679.uml#_XfUsAEMwEeiEZ5Jtfh6-KA"/>
+ <layoutConstraint xmi:type="notation:Location" xmi:id="_Xfg5REMwEeiEZ5Jtfh6-KA" x="1" y="-13"/>
+ </children>
+ <children xmi:type="notation:DecorationNode" xmi:id="_XfhgUEMwEeiEZ5Jtfh6-KA" type="Message_SynchStereotypeLabel">
+ <element xmi:type="uml:Message" href="bug533679.uml#_XfUsAEMwEeiEZ5Jtfh6-KA"/>
+ <layoutConstraint xmi:type="notation:Location" xmi:id="_XfhgUUMwEeiEZ5Jtfh6-KA" x="1" y="-33"/>
+ </children>
+ <styles xmi:type="notation:FontStyle" xmi:id="_Xfg5QUMwEeiEZ5Jtfh6-KA"/>
+ <styles xmi:type="notation:LineStyle" xmi:id="_XfhgUkMwEeiEZ5Jtfh6-KA"/>
+ <element xmi:type="uml:Message" href="bug533679.uml#_XfUsAEMwEeiEZ5Jtfh6-KA"/>
+ <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_Xfg5QkMwEeiEZ5Jtfh6-KA" points="[0, 0, -179, 0]$[179, 0, 0, 0]"/>
+ <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_XftGgEMwEeiEZ5Jtfh6-KA" id="(0.5,0.21428571428571427)"/>
+ <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_XfttkEMwEeiEZ5Jtfh6-KA" id="(0.5,0.29515418502202645)"/>
+ </edges>
+ <edges xmi:type="notation:Connector" xmi:id="_XgIkUEMwEeiEZ5Jtfh6-KA" type="Message_ReplyEdge" source="_R0X7YEMwEeiEZ5Jtfh6-KA" target="_RZ4XwEMwEeiEZ5Jtfh6-KA">
+ <children xmi:type="notation:DecorationNode" xmi:id="_XgIkU0MwEeiEZ5Jtfh6-KA" type="Message_ReplyNameLabel">
+ <element xmi:type="uml:Message" href="bug533679.uml#_XgGvIEMwEeiEZ5Jtfh6-KA"/>
+ <layoutConstraint xmi:type="notation:Location" xmi:id="_XgIkVEMwEeiEZ5Jtfh6-KA" x="1" y="-13"/>
+ </children>
+ <children xmi:type="notation:DecorationNode" xmi:id="_XgIkVUMwEeiEZ5Jtfh6-KA" type="Message_ReplyStereotypeLabel">
+ <element xmi:type="uml:Message" href="bug533679.uml#_XgGvIEMwEeiEZ5Jtfh6-KA"/>
+ <layoutConstraint xmi:type="notation:Location" xmi:id="_XgIkVkMwEeiEZ5Jtfh6-KA" x="1" y="-33"/>
+ </children>
+ <styles xmi:type="notation:FontStyle" xmi:id="_XgIkUUMwEeiEZ5Jtfh6-KA"/>
+ <styles xmi:type="notation:LineStyle" xmi:id="_XgIkV0MwEeiEZ5Jtfh6-KA"/>
+ <element xmi:type="uml:Message" href="bug533679.uml#_XgGvIEMwEeiEZ5Jtfh6-KA"/>
+ <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_XgIkUkMwEeiEZ5Jtfh6-KA" points="[0, 0, 179, 0]$[-179, 0, 0, 0]"/>
+ <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_XgJycEMwEeiEZ5Jtfh6-KA" id="(0.5,0.73568281938326)"/>
+ <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_XgJycUMwEeiEZ5Jtfh6-KA" id="(0.5,0.35714285714285715)"/>
+ </edges>
+ <edges xmi:type="notation:Connector" xmi:id="_m7U80FIfEeitkMuoPZuJ9g" type="Message_CreateEdge" source="_RZ4XwEMwEeiEZ5Jtfh6-KA" target="_R0X7YEMwEeiEZ5Jtfh6-KA">
+ <children xmi:type="notation:DecorationNode" xmi:id="_m7WK8FIfEeitkMuoPZuJ9g" type="Message_CreateNameLabel">
+ <layoutConstraint xmi:type="notation:Location" xmi:id="_m7WK8VIfEeitkMuoPZuJ9g" x="1" y="-13"/>
+ </children>
+ <children xmi:type="notation:DecorationNode" xmi:id="_m7WK8lIfEeitkMuoPZuJ9g" type="Message_CreateStereotypeLabel">
+ <layoutConstraint xmi:type="notation:Location" xmi:id="_m7WyAFIfEeitkMuoPZuJ9g" x="1" y="-33"/>
+ </children>
+ <styles xmi:type="notation:FontStyle" xmi:id="_m7U80VIfEeitkMuoPZuJ9g"/>
+ <styles xmi:type="notation:LineStyle" xmi:id="_m7WyAVIfEeitkMuoPZuJ9g"/>
+ <element xmi:type="uml:Message" href="bug533679.uml#_m63p0FIfEeitkMuoPZuJ9g"/>
+ <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_m7U80lIfEeitkMuoPZuJ9g" points="[0, 0, -179, 0]$[179, 0, 0, 0]"/>
+ <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_m7vzkFIfEeitkMuoPZuJ9g" id="(0.5,0.12857142857142856)"/>
+ <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_m7vzkVIfEeitkMuoPZuJ9g" id="(0.5,0.03083700440528634)"/>
+ </edges>
+ <edges xmi:type="notation:Connector" xmi:id="_vBnugFIfEeitkMuoPZuJ9g" type="Message_DeleteEdge" source="_RZ4XwEMwEeiEZ5Jtfh6-KA" target="_R0X7YEMwEeiEZ5Jtfh6-KA">
+ <children xmi:type="notation:DecorationNode" xmi:id="_vBnug1IfEeitkMuoPZuJ9g" type="Message_DeleteNameLabel">
+ <layoutConstraint xmi:type="notation:Location" xmi:id="_vBnuhFIfEeitkMuoPZuJ9g" x="1" y="-13"/>
+ </children>
+ <children xmi:type="notation:DecorationNode" xmi:id="_vBnuhVIfEeitkMuoPZuJ9g" type="Message_DeleteStereotypeLabel">
+ <layoutConstraint xmi:type="notation:Location" xmi:id="_vBnuhlIfEeitkMuoPZuJ9g" x="1" y="-33"/>
+ </children>
+ <styles xmi:type="notation:FontStyle" xmi:id="_vBnugVIfEeitkMuoPZuJ9g"/>
+ <styles xmi:type="notation:LineStyle" xmi:id="_vBnuh1IfEeitkMuoPZuJ9g"/>
+ <element xmi:type="uml:Message" href="bug533679.uml#_vBekkFIfEeitkMuoPZuJ9g"/>
+ <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_vBnuglIfEeitkMuoPZuJ9g" points="[0, 0, -179, 0]$[179, 0, 0, 0]"/>
+ <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_vB-64FIfEeitkMuoPZuJ9g" id="(0.5,0.44285714285714284)"/>
+ <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_vB-64VIfEeitkMuoPZuJ9g" id="(0.5,1.0)"/>
+ </edges>
+ <edges xmi:type="notation:Connector" xmi:id="_zvfy4FIfEeitkMuoPZuJ9g" type="Message_LostEdge" source="_RZ4XwEMwEeiEZ5Jtfh6-KA" target="_QKU0YUMwEeiEZ5Jtfh6-KA">
+ <children xmi:type="notation:DecorationNode" xmi:id="_zvfy41IfEeitkMuoPZuJ9g" type="Message_LostNameLabel">
+ <layoutConstraint xmi:type="notation:Location" xmi:id="_zvfy5FIfEeitkMuoPZuJ9g" x="1" y="-13"/>
+ </children>
+ <children xmi:type="notation:DecorationNode" xmi:id="_zvfy5VIfEeitkMuoPZuJ9g" type="Message_LostStereotypeLabel">
+ <layoutConstraint xmi:type="notation:Location" xmi:id="_zvfy5lIfEeitkMuoPZuJ9g" x="1" y="-33"/>
+ </children>
+ <styles xmi:type="notation:FontStyle" xmi:id="_zvfy4VIfEeitkMuoPZuJ9g"/>
+ <styles xmi:type="notation:LineStyle" xmi:id="_zvfy51IfEeitkMuoPZuJ9g"/>
+ <element xmi:type="uml:Message" href="bug533679.uml#_zvX3EFIfEeitkMuoPZuJ9g"/>
+ <bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_zvfy4lIfEeitkMuoPZuJ9g" points="[0, 0, -106, 0]$[106, 0, 0, 0]"/>
+ <sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_zv3mUFIfEeitkMuoPZuJ9g" id="(0.5,0.5)"/>
+ <targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_zv3mUVIfEeitkMuoPZuJ9g" id="(220.0,380.0)"/>
+ </edges>
+</notation:Diagram>
diff --git a/tests/junit/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence.tests/resource/bugs/bug533679.uml b/tests/junit/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence.tests/resource/bugs/bug533679.uml
new file mode 100644
index 00000000000..097b5898ac6
--- /dev/null
+++ b/tests/junit/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence.tests/resource/bugs/bug533679.uml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<uml:Model xmi:version="20131001" xmlns:xmi="http://www.omg.org/spec/XMI/20131001" xmlns:uml="http://www.eclipse.org/uml2/5.0.0/UML" xmi:id="_HWN6cEMwEeiEZ5Jtfh6-KA" name="bug533679">
+ <packageImport xmi:type="uml:PackageImport" xmi:id="_HeHSMEMwEeiEZ5Jtfh6-KA">
+ <importedPackage xmi:type="uml:Model" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#_0"/>
+ </packageImport>
+ <packagedElement xmi:type="uml:Class" xmi:id="_Jqu6wEMwEeiEZ5Jtfh6-KA" name="Foo" classifierBehavior="_POkWwEMwEeiEZ5Jtfh6-KA">
+ <ownedAttribute xmi:type="uml:Property" xmi:id="_LwEPkEMwEeiEZ5Jtfh6-KA" name="a"/>
+ <ownedAttribute xmi:type="uml:Property" xmi:id="_M6OVkEMwEeiEZ5Jtfh6-KA" name="b"/>
+ <ownedBehavior xmi:type="uml:Interaction" xmi:id="_POkWwEMwEeiEZ5Jtfh6-KA" name="DoIt">
+ <lifeline xmi:type="uml:Lifeline" xmi:id="_RZwb8EMwEeiEZ5Jtfh6-KA" name="a" represents="_LwEPkEMwEeiEZ5Jtfh6-KA" coveredBy="_m7AMsFIfEeitkMuoPZuJ9g _XfZkgEMwEeiEZ5Jtfh6-KA _XgH9QEMwEeiEZ5Jtfh6-KA _vBfLoFIfEeitkMuoPZuJ9g _zvYeIFIfEeitkMuoPZuJ9g"/>
+ <lifeline xmi:type="uml:Lifeline" xmi:id="_R0URAEMwEeiEZ5Jtfh6-KA" name="b" represents="_M6OVkEMwEeiEZ5Jtfh6-KA" coveredBy="_m7AzwFIfEeitkMuoPZuJ9g _XfaLkEMwEeiEZ5Jtfh6-KA _XgHWMEMwEeiEZ5Jtfh6-KA _vBfysFIfEeitkMuoPZuJ9g _Xf7wAEMwEeiEZ5Jtfh6-KA"/>
+ <fragment xmi:type="uml:MessageOccurrenceSpecification" xmi:id="_m7AMsFIfEeitkMuoPZuJ9g" name="create-send" covered="_RZwb8EMwEeiEZ5Jtfh6-KA" message="_m63p0FIfEeitkMuoPZuJ9g"/>
+ <fragment xmi:type="uml:MessageOccurrenceSpecification" xmi:id="_m7AzwFIfEeitkMuoPZuJ9g" name="created" covered="_R0URAEMwEeiEZ5Jtfh6-KA" message="_m63p0FIfEeitkMuoPZuJ9g"/>
+ <fragment xmi:type="uml:MessageOccurrenceSpecification" xmi:id="_XfZkgEMwEeiEZ5Jtfh6-KA" name="request-send" covered="_RZwb8EMwEeiEZ5Jtfh6-KA" message="_XfUsAEMwEeiEZ5Jtfh6-KA"/>
+ <fragment xmi:type="uml:MessageOccurrenceSpecification" xmi:id="_XfaLkEMwEeiEZ5Jtfh6-KA" name="request-recv" covered="_R0URAEMwEeiEZ5Jtfh6-KA" message="_XfUsAEMwEeiEZ5Jtfh6-KA"/>
+ <fragment xmi:type="uml:BehaviorExecutionSpecification" xmi:id="_Xf7wAEMwEeiEZ5Jtfh6-KA" name="exec" covered="_R0URAEMwEeiEZ5Jtfh6-KA" finish="_XgHWMEMwEeiEZ5Jtfh6-KA" start="_XfaLkEMwEeiEZ5Jtfh6-KA"/>
+ <fragment xmi:type="uml:MessageOccurrenceSpecification" xmi:id="_XgHWMEMwEeiEZ5Jtfh6-KA" name="reply-send" covered="_R0URAEMwEeiEZ5Jtfh6-KA" message="_XgGvIEMwEeiEZ5Jtfh6-KA"/>
+ <fragment xmi:type="uml:MessageOccurrenceSpecification" xmi:id="_XgH9QEMwEeiEZ5Jtfh6-KA" name="reply-recv" covered="_RZwb8EMwEeiEZ5Jtfh6-KA" message="_XgGvIEMwEeiEZ5Jtfh6-KA"/>
+ <fragment xmi:type="uml:MessageOccurrenceSpecification" xmi:id="_vBfLoFIfEeitkMuoPZuJ9g" name="delete-send" covered="_RZwb8EMwEeiEZ5Jtfh6-KA" message="_vBekkFIfEeitkMuoPZuJ9g"/>
+ <fragment xmi:type="uml:DestructionOccurrenceSpecification" xmi:id="_vBfysFIfEeitkMuoPZuJ9g" name="deleted" covered="_R0URAEMwEeiEZ5Jtfh6-KA" message="_vBekkFIfEeitkMuoPZuJ9g"/>
+ <fragment xmi:type="uml:MessageOccurrenceSpecification" xmi:id="_zvYeIFIfEeitkMuoPZuJ9g" name="oops-send" covered="_RZwb8EMwEeiEZ5Jtfh6-KA" message="_zvX3EFIfEeitkMuoPZuJ9g"/>
+ <message xmi:type="uml:Message" xmi:id="_XfUsAEMwEeiEZ5Jtfh6-KA" name="request" receiveEvent="_XfaLkEMwEeiEZ5Jtfh6-KA" sendEvent="_XfZkgEMwEeiEZ5Jtfh6-KA"/>
+ <message xmi:type="uml:Message" xmi:id="_XgGvIEMwEeiEZ5Jtfh6-KA" messageSort="reply" receiveEvent="_XgH9QEMwEeiEZ5Jtfh6-KA" sendEvent="_XgHWMEMwEeiEZ5Jtfh6-KA"/>
+ <message xmi:type="uml:Message" xmi:id="_m63p0FIfEeitkMuoPZuJ9g" name="create" messageSort="createMessage" receiveEvent="_m7AzwFIfEeitkMuoPZuJ9g" sendEvent="_m7AMsFIfEeitkMuoPZuJ9g"/>
+ <message xmi:type="uml:Message" xmi:id="_vBekkFIfEeitkMuoPZuJ9g" name="delete" messageSort="deleteMessage" receiveEvent="_vBfysFIfEeitkMuoPZuJ9g" sendEvent="_vBfLoFIfEeitkMuoPZuJ9g"/>
+ <message xmi:type="uml:Message" xmi:id="_zvX3EFIfEeitkMuoPZuJ9g" name="oops" messageSort="asynchCall" sendEvent="_zvYeIFIfEeitkMuoPZuJ9g"/>
+ </ownedBehavior>
+ </packagedElement>
+</uml:Model>
diff --git a/tests/junit/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence.tests/src/org/eclipse/papyrus/uml/diagram/sequence/tests/bug/CombinedFragmentRegressionTest.java b/tests/junit/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence.tests/src/org/eclipse/papyrus/uml/diagram/sequence/tests/bug/CombinedFragmentRegressionTest.java
index 8a7f37f7129..4fda5bfdf62 100644
--- a/tests/junit/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence.tests/src/org/eclipse/papyrus/uml/diagram/sequence/tests/bug/CombinedFragmentRegressionTest.java
+++ b/tests/junit/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence.tests/src/org/eclipse/papyrus/uml/diagram/sequence/tests/bug/CombinedFragmentRegressionTest.java
@@ -35,15 +35,20 @@ import static org.junit.Assume.assumeThat;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import org.eclipse.draw2d.Connection;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.validation.model.EvaluationMode;
@@ -710,6 +715,50 @@ public class CombinedFragmentRegressionTest extends AbstractPapyrusTest {
assertThat("No batch validation occurred", validationOccurred[0], is(true));
}
+ /**
+ * Verify that the creation of a combined fragment doesn't cause the interaction
+ * fragments that it encloses to move visually.
+ */
+ @Test
+ @PluginResource("resource/bugs/bug533679.di")
+ public void createCFragDoesNotMoveExistingFragments_533679() {
+ EditPart interactionEP = editor.findEditPart("DoIt", Interaction.class);
+ EditPart interactionCompartment = editor.getShapeCompartment(interactionEP);
+ Interaction interaction = interactionEP.getAdapter(Interaction.class);
+
+ // Collect the geometries of existing interaction fragments and messages
+ Map<EObject, Object> geometries = interaction.eContents().stream()
+ .collect(Collectors.toMap(Function.identity(), this::getGeometry));
+
+ editor.createShape(interactionCompartment, UMLElementTypes.CombinedFragment_Shape,
+ at(40, 60), sized(360, 360));
+
+ assumeThat(geometries.size(), greaterThanOrEqual(8));
+ geometries.forEach((element, geometry) -> assertThat(getGeometry(element), equalGeometry(geometry)));
+ }
+
+ /**
+ * Verify that the creation of a combined fragment causes all enclosed interaction
+ * fragments to be owned by the initial operand.
+ */
+ @Test
+ @PluginResource("resource/bugs/bug533679.di")
+ public void createCFragEnclosedFragments_533679() {
+ EditPart interactionEP = editor.findEditPart("DoIt", Interaction.class);
+ Interaction interaction = interactionEP.getAdapter(Interaction.class);
+
+ // Collect the interaction fragments that should move to the new operand
+ InteractionFragment[] fragments = interaction.getFragments().toArray(new InteractionFragment[0]);
+
+ EditPart cfragEP = editor.createShape(UMLElementTypes.CombinedFragment_Shape,
+ at(20, 60), sized(360, 360));
+
+ // All preƫxisting interaction fragments are contained now in the operand
+ CombinedFragment cfrag = cfragEP.getAdapter(CombinedFragment.class);
+ InteractionOperand operand = cfrag.getOperands().get(0);
+ assertThat(operand.getFragments(), hasItems(fragments));
+ }
+
//
// Test framework
//
@@ -722,4 +771,51 @@ public class CombinedFragmentRegressionTest extends AbstractPapyrusTest {
}
};
}
+
+ /**
+ * Work around the absence of an {@code equals} method in the {@link PointList} class.
+ *
+ * @param geometry
+ * a geometry to test for equality with an actual observed geometry
+ * @return the geometry matcher
+ */
+ static Matcher<Object> equalGeometry(Object geometry) {
+ return new CustomTypeSafeMatcher<Object>("equals " + geometry) {
+ @Override
+ protected boolean matchesSafely(Object item) {
+
+ return ((item instanceof PointList) && (geometry instanceof PointList))
+ ? Arrays.equals(((PointList) item).toIntArray(),
+ ((PointList) geometry).toIntArray())
+ : Objects.equals(item, geometry);
+ }
+ };
+ }
+
+ /**
+ * Query the geometry of an interaction element in the diagram.
+ *
+ * @param interactionElement
+ * an interaction element (interaction fragment or message)
+ *
+ * @return its geometry, either a {@link Rectangle}, {@link PointList}, or {@code null}
+ * for elements that have no geometry of their own but would be implied by others (such
+ * as execution occurrences)
+ */
+ Object getGeometry(EObject interactionElement) {
+ Object result = null;
+ GraphicalEditPart editPart = Optional.ofNullable(editor.findEditPart(interactionElement))
+ .filter(GraphicalEditPart.class::isInstance).map(GraphicalEditPart.class::cast)
+ .orElse(null);
+
+ // Some things don't have edit-parts, such as execution occurrences
+ if (editPart != null) {
+ IFigure figure = editPart.getFigure();
+ result = (figure instanceof Connection)
+ ? ((Connection) figure).getPoints().getCopy()
+ : figure.getBounds().getCopy();
+ }
+
+ return result;
+ }
}

Back to the top