/*****************************************************************************
* Copyright (c) 2009 CEA
*
*
* 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:
* Atos Origin - Initial API and implementation
* Camille Letavernier (camille.letavernier@cea.fr) - Loosen the MessageSortChange restriction
*
*****************************************************************************/
package org.eclipse.papyrus.uml.diagram.sequence.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.draw2d.AbstractPointListShape;
import org.eclipse.draw2d.Connection;
import org.eclipse.draw2d.ConnectionAnchor;
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.PrecisionPoint;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature.Setting;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.edit.command.SetCommand;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gmf.runtime.common.core.command.CommandResult;
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.commands.SetBoundsCommand;
import org.eclipse.gmf.runtime.diagram.ui.editparts.ConnectionEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.ConnectionNodeEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IBorderItemEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeNodeEditPart;
import org.eclipse.gmf.runtime.diagram.ui.l10n.DiagramUIMessages;
import org.eclipse.gmf.runtime.draw2d.ui.figures.BaseSlidableAnchor;
import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand;
import org.eclipse.gmf.runtime.emf.core.util.EObjectAdapter;
import org.eclipse.gmf.runtime.notation.Anchor;
import org.eclipse.gmf.runtime.notation.Bounds;
import org.eclipse.gmf.runtime.notation.Edge;
import org.eclipse.gmf.runtime.notation.IdentityAnchor;
import org.eclipse.gmf.runtime.notation.LayoutConstraint;
import org.eclipse.gmf.runtime.notation.Node;
import org.eclipse.gmf.runtime.notation.NotationPackage;
import org.eclipse.gmf.runtime.notation.Shape;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.gmf.runtime.notation.impl.ShapeImpl;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.papyrus.uml.diagram.common.helper.DurationConstraintHelper;
import org.eclipse.papyrus.uml.diagram.common.helper.InteractionFragmentHelper;
import org.eclipse.papyrus.uml.diagram.common.util.DiagramEditPartsUtil;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.ActionExecutionSpecificationEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.BehaviorExecutionSpecificationEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.CombinedFragment2EditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.CombinedFragmentEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.ContinuationEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.DestructionOccurrenceSpecificationEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.DurationObservationEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.InteractionEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.InteractionOperandEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.InteractionUseEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.LifelineEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.Message2EditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.Message3EditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.Message4EditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.Message5EditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.Message6EditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.Message7EditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.MessageAsyncAppliedStereotypeEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.MessageCreateAppliedStereotypeEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.MessageDeleteAppliedStereotypeEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.MessageEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.MessageFoundAppliedStereotypeEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.MessageLostAppliedStereotypeEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.MessageName2EditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.MessageName3EditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.MessageName4EditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.MessageName5EditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.MessageName6EditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.MessageName7EditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.MessageNameEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.MessageReplyAppliedStereotypeEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.MessageSyncAppliedStereotypeEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.ObservationLinkEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.StateInvariantEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.TimeObservationLabelEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.policies.ObservationLinkPolicy;
import org.eclipse.swt.widgets.Display;
import org.eclipse.uml2.common.util.CacheAdapter;
import org.eclipse.uml2.uml.CombinedFragment;
import org.eclipse.uml2.uml.Continuation;
import org.eclipse.uml2.uml.DestructionOccurrenceSpecification;
import org.eclipse.uml2.uml.DurationConstraint;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.ExecutionOccurrenceSpecification;
import org.eclipse.uml2.uml.ExecutionSpecification;
import org.eclipse.uml2.uml.Interaction;
import org.eclipse.uml2.uml.InteractionFragment;
import org.eclipse.uml2.uml.InteractionOperand;
import org.eclipse.uml2.uml.InteractionOperatorKind;
import org.eclipse.uml2.uml.InteractionUse;
import org.eclipse.uml2.uml.Lifeline;
import org.eclipse.uml2.uml.Message;
import org.eclipse.uml2.uml.MessageEnd;
import org.eclipse.uml2.uml.MessageOccurrenceSpecification;
import org.eclipse.uml2.uml.MessageSort;
import org.eclipse.uml2.uml.OccurrenceSpecification;
import org.eclipse.uml2.uml.StateInvariant;
import org.eclipse.uml2.uml.TimeConstraint;
import org.eclipse.uml2.uml.TimeObservation;
import org.eclipse.uml2.uml.UMLPackage;
public class SequenceUtil {
private static final double MAXIMAL_DISTANCE_FROM_EVENT = 10;
/**
* Request type of observation link
*/
public static final String OBSERVATION_LINK_TYPE = "observation link"; //$NON-NLS-1$
public static final String OBSERVATION_LINK_REQUEST_START = "observation connection start"; //$NON-NLS-1$
public static final String OBSERVATION_LINK_REQUEST_END = "observation connection end"; //$NON-NLS-1$
public static final String OBSERVATION_LINK_REQUEST_RECONNECT_SOURCE = "observation reconnect source"; //$NON-NLS-1$
public static final String OBSERVATION_LINK_REQUEST_RECONNECT_TARGET = "observation reconnect target"; //$NON-NLS-1$
/**
* Default vertical offset of lifeline
*/
public static final int LIFELINE_VERTICAL_OFFSET = 10;
/**
* Title for dialog of block message sort modification error
*/
private static final String BLOCK_SORT_MODIFICATION_TITLE = "Forbidden action"; //$NON-NLS-1$
/**
* Message for dialog of block message sort modification error
*/
private static final String BLOCK_SORT_MODIFICATION_MSG = "It's impossible to change the message sort."; //$NON-NLS-1$
/**
* apex added
*
* @param location
* @param hostEditPart
* @return
*/
public static InteractionFragment findInteractionFragmentContainerAt(Point location, EditPart hostEditPart) {
return findInteractionFragmentContainerAt(location, hostEditPart, true);
}
/**
* apex updated
*
* Find the container interaction fragment at the given location.
* The elements are drawn under the lifeline, but their model container is an interaction.
* It can be of type Interaction or InteractionOperand.
*
* @param location
* the location
* @param hostEditPart
* @param isIncluding
* true일 경우 hostEditPart가 IOEP가 하나인 CombinedFragmentEditPart일 경우 그 CFEP의 container로 그 CFEP의 자식인 IO를 반환한다
* false일 경우 CF의 자식인 IO는 반환하지 않음
* @return the interaction or null
*/
public static InteractionFragment findInteractionFragmentContainerAt(Point location, EditPart hostEditPart, boolean isIncludingCFItself) {
Rectangle bounds = new Rectangle();
bounds.setLocation(location);
/* apex improved start */
return findInteractionFragmentContainerAt(bounds, hostEditPart, isIncludingCFItself);
/* apex improved end */
/* apex replaced
return findInteractionFragmentContainerAt(bounds, hostEditPart);
*/
}
/**
* apex added
*
* @param bounds
* @param hostEditPart
* @return
*/
@SuppressWarnings("unchecked")
public static InteractionFragment findInteractionFragmentContainerAt(Rectangle bounds, EditPart hostEditPart) {
return findInteractionFragmentContainerAt(bounds, hostEditPart, true);
}
/**
* apex updated
*
* 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 adit part in the corresponding diagram
* @param isIncludingCFItself
* true일 경우 hostEditPart가 IOEP가 하나인 CombinedFragmentEditPart일 경우 그 CFEP의 container로 그 CFEP의 자식인 IO를 반환한다
* false일 경우 CF의 자식인 IO는 반환하지 않음
* @return the interaction or null
*/
@SuppressWarnings("unchecked")
public static InteractionFragment findInteractionFragmentContainerAt(Rectangle bounds, EditPart hostEditPart, boolean isIncludingCFItself) {
if(hostEditPart == null) {
return null;
}
/* apex added start */
// 지워진 EditPart의 경우 getViewer()가 null을 반환하여 NullPointException 발생시킴
if ( hostEditPart.getViewer() == null ) {
return null;
}
/* apex added end */
InteractionFragment container = null;
Set<InteractionFragment> coveredInteractions = new HashSet<InteractionFragment>();
Set<CombinedFragment> coveredCF = new HashSet<CombinedFragment>();
Set<Entry<Object, EditPart>> allEditPartEntries = hostEditPart.getViewer().getEditPartRegistry().entrySet();
for(Entry<Object, EditPart> epEntry : allEditPartEntries) {
EditPart ep = epEntry.getValue();
if(ep instanceof ShapeEditPart) {
ShapeEditPart sep = (ShapeEditPart)ep;
EObject eObject = sep.resolveSemanticElement();
if(eObject instanceof Interaction || eObject instanceof InteractionOperand) {
Rectangle figureBounds = getAbsoluteBounds(sep);
if(figureBounds.contains(bounds)) {
coveredInteractions.add((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);
}
}
}
}
// 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)) {
// bounds are in the header, add the first operand
/* apex improved started */
if ( isIncludingCFItself ) {
coveredInteractions.add(operands.get(0));
}
/* apex improved end */
/* apex replaced
coveredInteractions.add(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;
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(!subiftFounded && 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(!subiftFounded) {
container = ift;
break;
}
}
return container;
}
/**
* Find the location on the lifeline of an interaction fragment
*
* @param lifelineEditPart
* the lifeline edit part
* @param fragment
* the searched interaction fragment
* @return the absolute location or null if not found
*/
public static Point findLocationOfEvent(LifelineEditPart lifelineEditPart, InteractionFragment fragment) {
if(lifelineEditPart == null) {
return null;
}
// Search for corresponding node edit part out of the lifeline.
if(fragment instanceof CombinedFragment || fragment instanceof Continuation || fragment instanceof InteractionOperand || fragment instanceof InteractionUse || fragment instanceof Interaction) {
List<View> views = DiagramEditPartsUtil.findViews(fragment, lifelineEditPart.getViewer());
for(View view : views) {
EditPart part = DiagramEditPartsUtil.getEditPartFromView(view, lifelineEditPart);
boolean isCombinedFragment = part instanceof CombinedFragmentEditPart || part instanceof CombinedFragment2EditPart;
boolean isContinuation = part instanceof ContinuationEditPart;
boolean isInteractionOperand = part instanceof InteractionOperandEditPart;
boolean isInteractionUse = part instanceof InteractionUseEditPart;
boolean isInteraction = part instanceof InteractionEditPart;
if(isCombinedFragment || isContinuation || isInteractionOperand || isInteractionUse || isInteraction) {
Rectangle bounds = getAbsoluteBounds((GraphicalEditPart)part);
return bounds.getTop();
}
}
} else {
// search on graphical children of the lifeline
List<?> children = lifelineEditPart.getChildren();
for(Object child : children) {
// check destruction event
if(child instanceof DestructionOccurrenceSpecificationEditPart) {
EObject destructionOccurence = ((GraphicalEditPart)child).resolveSemanticElement();
EObject lifeline = lifelineEditPart.resolveSemanticElement();
if(destructionOccurence instanceof DestructionOccurrenceSpecification && lifeline instanceof Lifeline && fragment instanceof DestructionOccurrenceSpecification) {
DestructionOccurrenceSpecification destEvent = ((DestructionOccurrenceSpecification)fragment);
if(destEvent != null && destEvent.equals(destructionOccurence)) {
Rectangle bounds = getAbsoluteBounds((GraphicalEditPart)child);
return bounds.getCenter();
}
}
}
// check in children executions
if(child instanceof ActionExecutionSpecificationEditPart || child instanceof BehaviorExecutionSpecificationEditPart) {
if(fragment instanceof ExecutionSpecification) {
// check the execution
EObject element = ((GraphicalEditPart)child).resolveSemanticElement();
if(element instanceof ExecutionSpecification) {
if(fragment.equals(element)) {
Rectangle bounds = getAbsoluteBounds((GraphicalEditPart)child);
return bounds.getTop();
}
}
} else if(fragment instanceof ExecutionOccurrenceSpecification) {
// check start and finish events of the execution
EObject element = ((GraphicalEditPart)child).resolveSemanticElement();
if(element instanceof ExecutionSpecification) {
if(fragment.equals(((ExecutionSpecification)element).getStart())) {
Rectangle bounds = getAbsoluteBounds((GraphicalEditPart)child);
return bounds.getTop();
} else if(fragment.equals(((ExecutionSpecification)element).getFinish())) {
Rectangle bounds = getAbsoluteBounds((GraphicalEditPart)child);
return bounds.getBottom();
}
}
} else if(fragment instanceof MessageOccurrenceSpecification) {
// check messages to and from the execution
Point loc = findLocationOfMessageOccurrence((GraphicalEditPart)child, (MessageOccurrenceSpecification)fragment);
if(loc != null) {
return loc;
}
}
}
// check in children StateInvariant
if(child instanceof StateInvariantEditPart) {
if(fragment instanceof StateInvariant) {
// check the StateInvariant
EObject element = ((GraphicalEditPart)child).resolveSemanticElement();
if(element instanceof StateInvariant) {
if(fragment.equals(element)) {
Rectangle bounds = getAbsoluteBounds((GraphicalEditPart)child);
return bounds.getTop();
}
}
} else if(fragment instanceof MessageOccurrenceSpecification) {
// check messages to and from the execution
Point loc = findLocationOfMessageOccurrence((GraphicalEditPart)child, (MessageOccurrenceSpecification)fragment);
if(loc != null) {
return loc;
}
}
}
}
if(fragment instanceof MessageOccurrenceSpecification) {
// check messages to and from the lifeline
Point loc = findLocationOfMessageOccurrence(lifelineEditPart, (MessageOccurrenceSpecification)fragment);
if(loc != null) {
return loc;
}
}
}
// If we found nothing, this may be a sync message receive
if(fragment instanceof MessageOccurrenceSpecification) {
boolean isSync = ((MessageOccurrenceSpecification)fragment).getMessage() != null && MessageSort.SYNCH_CALL_LITERAL.equals(((MessageOccurrenceSpecification)fragment).getMessage().getMessageSort());
if(isSync) {
// sync message should trigger an execution specification start. Find and return the corresponding start.
EObject container = fragment.eContainer();
EObject lifeline = lifelineEditPart.resolveSemanticElement();
InteractionFragment nextFragment = InteractionFragmentHelper.findNextFragment(fragment, container);
while(nextFragment != null && nextFragment.getCovereds().contains(lifeline)) {
if(nextFragment.getCovereds().contains(lifeline)) {
// Found next event of lifeline. Check if it really is a start.
if(nextFragment instanceof ExecutionOccurrenceSpecification) {
ExecutionSpecification exe = ((ExecutionOccurrenceSpecification)nextFragment).getExecution();
if(exe != null && EcoreUtil.equals(exe.getStart(), nextFragment)) {
// return location of the start.
return findLocationOfEvent(lifelineEditPart, nextFragment);
}
}
break;
} else {
nextFragment = InteractionFragmentHelper.findNextFragment(nextFragment, container);
}
}
}
}
return null;
}
/**
* Get the bounds of an edit part
*
* @param part
* edit part to find bounds
* @return part's bounds in absolute coordinates
*/
public static Rectangle getAbsoluteBounds(IGraphicalEditPart part) {
// take bounds from figure
Rectangle bounds = part.getFigure().getBounds().getCopy();
if(part.getNotationView() instanceof Node) {
// rather update with up to date model bounds
Node node = (Node)part.getNotationView();
LayoutConstraint cst = node.getLayoutConstraint();
if(cst instanceof Bounds) {
Bounds b = (Bounds)cst;
Point parentLoc = part.getFigure().getParent().getBounds().getLocation();
if(b.getX() > 0) {
bounds.x = b.getX() + parentLoc.x;
}
if(b.getY() > 0) {
bounds.y = b.getY() + parentLoc.y;
}
if(b.getHeight() != -1) {
bounds.height = b.getHeight();
}
if(b.getWidth() != -1) {
bounds.width = b.getWidth();
}
}
}
part.getFigure().getParent().translateToAbsolute(bounds);
return bounds;
}
/**
* Get the extremity of a connection edit part
*
* @param connection
* the connection edit part to find extremity
* @param isStart
* true to find the start, false for the end
* @return connection's extremity in absolute coordinates or null
*/
public static Point getAbsoluteEdgeExtremity(ConnectionNodeEditPart connection, boolean isStart) {
Connection msgFigure = connection.getConnectionFigure();
if(connection.getNotationView() instanceof Edge) {
// rather take up to date model information
Edge edge = (Edge)connection.getNotationView();
Anchor idAnchor = null;
ConnectionAnchor conAnchor = null;
Object part = null;
if(isStart && connection.getSource() instanceof IGraphicalEditPart) {
View linkedFigure = edge.getSource();
// connection.getSource() may be not up to date, get part for linkedFigure
part = connection.getSource().getViewer().getEditPartRegistry().get(linkedFigure);
idAnchor = edge.getSourceAnchor();
conAnchor = msgFigure.getSourceAnchor();
} else if(!isStart && connection.getTarget() instanceof IGraphicalEditPart) {
View linkedFigure = edge.getTarget();
// connection.getTarget() may be not up to date, get part for linkedFigure
part = connection.getTarget().getViewer().getEditPartRegistry().get(linkedFigure);
idAnchor = edge.getTargetAnchor();
conAnchor = msgFigure.getTargetAnchor();
}
if(part instanceof IGraphicalEditPart && idAnchor instanceof IdentityAnchor && conAnchor != null) {
// take up to date bounds of the linked part in case it is moved
Rectangle linkedPartBounds = getAbsoluteBounds((IGraphicalEditPart)part);
IFigure anchorOwningFigure = conAnchor.getOwner();
IFigure partFigure = ((IGraphicalEditPart)part).getFigure();
Dimension delta = anchorOwningFigure.getBounds().getLocation().getDifference(partFigure.getBounds().getLocation());
// get position from anchor id
String oldTerminal = ((IdentityAnchor)idAnchor).getId();
PrecisionPoint pp = BaseSlidableAnchor.parseTerminalString(oldTerminal);
int xPos = linkedPartBounds.x + delta.width + (int)Math.round(anchorOwningFigure.getBounds().width * pp.preciseX);
int yPos = linkedPartBounds.y + delta.height + (int)Math.round(anchorOwningFigure.getBounds().height * pp.preciseY);
return new Point(xPos, yPos);
}
}
// can not get from model, rely on figure
if(msgFigure instanceof AbstractPointListShape) {
Point extremity;
if(isStart) {
// start event of the message
extremity = ((AbstractPointListShape)msgFigure).getStart().getCopy();
} else {
// finish event of the message
extremity = ((AbstractPointListShape)msgFigure).getEnd().getCopy();
}
msgFigure.getParent().translateToAbsolute(extremity);
return extremity;
}
return null;
}
/**
* Find the location on a node of a message occurrence specification
*
* @param nodeEditPart
* the node edit part which to check incoming and outgoing messages
* @param event
* the message occurrence specification
* @return the absolute location or null
*/
public static Point findLocationOfMessageOccurrence(GraphicalEditPart nodeEditPart, MessageOccurrenceSpecification event) {
// messages to the node
List<?> targetConnections = nodeEditPart.getTargetConnections();
for(Object conn : targetConnections) {
if(conn instanceof ConnectionNodeEditPart) {
EObject element = ((ConnectionNodeEditPart)conn).resolveSemanticElement();
if(element instanceof Message && event.equals(((Message)element).getReceiveEvent())) {
// finish event of the message
IFigure figure = ((ConnectionNodeEditPart)conn).getFigure();
if(figure instanceof AbstractPointListShape) {
return getAbsoluteEdgeExtremity((ConnectionNodeEditPart)conn, false);
}
}
}
}
// messages from the node
List<?> sourceConnections = nodeEditPart.getSourceConnections();
for(Object conn : sourceConnections) {
if(conn instanceof ConnectionNodeEditPart) {
EObject element = ((ConnectionNodeEditPart)conn).resolveSemanticElement();
if(element instanceof Message && event.equals(((Message)element).getSendEvent())) {
// start event of the message
IFigure figure = ((ConnectionNodeEditPart)conn).getFigure();
if(figure instanceof AbstractPointListShape) {
return getAbsoluteEdgeExtremity((ConnectionNodeEditPart)conn, true);
}
}
}
}
return null;
}
/**
* Find the location on a node of a execution occurrence specification
*
* @param nodeEditPart
* the node edit part which to check incoming and outgoing messages
* @param event
* the execution occurrence specification
* @return the absolute location or null
*/
public static Point findLocationOfExecutionOccurrence(GraphicalEditPart nodeEditPart, ExecutionOccurrenceSpecification event) {
// child to the node
List<?> children = nodeEditPart.getChildren();
for(Object child : children) {
if(child instanceof ActionExecutionSpecificationEditPart) {
EObject element = ((ActionExecutionSpecificationEditPart)child).resolveSemanticElement();
if(element != null && element instanceof ExecutionSpecification) {
IFigure figure = ((ActionExecutionSpecificationEditPart)child).getFigure();
Rectangle copy = figure.getBounds().getCopy();
figure.translateToAbsolute(copy);
if(event.equals(((ExecutionSpecification)element).getStart())){
return copy.getTop();
}else if(event.equals(((ExecutionSpecification)element).getFinish())){
return copy.getBottom();
}
}
}else if(child instanceof BehaviorExecutionSpecificationEditPart) {
EObject element = ((BehaviorExecutionSpecificationEditPart)child).resolveSemanticElement();
if(element != null && element instanceof ExecutionSpecification) {
IFigure figure = ((BehaviorExecutionSpecificationEditPart)child).getFigure();
Rectangle copy = figure.getBounds().getCopy();
figure.translateToAbsolute(copy);
if(event.equals(((ExecutionSpecification)element).getStart())){
return copy.getTop();
}else if(event.equals(((ExecutionSpecification)element).getFinish())){
return copy.getBottom();
}
}
}
}
return null;
}
/**
* Find the occurrence specification covering the lifeline near the given location.
* If none is close enough, null is returned.
*
* @param location
* the location
* @param lifelineEditPart
* the Lifeline edit part
* @return an entry with the nearest OccurrenceSpecification(s) and its corresponding location or null if none is close enough
*/
public static Entry<Point, List<OccurrenceSpecification>> findNearestEvent(Point location, LifelineEditPart lifelineEditPart) {
if(lifelineEditPart == null) {
return null;
}
// Map referencing children occurrences by their location on the lifeline.
Map<Point, List<OccurrenceSpecification>> occurrences = new HashMap<Point, List<OccurrenceSpecification>>();
// graphical children of the lifeline
List<?> children = lifelineEditPart.getChildren();
for(Object child : children) {
// children executions
if(child instanceof ActionExecutionSpecificationEditPart || child instanceof BehaviorExecutionSpecificationEditPart) {
EObject element = ((GraphicalEditPart)child).resolveSemanticElement();
if(element instanceof ExecutionSpecification) {
// find start and finish events of the execution
Rectangle bounds = getAbsoluteBounds((GraphicalEditPart)child);
if(!occurrences.containsKey(bounds.getTop())) {
// there should be at most 2 occurrences (with starting message)
occurrences.put(bounds.getTop(), new ArrayList<OccurrenceSpecification>(2));
}
occurrences.get(bounds.getTop()).add(((ExecutionSpecification)element).getStart());
if(!occurrences.containsKey(bounds.getBottom())) {
occurrences.put(bounds.getBottom(), new ArrayList<OccurrenceSpecification>(1));
}
occurrences.get(bounds.getBottom()).add(((ExecutionSpecification)element).getFinish());
// messages to and from the execution
completeOccurrencesMapWithMessages((GraphicalEditPart)child, occurrences);
}
}
// destruction event
if(child instanceof DestructionOccurrenceSpecificationEditPart) {
EObject destructionOccurence = ((GraphicalEditPart)child).resolveSemanticElement();
EObject lifeline = lifelineEditPart.resolveSemanticElement();
if(destructionOccurence instanceof DestructionOccurrenceSpecification && lifeline instanceof Lifeline) {
for(InteractionFragment occurence : ((Lifeline)lifeline).getCoveredBys()) {
if(occurence instanceof DestructionOccurrenceSpecification) {
DestructionOccurrenceSpecification currentOccurence = ((DestructionOccurrenceSpecification)occurence);
if(destructionOccurence.equals(currentOccurence)) {
Rectangle bounds = getAbsoluteBounds((GraphicalEditPart)child);
if(!occurrences.containsKey(bounds.getCenter())) {
occurrences.put(bounds.getCenter(), new ArrayList<OccurrenceSpecification>(2));
}
occurrences.get(bounds.getCenter()).add((OccurrenceSpecification)occurence);
break;
}
}
}
}
}
}
// messages to and from the lifeline
completeOccurrencesMapWithMessages(lifelineEditPart, occurrences);
// Find the nearest object within acceptable distance
double smallerDistance = MAXIMAL_DISTANCE_FROM_EVENT;
Entry<Point, List<OccurrenceSpecification>> nearestObject = null;
for(Entry<Point, List<OccurrenceSpecification>> entry : occurrences.entrySet()) {
double distance = location.getDistance(entry.getKey());
if(distance < smallerDistance) {
smallerDistance = distance;
nearestObject = entry;
} else if(distance == smallerDistance && nearestObject != null) {
// two events at the exact same distance.
// Keep both so the best one can be used
if(entry.getValue() instanceof MessageOccurrenceSpecification) {
nearestObject.getValue().addAll(entry.getValue());
}
}
}
return nearestObject;
}
/**
* Complete the map of occurrences and their location, by taking in account messages from and to the node edit part
*
* @param nodeEditPart
* part to consider message around
* @param occurrencesMap
* the map to complete
*/
private static void completeOccurrencesMapWithMessages(GraphicalEditPart nodeEditPart, Map<Point, List<OccurrenceSpecification>> occurrencesMap) {
// messages to the node
List<?> targetConnections = nodeEditPart.getTargetConnections();
for(Object conn : targetConnections) {
if(conn instanceof ConnectionNodeEditPart) {
EObject element = ((ConnectionNodeEditPart)conn).resolveSemanticElement();
if(element instanceof Message && ((Message)element).getReceiveEvent() instanceof MessageOccurrenceSpecification) {
// finish events of the message
IFigure figure = ((ConnectionNodeEditPart)conn).getFigure();
if(figure instanceof AbstractPointListShape) {
Point end = getAbsoluteEdgeExtremity((ConnectionNodeEditPart)conn, false);
if(!occurrencesMap.containsKey(end)) {
occurrencesMap.put(end, new ArrayList<OccurrenceSpecification>(1));
}
occurrencesMap.get(end).add((MessageOccurrenceSpecification)((Message)element).getReceiveEvent());
}
}
}
}
// messages from the node
List<?> sourceConnections = nodeEditPart.getSourceConnections();
for(Object conn : sourceConnections) {
if(conn instanceof ConnectionNodeEditPart) {
EObject element = ((ConnectionNodeEditPart)conn).resolveSemanticElement();
if(element instanceof Message && ((Message)element).getSendEvent() instanceof MessageOccurrenceSpecification) {
// start events of the message
IFigure figure = ((ConnectionNodeEditPart)conn).getFigure();
if(figure instanceof AbstractPointListShape) {
Point start = getAbsoluteEdgeExtremity((ConnectionNodeEditPart)conn, true);
if(!occurrencesMap.containsKey(start)) {
occurrencesMap.put(start, new ArrayList<OccurrenceSpecification>(1));
}
occurrencesMap.get(start).add((MessageOccurrenceSpecification)((Message)element).getSendEvent());
}
}
}
}
}
/**
* The position of the part where the event is linked
*
* @param occSpec
* the occurrence specification
* @param timeElementPart
* the part representing time element (duration/time constraint/observation)
* @return one of {@link PositionConstants#TOP}, {@link PositionConstants#CENTER}, {@link PositionConstants#BOTTOM},
* {@link PositionConstants#NONE}
*/
public static int positionWhereEventIsLinkedToPart(OccurrenceSpecification occSpec, IBorderItemEditPart timeElementPart) {
EObject timeElement = timeElementPart.resolveSemanticElement();
if(timeElement instanceof TimeObservation) {
if(occSpec.equals(((TimeObservation)timeElement).getEvent())) {
return PositionConstants.CENTER;
} else {
return PositionConstants.NONE;
}
} else if(timeElement instanceof TimeConstraint) {
if(((TimeConstraint)timeElement).getConstrainedElements().contains(occSpec)) {
return PositionConstants.CENTER;
} else {
return PositionConstants.NONE;
}
} else if(timeElement instanceof DurationConstraint) {
if(((DurationConstraint)timeElement).getConstrainedElements().contains(occSpec)) {
List<Element> events = ((DurationConstraint)timeElement).getConstrainedElements();
LifelineEditPart lifelinePart = getParentLifelinePart(timeElementPart);
if(lifelinePart != null && events.size() >= 2) {
OccurrenceSpecification otherEvent = null;
if(!occSpec.equals(events.get(0)) && events.get(0) instanceof OccurrenceSpecification) {
otherEvent = (OccurrenceSpecification)events.get(0);
} else if(!occSpec.equals(events.get(1)) && events.get(1) instanceof OccurrenceSpecification) {
otherEvent = (OccurrenceSpecification)events.get(1);
}
if(otherEvent != null) {
Point otherLoc = findLocationOfEvent(lifelinePart, otherEvent);
Point thisLoc = findLocationOfEvent(lifelinePart, occSpec);
if(otherLoc != null && thisLoc != null) {
if(otherLoc.y > thisLoc.y) {
return PositionConstants.TOP;
} else {
return PositionConstants.BOTTOM;
}
}
}
}
} else {
return PositionConstants.NONE;
}
}
return PositionConstants.NONE;
}
/**
* Return the lifeline edit part containing this part (directly or indirectly)
*
* @param nodeEditPart
* the contained edit part or itself
* @return lifeline edit part or null
*/
public static LifelineEditPart getParentLifelinePart(EditPart nodeEditPart) {
EditPart parent = nodeEditPart;
while(parent != null) {
if(parent instanceof LifelineEditPart) {
return (LifelineEditPart)parent;
} else {
parent = parent.getParent();
}
}
return null;
}
/**
* Get the edit part (message, execution, or destruction event) which starts or finishes with the event on the given lifeline part
*
* @param lifelinePart
* the lifeline edit part on which the event is located
* @param event
* the event
* @return the edit part of which an end is defined by event on the lifelinePart edit part
*/
public static EditPart getLinkedEditPart(EditPart lifelinePart, OccurrenceSpecification event) {
if(event instanceof MessageOccurrenceSpecification) {
// get parts representing the message linked with the event
Message message = ((MessageOccurrenceSpecification)event).getMessage();
if(message == null) {
return null;
}
Collection<Setting> settings = CacheAdapter.INSTANCE.getNonNavigableInverseReferences(message);
for(Setting ref : settings) {
if(NotationPackage.eINSTANCE.getView_Element().equals(ref.getEStructuralFeature())) {
View view = (View)ref.getEObject();
EditPart part = DiagramEditPartsUtil.getEditPartFromView(view, lifelinePart);
// the message part must start or finish on the lifeline (with the event)
if(part instanceof ConnectionEditPart) {
EditPart lifelineChild = null;
if(event.equals(message.getSendEvent())) {
lifelineChild = ((ConnectionEditPart)part).getSource();
} else if(event.equals(message.getReceiveEvent())) {
lifelineChild = ((ConnectionEditPart)part).getTarget();
}
LifelineEditPart parentLifeline = getParentLifelinePart(lifelineChild);
if(lifelinePart.equals(parentLifeline)) {
return part;
}
}
}
}
} else if(event instanceof ExecutionOccurrenceSpecification) {
// get parts representing the execution linked with the event
ExecutionSpecification execution = ((ExecutionOccurrenceSpecification)event).getExecution();
if(execution == null) {
return null;
}
Collection<Setting> settings = CacheAdapter.INSTANCE.getNonNavigableInverseReferences(execution);
for(Setting ref : settings) {
if(NotationPackage.eINSTANCE.getView_Element().equals(ref.getEStructuralFeature())) {
View view = (View)ref.getEObject();
EditPart part = DiagramEditPartsUtil.getEditPartFromView(view, lifelinePart);
// the execution part must be on the lifeline
EditPart lifelineChild = part;
LifelineEditPart parentLifeline = getParentLifelinePart(lifelineChild);
if(lifelinePart.equals(parentLifeline)) {
return part;
}
}
}
} else {
// get parts representing the destruction event linked with the event
for(Object lifelineChild : lifelinePart.getChildren()) {
if(lifelineChild instanceof DestructionOccurrenceSpecificationEditPart) {
EObject destr = ((DestructionOccurrenceSpecificationEditPart)lifelineChild).resolveSemanticElement();
if(destr instanceof DestructionOccurrenceSpecification && destr.equals(event)) {
return (EditPart)lifelineChild;
}
}
}
}
return null;
}
/**
* Get the object safely casted as a list of OccurrenceSpecification
*
* @param occurrenceSpecificationList
* the object which is supposed to be a list of OccurrenceSpecification
*/
public static List<OccurrenceSpecification> getAsOccSpecList(Object occurrenceSpecificationList) {
if(occurrenceSpecificationList instanceof List<?>) {
List<?> list = (List<?>)occurrenceSpecificationList;
if(!list.isEmpty()) {
List<OccurrenceSpecification> newList = new ArrayList<OccurrenceSpecification>(list.size());
for(Object elt : list) {
if(elt instanceof OccurrenceSpecification) {
newList.add((OccurrenceSpecification)elt);
}
}
return newList;
}
}
return Collections.emptyList();
}
/**
* Get the pair of OccurrenceSpecification which a duration constraint or observation should be created between
*
* @param occ1List
* the list of occurrences at the same time, among which the first one must be chosen
* @param occ2List
* the list of occurrences at the same time, among which the second one must be chosen
* @return size two array of OccurrenceSpecification which can be linked or null
*/
public static OccurrenceSpecification[] getPairOfCorrespondingOccSpec(List<OccurrenceSpecification> occ1List, List<OccurrenceSpecification> occ2List) {
// check for occurrences linked by a message
for(OccurrenceSpecification occ1 : occ1List) {
for(OccurrenceSpecification occ2 : occ2List) {
if(DurationConstraintHelper.endsOfSameMessage(occ1, occ2)) {
// we must link occurrences of a message
return new OccurrenceSpecification[]{ occ1, occ2 };
}
}
}
// check for occurrences on a same lifeline
for(OccurrenceSpecification occ1 : occ1List) {
if(occ1 instanceof MessageOccurrenceSpecification) {
Message mess = ((MessageOccurrenceSpecification)occ1).getMessage();
if(mess.getReceiveEvent().equals(occ1) && MessageSort.SYNCH_CALL_LITERAL.equals(mess.getMessageSort())) {
// filter receive event, we prefer the corresponding start event at the same location
continue;
}
}
for(OccurrenceSpecification occ2 : occ2List) {
if(occ2 instanceof MessageOccurrenceSpecification) {
Message mess = ((MessageOccurrenceSpecification)occ2).getMessage();
if(mess.getReceiveEvent().equals(occ2) && MessageSort.SYNCH_CALL_LITERAL.equals(mess.getMessageSort())) {
// filter receive event, we prefer the corresponding start event at the same location
continue;
}
}
if(DurationConstraintHelper.coversSameLifeline(occ1, occ2)) {
// we must link occurrences on a same lifeline
return new OccurrenceSpecification[]{ occ1, occ2 };
}
}
}
return null;
}
public static List<Element> getCombinedFragmentAssociatedElement(CombinedFragment cf) {
List<Element> elements = new LinkedList<Element>();
for(InteractionOperand operand : cf.getOperands()) {
// Add all elements related to this operand
elements.addAll(getInteractionOperandAssociatedElement(operand));
// Add this operand
elements.add(operand);
}
return elements;
}
public static List<Element> getInteractionOperandAssociatedElement(InteractionOperand interactionOperand) {
List<Element> elements = new LinkedList<Element>();
for(InteractionFragment itf : interactionOperand.getFragments()) {
if(itf instanceof CombinedFragment) {
// add the combinedFragment
elements.addAll(getCombinedFragmentAssociatedElement((CombinedFragment)itf));
}
elements.add(itf);
if(itf instanceof MessageOccurrenceSpecification) {
MessageOccurrenceSpecification mos = (MessageOccurrenceSpecification)itf;
if(mos.getMessage() != null) {
elements.add(mos.getMessage());
}
}
}
return elements;
}
public static void handleMessageSortChange(EditingDomain editingDomain, Notification notification, Message message, MessageSort expectedMessageSort) {
//This restriction isn't needed anymore, as the Property View offers a refactoring
//facility for the MessageSort. The refactoring is only available for AsynchCall to
//AsynchSignal and vice-versa.
//However, the modification of the MessageSort from the "Advanced" property view should still be forbidden.
Object feature = notification.getFeature();
if(UMLPackage.eINSTANCE.getMessage_MessageSort().equals(feature) && !expectedMessageSort.equals(notification.getNewValue())) {
Object oldValue = notification.getOldValue();
Object newValue = notification.getNewValue();
if(oldValue instanceof MessageSort) {
if(!((oldValue == MessageSort.ASYNCH_CALL_LITERAL && newValue == MessageSort.ASYNCH_SIGNAL_LITERAL) || (oldValue == MessageSort.ASYNCH_SIGNAL_LITERAL && newValue == MessageSort.ASYNCH_CALL_LITERAL))) {
MessageDialog.openWarning(Display.getCurrent().getActiveShell(), BLOCK_SORT_MODIFICATION_TITLE, BLOCK_SORT_MODIFICATION_MSG);
CommandHelper.executeCommandWithoutHistory(editingDomain, SetCommand.create(editingDomain, message, feature, notification.getOldValue()), true);
return;
}
}
}
}
@SuppressWarnings("unchecked")
public static Set<Lifeline> getCoveredLifelines(Rectangle selectionRect, EditPart hostEditPart) {
Set<Lifeline> coveredLifelines = new HashSet<Lifeline>();
// retrieve all the edit parts in the registry
Set<Entry<Object, EditPart>> allEditPartEntries = hostEditPart.getViewer().getEditPartRegistry().entrySet();
for(Entry<Object, EditPart> epEntry : allEditPartEntries) {
EditPart ep = epEntry.getValue();
if(ep instanceof ShapeEditPart) {
ShapeEditPart sep = (ShapeEditPart)ep;
EObject elem = sep.getNotationView().getElement();
if(elem instanceof Lifeline) {
Rectangle figureBounds = getAbsoluteBounds(sep);
if(selectionRect.intersects(figureBounds)) {
coveredLifelines.add((Lifeline)elem);
}
}
}
}
return coveredLifelines;
}
/**
* retrieve all the interaction fragments and their related ift at least partially covered by the rectangle, including sub ift like
* interaction operands in combined fragment.
*
* @param selectionRect
* the rectangle where to look for ift.
* @param hostEditPart
* the host edit part used to retrieve all the edit parts in the registry.
* @param ignoreSet
* a set of ift to ignore.
* @return
* a set containing the covered ift or null if an ift not ignored is not fully covered.
*/
@SuppressWarnings("unchecked")
public static Set<InteractionFragment> getCoveredInteractionFragments(Rectangle selectionRect, EditPart hostEditPart, Set<InteractionFragment> ignoreSet) {
Set<InteractionFragment> coveredInteractionFragments = new HashSet<InteractionFragment>();
if(ignoreSet == null) {
ignoreSet = new HashSet<InteractionFragment>();
}
// retrieve all the edit parts in the registry
Set<Entry<Object, EditPart>> allEditPartEntries = hostEditPart.getViewer().getEditPartRegistry().entrySet();
for(Entry<Object, EditPart> epEntry : allEditPartEntries) {
EditPart ep = epEntry.getValue();
if(ep instanceof ShapeEditPart) {
ShapeEditPart sep = (ShapeEditPart)ep;
EObject elem = sep.getNotationView().getElement();
if(elem instanceof InteractionFragment && !ignoreSet.contains(elem)) {
Rectangle figureBounds = getAbsoluteBounds(sep);
// keep the fragment if its figure is completely in the selection
// if it is inside but not completely this method return null
if(selectionRect.contains(figureBounds)) {
coveredInteractionFragments.add((InteractionFragment)elem);
if(elem instanceof ExecutionSpecification) {
ExecutionSpecification es = (ExecutionSpecification)elem;
coveredInteractionFragments.add(es.getStart());
coveredInteractionFragments.add(es.getFinish());
}
} else {
Rectangle intersection = selectionRect.getIntersection(figureBounds);
if(!intersection.equals(new Rectangle()) && !intersection.equals(selectionRect)) {
return null;
}
}
}
} else if(ep instanceof ConnectionEditPart) {
ConnectionEditPart cep = (ConnectionEditPart)ep;
EObject elem = cep.getNotationView().getElement();
// for connections, messages have ends that are ift but don't have theirs own edit parts
// => use anchors to determine if they should be included in the set
if(elem instanceof Message) {
Message msg = (Message)elem;
Connection msgFigure = cep.getConnectionFigure();
Point sourcePoint = msgFigure.getSourceAnchor().getReferencePoint();
Point targetPoint = msgFigure.getTargetAnchor().getReferencePoint();
if(selectionRect.contains(sourcePoint)) {
MessageEnd msgSendEnd = msg.getSendEvent();
if(msgSendEnd instanceof InteractionFragment) {
coveredInteractionFragments.add((InteractionFragment)msgSendEnd);
}
}
if(selectionRect.contains(targetPoint)) {
MessageEnd msgReceiveEnd = msg.getReceiveEvent();
if(msgReceiveEnd instanceof InteractionFragment) {
coveredInteractionFragments.add((InteractionFragment)msgReceiveEnd);
}
}
}
}
}
return coveredInteractionFragments;
}
/**
* return a command to set the enclosing interaction or interaction operand of an interaction fragment.
*
* @param ed
* The transactional editing domain.
* @param ift
* The interaction fragment.
* @param io
* the new enclosing interaction.
* @return The command.
*/
public static ICommand getSetEnclosingInteractionCommand(final TransactionalEditingDomain ed, final InteractionFragment ift, final EObject interaction) {
return new AbstractTransactionalCommand(ed, "Set enclosing interaction command", null) {
@Override
protected CommandResult doExecuteWithResult(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
setEnclosingInteraction(ift, interaction, false);
return CommandResult.newOKCommandResult();
}
};
}
/**
* Set the interaction or interaction operand which contains a fragment
*
* @param ift
* fragment to update container
* @param interaction
* new containing interaction or interaction operand
* @param forceIfCoregion
* force the set even if fragment belong to a coregion. Use true only when you are sure the fragment no longer belongs to a coregion's
* operand.
*/
public static void setEnclosingInteraction(InteractionFragment ift, EObject interaction, boolean forceIfCoregion) {
if(ift != null) {
if(interaction instanceof Interaction) {
if(!interaction.equals(ift.getEnclosingInteraction())) {
// check case when mos looks outside but is in a coregion.
if(!forceIfCoregion && ift instanceof MessageOccurrenceSpecification) {
InteractionOperand operand = ift.getEnclosingOperand();
if(operand != null) {
Element cf = operand.getOwner();
if(cf instanceof CombinedFragment && InteractionOperatorKind.PAR_LITERAL.equals(((CombinedFragment)cf).getInteractionOperator())) {
// was in a coregion. Check whether other mos is still in the coregion
Message mess = ((MessageOccurrenceSpecification)ift).getMessage();
// find other mos
MessageOccurrenceSpecification otherMos = null;
if(ift.equals(mess.getSendEvent()) && mess.getReceiveEvent() instanceof MessageOccurrenceSpecification) {
otherMos = (MessageOccurrenceSpecification)mess.getReceiveEvent();
} else if(ift.equals(mess.getReceiveEvent()) && mess.getSendEvent() instanceof MessageOccurrenceSpecification) {
otherMos = (MessageOccurrenceSpecification)mess.getSendEvent();
}
if(otherMos != null) {
// check that it is in a coregion (specific code is in charge of taking it out in ReconnectMessageHelper)
if(operand.equals(otherMos.getEnclosingOperand())) {
return;
}
}
}
}
}
ift.setEnclosingOperand(null);
ift.setEnclosingInteraction((Interaction)interaction);
}
} else if(interaction instanceof InteractionOperand) {
if(!interaction.equals(ift.getEnclosingOperand())) {
ift.setEnclosingInteraction(null);
ift.setEnclosingOperand((InteractionOperand)interaction);
}
}
}
}
/**
* return a command to add a covered lifeline to an interaction fragment.
*
* @param ed
* The transactional editing domain.
* @param ift
* The interaction fragment.
* @param lifeline
* the lifeline.
* @return The command.
*/
public static ICommand getAddCoveredLifelineCommand(final TransactionalEditingDomain ed, final InteractionFragment ift, final Lifeline lifeline) {
return new AbstractTransactionalCommand(ed, "Add covered lifeline command", null) {
@Override
protected CommandResult doExecuteWithResult(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
ift.getCovereds().add(lifeline);
return CommandResult.newOKCommandResult();
}
};
}
/**
* return a command to remove a previously covered lifeline of an interaction fragment.
*
* @param ed
* The transactional editing domain.
* @param ift
* The interaction fragment.
* @param lifeline
* the lifeline.
* @return The command.
*/
public static ICommand getRemoveCoveredLifelineCommand(final TransactionalEditingDomain ed, final InteractionFragment ift, final Lifeline lifeline) {
return new AbstractTransactionalCommand(ed, "Add covered lifeline command", null) {
@Override
protected CommandResult doExecuteWithResult(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
ift.getCovereds().remove(lifeline);
return CommandResult.newOKCommandResult();
}
};
}
/**
* Create a command to update the enclosing interaction of a message end according to its new location.
*
* @param movedMos
* the moved Message Occurrence Specification
* @param newLocation
* the new absolute location
* @param editPart
* any adit part of the corresponding diagram
* @return the command or null if nothing changes
*/
//@SuppressWarnings("unchecked")
public static Command createUpdateEnclosingInteractionCommand(MessageOccurrenceSpecification movedMos, Point newLocation, GraphicalEditPart editPart) {
// // calculate new bounds for the execution specification
// Rectangle absoluteNewBounds = executionSpecificationEP.getFigure().getBounds().getCopy();
//
// executionSpecificationEP.getFigure().getParent().translateToAbsolute(absoluteNewBounds);
//
// absoluteNewBounds.translate(moveDelta);
// absoluteNewBounds.resize(sizeDelta);
//
// int xCenter = absoluteNewBounds.getCenter().x;
//
// Rectangle top = new Rectangle(xCenter, absoluteNewBounds.y, 0, 0);
// Rectangle bottom = new Rectangle(xCenter, absoluteNewBounds.bottom(), 0, 0);
//
// // associate es with its bounds, and start and finish event with the top and bottom of the bounds
HashMap<InteractionFragment, Rectangle> iftToCheckForUpdate = new HashMap<InteractionFragment, Rectangle>();
//
// ExecutionSpecification es = (ExecutionSpecification)executionSpecificationEP.resolveSemanticElement();
iftToCheckForUpdate.put(movedMos, new Rectangle(newLocation, new Dimension()));
// iftToCheckForUpdate.put(es.getStart(), top);
//
// iftToCheckForUpdate.put(es.getFinish(), bottom);
//
// List<ConnectionEditPart> sourceConnectionEPs = executionSpecificationEP.getSourceConnections();
//
// // find possible ifts associated with messages connected to the moved es
// for(ConnectionEditPart sourceConnectionEP : sourceConnectionEPs) {
// EObject elem = sourceConnectionEP.getNotationView().getElement();
//
// // for connections, messages have ends that can be ift but don't have theirs own edit parts
// // => use anchors to determine position
// if(elem instanceof Message) {
// Message msg = (Message)elem;
// MessageEnd sendEvent = msg.getSendEvent();
// if(sendEvent instanceof InteractionFragment) {
// Connection msgFigure = sourceConnectionEP.getConnectionFigure();
//
// Point sourcePoint = msgFigure.getSourceAnchor().getLocation(msgFigure.getTargetAnchor().getReferencePoint());
//
// iftToCheckForUpdate.put((InteractionFragment)sendEvent, new Rectangle(sourcePoint.x + moveDelta.x, sourcePoint.y + moveDelta.y, 0, 0));
// }
// }
// }
//
// List<ConnectionEditPart> targetConnectionEPs = executionSpecificationEP.getTargetConnections();
//
// for(ConnectionEditPart targetConnectionEP : targetConnectionEPs) {
// EObject elem = targetConnectionEP.getNotationView().getElement();
//
// if(elem instanceof Message) {
// Message msg = (Message)elem;
// MessageEnd receiveEvent = msg.getReceiveEvent();
// if(receiveEvent instanceof InteractionFragment) {
// Connection msgFigure = targetConnectionEP.getConnectionFigure();
//
// Point targetPoint = msgFigure.getTargetAnchor().getLocation(msgFigure.getSourceAnchor().getReferencePoint());
//
// iftToCheckForUpdate.put((InteractionFragment)receiveEvent, new Rectangle(targetPoint.x + moveDelta.x, targetPoint.y + moveDelta.y, 0, 0));
// }
// }
// }
CompoundCommand cmd = new CompoundCommand();
for(Map.Entry<InteractionFragment, Rectangle> entry : iftToCheckForUpdate.entrySet()) {
InteractionFragment newEnclosingInteraction = findInteractionFragmentContainerAt(entry.getValue(), editPart);
if(newEnclosingInteraction != null) {
cmd.add(new ICommandProxy(getSetEnclosingInteractionCommand(editPart.getEditingDomain(), entry.getKey(), newEnclosingInteraction)));
}
}
if(!cmd.isEmpty()) {
return cmd;
} else {
return null;
}
}
/**
* Create a command to update the enclosing interaction of an execution specification according to its new bounds.
*
* @param executionSpecificationEP
* the edit part of the execution specification
* @param absoluteNewBounds
* the new absolute bounds
* @return the command or null if nothing changes
*/
@SuppressWarnings("unchecked")
public static Command createUpdateEnclosingInteractionCommand(ShapeNodeEditPart executionSpecificationEP, Point moveDelta, Dimension sizeDelta) {
// calculate new bounds for the execution specification
Rectangle absoluteNewBounds = executionSpecificationEP.getFigure().getBounds().getCopy();
executionSpecificationEP.getFigure().getParent().translateToAbsolute(absoluteNewBounds);
absoluteNewBounds.translate(moveDelta);
absoluteNewBounds.resize(sizeDelta);
int xCenter = absoluteNewBounds.getCenter().x;
Rectangle top = new Rectangle(xCenter, absoluteNewBounds.y, 0, 0);
Rectangle bottom = new Rectangle(xCenter, absoluteNewBounds.bottom(), 0, 0);
// associate es with its bounds, and start and finish event with the top and bottom of the bounds
HashMap<InteractionFragment, Rectangle> iftToCheckForUpdate = new HashMap<InteractionFragment, Rectangle>();
ExecutionSpecification es = (ExecutionSpecification)executionSpecificationEP.resolveSemanticElement();
iftToCheckForUpdate.put(es, absoluteNewBounds);
iftToCheckForUpdate.put(es.getStart(), top);
iftToCheckForUpdate.put(es.getFinish(), bottom);
List<ConnectionEditPart> sourceConnectionEPs = executionSpecificationEP.getSourceConnections();
// find possible ifts associated with messages connected to the moved es
for(ConnectionEditPart sourceConnectionEP : sourceConnectionEPs) {
EObject elem = sourceConnectionEP.getNotationView().getElement();
// for connections, messages have ends that can be ift but don't have theirs own edit parts
// => use anchors to determine position
if(elem instanceof Message) {
Message msg = (Message)elem;
MessageEnd sendEvent = msg.getSendEvent();
if(sendEvent instanceof InteractionFragment) {
Connection msgFigure = sourceConnectionEP.getConnectionFigure();
Point sourcePoint = msgFigure.getSourceAnchor().getLocation(msgFigure.getTargetAnchor().getReferencePoint());
iftToCheckForUpdate.put((InteractionFragment)sendEvent, new Rectangle(sourcePoint.x + moveDelta.x, sourcePoint.y + moveDelta.y, 0, 0));
}
}
}
List<ConnectionEditPart> targetConnectionEPs = executionSpecificationEP.getTargetConnections();
for(ConnectionEditPart targetConnectionEP : targetConnectionEPs) {
EObject elem = targetConnectionEP.getNotationView().getElement();
if(elem instanceof Message) {
Message msg = (Message)elem;
MessageEnd receiveEvent = msg.getReceiveEvent();
if(receiveEvent instanceof InteractionFragment) {
Connection msgFigure = targetConnectionEP.getConnectionFigure();
Point targetPoint = msgFigure.getTargetAnchor().getLocation(msgFigure.getSourceAnchor().getReferencePoint());
iftToCheckForUpdate.put((InteractionFragment)receiveEvent, new Rectangle(targetPoint.x + moveDelta.x, targetPoint.y + moveDelta.y, 0, 0));
}
}
}
CompoundCommand cmd = new CompoundCommand();
for(Map.Entry<InteractionFragment, Rectangle> entry : iftToCheckForUpdate.entrySet()) {
InteractionFragment newEnclosingInteraction = findInteractionFragmentContainerAt(entry.getValue(), executionSpecificationEP);
if(newEnclosingInteraction != null) {
cmd.add(new ICommandProxy(getSetEnclosingInteractionCommand(executionSpecificationEP.getEditingDomain(), entry.getKey(), newEnclosingInteraction)));
}
}
if(!cmd.isEmpty()) {
return cmd;
} else {
return null;
}
}
/**
* Find the edit part a connection should be reconnected to at a given reference point on a lifeline
*
* @param lifelinePart
* lifeline part on which the reconnection must be performed
* @param referencePoint
* the reference point
* @return lifeline or execution specification edit part to reconnect to (the most external in the lifeline)
*/
public static GraphicalEditPart findPartToReconnectTo(LifelineEditPart lifelinePart, Point referencePoint) {
Rectangle absoluteLifelineBounds = getAbsoluteBounds(lifelinePart);
// inspect children nodes of lifeline
List<?> children = lifelinePart.getChildren();
GraphicalEditPart adequateExecutionPart = null;
int maxDeltaWithMiddle = 0;
for(Object child : children) {
// children executions
if(child instanceof ActionExecutionSpecificationEditPart || child instanceof BehaviorExecutionSpecificationEditPart || child instanceof CombinedFragment2EditPart) {
GraphicalEditPart childPart = (GraphicalEditPart)child;
Rectangle absoluteBounds = getAbsoluteBounds(childPart);
// enlarge absolute bounds to contain also the right and bottom edges.
absoluteBounds.expand(1, 1);
if(absoluteBounds.contains(referencePoint)) {
// this is an adequate execution part, take the most external one
int deltaWithMiddle = Math.abs(absoluteBounds.getTop().x - absoluteLifelineBounds.getTop().x);
if(deltaWithMiddle >= maxDeltaWithMiddle) {
maxDeltaWithMiddle = deltaWithMiddle;
adequateExecutionPart = childPart;
}
}
}
}
if(adequateExecutionPart != null) {
return adequateExecutionPart;
}
return lifelinePart;
}
/**
* Find the range of possible locations an occurrence specification should be drawn in.
*
* @param lifelineEditPart
* the lifeline on which the occurrence specification appears.
* @param occSpec
* the occurrence specification to find locations for.
* @return rectangle within which the occurrence specification must be drawn (width is not significative)
*/
public static Rectangle findPossibleLocationsForEvent(LifelineEditPart lifelineEditPart, OccurrenceSpecification occSpec) {
// at least, we know the event is in the drawn lifeline
Rectangle result = lifelineEditPart.getContentPane().getBounds().getCopy();
lifelineEditPart.getFigure().translateToAbsolute(result);
// find the containing pane
IGraphicalEditPart containerPart = findDrawnContainerEditPart(lifelineEditPart, occSpec);
IFigure drawnContentPane = getContentPaneThatCanContainFragments(containerPart);
if(drawnContentPane != null) {
// content pane is the smallest drawn owning rectangle
Rectangle bounds = drawnContentPane.getBounds().getCopy();
drawnContentPane.getParent().translateToAbsolute(bounds);
// intersect with the lifeline's content
result.intersect(bounds);
}
// we must search surrounding interaction fragments within uppestContainerToSearchInto
EObject uppestContainerToSearchInto = containerPart.resolveSemanticElement();
InteractionFragment after = InteractionFragmentHelper.findNextFragment(occSpec, uppestContainerToSearchInto);
boolean foundNextFragment = false;
while(!foundNextFragment && after != null) {
Point bottom = findLocationOfEvent(lifelineEditPart, after);
if(bottom != null && result.contains(bottom)) {
int diff = bottom.y - result.bottom();
result.resize(0, diff);
foundNextFragment = true;
} else {
// fragment not represented on lifeline, search next fragment
after = InteractionFragmentHelper.findNextFragment(after, uppestContainerToSearchInto);
}
}
InteractionFragment before = InteractionFragmentHelper.findPreviousFragment(occSpec, uppestContainerToSearchInto);
boolean foundPreviousFragment = false;
while(!foundPreviousFragment && before != null) {
Point top = findLocationOfEvent(lifelineEditPart, before);
if(top != null && result.contains(top)) {
int diff = top.y - result.y;
result.translate(0, diff);
result.resize(0, -diff);
foundPreviousFragment = true;
/*
* In case before is contained in an interaction operand or
* combined fragment which does not contain the searched event,
* we must also take in account the bottom border of this node.
*/
reduceByNodeContainingBefore(result, before, occSpec, lifelineEditPart);
} else {
// fragment not represented on lifeline, search next fragment
before = InteractionFragmentHelper.findPreviousFragment(before, uppestContainerToSearchInto);
}
}
return result;
}
/**
* Reduce the possible bounds by removing the area of an eventual interaction operand or combined fragment which contains the fragment "before"
* and not the occurrence specification for which we search a location.
*
* @param possibleBounds
* bounds to reduce, in which the location will be possible
* @param before
* the fragment which happens before
* @param occSpec
* the occurrence specification for which we search a location
* @param lifelineEditPart
* the lifeline on which the occurrence specification appears.
*/
private static void reduceByNodeContainingBefore(Rectangle possibleBounds, InteractionFragment before, OccurrenceSpecification occSpec, LifelineEditPart lifelineEditPart) {
Element eventualNodeElement = before;
// inspect each container of before, until it is common with occSpec
while(!EcoreUtil.isAncestor(eventualNodeElement, occSpec)) {
// test if eventualNodeElement has bounds excluding occSpec
// search for the eventualNodeElement's edit part
List<View> views = DiagramEditPartsUtil.findViews(eventualNodeElement, lifelineEditPart.getViewer());
for(View view : views) {
EditPart part = DiagramEditPartsUtil.getEditPartFromView(view, lifelineEditPart);
// test if edit part is an adequate node
if(part instanceof IGraphicalEditPart && getContentPaneThatCanContainFragments(part) != null) {
Rectangle bounds = getAbsoluteBounds((IGraphicalEditPart)part);
// reduce so that the bounds are excluded
int newPossibleTop = bounds.bottom();
if(possibleBounds.y < newPossibleTop) {
int diff = newPossibleTop - possibleBounds.y;
possibleBounds.translate(0, diff);
possibleBounds.resize(0, -diff);
}
}
}
eventualNodeElement = eventualNodeElement.getOwner();
}
}
/**
* Get the content pane of an edit part that can directly or indirectly contain interaction fragments (this excludes lifeline, which references)
*
* @param containerPart
* container edit part
* @return its content pane if the container can contain fragments, null otherwise.
*/
private static IFigure getContentPaneThatCanContainFragments(EditPart containerPart) {
// test all owner edit parts which can contain an interaction fragment
if(containerPart instanceof InteractionOperandEditPart) {
return ((InteractionOperandEditPart)containerPart).getContentPane();
} else if(containerPart instanceof CombinedFragmentEditPart) {
return ((CombinedFragmentEditPart)containerPart).getContentPane();
} else if(containerPart instanceof CombinedFragment2EditPart) {
return ((CombinedFragment2EditPart)containerPart).getContentPane();
} else if(containerPart instanceof ContinuationEditPart) {
return ((ContinuationEditPart)containerPart).getContentPane();
} else if(containerPart instanceof InteractionUseEditPart) {
return ((InteractionUseEditPart)containerPart).getContentPane();
} else if(containerPart instanceof InteractionEditPart) {
return ((InteractionEditPart)containerPart).getContentPane();
}
return null;
}
/**
* Find the smallest drawn edit part containing the occurrence specification.
*
* @param lifelineEditPart
* support lifeline edit part
* @param occSpec
* occurrence specification to localize
* @return a drawn edit part which element contains the occurrence specification or null
*/
private static IGraphicalEditPart findDrawnContainerEditPart(LifelineEditPart lifelineEditPart, OccurrenceSpecification occSpec) {
// find containing drawn edit parts
Element owner = occSpec.getOwner();
while(owner != null) {
// search for the owner's edit part
List<View> views = DiagramEditPartsUtil.findViews(owner, lifelineEditPart.getViewer());
for(View view : views) {
EditPart part = DiagramEditPartsUtil.getEditPartFromView(view, lifelineEditPart);
// test if edit part can contain the occurrence specification
if(part instanceof IGraphicalEditPart && getContentPaneThatCanContainFragments(part) != null) {
return (IGraphicalEditPart)part;
}
}
owner = owner.getOwner();
}
return null;
}
/**
* Check whether the Lifeline is Create Message's target node
* @param lifelineEP
* @return boolean
*/
public static boolean isCreateMessageEndLifeline(LifelineEditPart lifelineEP){
List<Object> targetConnections = lifelineEP.getTargetConnections();
if(targetConnections!=null && targetConnections.size()>0){
for(int i = 0;i<targetConnections.size();i++){
Object connection = targetConnections.get(i);
if(connection instanceof Message4EditPart){
return true;
}
}
}
return false;
}
/**
* Find Time Observations editpart which are related to specific OccurenceSpecification
* @param lifelinePart
* @param oss
* @return List<TimeObservationLabelEditPart>
*/
public static List<TimeObservationLabelEditPart> findOccurenceSpecificationRelatedTimeObservationPart(LifelineEditPart lifelinePart,List<OccurrenceSpecification> oss){
List<TimeObservationLabelEditPart> list = new ArrayList<TimeObservationLabelEditPart>();
if(oss == null || oss.size() == 0){
return list;
}
if(lifelinePart!=null&&lifelinePart.getTargetConnections().size()>0){
for(Object targetConnection : lifelinePart.getTargetConnections()){
if(targetConnection instanceof ObservationLinkEditPart){
ObservationLinkEditPart observationLinkEditPart = (ObservationLinkEditPart)targetConnection;
if(observationLinkEditPart.getSource() instanceof TimeObservationLabelEditPart){
TimeObservationLabelEditPart source = (TimeObservationLabelEditPart)observationLinkEditPart.getSource();
EObject timeElement = source.resolveSemanticElement();
if(timeElement instanceof TimeObservation) {
if(oss.contains(((TimeObservation)timeElement).getEvent())) {
list.add(source);
}
}
}
}
}
}
return list;
}
/**
* Find Time Observations editpart which are related to specific OccurenceSpecification
* @param lifelinePart
* @param os
* @return List<TimeObservationLabelEditPart>
*/
public static List<TimeObservationLabelEditPart> findOccurenceSpecificationRelatedTimeObservationPart(LifelineEditPart lifelinePart,OccurrenceSpecification os){
List<OccurrenceSpecification> oss = new ArrayList<OccurrenceSpecification>();
oss.add(os);
return findOccurenceSpecificationRelatedTimeObservationPart(lifelinePart, oss);
}
/**
* Find specific editpart by semantic model
* @param editPart
* @param targetElement
* @param targetClass
* @return EditPart
*/
public static EditPart getEditPart(EditPart editPart,
EObject targetElement, Class targetClass) {
if (editPart == null || targetElement == null || targetClass == null)
return null;
Map<?, ?> map = editPart.getViewer().getEditPartRegistry();
for (Entry<?, ?> entry : map.entrySet()) {
Object key = entry.getKey();
if (!(key instanceof View)) {
continue;
}
View view = (View) key;
EObject tempElement = view.getElement();
if (targetElement.equals(tempElement)) {
Object value = entry.getValue();
if (value.getClass() == targetClass) {
return (EditPart) value;
}
}
}
return null;
}
/**
* Intall observation link policy to specific editpart
* @param editPart
*/
public static void installObservationLinkPolicy(EditPart editPart) {
String editPolicy = "observationlink";
if (editPart instanceof LifelineEditPart
|| editPart instanceof TimeObservationLabelEditPart) {
editPart.installEditPolicy(editPolicy,
new ObservationLinkPolicy(editPart));
}
if (editPart instanceof DurationObservationEditPart) {
editPart.installEditPolicy(editPolicy,
new ObservationLinkPolicy(editPart));
}
if (editPart instanceof MessageEditPart
|| editPart instanceof Message2EditPart
|| editPart instanceof Message3EditPart
|| editPart instanceof Message4EditPart
|| editPart instanceof Message5EditPart
|| editPart instanceof Message6EditPart
|| editPart instanceof Message7EditPart) {
editPart.installEditPolicy(editPolicy,
new ObservationLinkPolicy(editPart));
} else if (editPart instanceof MessageNameEditPart
|| editPart instanceof MessageName2EditPart
|| editPart instanceof MessageName3EditPart
|| editPart instanceof MessageName4EditPart
|| editPart instanceof MessageName5EditPart
|| editPart instanceof MessageName6EditPart
|| editPart instanceof MessageName7EditPart) {
editPart.getParent().installEditPolicy(editPolicy,
new ObservationLinkPolicy(editPart));
} else if (editPart instanceof MessageSyncAppliedStereotypeEditPart
|| editPart instanceof MessageAsyncAppliedStereotypeEditPart
|| editPart instanceof MessageReplyAppliedStereotypeEditPart
|| editPart instanceof MessageCreateAppliedStereotypeEditPart
|| editPart instanceof MessageDeleteAppliedStereotypeEditPart
|| editPart instanceof MessageLostAppliedStereotypeEditPart
|| editPart instanceof MessageFoundAppliedStereotypeEditPart) {
editPart.getParent().installEditPolicy(editPolicy,
new ObservationLinkPolicy(editPart));
}
}
}