/***************************************************************************** * Copyright (c) 2010 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 * Saadia DHOUIB (CEA LIST) saadia.dhouib@cea.fr - adapted from sequence diagram *****************************************************************************/ package org.eclipse.papyrus.uml.diagram.communication.custom.helper; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.geometry.Point; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature.Setting; import org.eclipse.emf.transaction.TransactionalEditingDomain; import org.eclipse.gef.ConnectionEditPart; import org.eclipse.gef.EditPart; import org.eclipse.gef.GraphicalEditPart; import org.eclipse.gef.commands.Command; import org.eclipse.gef.commands.CompoundCommand; import org.eclipse.gmf.runtime.diagram.core.commands.DeleteCommand; import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil; import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy; import org.eclipse.gmf.runtime.diagram.ui.editparts.IBorderItemEditPart; import org.eclipse.gmf.runtime.notation.Node; import org.eclipse.gmf.runtime.notation.NotationPackage; import org.eclipse.gmf.runtime.notation.View; import org.eclipse.papyrus.uml.diagram.common.util.DiagramEditPartsUtil; import org.eclipse.papyrus.uml.diagram.communication.edit.parts.LifelineEditPartCN; import org.eclipse.uml2.common.util.CacheAdapter; 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.IntervalConstraint; 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.NamedElement; import org.eclipse.uml2.uml.OccurrenceSpecification; import org.eclipse.uml2.uml.TimeConstraint; import org.eclipse.uml2.uml.TimeObservation; /** * * @author This is for special commands for communication diagram * */ public class CommunicationUtil { /** * Find the container interaction fragment at the given location. The * elements are drawn under the lifeline, but their model container is an * interaction fragment. It can be of type Interaction or InteractionOperand * * @param location * the location * @param host * the host edit part * @return the interaction fragment or null */ public static InteractionFragment findInteractionFragmentAt(Point location, EditPart host) { if(host == null) { return null; } List<IFigure> exclusionSet = new ArrayList<IFigure>(); InteractionFragment interactionFragment = null; EditPart ep = host.getRoot().getViewer().findObjectAtExcluding(location, exclusionSet); while(ep instanceof LifelineEditPartCN) { exclusionSet.add(((GraphicalEditPart)ep).getFigure()); ep = host.getRoot().getViewer().findObjectAtExcluding(location, exclusionSet); } // Get the rootEditpart Content if(ep != null && ep.getModel() instanceof View) { EObject eObject = ViewUtil.resolveSemanticElement((View)ep.getModel()); if(eObject instanceof InteractionOperand || eObject instanceof Interaction) { interactionFragment = (InteractionFragment)eObject; } } return interactionFragment; } /** * Complete an ICommand which destroys an DestructionEvent element to also destroy dependent time/duration constraint/observation linked with * these ends * * @param deleteViewsCmd * the command to complete * @param editingDomain * the editing domain * @param destructionEventPart * the execution specification edit part on which the request is called * @return the deletion command deleteViewsCmd for convenience */ public static CompoundCommand completeDeleteDestructionEventViewCommand(CompoundCommand deleteViewsCmd, TransactionalEditingDomain editingDomain, EditPart destructionEventPart) { Object model = destructionEventPart.getModel(); if(model instanceof Node) { EObject obj = ((Node)model).getElement(); if(obj instanceof DestructionOccurrenceSpecification) { LifelineEditPartCN lifelinePart = getParentLifelinePart(destructionEventPart); if(lifelinePart != null) { for(Object lifelineChild : lifelinePart.getChildren()) { if(lifelineChild instanceof IBorderItemEditPart) { final IBorderItemEditPart timePart = (IBorderItemEditPart)lifelineChild; //At most one destruction event. Only parts linked to it can not move for now. boolean isNotLinked = CommunicationUtil.canTimeElementPartBeYMoved(timePart); if(!isNotLinked) { // time part is linked, delete the view Command deleteTimeViewCommand = new ICommandProxy(new DeleteCommand(editingDomain, (View)timePart.getModel())); deleteViewsCmd.add(deleteTimeViewCommand); } } } } } } return deleteViewsCmd; } /** * Know whether this time element part can be moved within the lifeline or not. * Parts linked with a destruction event can not be moved since the destruction event is always at the end. * * @param timeElementPart * the part representing a time/duration constraint/observation * @return true if the part can be moved */ public static boolean canTimeElementPartBeYMoved(IBorderItemEditPart timeElementPart) { EObject timeElement = timeElementPart.resolveSemanticElement(); List<? extends Element> occurrences = Collections.emptyList(); if(timeElement instanceof TimeObservation) { NamedElement occurence = ((TimeObservation)timeElement).getEvent(); occurrences = Collections.singletonList(occurence); } else if(timeElement instanceof TimeConstraint || timeElement instanceof DurationConstraint) { occurrences = ((IntervalConstraint)timeElement).getConstrainedElements(); } // check whether one of the time occurrences correspond to a DestructionEvent for(Element occurrence : occurrences) { if(occurrence instanceof DestructionOccurrenceSpecification) { return false; } } return true; } /** * 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 LifelineEditPartCN getParentLifelinePart(EditPart nodeEditPart) { EditPart parent = nodeEditPart; while(parent != null) { if(parent instanceof LifelineEditPartCN) { return (LifelineEditPartCN)parent; } else { parent = parent.getParent(); } } return null; } /** * Get the edit part 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(); } LifelineEditPartCN parentLifeline = CommunicationUtil.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; LifelineEditPartCN parentLifeline = CommunicationUtil.getParentLifelinePart(lifelineChild); if(lifelinePart.equals(parentLifeline)) { return part; } } } } return null; } /** * This methods verifies if two UML Lifelines are already connected * * @return * the list of messages between the two lifelines if Lifelines are connected * else it returns null */ public static Set<Message> verifyUMLLifelinesConnected(Lifeline lifeline1, Lifeline lifeline2) { EList<InteractionFragment> lifeline1Events = lifeline1.getCoveredBys(); EList<InteractionFragment> lifeline2Events = lifeline2.getCoveredBys(); Set<Message> messages = null; for(InteractionFragment current1 : lifeline1Events) { MessageEnd me1 = (MessageEnd)current1; if(!(me1.getMessage() == null)) { for(InteractionFragment current2 : lifeline2Events) { MessageEnd me2 = (MessageEnd)current2; if(!(me2.getMessage() == null)) { if(me1.getMessage().equals(me2.getMessage())) { if(messages == null) { messages = new HashSet<Message>(); messages.add(me1.getMessage()); } else { messages.add(me1.getMessage()); } } } } } } return messages; } /** * Verify if lifelines Ediparts are connected. * * @param lifeline1EditPart * the first lifeline edit part * @param lifeline2EditPart * the second lifeline edit part * @return the connection edit part if lifelines are connected, else it returns null */ @SuppressWarnings({ "rawtypes" }) public static ConnectionEditPart verifyIfLifelinesEPConnected(EditPart lifeline1EditPart, EditPart lifeline2EditPart) { List sourceConnectionslifeline1 = ((GraphicalEditPart)lifeline1EditPart).getSourceConnections(); List targetConnectionslifeline2 = ((GraphicalEditPart)lifeline2EditPart).getTargetConnections(); List sourceConnectionslifeline2 = ((GraphicalEditPart)lifeline2EditPart).getSourceConnections(); List targetConnectionslifeline1 = ((GraphicalEditPart)lifeline1EditPart).getTargetConnections(); if((!sourceConnectionslifeline1.isEmpty()) && (!targetConnectionslifeline2.isEmpty())) { for(int i = 0; i < sourceConnectionslifeline1.size(); i++) { for(int j = 0; j < targetConnectionslifeline2.size(); j++) { ConnectionEditPart link1 = (ConnectionEditPart)sourceConnectionslifeline1.get(i); ConnectionEditPart link2 = (ConnectionEditPart)targetConnectionslifeline2.get(j); //System.err.println("+-> ConnectionEditPart link1:" + link1); if(link1.equals(link2)) { //System.out.println("Source and target have existent same connection"); return link1; } } } } else if((!sourceConnectionslifeline2.isEmpty()) && (!targetConnectionslifeline1.isEmpty())) { for(int i = 0; i < sourceConnectionslifeline2.size(); i++) { for(int j = 0; j < targetConnectionslifeline1.size(); j++) { ConnectionEditPart link1 = (ConnectionEditPart)sourceConnectionslifeline2.get(i); ConnectionEditPart link2 = (ConnectionEditPart)targetConnectionslifeline1.get(j); //System.err.println("+-> ConnectionEditPart link1:" + link1); if(link1.equals(link2)) { //System.out.println("Source and target have existent same connection"); return link1; } } } } return null; } }