Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tests for semantic ordering after creation on top of lifeline #1

Closed
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ target/
*~
tests/**/.metadata/
**/src/site/resources/images/rcptt-screenshots
.vscode
.vscode
**/.polyglot.build.properties
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.eclipse.gmf.runtime.notation.View;
Expand Down Expand Up @@ -100,7 +99,6 @@ default boolean precedes(Vertex other) {
* @return whether I succeed the {@code other} in the interaction
*/
default boolean succeeds(Vertex other) {
predecessors().collect(Collectors.toList());
return predecessors().anyMatch(other::equals);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,17 +180,43 @@ protected Command createCommand() {

// Now we have commands to add the execution specification. But, first we must make
// room for it in the diagram. Nudge the element that will follow the new execution
Optional<Command> makeSpace = createNudgeCommand(insertionPoint, execParams.isInsertBefore());
if (makeSpace.isPresent()) {
result = makeSpace.get().chain(result);
}

return result;
}

/**
* Creates the nudge command for the insertion of the execution.
* <p>
* If the insertion of the execution <code>isInsertBefore</code>, the <code>insertionPoint</code> also
* needs to be nudged, otherwise we need to nudge everything after the insertion point.
* </p>
*
* @param insertionPoint
* the insertion point of the execution's insert command.
* @param isInsertBefore
* whether or not, the insert command inserted before or after the insertion point.
* @return the nudge command.
*/
protected Optional<Command> createNudgeCommand(MElement<? extends Element> insertionPoint,
boolean isInsertBefore) {
final MElement<? extends Element> nudgeStart;
if (isInsertBefore) {
nudgeStart = getTarget().preceding(insertionPoint).orElse(before);
} else {
nudgeStart = insertionPoint;
}

MElement<?> distanceFrom = insertionPoint;
Optional<Command> makeSpace = getTarget().following(insertionPoint).map(el -> {
Optional<Command> makeSpace = getTarget().following(nudgeStart).map(el -> {
OptionalInt distance = el.verticalDistance(distanceFrom);
return distance.isPresent() ? el.nudge(height) : null;
});
if (makeSpace.isPresent()) {
result = makeSpace.get().chain(result);
}

return result;
return makeSpace;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,15 @@
import org.eclipse.papyrus.uml.interaction.model.MLifeline;
import org.eclipse.papyrus.uml.interaction.model.MMessage;
import org.eclipse.papyrus.uml.interaction.model.MOccurrence;
import org.eclipse.papyrus.uml.interaction.model.spi.LayoutConstraints;
import org.eclipse.papyrus.uml.interaction.model.spi.ViewTypes;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.MessageSort;

/**
* This command analyses the current graph and fills the white space created by the deletion of elements.
*/
@SuppressWarnings("boxing")
public class NudgeOnRemovalCommand extends ModelCommand<MInteractionImpl> {

private static final int DEFAULT_VERTICAL_OFFSET = 10;
Expand Down Expand Up @@ -149,23 +151,23 @@ private List<Command> createVerticalFullLifelineNudgeCommands() {
return nudgeCommands;
}

@SuppressWarnings("boxing")
private int getDefaultLifelineTop(MLifeline lifeline) {
/* move lifeline header up to very top */
int offsetY = layoutHelper().getConstraints().getYOffset(ViewTypes.LIFELINE_HEADER);
int offsetY = constraints().getYOffset(ViewTypes.LIFELINE_HEADER);
return lifeline.getDiagramView().map(v -> layoutHelper().toAbsoluteY(v, offsetY)).orElse(0);
}

@SuppressWarnings("boxing")
private List<Command> createVerticalNudgeCommands() {
List<Command> nudgeCommands = new ArrayList<>();
/* find out the deleted range */
int deletedTop = Integer.MAX_VALUE;
int deletedBottom = Integer.MIN_VALUE;
MElement<? extends Element> topMostDeletedElement = null;
for (MElement<? extends Element> element : mElementsToRemove) {
OptionalInt top = getTop(element);
if (top.isPresent() && top.getAsInt() < deletedTop) {
deletedTop = top.getAsInt();
topMostDeletedElement = element;
}
OptionalInt bottom = element.getBottom();
if (bottom.isPresent() && bottom.getAsInt() > deletedBottom) {
Expand All @@ -187,6 +189,14 @@ private List<Command> createVerticalNudgeCommands() {
putValuesInMaps(message, topToElements, bottomToElements);
}

/* put bottom of lowest lifeline's header into bottomToElements map */
Optional<MLifeline> lowestRelatedLifeline = getRelatedLifelineWithLowestTop(topMostDeletedElement);
if (lowestRelatedLifeline.isPresent() && lowestRelatedLifeline.get().getTop().isPresent()) {
int lifelineHeaderHeight = constraints().getMinimumHeight(ViewTypes.LIFELINE_HEADER);
int bottom = lowestRelatedLifeline.get().getTop().getAsInt() + lifelineHeaderHeight;
putValueInMap(bottom, lowestRelatedLifeline.get(), bottomToElements);
}

/* analyse the starting points of remaining elements in comparison with deleted range */

/* find out if we have to move elements which start in the deleted range */
Expand Down Expand Up @@ -228,7 +238,7 @@ private List<Command> createVerticalNudgeCommands() {
/*
* keep previous distance between delete range and next element and move below deleted range
*/
delta = firstBottomAboveDeletion.orElse(bottomFinal) - bottomFinal
delta = firstBottomAboveDeletion.orElse(deletedTop) - bottomFinal
+ additionalVerticalOffSet();
}

Expand All @@ -244,6 +254,31 @@ private List<Command> createVerticalNudgeCommands() {
return nudgeCommands;
}

private Optional<MLifeline> getRelatedLifelineWithLowestTop(MElement<? extends Element> element) {
if (element == null) {
return Optional.empty();
}

final List<MLifeline> relatedLifelines = new ArrayList<>();

if (element.getOwner() instanceof MLifeline) {
relatedLifelines.add((MLifeline)element.getOwner());
}

if (element instanceof MMessage) {
MMessage mMessage = (MMessage)element;
mMessage.getSender().ifPresent(relatedLifelines::add);
mMessage.getReceiver().ifPresent(relatedLifelines::add);
}

return relatedLifelines.stream().reduce((lowest, current) -> lowest.getTop()
.orElse(Integer.MIN_VALUE) >= current.getTop().orElse(Integer.MIN_VALUE) ? lowest : current);
}

private LayoutConstraints constraints() {
return layoutHelper().getConstraints();
}

/**
* Checks if an additional vertical offset is required.
*/
Expand Down Expand Up @@ -305,7 +340,6 @@ private OptionalInt getTop(MElement<? extends Element> element) {
}

/** Whether elements in the deleted range have to be moved up. */
@SuppressWarnings("boxing")
private boolean shouldMoveUpperElements(final int topFinal, final int bottomFinal,
Map<Integer, Set<MElement<? extends Element>>> topToElements,
Map<Integer, Set<MElement<? extends Element>>> bottomToElements) {
Expand All @@ -330,7 +364,6 @@ private boolean shouldMoveUpperElements(final int topFinal, final int bottomFina
}

/** Whether elements starting below the deleted range need to be moved up. */
@SuppressWarnings("boxing")
private boolean shouldMoveBottomElements(final int bottomFinal,
Map<Integer, Set<MElement<? extends Element>>> topToElements) {
/*
Expand All @@ -345,7 +378,6 @@ private boolean shouldMoveBottomElements(final int bottomFinal,
.isPresent();
}

@SuppressWarnings("boxing")
private void putValuesInMaps(MElement<? extends Element> element,
Map<Integer, Set<MElement<? extends Element>>> topToElements,
Map<Integer, Set<MElement<? extends Element>>> bottomToElements) {
Expand All @@ -355,14 +387,16 @@ private void putValuesInMaps(MElement<? extends Element> element,

Integer top = getTop(element).orElse(Integer.MAX_VALUE);
Integer bottom = element.getBottom().orElse(Integer.MIN_VALUE);
if (!topToElements.containsKey(top)) {
topToElements.put(top, new LinkedHashSet<>());
}
if (!bottomToElements.containsKey(bottom)) {
bottomToElements.put(bottom, new LinkedHashSet<>());
putValueInMap(top, element, topToElements);
putValueInMap(bottom, element, bottomToElements);
}

protected void putValueInMap(Integer value, MElement<? extends Element> element,
Map<Integer, Set<MElement<? extends Element>>> map) {
if (!map.containsKey(value)) {
map.put(value, new LinkedHashSet<>());
}
topToElements.get(top).add(element);
bottomToElements.get(bottom).add(element);
map.get(value).add(element);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*****************************************************************************
* Copyright (c) 2018 EclipseSource 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:
* Philip Langer - Initial API and implementation
*****************************************************************************/
package org.eclipse.papyrus.uml.interaction.model.tests;

import java.util.List;
import java.util.Optional;

import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.papyrus.uml.interaction.internal.model.impl.LogicalModelPlugin;
import org.eclipse.papyrus.uml.interaction.model.MElement;
import org.eclipse.papyrus.uml.interaction.model.MExecution;
import org.eclipse.papyrus.uml.interaction.model.MInteraction;
import org.eclipse.papyrus.uml.interaction.model.MLifeline;
import org.eclipse.papyrus.uml.interaction.model.MMessage;
import org.eclipse.papyrus.uml.interaction.model.MOccurrence;
import org.eclipse.papyrus.uml.interaction.model.spi.DiagramHelper;
import org.eclipse.papyrus.uml.interaction.model.spi.LayoutHelper;
import org.eclipse.papyrus.uml.interaction.tests.rules.ModelFixture.Edit;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.ExecutionSpecification;
import org.eclipse.uml2.uml.InteractionFragment;
import org.eclipse.uml2.uml.Message;

@SuppressWarnings("restriction")
public class ModelEditFixture extends Edit {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! We've needed this for a while. 👍

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, glad you like it!


public DiagramHelper diagramHelper() {
return LogicalModelPlugin.getInstance().getDiagramHelper(this.getEditingDomain());
}

public LayoutHelper layoutHelper() {
return LogicalModelPlugin.getInstance().getLayoutHelper(this.getEditingDomain());
}

public int getLifelineBodyTop(MLifeline lifeline) {
View shape = lifeline.getDiagramView().orElse(null);
return layoutHelper().getTop(diagramHelper().getLifelineBodyShape(shape));
}

public MInteraction getMInteraction() {
return MInteraction.getInstance(getInteraction(), getSequenceDiagram().get());
}

public Optional<MMessage> getMMessage(Message message) {
return getMInteraction().getMessage(message);
}

public Optional<MExecution> getMExecution(ExecutionSpecification execution) {
Optional<MElement<? extends ExecutionSpecification>> element = getMInteraction()
.getElement(execution);
if (element.isPresent() && element.get() instanceof MExecution) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The usual way to do this is

return element.filter(MExecution.class::isInstance).map(MExecution.class::cast);

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(y)

return Optional.of((MExecution)element.get());
}
return Optional.empty();
}

public <T extends MElement<? extends Element>> T getElement(String qnFormat, String name, Class<T> type) {
cdamus marked this conversation as resolved.
Show resolved Hide resolved
String qName = String.format(qnFormat, name);

Element element = getElement(qName);
return Optional.ofNullable(element).flatMap(getMInteraction()::getElement).filter(type::isInstance)
.map(type::cast).orElseThrow(() -> new AssertionError("no such element: " + name)); //$NON-NLS-1$
}

@SuppressWarnings("unchecked")
public <T extends MElement<? extends Element>> T getElement(String qnFormat, String name) {
return (T)getElement(qnFormat, name, MElement.class);
}

@SuppressWarnings({"boxing" })
public Object isSemanticallyBefore(MOccurrence<? extends Element> one,
MOccurrence<? extends Element> other) {
List<InteractionFragment> fragments = getMInteraction().getElement().getFragments();
int indexOfOne = fragments.indexOf(one.getElement());
int indexOfOther = fragments.indexOf(other.getElement());
return indexOfOne >= 0 && indexOfOther >= 0 && indexOfOther > indexOfOne;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@

import org.eclipse.papyrus.uml.interaction.internal.model.spi.impl.tests.DefaultLayoutHelperTest;
import org.eclipse.papyrus.uml.interaction.internal.model.spi.impl.tests.LogicalModelAdapterTest;
import org.eclipse.papyrus.uml.interaction.model.tests.creation.CreateExecutionTest;
import org.eclipse.papyrus.uml.interaction.model.tests.creation.CreateMessageTest;
import org.eclipse.papyrus.uml.interaction.model.tests.creation.CreateMessageTestB;
import org.eclipse.papyrus.uml.interaction.model.tests.creation.SemanticOrderAfterCreationOfElementOnTopTest;
import org.eclipse.papyrus.uml.interaction.model.tests.deletion.BasicDeletionTest;
import org.eclipse.papyrus.uml.interaction.model.tests.deletion.CreationMessageDeletionTest;
import org.eclipse.papyrus.uml.interaction.model.tests.deletion.DeleteExecutionTest;
import org.eclipse.papyrus.uml.interaction.model.tests.deletion.DeletionMessageDeletionTest;
import org.eclipse.papyrus.uml.interaction.model.util.tests.LogicalModelPredicatesTest;
import org.junit.runner.RunWith;
Expand All @@ -33,7 +36,9 @@
@SuiteClasses({ //
DefaultLayoutHelperTest.class, //
CreateMessageTest.class, CreateMessageTestB.class, //
CreateExecutionTest.class, DeleteExecutionTest.class, //
CreationMessageDeletionTest.class, BasicDeletionTest.class, DeletionMessageDeletionTest.class, //
SemanticOrderAfterCreationOfElementOnTopTest.class, //
LogicalModelAdapterTest.class, //
LogicalModelPredicatesTest.class, //
})
Expand Down
Loading