aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian W. Damus2018-04-25 08:23:11 -0400
committerNicolas FAUVERGUE2018-05-14 11:15:01 -0400
commit6ba0f3d2f43bae21f387ac58ec5a357767620115 (patch)
tree244ae9092ce8d87e647292a5bcd619cd712d03ed
parent044dc1170629e1657f39305fdaf900abca5a7aa1 (diff)
downloadorg.eclipse.papyrus-6ba0f3d2f43bae21f387ac58ec5a357767620115.tar.gz
org.eclipse.papyrus-6ba0f3d2f43bae21f387ac58ec5a357767620115.tar.xz
org.eclipse.papyrus-6ba0f3d2f43bae21f387ac58ec5a357767620115.zip
Bug 533672: [Sequence Diagram] InteractionOperand should be created on Lifeline and Interaction
Ensure that the Interaction Operand tool is responsive on lifelines where they are covered by combined fragments. Change-Id: Idd29228845e4ac3cd3aa84cbe4d8c4ccd12cf3ce Signed-off-by: Christian W. Damus <give.a.damus@gmail.com>
-rw-r--r--plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/edit/parts/CLifeLineEditPart.java35
-rw-r--r--plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/SequenceUtil.java112
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/matchers/CommandMatchers.java103
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/matchers/MoreMatchers.java104
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/PapyrusEditorFixture.java9
-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.java114
6 files changed, 420 insertions, 57 deletions
diff --git a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/edit/parts/CLifeLineEditPart.java b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/edit/parts/CLifeLineEditPart.java
index 23238d8f6ff..a9e48783d3a 100644
--- a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/edit/parts/CLifeLineEditPart.java
+++ b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/edit/parts/CLifeLineEditPart.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2017, 2018 CEA LIST, ALL4TEC and others.
+ * Copyright (c) 2017, 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 519621, 526803
* Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Bug 531520
+ * Christian W. Damus - bug 533672
*****************************************************************************/
package org.eclipse.papyrus.uml.diagram.sequence.edit.parts;
@@ -22,6 +23,9 @@ import org.eclipse.draw2d.IFigure;
import org.eclipse.gef.ConnectionEditPart;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPolicy;
+import org.eclipse.gef.Request;
+import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewAndElementRequest;
+import org.eclipse.gmf.runtime.emf.type.core.IElementType;
import org.eclipse.gmf.runtime.gef.ui.figures.NodeFigure;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.papyrus.uml.diagram.common.draw2d.anchors.FixedAnchor;
@@ -35,6 +39,8 @@ import org.eclipse.papyrus.uml.diagram.sequence.figures.ILifelineInternalFigure;
import org.eclipse.papyrus.uml.diagram.sequence.figures.LifeLineLayoutManager;
import org.eclipse.papyrus.uml.diagram.sequence.figures.LifelineFigure;
import org.eclipse.papyrus.uml.diagram.sequence.locator.MessageCreateLifelineAnchor;
+import org.eclipse.papyrus.uml.diagram.sequence.providers.UMLElementTypes;
+import org.eclipse.papyrus.uml.diagram.sequence.util.SequenceUtil;
/**
* @author Patrick Tessier
@@ -87,8 +93,8 @@ public class CLifeLineEditPart extends LifelineEditPart {
@Override
public void refresh() {
if (getPrimaryShape() instanceof LifelineFigure) {
- //Bug 531520: we redefine the border of the lifeline, in order to include the children
- //the message are connected to the middle line of the Lifeline, but they must be drawn as connected on the ExecutionSpeficiation
+ // Bug 531520: we redefine the border of the lifeline, in order to include the children
+ // the message are connected to the middle line of the Lifeline, but they must be drawn as connected on the ExecutionSpeficiation
final List<NodeFigure> childrenFigure = new ArrayList<>();
for (final Object current : getChildren()) {
if (current instanceof AbstractExecutionSpecificationEditPart) {
@@ -177,4 +183,27 @@ public class CLifeLineEditPart extends LifelineEditPart {
return super.getTargetConnectionAnchor(connEditPart);
}
+ /**
+ * @since 5.0
+ */
+ @Override
+ public EditPart getTargetEditPart(Request request) {
+ if (request instanceof CreateViewAndElementRequest) {
+ CreateViewAndElementRequest req = (CreateViewAndElementRequest) request;
+
+ // If we're creating an operand, it needs to be done by the covering combined fragment
+ if (UMLElementTypes.InteractionOperand_Shape.equals(req.getViewAndElementDescriptor().getElementAdapter().getAdapter(IElementType.class))) {
+ EditPart container = SequenceUtil.findInteractionFragmentContainerEditPartAt(req.getLocation(), this);
+ if (container instanceof CInteractionOperandEditPart) {
+ // Delegate again to the combined fragment
+ container = SequenceUtil.getParentCombinedFragmentPart(container);
+ }
+ if (container != null) {
+ return container.getTargetEditPart(request);
+ }
+ }
+ }
+
+ return super.getTargetEditPart(request);
+ }
}
diff --git a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/SequenceUtil.java b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/SequenceUtil.java
index e037d703086..59b2928ce6a 100644
--- a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/SequenceUtil.java
+++ b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/SequenceUtil.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2009 CEA
+ * Copyright (c) 2009, 2018 CEA, Christian W. Damus, and others
*
*
* All rights reserved. This program and the accompanying materials
@@ -11,6 +11,7 @@
* Atos Origin - Initial API and implementation
* Camille Letavernier (camille.letavernier@cea.fr) - Loosen the MessageSortChange restriction
* Nicolas FAUVERGUE (CEA LIST) nicolas.fauvergue@cea.fr - Bug 531596
+ * Christian W. Damus - bug 533672
*
*****************************************************************************/
package org.eclipse.papyrus.uml.diagram.sequence.util;
@@ -138,6 +139,9 @@ import org.eclipse.uml2.uml.TimeConstraint;
import org.eclipse.uml2.uml.TimeObservation;
import org.eclipse.uml2.uml.UMLPackage;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+
public class SequenceUtil {
private static final double MAXIMAL_DISTANCE_FROM_EVENT = 10;
@@ -182,6 +186,8 @@ public class SequenceUtil {
*
* @param location
* the location
+ * @param hostEditPart
+ * any edit part in the contextual diagram
* @return the interaction or null
*/
public static InteractionFragment findInteractionFragmentContainerAt(Point location, EditPart hostEditPart) {
@@ -191,24 +197,25 @@ public class SequenceUtil {
}
/**
- * Find the container interaction fragment for the given bounds.
+ * Find the interaction fragment container edit-part for the given bounds.
* The elements are drawn under the lifeline, but their model container is an interaction.
- * It can be of type Interaction or InteractionOperand.
+ * The resulting edit-part can present either an Interaction or an InteractionOperand.
*
* @param bounds
* the bounds
* @param hostEditPart
- * any adit part in the corresponding diagram
- * @return the interaction or null
+ * any edit part in the contextual diagram
+ * @return the interaction or operand edit-part, or {@code null} if none
+ * @since 5.0
*/
- @SuppressWarnings("unchecked")
- public static InteractionFragment findInteractionFragmentContainerAt(Rectangle bounds, EditPart hostEditPart) {
+ public static IGraphicalEditPart findInteractionFragmentContainerEditPartAt(Rectangle bounds, EditPart hostEditPart) {
if (hostEditPart == null) {
return null;
}
- InteractionFragment container = null;
- Set<InteractionFragment> coveredInteractions = new HashSet<>();
- Set<CombinedFragment> coveredCF = new HashSet<>();
+ IGraphicalEditPart result = null;
+ BiMap<IGraphicalEditPart, InteractionFragment> coveredInteractions = HashBiMap.create();
+ BiMap<IGraphicalEditPart, CombinedFragment> coveredCF = HashBiMap.create();
+ @SuppressWarnings("unchecked")
Set<Entry<Object, EditPart>> allEditPartEntries = hostEditPart.getViewer().getEditPartRegistry().entrySet();
for (Entry<Object, EditPart> epEntry : allEditPartEntries) {
EditPart ep = epEntry.getValue();
@@ -218,57 +225,96 @@ public class SequenceUtil {
if (eObject instanceof Interaction || eObject instanceof InteractionOperand) {
Rectangle figureBounds = getAbsoluteBounds(sep);
if (figureBounds.contains(bounds)) {
- coveredInteractions.add((InteractionFragment) eObject);
+ coveredInteractions.put(sep, (InteractionFragment) eObject);
}
} else if (eObject instanceof CombinedFragment) {
// handle case when the figure is located in the CF header as if it were in the first Interaction Operand
Rectangle figureBounds = getAbsoluteBounds(sep);
if (figureBounds.contains(bounds)) {
- coveredCF.add((CombinedFragment) eObject);
+ coveredCF.put(sep, (CombinedFragment) eObject);
}
}
}
}
// inspect coveredCF to ensure at least on child operand is in coveredInteractions list
- for (CombinedFragment cf : coveredCF) {
- List<InteractionOperand> operands = cf.getOperands();
- if (operands.size() > 0 && Collections.disjoint(operands, coveredInteractions)) {
+ for (Map.Entry<IGraphicalEditPart, CombinedFragment> cf : coveredCF.entrySet()) {
+ List<InteractionOperand> operands = cf.getValue().getOperands();
+ if (operands.size() > 0 && Collections.disjoint(operands, coveredInteractions.values())) {
// bounds are in the header, add the first operand
- coveredInteractions.add(operands.get(0));
+ coveredInteractions.put(DiagramEditPartsUtil.getChildByEObject(operands.get(0), cf.getKey(), false), operands.get(0));
}
}
// for each interaction verify if its children list does not contain an other covered interaction
// if it doesn't we have found the top-level interaction
- for (InteractionFragment ift : coveredInteractions) {
- boolean subiftFounded = false;
+ out: for (InteractionFragment ift : coveredInteractions.values()) {
if (ift instanceof Interaction) {
for (InteractionFragment subift : ((Interaction) ift).getFragments()) {
if (subift instanceof CombinedFragment) {
for (InteractionOperand io : ((CombinedFragment) subift).getOperands()) {
- if (coveredInteractions.contains(io)) {
- subiftFounded = true;
+ if (coveredInteractions.containsValue(io)) {
+ result = coveredInteractions.inverse().get(io);
+ break out;
}
}
}
}
- }
- if (!subiftFounded && ift instanceof InteractionOperand) {
+ } else if (ift instanceof InteractionOperand) {
for (InteractionFragment subift : ((InteractionOperand) ift).getFragments()) {
if (subift instanceof CombinedFragment) {
for (InteractionOperand io : ((CombinedFragment) subift).getOperands()) {
- if (coveredInteractions.contains(io)) {
- subiftFounded = true;
+ if (coveredInteractions.containsValue(io)) {
+ result = coveredInteractions.inverse().get(io);
+ break out;
}
}
}
}
}
- if (!subiftFounded) {
- container = ift;
- break;
+ }
+ return result;
+ }
+
+ /**
+ * Find the interaction fragment container edit-part at the given location.
+ * The elements are drawn under the lifeline, but their model container is an interaction.
+ * The resulting edit-part can present either an Interaction or an InteractionOperand.
+ *
+ * @param location
+ * the location
+ * @param hostEditPart
+ * any edit part in the contextual diagram
+ * @return the interaction or operand edit-part, or {@code null} if none
+ * @since 5.0
+ */
+ public static IGraphicalEditPart findInteractionFragmentContainerEditPartAt(Point location, EditPart hostEditPart) {
+ Rectangle bounds = new Rectangle();
+ bounds.setLocation(location);
+ return findInteractionFragmentContainerEditPartAt(bounds, hostEditPart);
+ }
+
+ /**
+ * Find the container interaction fragment for the given bounds.
+ * The elements are drawn under the lifeline, but their model container is an interaction.
+ * It can be of type Interaction or InteractionOperand.
+ *
+ * @param bounds
+ * the bounds
+ * @param hostEditPart
+ * any edit part in the contextual diagram
+ * @return the interaction or null
+ */
+ public static InteractionFragment findInteractionFragmentContainerAt(Rectangle bounds, EditPart hostEditPart) {
+ InteractionFragment result = null;
+
+ IGraphicalEditPart container = findInteractionFragmentContainerEditPartAt(bounds, hostEditPart);
+ if (container != null) {
+ EObject semantic = container.resolveSemanticElement();
+ if (semantic instanceof InteractionFragment) {
+ result = (InteractionFragment) semantic;
}
}
- return container;
+
+ return result;
}
/**
@@ -1776,11 +1822,11 @@ public class SequenceUtil {
public static EditPart getInteractionCompartment(final EditPart editPart) {
EditPart currentEditPart = editPart;
- if(editPart instanceof AbstractMessageEditPart) {
- if(((AbstractMessageEditPart)editPart).getSource() instanceof LifelineEditPart) {
- currentEditPart = ((AbstractMessageEditPart)editPart).getSource();
- } else if(((AbstractMessageEditPart)editPart).getTarget() instanceof LifelineEditPart) {
- currentEditPart = ((AbstractMessageEditPart)editPart).getTarget();
+ if (editPart instanceof AbstractMessageEditPart) {
+ if (((AbstractMessageEditPart) editPart).getSource() instanceof LifelineEditPart) {
+ currentEditPart = ((AbstractMessageEditPart) editPart).getSource();
+ } else if (((AbstractMessageEditPart) editPart).getTarget() instanceof LifelineEditPart) {
+ currentEditPart = ((AbstractMessageEditPart) editPart).getTarget();
}
}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/matchers/CommandMatchers.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/matchers/CommandMatchers.java
new file mode 100644
index 00000000000..3cd355c83b3
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/matchers/CommandMatchers.java
@@ -0,0 +1,103 @@
+/*****************************************************************************
+ * 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.junit.matchers;
+
+import java.util.function.Predicate;
+
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+/**
+ * Matchers for commands of various flavours, which are defined in nested classes.
+ *
+ * @since 2.2
+ */
+public final class CommandMatchers {
+
+ /**
+ * Not instantiable by clients.
+ */
+ private CommandMatchers() {
+ super();
+ }
+
+ static <C> Matcher<C> executable(Predicate<? super C> canExecute) {
+ return new TypeSafeMatcher<C>() {
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("unexecutable");
+ }
+
+ @Override
+ protected boolean matchesSafely(C item) {
+ return canExecute.test(item);
+ }
+ };
+ }
+
+ /**
+ * Matchers for GMF commands.
+ *
+ * @since 2.2
+ */
+ public static final class GMF {
+ /**
+ * Not instantiable by clients.
+ */
+ private GMF() {
+ super();
+ }
+
+ public static Matcher<org.eclipse.gmf.runtime.common.core.command.ICommand> canExecute() {
+ return executable(org.eclipse.gmf.runtime.common.core.command.ICommand::canExecute);
+ }
+ }
+
+ /**
+ * Matchers for EMF commands.
+ *
+ * @since 2.2
+ */
+ public static class EMF {
+ /**
+ * Not instantiable by clients.
+ */
+ private EMF() {
+ super();
+ }
+
+ public static Matcher<org.eclipse.emf.common.command.Command> canExecute() {
+ return executable(org.eclipse.emf.common.command.Command::canExecute);
+ }
+ }
+
+ /**
+ * Matchers for GEF commands.
+ *
+ * @since 2.2
+ */
+ public static final class GEF {
+ /**
+ * Not instantiable by clients.
+ */
+ private GEF() {
+ super();
+ }
+
+ public static Matcher<org.eclipse.gef.commands.Command> canExecute() {
+ return executable(org.eclipse.gef.commands.Command::canExecute);
+ }
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/matchers/MoreMatchers.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/matchers/MoreMatchers.java
index 9765f99db05..9dccc7de618 100644
--- a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/matchers/MoreMatchers.java
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/matchers/MoreMatchers.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2014, 2015 Christian W. Damus and others.
+ * Copyright (c) 2014, 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
@@ -19,6 +19,7 @@ import org.eclipse.core.runtime.IStatus;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
import org.hamcrest.core.CombinableMatcher;
import com.google.common.base.Strings;
@@ -33,32 +34,99 @@ public class MoreMatchers {
super();
}
+ /**
+ * Obtain a matcher for numbers greater than a {@code minimum}.
+ *
+ * @param minimum
+ * the lower bound (exclusive) to match against
+ * @return the matcher
+ */
public static <N extends Number & Comparable<N>> Matcher<N> greaterThan(final N minimum) {
- return new BaseMatcher<N>() {
- @Override
- public void describeTo(Description description) {
- description.appendText("greater than ").appendValue(minimum);
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public boolean matches(Object item) {
- return ((N) item).compareTo(minimum) > 0;
- }
- };
+ return comparesAs(minimum, +1, false);
}
+ /**
+ * Obtain a matcher for numbers less than a {@code maximum}.
+ *
+ * @param maximum
+ * the upper bound (exclusive) to match against
+ * @return the matcher
+ */
public static <N extends Number & Comparable<N>> Matcher<N> lessThan(final N maximum) {
- return new BaseMatcher<N>() {
+ return comparesAs(maximum, -1, false);
+ }
+
+ /**
+ * Obtain a matcher for numbers greater or equal to a {@code minimum}.
+ *
+ * @param minimum
+ * the lower bound (inclusive) to match against
+ * @return the matcher
+ * @since 2.2
+ */
+ public static <N extends Number & Comparable<N>> Matcher<N> greaterThanOrEqual(final N minimum) {
+ return comparesAs(minimum, +1, true);
+ }
+
+ /**
+ * Obtain a matcher for numbers less or equal to a {@code maximum}.
+ *
+ * @param maximum
+ * the upper bound (inclusive) to match against
+ * @return the matcher
+ * @since 2.2
+ */
+ public static <N extends Number & Comparable<N>> Matcher<N> lessThanOrEqual(final N maximum) {
+ return comparesAs(maximum, -1, true);
+ }
+
+ /**
+ * Obtain a matcher for comparables that matches comparisons yielding the given
+ * {@code sign}.
+ *
+ * @param compareTo
+ * the value to compare with
+ * @param sign
+ * the sign of the comparison result, either negative for less
+ * than {@code compareTo}, positive for greater than {@code compareTo},
+ * or zero for equal to {@code compareTo}
+ * @param orEqual
+ * in the case of non-zero {@code sign}, whether to match equality also
+ *
+ * @return the matcher
+ *
+ * @since 2.2
+ */
+ public static <C extends Comparable<C>> Matcher<C> comparesAs(final C compareTo, final int sign, final boolean orEqual) {
+ final int normalizedSign = Integer.signum(sign);
+ return new TypeSafeMatcher<C>() {
@Override
public void describeTo(Description description) {
- description.appendText("less than ").appendValue(maximum);
+ switch (normalizedSign) {
+ case -1:
+ description.appendText(orEqual ? "≤ " : "< ");
+ break;
+ case +1:
+ description.appendText(orEqual ? "≥ " : "> ");
+ break;
+ default:
+ description.appendText("= ");
+ break;
+ }
+ description.appendValue(compareTo);
}
@Override
- @SuppressWarnings("unchecked")
- public boolean matches(Object item) {
- return ((N) item).compareTo(maximum) < 0;
+ protected boolean matchesSafely(C item) {
+ int comparison = item.compareTo(compareTo);
+ switch (normalizedSign) {
+ case -1:
+ return orEqual ? comparison <= 0 : comparison < 0;
+ case +1:
+ return orEqual ? comparison >= 0 : comparison > 0;
+ default:
+ return comparison == 0;
+ }
}
};
}
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 759cf747fb5..8753f5f3de8 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
@@ -14,7 +14,6 @@
package org.eclipse.papyrus.junit.utils.rules;
import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.fail;
@@ -101,6 +100,7 @@ import org.eclipse.papyrus.infra.services.edit.service.IElementEditService;
import org.eclipse.papyrus.infra.tools.util.PlatformHelper;
import org.eclipse.papyrus.infra.tools.util.TypeUtils;
import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor;
+import org.eclipse.papyrus.junit.matchers.CommandMatchers;
import org.eclipse.papyrus.junit.utils.EditorUtils;
import org.eclipse.papyrus.junit.utils.JUnitUtils;
import org.eclipse.papyrus.junit.utils.tests.AbstractEditorTest;
@@ -1163,7 +1163,7 @@ public class PapyrusEditorFixture extends AbstractModelFixture<TransactionalEdit
public void execute(org.eclipse.gef.commands.Command command) {
assertThat("No command", command, notNullValue());
- assertThat("Command not executable", command.canExecute(), is(true));
+ assertThat(command, CommandMatchers.GEF.canExecute());
getActiveDiagramEditor().getDiagramEditDomain().getDiagramCommandStack().execute(command);
flushDisplayEvents();
}
@@ -1493,7 +1493,10 @@ public class PapyrusEditorFixture extends AbstractModelFixture<TransactionalEdit
request.setLocation(location);
request.setSize(size);
- org.eclipse.gef.commands.Command command = parent.getCommand(request);
+
+ EditPart target = parent.getTargetEditPart(request);
+ assertThat("No target edit part", target, notNullValue());
+ org.eclipse.gef.commands.Command command = target.getCommand(request);
execute(command);
// Find the new edit-part
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 92f61b1cbef..6b1249663f8 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
@@ -14,6 +14,7 @@
package org.eclipse.papyrus.uml.diagram.sequence.tests.bug;
import static org.eclipse.papyrus.junit.matchers.MoreMatchers.greaterThan;
+import static org.eclipse.papyrus.junit.matchers.MoreMatchers.greaterThanOrEqual;
import static org.eclipse.papyrus.junit.matchers.MoreMatchers.isEmpty;
import static org.eclipse.papyrus.junit.matchers.MoreMatchers.lessThan;
import static org.eclipse.papyrus.junit.utils.rules.PapyrusEditorFixture.at;
@@ -35,12 +36,17 @@ import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.GraphicalEditPart;
+import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
+import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest;
+import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequestFactory;
import org.eclipse.papyrus.junit.framework.classification.tests.AbstractPapyrusTest;
+import org.eclipse.papyrus.junit.matchers.CommandMatchers;
import org.eclipse.papyrus.junit.matchers.DiagramMatchers;
import org.eclipse.papyrus.junit.utils.rules.ActiveDiagram;
import org.eclipse.papyrus.junit.utils.rules.PapyrusEditorFixture;
import org.eclipse.papyrus.junit.utils.rules.PluginResource;
import org.eclipse.papyrus.uml.diagram.sequence.providers.UMLElementTypes;
+import org.eclipse.papyrus.uml.diagram.sequence.util.SequenceUtil;
import org.eclipse.uml2.uml.CombinedFragment;
import org.eclipse.uml2.uml.ExecutionSpecification;
import org.eclipse.uml2.uml.Interaction;
@@ -359,4 +365,112 @@ public class CombinedFragmentRegressionTest extends AbstractPapyrusTest {
combinedFragmentEP = (GraphicalEditPart) editor.findEditPart(cfrag);
verifyCFrag.accept(combinedFragmentEP);
}
+
+ /**
+ * Verify the creation of an interaction operand by dropping the tool over a lifeline
+ * that is covered by (but in the diagram actually occludes) a combined fragment.
+ */
+ @Test
+ @PluginResource("resource/bugs/bug533683.di")
+ public void createOperandOnLifeline_533672() {
+ GraphicalEditPart lifelineEP = (GraphicalEditPart) editor.findEditPart("a", Lifeline.class);
+ EditPart combinedFragmentEP = editor.findEditPart("cfrag", CombinedFragment.class);
+ assumeThat("Combined fragment not found", combinedFragmentEP, notNullValue());
+
+ GraphicalEditPart interactionOperandEP = (GraphicalEditPart) editor.createShape(
+ lifelineEP, UMLElementTypes.InteractionOperand_Shape,
+ at(80, 80), null); // Null rectangle for the default size
+
+ Consumer<GraphicalEditPart> verifyOperand = operandEP -> {
+ assertThat(operandEP, DiagramMatchers.semanticThat(instanceOf(InteractionOperand.class)));
+ EditPart cfragEP = SequenceUtil.getParentCombinedFragmentPart(operandEP);
+ assertThat("No containing combined fragment", cfragEP, notNullValue());
+ assertThat("Wrong combined fragment", cfragEP, is(combinedFragmentEP));
+
+ Rectangle lifelineBounds = lifelineEP.getFigure().getBounds();
+ Rectangle operandBounds = operandEP.getFigure().getBounds();
+ Rectangle intersection = operandBounds.intersect(lifelineBounds);
+ assertThat("Interaction operand does not span lifeline",
+ intersection.width(), greaterThanOrEqual(lifelineBounds.width()));
+ };
+ verifyOperand.accept(interactionOperandEP);
+
+ InteractionOperand operand = (InteractionOperand) interactionOperandEP.getAdapter(EObject.class);
+
+ editor.undo();
+
+ interactionOperandEP = (GraphicalEditPart) editor.findEditPart(operand);
+ assertThat("Interaction operand still present in the diagram", interactionOperandEP, nullValue());
+
+ editor.redo();
+
+ interactionOperandEP = (GraphicalEditPart) editor.findEditPart(operand);
+ verifyOperand.accept(interactionOperandEP);
+ }
+
+ /**
+ * Verify the creation of an interaction operand by drawing the tool over two lifelines
+ * that are covered by (but in the diagram actually occlude) a combined fragment.
+ */
+ @Test
+ @PluginResource("resource/bugs/bug533683.di")
+ public void createOperandOverLifelines_533672() {
+ GraphicalEditPart aEP = (GraphicalEditPart) editor.findEditPart("a", Lifeline.class);
+ GraphicalEditPart bEP = (GraphicalEditPart) editor.findEditPart("b", Lifeline.class);
+ EditPart combinedFragmentEP = editor.findEditPart("cfrag", CombinedFragment.class);
+ assumeThat("Combined fragment not found", combinedFragmentEP, notNullValue());
+
+ GraphicalEditPart interactionOperandEP = (GraphicalEditPart) editor.createShape(
+ aEP, UMLElementTypes.InteractionOperand_Shape,
+ at(80, 80), sized(220, 120)); // Extend to a point within lifeline b
+
+ Consumer<GraphicalEditPart> verifyOperand = operandEP -> {
+ assertThat(operandEP, DiagramMatchers.semanticThat(instanceOf(InteractionOperand.class)));
+ EditPart cfragEP = SequenceUtil.getParentCombinedFragmentPart(operandEP);
+ assertThat("No containing combined fragment", cfragEP, notNullValue());
+ assertThat("Wrong combined fragment", cfragEP, is(combinedFragmentEP));
+
+ Rectangle lifelineBounds = aEP.getFigure().getBounds().getUnion(
+ bEP.getFigure().getBounds());
+ Rectangle operandBounds = operandEP.getFigure().getBounds();
+ Rectangle intersection = operandBounds.intersect(lifelineBounds);
+ assertThat("Interaction operand does not span lifeline",
+ intersection.width(), greaterThanOrEqual(lifelineBounds.width()));
+ };
+ verifyOperand.accept(interactionOperandEP);
+
+ InteractionOperand operand = (InteractionOperand) interactionOperandEP.getAdapter(EObject.class);
+
+ editor.undo();
+
+ interactionOperandEP = (GraphicalEditPart) editor.findEditPart(operand);
+ assertThat("Interaction operand still present in the diagram", interactionOperandEP, nullValue());
+
+ editor.redo();
+
+ interactionOperandEP = (GraphicalEditPart) editor.findEditPart(operand);
+ verifyOperand.accept(interactionOperandEP);
+ }
+
+ /**
+ * Verify that an interaction operand cannot be created on a lifeline that is not
+ * covered by a combined fragment.
+ */
+ @Test
+ @PluginResource("resource/bugs/bug533673.di")
+ public void noCreateOperandOnLifelineWithoutCFrag_533672() {
+ IGraphicalEditPart lifelineEP = (IGraphicalEditPart) editor.findEditPart("a", Lifeline.class);
+
+ CreateViewRequest request = CreateViewRequestFactory.getCreateShapeRequest(
+ UMLElementTypes.InteractionOperand_Shape,
+ lifelineEP.getDiagramPreferencesHint());
+
+ request.setLocation(at(80, 80));
+ // Default size
+
+ EditPart target = lifelineEP.getTargetEditPart(request);
+ assertThat("No target edit part", target, notNullValue());
+ org.eclipse.gef.commands.Command command = target.getCommand(request);
+ assertThat("Should not be able to create", command, not(CommandMatchers.GEF.canExecute()));
+ }
}