From 5cf53378e4a084aefb90d9648d5db61d83881f58 Mon Sep 17 00:00:00 2001 From: Stephane Bonnet Date: Thu, 5 Sep 2013 16:35:04 +0200 Subject: Initial version. --- .../internal/VerticalPositionFunction.java | 113 ++ .../business/internal/VerticalRangeFunction.java | 45 + .../color/DefaultColorStyleDescription.java | 70 ++ .../business/internal/elements/AbstractFrame.java | 221 ++++ .../internal/elements/AbstractNodeEvent.java | 255 +++++ .../internal/elements/AbstractSequenceElement.java | 152 +++ .../internal/elements/AbstractSequenceNode.java | 73 ++ .../internal/elements/CombinedFragment.java | 211 ++++ .../business/internal/elements/EndOfLife.java | 140 +++ .../business/internal/elements/Execution.java | 468 +++++++++ .../internal/elements/ISequenceElement.java | 98 ++ .../elements/ISequenceElementAccessor.java | 425 ++++++++ .../business/internal/elements/ISequenceEvent.java | 172 ++++ .../business/internal/elements/ISequenceNode.java | 38 + .../business/internal/elements/InstanceRole.java | 189 ++++ .../business/internal/elements/InteractionUse.java | 126 +++ .../business/internal/elements/Lifeline.java | 331 ++++++ .../business/internal/elements/LostMessageEnd.java | 147 +++ .../business/internal/elements/Message.java | 552 ++++++++++ .../internal/elements/ObservationPoint.java | 139 +++ .../business/internal/elements/Operand.java | 330 ++++++ .../internal/elements/SequenceDiagram.java | 463 +++++++++ .../sequence/business/internal/elements/State.java | 215 ++++ .../business/internal/elements/package-info.java | 12 + .../internal/layout/AbstractSequenceLayout.java | 214 ++++ .../layout/AbstractSequenceOrderingLayout.java | 168 +++ .../layout/EventEndToPositionFunction.java | 137 +++ .../business/internal/layout/LayoutConstants.java | 193 ++++ .../business/internal/layout/SequenceLayout.java | 138 +++ .../AbstractSequenceAbsoluteBoundsFlagger.java | 93 ++ .../flag/SequenceDiagramAbsoluteBoundsFlagger.java | 54 + .../flag/SequenceEventAbsoluteBoundsFlagger.java | 51 + .../LostMessageEndHorizontalLayoutHelper.java | 451 ++++++++ .../horizontal/SequenceHorizontalLayout.java | 656 ++++++++++++ .../observation/SequenceObservationLayout.java | 165 +++ .../layout/vertical/SequenceVerticalLayout.java | 1080 ++++++++++++++++++++ .../internal/metamodel/SequenceDDiagramSpec.java | 286 ++++++ .../description/BasicMessageMappingSpec.java | 153 +++ .../description/CombinedFragmentMappingSpec.java | 209 ++++ .../description/CreationMessageMappingSpec.java | 154 +++ .../description/DestructionMessageMappingSpec.java | 154 +++ .../description/EndOfLifeMappingSpec.java | 211 ++++ .../description/ExecutionMappingSpec.java | 211 ++++ .../description/InstanceRoleMappingSpec.java | 211 ++++ .../description/InteractionUseMappingSpec.java | 209 ++++ .../description/ObservationPointMappingSpec.java | 211 ++++ .../metamodel/description/OperandMappingSpec.java | 209 ++++ .../description/ReturnMessageMappingSpec.java | 154 +++ .../SequenceDiagramDescriptionSpec.java | 120 +++ .../metamodel/description/StateMappingSpec.java | 211 ++++ .../description/tool/MessageCreationToolSpec.java | 42 + .../metamodel/ordering/CompoundEventEndSpec.java | 78 ++ .../metamodel/ordering/SingleEventEndSpec.java | 50 + .../internal/operation/EndOfLifeMoveOperation.java | 53 + .../operation/FixGraphicalOrderingOperation.java | 73 ++ .../operation/ISequenceNodeMoveOperation.java | 80 ++ .../InverseRelativeNodePositionOperation.java | 54 + .../RefreshGraphicalOrderingOperation.java | 144 +++ .../RefreshSemanticOrderingsOperation.java | 173 ++++ .../operation/ReparentExecutionOperation.java | 78 ++ .../operation/SequenceMessageRangeHelper.java | 176 ++++ .../operation/SetMessageRangeOperation.java | 141 +++ .../operation/SetVerticalRangeOperation.java | 54 + .../ShiftDirectSubExecutionsOperation.java | 72 ++ .../SynchronizeGraphicalOrderingOperation.java | 79 ++ ...zeISequenceEventsSemanticOrderingOperation.java | 330 ++++++ ...onizeInstanceRoleSemanticOrderingOperation.java | 200 ++++ .../internal/operation/VerticalSpaceExpansion.java | 339 ++++++ .../business/internal/ordering/EventEndHelper.java | 220 ++++ .../internal/ordering/RefreshOrderingHelper.java | 245 +++++ .../internal/query/ISequenceElementQuery.java | 80 ++ .../internal/query/ISequenceEventQuery.java | 309 ++++++ .../business/internal/query/InstanceRoleQuery.java | 51 + .../business/internal/query/RangeComparator.java | 68 ++ .../internal/query/ReversedRangeComparator.java | 68 ++ .../query/SequenceDiagramDescriptionQuery.java | 119 +++ .../internal/query/SequenceDiagramQuery.java | 342 +++++++ .../internal/query/SequenceMessageViewQuery.java | 292 ++++++ .../business/internal/query/SequenceNodeQuery.java | 95 ++ .../internal/query/TSequenceDiagramQuery.java | 94 ++ .../refresh/FixBendpointsOnCreationCommand.java | 266 +++++ .../internal/refresh/RefreshLayoutCommand.java | 87 ++ .../internal/refresh/RefreshLayoutScope.java | 133 +++ .../internal/refresh/RefreshLayoutTrigger.java | 68 ++ .../SequenceCanonicalSynchronizerAdapter.java | 357 +++++++ .../SequenceCanonicalSynchronizerAdapterScope.java | 85 ++ .../internal/refresh/SequenceRefreshExtension.java | 107 ++ .../refresh/SequenceRefreshExtensionProvider.java | 39 + .../internal/refresh/SequenceSlidableAnchor.java | 45 + .../business/internal/tool/ToolCommandBuilder.java | 504 +++++++++ .../business/internal/util/BendpointsHelper.java | 67 ++ .../business/internal/util/EventFinder.java | 239 +++++ .../internal/util/ISequenceElementSwitch.java | 579 +++++++++++ .../internal/util/ISequenceEventsTreeIterator.java | 68 ++ .../internal/util/ParentOperandFinder.java | 141 +++ .../business/internal/util/RangeSetter.java | 314 ++++++ .../internal/util/SameLifelinePredicate.java | 73 ++ .../business/internal/util/SubEventsHelper.java | 385 +++++++ 98 files changed, 18844 insertions(+) create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/VerticalPositionFunction.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/VerticalRangeFunction.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/color/DefaultColorStyleDescription.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/AbstractFrame.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/AbstractNodeEvent.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/AbstractSequenceElement.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/AbstractSequenceNode.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/CombinedFragment.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/EndOfLife.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/Execution.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/ISequenceElement.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/ISequenceElementAccessor.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/ISequenceEvent.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/ISequenceNode.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/InstanceRole.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/InteractionUse.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/Lifeline.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/LostMessageEnd.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/Message.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/ObservationPoint.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/Operand.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/SequenceDiagram.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/State.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/package-info.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/AbstractSequenceLayout.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/AbstractSequenceOrderingLayout.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/EventEndToPositionFunction.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/LayoutConstants.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/SequenceLayout.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/flag/AbstractSequenceAbsoluteBoundsFlagger.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/flag/SequenceDiagramAbsoluteBoundsFlagger.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/flag/SequenceEventAbsoluteBoundsFlagger.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/horizontal/LostMessageEndHorizontalLayoutHelper.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/horizontal/SequenceHorizontalLayout.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/observation/SequenceObservationLayout.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/vertical/SequenceVerticalLayout.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/SequenceDDiagramSpec.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/BasicMessageMappingSpec.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/CombinedFragmentMappingSpec.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/CreationMessageMappingSpec.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/DestructionMessageMappingSpec.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/EndOfLifeMappingSpec.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/ExecutionMappingSpec.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/InstanceRoleMappingSpec.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/InteractionUseMappingSpec.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/ObservationPointMappingSpec.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/OperandMappingSpec.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/ReturnMessageMappingSpec.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/SequenceDiagramDescriptionSpec.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/StateMappingSpec.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/tool/MessageCreationToolSpec.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/ordering/CompoundEventEndSpec.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/ordering/SingleEventEndSpec.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/EndOfLifeMoveOperation.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/FixGraphicalOrderingOperation.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/ISequenceNodeMoveOperation.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/InverseRelativeNodePositionOperation.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/RefreshGraphicalOrderingOperation.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/RefreshSemanticOrderingsOperation.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/ReparentExecutionOperation.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/SequenceMessageRangeHelper.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/SetMessageRangeOperation.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/SetVerticalRangeOperation.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/ShiftDirectSubExecutionsOperation.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/SynchronizeGraphicalOrderingOperation.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/SynchronizeISequenceEventsSemanticOrderingOperation.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/SynchronizeInstanceRoleSemanticOrderingOperation.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/VerticalSpaceExpansion.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/ordering/EventEndHelper.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/ordering/RefreshOrderingHelper.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/ISequenceElementQuery.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/ISequenceEventQuery.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/InstanceRoleQuery.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/RangeComparator.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/ReversedRangeComparator.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/SequenceDiagramDescriptionQuery.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/SequenceDiagramQuery.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/SequenceMessageViewQuery.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/SequenceNodeQuery.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/TSequenceDiagramQuery.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/FixBendpointsOnCreationCommand.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/RefreshLayoutCommand.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/RefreshLayoutScope.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/RefreshLayoutTrigger.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/SequenceCanonicalSynchronizerAdapter.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/SequenceCanonicalSynchronizerAdapterScope.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/SequenceRefreshExtension.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/SequenceRefreshExtensionProvider.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/SequenceSlidableAnchor.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/tool/ToolCommandBuilder.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/BendpointsHelper.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/EventFinder.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/ISequenceElementSwitch.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/ISequenceEventsTreeIterator.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/ParentOperandFinder.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/RangeSetter.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/SameLifelinePredicate.java create mode 100644 plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/SubEventsHelper.java (limited to 'plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business') diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/VerticalPositionFunction.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/VerticalPositionFunction.java new file mode 100644 index 0000000000..9a1777daaa --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/VerticalPositionFunction.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (c) 2010, 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Function; +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.ordering.EventEndHelper; +import org.eclipse.sirius.diagram.sequence.ordering.CompoundEventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.SingleEventEnd; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * A function which computes the vertical position (in absolute, normalized + * coordinates) of an {@link EventEnd}. + * + * @author pcdavid + */ +public class VerticalPositionFunction implements Function { + /** + * The value returned by the function to indicate an invalid input from + * which a position can not be determined. + */ + public static final int INVALID_POSITION = Integer.MAX_VALUE; + + private final SequenceDDiagram diagram; + + /** + * Constructor. + * + * @param diagram + * the diagram on which the compute the position of the ends. + */ + public VerticalPositionFunction(SequenceDDiagram diagram) { + this.diagram = diagram; + } + + /** + * Returns the vertical position of the specified end as it appears on the + * diagram associated to this function, or INVALID_POSITION if + * the end is invalid or is not part of the diagram. + * + * @param end + * the end for which to compute the position. + * @return the vertical position of the end, or + * INVALID_POSITION. + */ + public Integer apply(EventEnd end) { + Integer result; + SingleEventEnd see = null; + EObject semanticEvent = null; + if (end instanceof SingleEventEnd) { + see = (SingleEventEnd) end; + } else if (end instanceof CompoundEventEnd) { + see = ((CompoundEventEnd) end).getEventEnds().iterator().next(); + } + + if (see != null) { + semanticEvent = see.getSemanticEvent(); + } + + Iterable eventViews = ISequenceElementAccessor.getViewsForSemanticElement(diagram, semanticEvent); + + if (Iterables.isEmpty(eventViews) && end instanceof CompoundEventEnd) { + for (SingleEventEnd see2 : ((CompoundEventEnd) end).getEventEnds()) { + if (see != null) { + semanticEvent = see2.getSemanticEvent(); + } + eventViews = ISequenceElementAccessor.getViewsForSemanticElement(diagram, semanticEvent); + if (!Iterables.isEmpty(eventViews)) { + break; + } + } + } + + if (Iterables.isEmpty(eventViews)) { + result = INVALID_POSITION; + } else { + result = INVALID_POSITION; + Range range = Range.emptyRange(); + + for (View potentialView : eventViews) { + range = VerticalRangeFunction.INSTANCE.apply(potentialView); + if (!range.isEmpty()) { + break; + } + } + + if (EventEndHelper.PUNCTUAL_COMPOUND_EVENT_END.apply(end)) { + result = (int) range.middleValue(); + } else if (EventEndHelper.PUNCTUAL_COMPOUND_EVENT_END.apply(end)) { + result = (int) range.middleValue(); + } else { + result = (int) (see.isStart() ? range.getLowerBound() : range.getUpperBound()); + } + } + return result; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/VerticalRangeFunction.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/VerticalRangeFunction.java new file mode 100644 index 0000000000..8a268c4925 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/VerticalRangeFunction.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal; + +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Function; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * Computes the absolute vertical range occupied by an element of a sequence + * diagram, from its GMF View. + * + * @author pcdavid + */ +public enum VerticalRangeFunction implements Function { + /** + * The default instance. + */ + INSTANCE; + + /** + * {@inheritDoc} + */ + public Range apply(View view) { + Range result = Range.emptyRange(); + Option iSequenceEvent = ISequenceElementAccessor.getISequenceEvent(view); + if (iSequenceEvent.some()) { + result = iSequenceEvent.get().getVerticalRange(); + } + return result; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/color/DefaultColorStyleDescription.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/color/DefaultColorStyleDescription.java new file mode 100644 index 0000000000..dc5f785205 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/color/DefaultColorStyleDescription.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.color; + +import org.eclipse.emf.ecore.EObject; + +import org.eclipse.sirius.diagram.sequence.template.TExecutionStyle; +import org.eclipse.sirius.diagram.sequence.template.TLifelineStyle; +import org.eclipse.sirius.diagram.sequence.template.TMessageStyle; +import org.eclipse.sirius.diagram.sequence.template.util.TemplateSwitch; +import org.eclipse.sirius.tools.api.ui.color.EnvironmentSystemColorFactory; + +/** + * Class responsible for setting default color values on style descriptions. + * + * @author mporhel + * + */ +public class DefaultColorStyleDescription extends TemplateSwitch { + + private static final String BLACK = "black"; + + private static final String GRAY = "gray"; + + /** + * Set the default color descriptions on the given EObject. + * + * @param theEObject + * the object to update. + */ + public void setDefaultColors(final EObject theEObject) { + doSwitch(theEObject); + } + + /** + * {@inheritDoc} + */ + @Override + public EObject caseTLifelineStyle(TLifelineStyle object) { + object.setLifelineColor(EnvironmentSystemColorFactory.getDefault().getSystemColorDescription(BLACK)); + return super.caseTLifelineStyle(object); + } + + /** + * {@inheritDoc} + */ + @Override + public EObject caseTExecutionStyle(TExecutionStyle object) { + object.setBorderColor(EnvironmentSystemColorFactory.getDefault().getSystemColorDescription(BLACK)); + object.setBackgroundColor(EnvironmentSystemColorFactory.getDefault().getSystemColorDescription(GRAY)); + return super.caseTExecutionStyle(object); + } + + /** + * {@inheritDoc} + */ + @Override + public EObject caseTMessageStyle(TMessageStyle object) { + object.setStrokeColor(EnvironmentSystemColorFactory.getDefault().getSystemColorDescription(BLACK)); + return super.caseTMessageStyle(object); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/AbstractFrame.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/AbstractFrame.java new file mode 100644 index 0000000000..0f55823599 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/AbstractFrame.java @@ -0,0 +1,221 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.elements; + +import java.util.Collection; +import java.util.List; + +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.common.tools.DslCommonPlugin; +import org.eclipse.sirius.common.tools.api.interpreter.EvaluationException; +import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; +import org.eclipse.sirius.common.tools.api.profiler.ProfilerTask; +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.common.tools.api.util.StringUtil; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.business.api.logger.RuntimeLoggerManager; +import org.eclipse.sirius.diagram.sequence.business.internal.util.EventFinder; +import org.eclipse.sirius.diagram.sequence.business.internal.util.ParentOperandFinder; +import org.eclipse.sirius.diagram.sequence.description.DescriptionPackage; +import org.eclipse.sirius.diagram.sequence.description.FrameMapping; +import org.eclipse.sirius.diagram.sequence.util.Range; +import org.eclipse.sirius.provider.SiriusEditPlugin; +import org.eclipse.sirius.tools.api.interpreter.InterpreterUtil; +import org.eclipse.sirius.ui.tools.api.profiler.SiriusTasks; + +/** + * Represents a frame container. + * + * @author mporhel + */ +public abstract class AbstractFrame extends AbstractSequenceNode implements ISequenceEvent { + + private static final ProfilerTask COVERAGE = new ProfilerTask("Sequence", "Compute interaction use coverage", SiriusEditPlugin.getPlugin().getBundledImage(SiriusTasks.IMAGES_VIEWPOINT)); + + /** + * Constructor. + * + * @param node + * the GMF Node representing the combined fragment. + */ + AbstractFrame(Node node) { + super(node); + } + + /** + * Returns a predicate to check whether a GMF View represents a frame. + * + * @return a predicate to check whether a GMF View represents a frame. + */ + public static Predicate notationPredicate() { + return Predicates.or(InteractionUse.notationPredicate(), CombinedFragment.notationPredicate()); + } + + public Rectangle getProperLogicalBounds() { + /* + * Combined Fragments are directly on the diagram itself, so we can use + * the raw GMF bounds as is. + */ + return getRawNotationBounds(); + } + + /** + * Get the covered lifelines. + * + * @return the covered lifelines. + */ + public Collection computeCoveredLifelines() { + DslCommonPlugin.PROFILER.startWork(COVERAGE); + Collection semLifelines = Lists.newArrayList(); + Collection coveredLifelines = Lists.newArrayList(); + + EObject element = getNotationNode().getElement(); + if (element instanceof DDiagramElement && ((DDiagramElement) element).getDiagramElementMapping() instanceof FrameMapping) { + DDiagramElement dde = (DDiagramElement) element; + FrameMapping mapping = (FrameMapping) dde.getDiagramElementMapping(); + EObject semanticInteractionUse = dde.getTarget(); + IInterpreter interpreter = InterpreterUtil.getInterpreter(semanticInteractionUse); + + if (interpreter != null && !StringUtil.isEmpty(mapping.getCoveredLifelinesExpression())) { + try { + semLifelines = interpreter.evaluateCollection(semanticInteractionUse, mapping.getCoveredLifelinesExpression()); + } catch (final EvaluationException e) { + RuntimeLoggerManager.INSTANCE.error(mapping, DescriptionPackage.eINSTANCE.getFrameMapping_CoveredLifelinesExpression(), e); + } + } + } + + Collection allLifelines = Lists.newArrayList(getDiagram().getAllLifelines()); + for (Lifeline lifeline : allLifelines) { + EObject sem = ISequenceElement.SEMANTIC_TARGET.apply(lifeline); + if (semLifelines.contains(sem)) { + coveredLifelines.add(lifeline); + } + } + + DslCommonPlugin.PROFILER.stopWork(COVERAGE); + return coveredLifelines; + } + + /** + * Get the covered lifelines. + * + * @return the covered lifelines. + */ + public Collection computeParentEvents() { + Collection coveredLifelines = computeCoveredLifelines(); + return computeParentEvents(coveredLifelines); + } + + /** + * Get the covered lifelines. + * + * @param coveredLifelines + * a collection of lifelines that should be a subset of computed + * lifelines (NO CHECK) + * + * @return the covered lifelines. + */ + public Collection computeParentEvents(Collection coveredLifelines) { + Collection parentEvents = Sets.newHashSet(); + for (Lifeline lifeline : coveredLifelines) { + EventFinder finder = new EventFinder(lifeline); + finder.setEventsToIgnore(Predicates.equalTo((ISequenceEvent) this)); + ISequenceEvent localParent = finder.findMostSpecificEvent(this.getVerticalRange()); + if (localParent != null && localParent.getVerticalRange().includes(this.getVerticalRange())) { + parentEvents.add(localParent); + } + } + return parentEvents; + } + + /** + * Combined fragments are not associated to a particular lifeline. + *

+ * {@inheritDoc} + */ + public Option getLifeline() { + return Options.newNone(); + } + + /** + * {@inheritDoc} + */ + public ISequenceEvent getParentEvent() { + return null; + } + + /** + * {@inheritDoc} + */ + public boolean isLogicallyInstantaneous() { + return false; + } + + /** + * {@inheritDoc} + */ + public boolean canChildOccupy(ISequenceEvent child, Range range) { + return false; + } + + /** + * {@inheritDoc} + */ + public boolean canChildOccupy(ISequenceEvent child, Range range, List eventsToIgnore, Collection lifelines) { + return false; + } + + /** + * {@inheritDoc} + */ + public Range getOccupiedRange() { + return Range.emptyRange(); + } + + /** + * {@inheritDoc} + */ + public Range getValidSubEventsRange() { + return Range.emptyRange(); + } + + /** + * {@inheritDoc} + */ + public ISequenceEvent getHierarchicalParentEvent() { + return null; + } + + /** + * {@inheritDoc} + */ + public Option getParentOperand() { + return new ParentOperandFinder(this).getParentOperand(); + } + + /** + * {@inheritDoc} + */ + public Collection getEventsToMoveWith() { + return getSubEvents(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/AbstractNodeEvent.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/AbstractNodeEvent.java new file mode 100644 index 0000000000..dcd0356359 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/AbstractNodeEvent.java @@ -0,0 +1,255 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.elements; + +import java.util.List; + +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.notation.Bounds; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.NotationPackage; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.diagram.sequence.business.internal.query.SequenceNodeQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.util.ParentOperandFinder; +import org.eclipse.sirius.diagram.sequence.business.internal.util.RangeSetter; +import org.eclipse.sirius.diagram.sequence.description.DescriptionPackage; +import org.eclipse.sirius.diagram.sequence.util.NotationPredicate; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * Represents an execution on a lifeline or another parent execution. + * + * @author mporhel, pcdavid, smonnier + */ +public abstract class AbstractNodeEvent extends AbstractSequenceNode implements ISequenceEvent { + /** + * Predicate to filter Frames and Operand from possible new parents of an + * execution reparent. + */ + public static final Predicate NO_REPARENTABLE_EVENTS = new Predicate() { + public boolean apply(ISequenceEvent input) { + return input instanceof AbstractFrame || input instanceof State || input instanceof Operand || input instanceof Message; + } + }; + + /** + * The visual ID. Same as a normal bordered node. + * + * see org.eclipse.sirius.diagram.internal.edit.parts.DNode2EditPart. + * VISUAL_ID + */ + public static final int VISUAL_ID = 3001; + + /** + * Predicate to check whether a Sirius DDiagramElement represents an + * execution. + */ + private static enum SiriusElementPredicate implements Predicate { + INSTANCE; + + public boolean apply(DDiagramElement input) { + return (AbstractSequenceElement.isSequenceDiagramElement(input, DescriptionPackage.eINSTANCE.getExecutionMapping()) || AbstractSequenceElement.isSequenceDiagramElement(input, + DescriptionPackage.eINSTANCE.getStateMapping())) && !InstanceRole.viewpointElementPredicate().apply((DDiagramElement) input.eContainer()); + } + } + + /** + * Constructor. + * + * @param node + * the GMF Node representing this execution. + */ + AbstractNodeEvent(Node node) { + super(node); + Preconditions.checkArgument(AbstractNodeEvent.notationPredicate().apply(node), "The node does not represent an AbstractNodeEvent."); + } + + /** + * Returns a predicate to check whether a GMF View represents an execution. + * + * @return a predicate to check whether a GMF View represents an execution. + */ + public static Predicate notationPredicate() { + return new NotationPredicate(NotationPackage.eINSTANCE.getNode(), VISUAL_ID, AbstractNodeEvent.viewpointElementPredicate()); + } + + /** + * Returns a predicate to check whether a Sirius DDiagramElement + * represents an execution. + * + * @return a predicate to check whether a Sirius DDiagramElement + * represents an execution. + */ + public static Predicate viewpointElementPredicate() { + return SiriusElementPredicate.INSTANCE; + } + + /** + * {@inheritDoc} + */ + public ISequenceEvent getParentEvent() { + ISequenceEvent parent = getHierarchicalParentEvent(); + + List potentialSiblings = parent.getSubEvents(); + if (!potentialSiblings.contains(this)) { + // look for parentOperand + parent = getParentOperand().get(); + } + return parent; + } + + /** + * {@inheritDoc} + */ + public ISequenceEvent getHierarchicalParentEvent() { + EObject viewContainer = this.view.eContainer(); + if (viewContainer instanceof View) { + View parentView = (View) viewContainer; + Option parentElement = ISequenceElementAccessor.getISequenceEvent(parentView); + if (parentElement.some()) { + return parentElement.get(); + } + } + throw new RuntimeException("Invalid context for execution " + this); + } + + /** + * Finds the deepest Operand container including the position if existing. + * + * @param verticalPosition + * the position where to look for the deepest operand + * @return the deepest Operand convering this lifeline at this range + * @see ISequenceEvent#getParentOperand() + */ + public Option getParentOperand(final int verticalPosition) { + return new ParentOperandFinder(this).getParentOperand(new Range(verticalPosition, verticalPosition)); + } + + /** + * Finds the deepest Operand container including the position if existing. + * + * @param range + * the range where to look for the deepest operand + * @return the deepest Operand convering this lifeline at this range + * @see ISequenceEvent#getParentOperand() + */ + public Option getParentOperand(final Range range) { + return new ParentOperandFinder(this).getParentOperand(range); + } + + /** + * Finds the deepest Operand container if existing. + * + * @return the deepest Operand container if existing + */ + public Option getParentOperand() { + return new ParentOperandFinder(this).getParentOperand(); + } + + /** + * {@inheritDoc} + */ + public Range getVerticalRange() { + return new SequenceNodeQuery(getNotationNode()).getVerticalRange(); + } + + /** + * {@inheritDoc} + */ + public boolean isLogicallyInstantaneous() { + return false; + } + + /** + * {@inheritDoc} + */ + public void setVerticalRange(Range range) throws IllegalStateException { + RangeSetter.setVerticalRange(this, range); + } + + /** + * {@inheritDoc} + */ + public Option getLifeline() { + return getParentLifeline(); + } + + /** + * {@inheritDoc} + */ + public Rectangle getProperLogicalBounds() { + if (getNotationNode().getLayoutConstraint() instanceof Bounds) { + Bounds bounds = (Bounds) getNotationNode().getLayoutConstraint(); + ISequenceEvent parent = getHierarchicalParentEvent(); + Rectangle parentLogicalBounds = parent.getProperLogicalBounds(); + + Point location = getProperLogicalLocation(parent, bounds, parentLogicalBounds); + Dimension size = new Dimension(bounds.getWidth(), bounds.getHeight()); + return new Rectangle(location, size); + } else { + throw new RuntimeException(); + } + } + + private Point getProperLogicalLocation(ISequenceEvent parent, Bounds bounds, Rectangle parentLogicalBounds) { + int x = parentLogicalBounds.x; + int y = parentLogicalBounds.y + bounds.getY(); + + if (Lifeline.notationPredicate().apply(parent.getNotationView()) || this instanceof State) { + /* + * Top-level executions which are directly on a lifeline are + * horizontally centered on the lifeline. + */ + Point top = parentLogicalBounds.getTop(); + int width = bounds.getWidth(); + x = top.x - width / 2; + } else { + /* + * Sub-executions horizontally overlap partially their parent + * execution of IBorderItemOffsets.DEFAULT_OFFSET.width. We can not + * depend on that type here (it is in a UI plug-in, but we use its + * value: 8 pixels. + */ + Point topRight = parentLogicalBounds.getTopRight(); + x = topRight.x - 5; + } + return new Point(x, y); + } + + /** + * Returns all the messages linked to one of the sides (top or bottom) of + * this execution. The resulting list can be: + *

    + *
  • empty, meaning there is no messages linked to any of this execution's + * sides.
  • + *
  • a list with a single element, in which case that element is the + * message linked to the top side of this execution.
  • + *
  • a list with two element, in which case the first element is the + * message linked to the top side of this execution and the second is the + * message linked to the bottom side.
  • + *
+ * Note that the case where there is only one linked message and it is + * linked to the bottom side is not supported. + * + * @return all the messages linked to one of the sides (top or bottom) of + * this execution. + */ + public abstract List getLinkedMessages(); +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/AbstractSequenceElement.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/AbstractSequenceElement.java new file mode 100644 index 0000000000..a8aa51f7db --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/AbstractSequenceElement.java @@ -0,0 +1,152 @@ +/******************************************************************************* + * Copyright (c) 2010, 2013 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.elements; + +import org.eclipse.emf.common.notify.impl.AdapterImpl; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.notation.Diagram; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Preconditions; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.business.api.query.DiagramElementMappingQuery; +import org.eclipse.sirius.description.DiagramElementMapping; + +/** + * Partial abstract implementation of {@link ISequenceElement}. + * + * @author mporhel + */ +public abstract class AbstractSequenceElement extends AdapterImpl implements ISequenceElement { + /** + * The underlying GMF View. + */ + protected final View view; + + /** + * Constructor. + * + * @param view + * the underlying GMF View. + */ + AbstractSequenceElement(View view) { + this.view = Preconditions.checkNotNull(view); + } + + /** + * Generic test function used by all the more specific predicates: checks + * that a diagram element is valid, is part of a sequence diagram, and has + * the specified type of mapping (or a sub-type). + * + * This method handle edge mapping imports. + * + * @param element + * {@link DDiagramElement} + * @param mappingType + * type of the mapping + * + * @return true is element is a sequence diagram element + */ + protected static final boolean isSequenceDiagramElement(DDiagramElement element, EClass mappingType) { + Preconditions.checkNotNull(mappingType); + if (element == null || element.getDiagramElementMapping() == null) { + return false; + } else { + DiagramElementMapping mappingToCheck = new DiagramElementMappingQuery(element.getDiagramElementMapping()).getRootMapping(); + return mappingType.isInstance(mappingToCheck) && SequenceDiagram.viewpointElementPredicate().apply(element.getParentDiagram()); + } + } + + /** + * {@inheritDoc} + */ + public View getNotationView() { + return view; + } + + /** + * {@inheritDoc} + */ + public SequenceDiagram getDiagram() { + Diagram gmfDiagram = view.getDiagram(); + Option diagram = ISequenceElementAccessor.getSequenceDiagram(gmfDiagram); + assert diagram.some() : "The element is not part of a sequence diagram."; + return diagram.get(); + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return view.hashCode(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + boolean result = false; + if (this == obj) { + result = true; + } else if (obj != null && getClass().equals(obj.getClass())) { + AbstractSequenceElement other = (AbstractSequenceElement) obj; + if (view != null && view.equals(other.view)) { + result = true; + } + } + return result; + } + + /** + * {@inheritDoc} + */ + public Option getSemanticTargetElement() { + if (view.getElement() instanceof DSemanticDecorator) { + return Options.newSome(((DSemanticDecorator) view.getElement()).getTarget()); + } else { + return Options.newNone(); + } + } + + /** + * Tries to find a lifeline among the ancestors of this element (including + * the element itself). + * + * @return option on the parent lifeline of this sequenceElement + */ + protected Option getParentLifeline() { + View current = view; + do { + Option lifeline = ISequenceElementAccessor.getLifeline(current); + if (lifeline.some()) { + return lifeline; + } else { + current = (View) current.eContainer(); + } + } while (current != null && !(current instanceof Diagram)); + return Options.newNone(); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "#<" + getClass().getSimpleName() + ": " + view.getElement() + ">"; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/AbstractSequenceNode.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/AbstractSequenceNode.java new file mode 100644 index 0000000000..22d2959d9c --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/AbstractSequenceNode.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.elements; + +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gmf.runtime.notation.Bounds; +import org.eclipse.gmf.runtime.notation.Node; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.diagram.ui.tools.api.util.GMFNotationHelper; + +/** + * Abstract base class for sequence elements which are represented by a GMF + * Node. + * + * @author mporhel, pcdavid + */ +abstract class AbstractSequenceNode extends AbstractSequenceElement implements ISequenceNode { + /** + * Constructor. + * + * @param node + * the GMF Node representing this element. + */ + AbstractSequenceNode(Node node) { + super(node); + } + + /** + * Convenience method to return the underlying GMF View as a Node. + * + * @return the GMF Node representing this element. + */ + public Node getNotationNode() { + return (Node) view; + } + + /** + * {@inheritDoc} + */ + public Rectangle getBounds() { + Node node = getNotationNode(); + if (!(node.getElement() instanceof DDiagramElement)) { + return null; + } else { + Point absLoc = GMFNotationHelper.getAbsoluteLocation(node); + int width = GMFNotationHelper.getWidth(node); + int height = GMFNotationHelper.getHeight(node); + return new Rectangle(absLoc.x, absLoc.y, width, height); + } + } + + /** + * Returns the raw bounds of this element as stored in the GMF Node. + */ + protected Rectangle getRawNotationBounds() { + if (getNotationNode().getLayoutConstraint() instanceof Bounds) { + Bounds bounds = (Bounds) getNotationNode().getLayoutConstraint(); + return new Rectangle(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight()); + } else { + throw new RuntimeException(); + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/CombinedFragment.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/CombinedFragment.java new file mode 100644 index 0000000000..0fd585ed84 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/CombinedFragment.java @@ -0,0 +1,211 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.elements; + +import java.util.Collections; +import java.util.List; + +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.NotationPackage; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.diagram.sequence.business.internal.query.SequenceNodeQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.util.RangeSetter; +import org.eclipse.sirius.diagram.sequence.description.DescriptionPackage; +import org.eclipse.sirius.diagram.sequence.util.NotationPredicate; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * Represents a combined fragment container. + * + * @author pcdavid + */ +public class CombinedFragment extends AbstractFrame { + /** + * The visual ID. Same as a normal container + * + * @see DNodeContainerEditPart.VISUAL_ID. + */ + public static final int VISUAL_ID = 2002; + + /** + * The visual ID of the compartment contained by the combined fragment. It + * is this compartment that contains the operands. + * + * @see DNodeContainerViewNodeContainerCompartmentEditPart.VISUAL_ID. + */ + public static final int COMPARTMENT_VISUAL_ID = 7001; + + /** + * Predicate to check whether a Sirius DDiagramElement represents an + * execution. + */ + private static enum SiriusElementPredicate implements Predicate { + INSTANCE; + + public boolean apply(DDiagramElement input) { + return AbstractSequenceElement.isSequenceDiagramElement(input, DescriptionPackage.eINSTANCE.getCombinedFragmentMapping()); + } + } + + /** + * Constructor. + * + * @param node + * the GMF Node representing the combined fragment. + */ + CombinedFragment(Node node) { + super(node); + Preconditions.checkArgument(CombinedFragment.notationPredicate().apply(node), "The node does not represent a combined fragment."); + } + + /** + * Returns a predicate to check whether a GMF View represents an combined + * fragment. + * + * @return a predicate to check whether a GMF View represents an combined + * fragment. + */ + public static Predicate notationPredicate() { + return new NotationPredicate(NotationPackage.eINSTANCE.getNode(), VISUAL_ID, CombinedFragment.viewpointElementPredicate()); + } + + /** + * Returns a predicate to check whether a GMF View represents an combined + * fragment compartment. + * + * @return a predicate to check whether a GMF View represents an combined + * fragment compartment. + */ + public static Predicate compartmentNotationPredicate() { + return new NotationPredicate(NotationPackage.eINSTANCE.getNode(), COMPARTMENT_VISUAL_ID, CombinedFragment.viewpointElementPredicate()); + } + + /** + * Returns a predicate to check whether a Sirius DDiagramElement + * represents an execution. + * + * @return a predicate to check whether a Sirius DDiagramElement + * represents an execution. + */ + public static Predicate viewpointElementPredicate() { + return SiriusElementPredicate.INSTANCE; + } + + /** + * {@inheritDoc} + */ + public Range getVerticalRange() { + // Rectangle logicalBounds = getProperLogicalBounds(); + // return new Range(logicalBounds.y, logicalBounds.bottom()); + return new SequenceNodeQuery(getNotationNode()).getVerticalRange(); + } + + /** + * {@inheritDoc} + */ + public void setVerticalRange(Range range) throws IllegalStateException { + RangeSetter.setVerticalRange(this, range); + } + + /** + * {@inheritDoc} + */ + public List getSubEvents() { + return Lists.newArrayList(Iterables.filter(getOperands(), ISequenceEvent.class)); + } + + /** + * Get the operands of the current combined fragment. + * + * @return the operands of the current combined fragment. + */ + public List getOperands() { + List result = Lists.newArrayList(); + Predicate compartementView = new Predicate() { + + public boolean apply(View input) { + return input.getType().equals(Integer.toString(COMPARTMENT_VISUAL_ID)); + } + }; + // The combined fragment contains a compartment that contains the + // operands + for (View view : Iterables.filter(Iterables.filter(this.view.eContents(), View.class), compartementView)) { + // Filtering compartments + for (View viewChild : Iterables.filter(view.eContents(), View.class)) { + // Filtering operands + Option operand = ISequenceElementAccessor.getOperand(viewChild); + if (operand.some()) { + result.add(operand.get()); + } + } + } + Collections.sort(result, Range.lowerBoundOrdering().onResultOf(ISequenceEvent.VERTICAL_RANGE)); + return result; + } + + /** + * Finds the operand identified by the index. + * + * @param index + * the position of the wanted operand + * @return an Option of Operand if there is an operand at the given index, + * an Options.newNone() otherwise + */ + public Option getOperand(int index) { + try { + return Options.newSome(getOperands().get(index)); + } catch (IndexOutOfBoundsException e) { + return Options.newNone(); + } + } + + /** + * calculate the index of the operand among the list of operands in this + * {@link CombinedFragment}. + * + * @param operand + * the operand to find out its index + * @return the index of the operand + */ + public int getIndexOfOperand(Operand operand) { + return getOperands().lastIndexOf(operand); + } + + /** + * Finds the first operand of this {@link CombinedFragment}. + * + * @return the first operand of this {@link CombinedFragment} + */ + public Operand getFirstOperand() { + // A combined fragment always have at least one operand + return getOperand(0).get(); + } + + /** + * Finds the last operand of this {@link CombinedFragment}. + * + * @return the last operand of this {@link CombinedFragment} + */ + public Operand getLastOperand() { + // A combined fragment always have at least one operand + int lastIndex = getOperands().size() - 1; + return getOperand(lastIndex).get(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/EndOfLife.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/EndOfLife.java new file mode 100644 index 0000000000..d52bba471d --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/EndOfLife.java @@ -0,0 +1,140 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.elements; + +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gmf.runtime.notation.Bounds; +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.NotationPackage; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.diagram.sequence.description.DescriptionPackage; +import org.eclipse.sirius.diagram.sequence.util.NotationPredicate; + +/** + * Represents the EndOfLife marker which can appear at the bottom of a lifeline. + * This element can be present even in the case where the lifeline is not + * explicitly destroyed by a destruction message. In that case, it is used as a + * convenience to allow the user to resize the lifeline vertically by dragging + * the EndOfLife marker. + * + * @author mporhel, pcdavid + */ +public class EndOfLife extends AbstractSequenceNode { + /** + * The visual ID. Same as a normal bordered node. + * + * @see DNode2EditPart.VISUAL_ID + */ + public static final int VISUAL_ID = 3001; + + /** + * Predicate to check whether a Sirius DDiagramElement represents an + * EndOfLife. + */ + private static enum SiriusElementPredicate implements Predicate { + INSTANCE; + + public boolean apply(DDiagramElement input) { + return AbstractSequenceElement.isSequenceDiagramElement(input, DescriptionPackage.eINSTANCE.getEndOfLifeMapping()); + } + } + + /** + * Constructor. + * + * @param node + * the GMF Node which represents this EOL. + */ + EndOfLife(Node node) { + super(node); + Preconditions.checkArgument(EndOfLife.notationPredicate().apply(node), "The node does not represent an end-of-life."); + } + + /** + * Returns a predicate to check whether a GMF View represents an EndOfLife. + * + * @return a predicate to check whether a GMF View represents an EndOfLife. + */ + public static Predicate notationPredicate() { + return new NotationPredicate(NotationPackage.eINSTANCE.getNode(), VISUAL_ID, EndOfLife.viewpointElementPredicate()); + } + + /** + * Returns a predicate to check whether a Sirius DDiagramElement + * represents an EndOfLife. + * + * @return a predicate to check whether a Sirius DDiagramElement + * represents an EndOfLife. + */ + public static Predicate viewpointElementPredicate() { + return SiriusElementPredicate.INSTANCE; + } + + /** + * {@inheritDoc} + */ + public Option getLifeline() { + return getParentLifeline(); + } + + /** + * Returns the destruction message which targets this EOL, if any. + * + * @return the destruction message which targets this EOL, if any. + */ + public Option getDestructionMessage() { + Node node = getNotationNode(); + for (Edge edge : Iterables.filter(node.getTargetEdges(), Edge.class)) { + Option message = ISequenceElementAccessor.getMessage(edge); + if (message.some() && message.get().getKind() == Message.Kind.DESTRUCTION) { + return message; + } + } + return Options.newNone(); + } + + /** + * {@inheritDoc} + */ + public Rectangle getProperLogicalBounds() { + if (getNotationNode().getLayoutConstraint() instanceof Bounds) { + Bounds bounds = (Bounds) getNotationNode().getLayoutConstraint(); + Rectangle llBounds = getLifeline().get().getProperLogicalBounds(); + Point bottom = llBounds.getBottom(); + int width = bounds.getWidth(); + return new Rectangle(bottom.x - width / 2, bottom.y, bounds.getWidth(), bounds.getHeight()); + } else { + throw new RuntimeException(); + } + } + + /** + * Tests whether this EOL marker (and thus the associated lifeline) is + * explicitly destroyed by a destruction message. + * + * @return true if this EOL marker is the target of a + * destruction message. + */ + public boolean isExplicitelyDestroyed() { + return getDestructionMessage().some(); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/Execution.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/Execution.java new file mode 100644 index 0000000000..ab0d8369fd --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/Execution.java @@ -0,0 +1,468 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.elements; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.NotationPackage; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.business.internal.ordering.EventEndHelper; +import org.eclipse.sirius.diagram.sequence.business.internal.query.ISequenceEventQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.query.SequenceNodeQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.util.ParentOperandFinder; +import org.eclipse.sirius.diagram.sequence.business.internal.util.RangeSetter; +import org.eclipse.sirius.diagram.sequence.business.internal.util.SubEventsHelper; +import org.eclipse.sirius.diagram.sequence.description.DescriptionPackage; +import org.eclipse.sirius.diagram.sequence.ordering.CompoundEventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.SingleEventEnd; +import org.eclipse.sirius.diagram.sequence.util.NotationPredicate; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * Represents an execution on a lifeline or another parent execution. + * + * @author mporhel, pcdavid, smonnier + */ +public class Execution extends AbstractNodeEvent { + /** + * Predicate to filter Frames and Operand from possible new parents of an + * execution reparent. + */ + public static final Predicate NO_REPARENTABLE_EVENTS = new Predicate() { + public boolean apply(ISequenceEvent input) { + return input instanceof AbstractFrame || input instanceof Operand || input instanceof Message; + } + }; + + /** + * The visual ID. Same as a normal bordered node. + */ + public static final int VISUAL_ID = 3001; + + /** + * Predicate to check whether a Sirius DDiagramElement represents an + * execution. + */ + private static enum SiriusElementPredicate implements Predicate { + INSTANCE; + + public boolean apply(DDiagramElement input) { + return AbstractSequenceElement.isSequenceDiagramElement(input, DescriptionPackage.eINSTANCE.getExecutionMapping()) + && !InstanceRole.viewpointElementPredicate().apply((DDiagramElement) input.eContainer()); + } + } + + /** + * Constructor. + * + * @param node + * the GMF Node representing this execution. + */ + Execution(Node node) { + super(node); + Preconditions.checkArgument(Execution.notationPredicate().apply(node), "The node does not represent an execution."); + } + + /** + * Returns a predicate to check whether a GMF View represents an execution. + * + * @return a predicate to check whether a GMF View represents an execution. + */ + public static Predicate notationPredicate() { + return new NotationPredicate(NotationPackage.eINSTANCE.getNode(), VISUAL_ID, Execution.viewpointElementPredicate()); + } + + /** + * Returns a predicate to check whether a Sirius DDiagramElement + * represents an execution. + * + * @return a predicate to check whether a Sirius DDiagramElement + * represents an execution. + */ + public static Predicate viewpointElementPredicate() { + return SiriusElementPredicate.INSTANCE; + } + + /** + * {@inheritDoc} + */ + @Override + public List getLinkedMessages() { + List linkedMessages = Lists.newArrayList(); + + Option startMessage = getStartMessage(); + if (startMessage.some()) { + linkedMessages.add(startMessage.get()); + } + + Option targetMessage = getEndMessage(); + if (targetMessage.some()) { + linkedMessages.add(targetMessage.get()); + } + + return linkedMessages; + } + + /** + * Returns the message linked to the start (i.e. top side) of this + * execution, if any. + * + * @return the message linked to the start of this execution, if any. + */ + public Option getStartMessage() { + return getCompoundMessage(true); + } + + private Option getCompoundMessage(boolean start) { + Node node = getNotationNode(); + Set edges = Sets.newHashSet(); + Iterables.addAll(edges, Iterables.filter(node.getSourceEdges(), Edge.class)); + Iterables.addAll(edges, Iterables.filter(node.getTargetEdges(), Edge.class)); + + List ends = EventEndHelper.findEndsFromSemanticOrdering(this); + for (Edge edge : edges) { + Option message = ISequenceElementAccessor.getMessage(edge); + if (message.some()) { + List messageEnds = EventEndHelper.findEndsFromSemanticOrdering(message.get()); + Iterables.retainAll(messageEnds, ends); + if (!messageEnds.isEmpty()) { + SingleEventEnd see = EventEndHelper.getSingleEventEnd(messageEnds.get(0), getSemanticTargetElement().get()); + if (start == see.isStart()) { + return message; + } + } + } + } + return Options.newNone(); + } + + /** + * Returns the message linked to the end (i.e. bottom side) of this + * execution, if any. + * + * @return the message linked to the end of this execution, if any. + */ + public Option getEndMessage() { + return getCompoundMessage(false); + } + + /** + * Tests whether this execution starts with a reflective message. + * + * @return true if this execution has a reflective message + * linked to its start. + */ + public boolean startsWithReflectiveMessage() { + Option startMessage = getStartMessage(); + if (startMessage.some()) { + return startMessage.get().isReflective(); + } else { + return false; + } + } + + /** + * Tests whether this execution ends with a reflective message. + * + * @return true if this execution has a reflective message + * linked to its end. + */ + public boolean endsWithReflectiveMessage() { + Option finishMessage = getEndMessage(); + if (finishMessage.some()) { + return finishMessage.get().isReflective(); + } else { + return false; + } + } + + /** + * Validate that the execution is reflective. Therefore, its start message + * must be reflective and its return message must be null (Asynchronous + * message) or reflexive. + * + * @return if the execution is reflective + */ + public boolean isReflective() { + Option startMessage = getStartMessage(); + Option endMessage = getEndMessage(); + return startMessage.some() && startMessage.get().isReflective() && (!endMessage.some() || endMessage.get().isReflective()); + } + + /** + * {@inheritDoc} + */ + @Override + public ISequenceEvent getParentEvent() { + ISequenceEvent parent = getHierarchicalParentEvent(); + + List potentialSiblings = parent.getSubEvents(); + if (!potentialSiblings.contains(this)) { + // look for parentOperand + parent = getParentOperand().get(); + } + return parent; + } + + /** + * {@inheritDoc} + */ + @Override + public ISequenceEvent getHierarchicalParentEvent() { + EObject viewContainer = this.view.eContainer(); + if (viewContainer instanceof View) { + View parentView = (View) viewContainer; + Option parentElement = ISequenceElementAccessor.getISequenceEvent(parentView); + if (parentElement.some()) { + return parentElement.get(); + } + } + throw new RuntimeException("Invalid context for execution " + this); + } + + /** + * Finds the deepest Operand container including the position if existing. + * + * @param verticalPosition + * the position where to look for the deepest operand + * @return the deepest Operand convering this lifeline at this range + * @see ISequenceEvent#getParentOperand() + */ + @Override + public Option getParentOperand(final int verticalPosition) { + return new ParentOperandFinder(this).getParentOperand(new Range(verticalPosition, verticalPosition)); + } + + /** + * Finds the deepest Operand container including the position if existing. + * + * @param range + * the range where to look for the deepest operand + * @return the deepest Operand convering this lifeline at this range + * @see ISequenceEvent#getParentOperand() + */ + @Override + public Option getParentOperand(final Range range) { + return new ParentOperandFinder(this).getParentOperand(range); + } + + /** + * Finds the deepest Operand container if existing. + * + * @return the deepest Operand container if existing + */ + @Override + public Option getParentOperand() { + return new ParentOperandFinder(this).getParentOperand(); + } + + /** + * {@inheritDoc} + */ + public List getSubEvents() { + return new SubEventsHelper(this).getSubEvents(); + } + + /** + * {@inheritDoc} + */ + public Collection getEventsToMoveWith() { + Set toMove = Sets.newLinkedHashSet(); + List subEvents = getSubEvents(); + toMove.addAll(findLinkedExecutions(subEvents)); + toMove.addAll(getLinkedMessages()); + toMove.addAll(findCoveredExecutions(subEvents)); + toMove.addAll(subEvents); + return toMove; + } + + private Collection findLinkedExecutions(List subEvents) { + Set linkedExecutions = Sets.newLinkedHashSet(); + for (Message message : Iterables.filter(subEvents, Message.class)) { + if (this.equals(message.getSourceElement()) && message.getTargetElement() instanceof Execution) { + Execution targetExecution = (Execution) message.getTargetElement(); + for (CompoundEventEnd messageCompoundEventEnd : Iterables.filter(EventEndHelper.findEndsFromSemanticOrdering(message), CompoundEventEnd.class)) { + for (CompoundEventEnd executionCompoundEventEnd : Iterables.filter(EventEndHelper.findEndsFromSemanticOrdering(targetExecution), CompoundEventEnd.class)) { + if (messageCompoundEventEnd.equals(executionCompoundEventEnd)) { + if (!this.equals(targetExecution)) { + linkedExecutions.add(targetExecution); + } + } + } + } + } + } + return linkedExecutions; + } + + private Collection findCoveredExecutions(List subEvents) { + Collection coveredExecutions = Lists.newArrayList(); + for (AbstractFrame frame : Iterables.filter(subEvents, AbstractFrame.class)) { + Collection parentEvents = frame.computeParentEvents(); + parentEvents.remove(this); + Iterables.addAll(coveredExecutions, Iterables.filter(parentEvents, Execution.class)); + } + return coveredExecutions; + } + + /** + * {@inheritDoc} + */ + @Override + public Range getVerticalRange() { + return new SequenceNodeQuery(getNotationNode()).getVerticalRange(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isLogicallyInstantaneous() { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public void setVerticalRange(Range range) throws IllegalStateException { + RangeSetter.setVerticalRange(this, range); + } + + /** + * {@inheritDoc} + */ + @Override + public Option getLifeline() { + return getParentLifeline(); + } + + /** + * {@inheritDoc} + */ + public boolean canChildOccupy(ISequenceEvent child, Range range) { + return new SubEventsHelper(this).canChildOccupy(child, range); + } + + /** + * {@inheritDoc} + */ + public boolean canChildOccupy(ISequenceEvent child, Range range, List eventsToIgnore, Collection lifelines) { + return new SubEventsHelper(this).canChildOccupy(child, range, eventsToIgnore, lifelines); + } + + /** + * {@inheritDoc} + */ + public Range getOccupiedRange() { + return new ISequenceEventQuery(this).getOccupiedRange(); + } + + /** + * Sub-events can occur anywhere on a normal execution as long as it is + * strictly inside. + *

+ * {@inheritDoc} + */ + public Range getValidSubEventsRange() { + Range range = getVerticalRange(); + if (range.width() > 2 * LayoutConstants.EXECUTION_CHILDREN_MARGIN) { + return range.shrinked(LayoutConstants.EXECUTION_CHILDREN_MARGIN); + } else { + return range; + } + } + + /** + * Finds all linked executions (by messages with CompoundEventEnd) from + * executionEditPart. Depending on the value of investigateRecursively, it + * will recursively investigate the linked execution. + * + * @param recurse + * investigate recursively if true + * @return the list of linked {@link Execution} from executionEditPart + * @deprecated + */ + @Deprecated + public List findLinkedExecutions(boolean recurse) { + List impactedExecutions = Lists.newArrayList(); + findLinkedExecutions(impactedExecutions, this, recurse); + return impactedExecutions; + } + + /** + * Recursive function of the previous one that add the result in the + * parameter list impactedExecutionEditPart. + * + * @param impactedExecutioExecutions + * the list of linked {@link Execution} from executionEditPart + * @param execution + * the current {@link Execution} + * @param recurse + * investigate recursively if true + */ + private void findLinkedExecutions(List impactedExecutions, Execution execution, boolean recurse) { + List messagesFrom = new ISequenceEventQuery(execution).getAllMessagesFrom(); + for (Message message : messagesFrom) { + boolean targetsUnseenExecution = message.getTargetElement() instanceof Execution && !impactedExecutions.contains(message.getTargetElement()); + if (targetsUnseenExecution) { + Execution targetExecution = (Execution) message.getTargetElement(); + for (CompoundEventEnd messageCompoundEventEnd : Iterables.filter(EventEndHelper.findEndsFromSemanticOrdering(message), CompoundEventEnd.class)) { + for (CompoundEventEnd executionCompoundEventEnd : Iterables.filter(EventEndHelper.findEndsFromSemanticOrdering(targetExecution), CompoundEventEnd.class)) { + if (messageCompoundEventEnd.equals(executionCompoundEventEnd)) { + if (!impactedExecutions.contains(targetExecution)) { + impactedExecutions.add(targetExecution); + if (recurse) { + findLinkedExecutions(impactedExecutions, targetExecution, recurse); + } + } + } + } + } + } + } + } + + /** + * Returns the extended vertical range of this execution, i.e. the vertical + * range of the execution including any extensions like branches for linked + * start/end reflective messages. This corresponds to the range of all the + * elements which are tied to the execution and will move along with it when + * the execution is moved. + * + * @return the extended vertical range of this execution. + */ + public Range getExtendedVerticalRange() { + Range result = getVerticalRange(); + for (Message linkedMessage : getLinkedMessages()) { + // For non-reflective and non-deferred messages, this is a no-op. + result = result.union(linkedMessage.getVerticalRange()); + } + return result; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/ISequenceElement.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/ISequenceElement.java new file mode 100644 index 0000000000..24194b9a14 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/ISequenceElement.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.elements; + +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.common.notify.Adapter; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Function; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.DSemanticDecorator; + +/** + * Common interface for all the elements of a sequence diagram. + * + * @author mporhel + */ +public interface ISequenceElement extends Adapter { + + /** + * Function to get the notation view. + */ + Function NOTATION_VIEW = new Function() { + public View apply(ISequenceElement from) { + return from.getNotationView(); + } + }; + + /** + * Function to compute the semantic target element from a sequence element. + */ + Function SEMANTIC_TARGET = new Function() { + public EObject apply(ISequenceElement from) { + EObject semantic = null; + View notationView = from.getNotationView(); + if (notationView != null && notationView.getElement() instanceof DSemanticDecorator) { + semantic = ((DSemanticDecorator) notationView.getElement()).getTarget(); + } + return semantic; + } + }; + + /** + * A function to compute the proper logical bounds a sequence element. + */ + Function PROPER_LOGICAL_BOUNDS = new Function() { + public Rectangle apply(ISequenceElement from) { + return from.getProperLogicalBounds(); + } + }; + + /** + * Returns the view corresponding to the current sequence element. + * + * @return the corresponding view. + */ + View getNotationView(); + + /** + * Returns the semantic object that this element represents. + * + * @return the semantic object that this element represents. + */ + Option getSemanticTargetElement(); + + /** + * Returns the sequence diagram this element is part of. + * + * @return the sequence diagram this element is part of. + */ + SequenceDiagram getDiagram(); + + /** + * Return an option referencing the current lifeline of the current sequence + * event. + * + * @return the lifeline of the current node. + */ + Option getLifeline(); + + /** + * Returns the logical bounds of this element itself, excluding any attached + * element (sub-executions, message branches...). + * + * @return the logical bounds of this element itself. + */ + Rectangle getProperLogicalBounds(); +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/ISequenceElementAccessor.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/ISequenceElementAccessor.java new file mode 100644 index 0000000000..4b73924803 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/ISequenceElementAccessor.java @@ -0,0 +1,425 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.elements; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature.Setting; +import org.eclipse.emf.ecore.util.ECrossReferenceAdapter; +import org.eclipse.gmf.runtime.notation.Diagram; +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.NotationPackage; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.DSemanticDiagram; +import org.eclipse.sirius.SiriusPackage; +import org.eclipse.sirius.business.api.session.Session; +import org.eclipse.sirius.business.api.session.SessionManager; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; + +/** + * Accessor to get the sequence element corresponding to a notation one. + * + * @author mporhel + * + */ +public final class ISequenceElementAccessor { + /** + * Avoid instanciation + */ + private ISequenceElementAccessor() { + + } + + /** + * TODO Comment. + * + * @param notationView + * . + * @return . + */ + public static Option getISequenceElement(View notationView) { + return ISequenceElementAccessor.getOrCreate(notationView, ISequenceElement.class); + } + + /** + * TODO Comment. + * + * @param diagramView + * . + * @return . + */ + public static Option getSequenceDiagram(Diagram diagramView) { + return ISequenceElementAccessor.getOrCreate(diagramView, SequenceDiagram.class); + } + + /** + * TODO COmment. + * + * @param view + * . + * @return . + */ + public static Option getISequenceEvent(View view) { + return ISequenceElementAccessor.getOrCreate(view, ISequenceEvent.class); + } + + /** + * . + * + * @param view + * . + * @return . + */ + public static Option getISequenceNode(View view) { + return ISequenceElementAccessor.getOrCreate(view, ISequenceNode.class); + } + + /** + * . + * + * @param view + * . + * @return . + */ + public static Option getInstanceRole(View view) { + return ISequenceElementAccessor.getOrCreate(view, InstanceRole.class); + } + + /** + * . + * + * @param view + * . + * @return . + */ + public static Option getLifeline(View view) { + return ISequenceElementAccessor.getOrCreate(view, Lifeline.class); + } + + /** + * . + * + * @param view + * . + * @return . + */ + public static Option getEndOfLife(View view) { + return ISequenceElementAccessor.getOrCreate(view, EndOfLife.class); + } + + /** + * . + * + * @param view + * . + * @return . + */ + public static Option getAbstractNodeEvent(View view) { + return ISequenceElementAccessor.getOrCreate(view, AbstractNodeEvent.class); + } + + /** + * . + * + * @param view + * . + * @return . + */ + public static Option getExecution(View view) { + return ISequenceElementAccessor.getOrCreate(view, Execution.class); + } + + /** + * . + * + * @param view + * . + * @return . + */ + public static Option getState(View view) { + return ISequenceElementAccessor.getOrCreate(view, State.class); + } + + /** + * . + * + * @param view + * . + * @return . + */ + public static Option getMessage(View view) { + return ISequenceElementAccessor.getOrCreate(view, Message.class); + } + + /** + * . + * + * @param view + * . + * @return . + */ + public static Option getInteractionUse(View view) { + return ISequenceElementAccessor.getOrCreate(view, InteractionUse.class); + } + + /** + * . + * + * @param view + * . + * @return . + */ + public static Option getCombinedFragment(View view) { + return ISequenceElementAccessor.getOrCreate(view, CombinedFragment.class); + } + + /** + * . + * + * @param view + * . + * @return . + */ + public static Option getOperand(View view) { + return ISequenceElementAccessor.getOrCreate(view, Operand.class); + } + + /** + * . + * + * @param view + * . + * @return . + */ + public static Option getLostMessageEnd(View view) { + return ISequenceElementAccessor.getOrCreate(view, LostMessageEnd.class); + } + + + /** + * . + * + * @param view + * . + * @return . + */ + public static Option getObservationPoint(View view) { + return ISequenceElementAccessor.getOrCreate(view, ObservationPoint.class); + } + + /** + * Get the existing {@link ISequenceElement} corresponding to the given View + * or create it. + * + * @param notationView + * the notation view. + * @return existing or new {@link ISequenceElement}. + */ + private static Option getOrCreate(View notationView, Class expectedType) { + T ise = null; + + if (notationView != null) { + Iterator it = Iterators.filter(notationView.eAdapters().iterator(), ISequenceElement.class); + if (it.hasNext()) { + ISequenceElement seqElt = it.next(); + if (expectedType.isInstance(seqElt)) { + ise = expectedType.cast(seqElt); + } + } else { + ISequenceElement element = ISequenceElementAccessor.createSequenceElement(notationView); + if (element != null && expectedType.isInstance(element)) { + ise = expectedType.cast(element); + } + } + } + return Options.newSome(ise); + } + + private static ISequenceElement createSequenceElement(View notationView) { + ISequenceElement created = null; + if (SequenceDiagram.notationPredicate().apply(notationView)) { + created = new SequenceDiagram((Diagram) notationView); + } else if (InstanceRole.notationPredicate().apply(notationView)) { + created = new InstanceRole((Node) notationView); + } else if (Lifeline.notationPredicate().apply(notationView)) { + created = new Lifeline((Node) notationView); + } else if (EndOfLife.notationPredicate().apply(notationView)) { + created = new EndOfLife((Node) notationView); + } else if (Execution.notationPredicate().apply(notationView)) { + created = new Execution((Node) notationView); + } else if (State.notationPredicate().apply(notationView)) { + created = new State((Node) notationView); + } else if (Message.notationPredicate().apply(notationView)) { + created = new Message((Edge) notationView); + } else if (CombinedFragment.notationPredicate().apply(notationView)) { + created = new CombinedFragment((Node) notationView); + } else if (Operand.notationPredicate().apply(notationView)) { + created = new Operand((Node) notationView); + } else if (InteractionUse.notationPredicate().apply(notationView)) { + created = new InteractionUse((Node) notationView); + } else if (LostMessageEnd.notationPredicate().apply(notationView)) { + created = new LostMessageEnd((Node) notationView); + } else if (ObservationPoint.notationPredicate().apply(notationView)) { + created = new ObservationPoint((Node) notationView); + } + + if (created != null) { + notationView.eAdapters().add(created); + } + return created; + } + + /** + * Finds all the events in the specified diagram which have a specific + * semantic target. + * + * @param diagram + * the sequence diagram. + * @param semanticElement + * the semantic element. + * @return all the events in the diagram which have the specified semantic + * element as target. + */ + public static Collection getEventsForSemanticElement(SequenceDiagram diagram, EObject semanticElement) { + ECrossReferenceAdapter xref = ISequenceElementAccessor.getCrossReferencer(semanticElement); + if (xref == null) { + return Collections.emptySet(); + } else { + Collection result = Lists.newArrayList(); + for (Setting setting : xref.getInverseReferences(semanticElement)) { + if (ISequenceElementAccessor.isDiagramElementTargetReference(setting)) { + DDiagramElement dde = (DDiagramElement) setting.getEObject(); + Option optView = ISequenceElementAccessor.getGMFView(dde, xref); + if (optView.some()) { + Option elt = ISequenceElementAccessor.getISequenceEvent(optView.get()); + if (elt.some() && diagram.equals(elt.get().getDiagram())) { + result.add(elt.get()); + } + } + } + } + return result; + } + } + + /** + * Finds all the events in the specified diagram which have a specific + * semantic target. + * + * @param diagram + * the sequence diagram. + * @param semanticElement + * the semantic element. + * @return all the events in the diagram which have the specified semantic + * element as target. + */ + public static Collection getViewsForSemanticElement(SequenceDDiagram diagram, EObject semanticElement) { + ECrossReferenceAdapter xref = ISequenceElementAccessor.getCrossReferencer(semanticElement); + if (xref == null || diagram == null) { + return Collections.emptySet(); + } else { + Collection result = Lists.newArrayList(); + for (Setting setting : xref.getInverseReferences(semanticElement)) { + if (ISequenceElementAccessor.isDiagramElementTargetReference(setting)) { + DDiagramElement dde = (DDiagramElement) setting.getEObject(); + Option optView = ISequenceElementAccessor.getGMFView(dde, xref); + if (optView.some() && diagram.equals(dde.getParentDiagram())) { + result.add(optView.get()); + } + } else if (ISequenceElementAccessor.isDiagramTargetReference(setting)) { + DSemanticDiagram foundDiag = (DSemanticDiagram) setting.getEObject(); + Option optView = ISequenceElementAccessor.getGMFView(foundDiag, xref); + if (optView.some() && diagram.equals(foundDiag)) { + result.add(optView.get()); + } + } + } + return result; + } + } + + /** + * Finds all the events in the specified diagram which have a specific + * semantic target. + * + * @param diagram + * the sequence diagram. + * @param semanticElement + * the semantic element. + * @return all the events in the diagram which have the specified semantic + * element as target. + */ + public static Collection getDiagramElementsForSemanticElement(SequenceDiagram diagram, EObject semanticElement) { + ECrossReferenceAdapter xref = ISequenceElementAccessor.getCrossReferencer(semanticElement); + if (xref == null) { + return Collections.emptySet(); + } else { + Collection result = Lists.newArrayList(); + for (Setting setting : xref.getInverseReferences(semanticElement)) { + if (ISequenceElementAccessor.isDiagramElementTargetReference(setting)) { + result.add((DDiagramElement) setting.getEObject()); + } + } + return result; + } + } + + private static Option getGMFView(DSemanticDecorator dSem, ECrossReferenceAdapter xref) { + for (Setting setting : xref.getInverseReferences(dSem)) { + if (ISequenceElementAccessor.isViewElementReference(setting)) { + EObject view = setting.getEObject(); + if (view instanceof View && ((View) view).getDiagram() != null) { + return Options.newSome((View) view); + } + } + } + return Options.newNone(); + } + + private static boolean isViewElementReference(Setting setting) { + EReference elementRef = NotationPackage.eINSTANCE.getView_Element(); + return setting.getEObject() instanceof View && setting.getEStructuralFeature().equals(elementRef); + } + + private static boolean isDiagramElementTargetReference(Setting setting) { + EReference targetReference = SiriusPackage.eINSTANCE.getDSemanticDecorator_Target(); + return setting.getEObject() instanceof DDiagramElement && setting.getEStructuralFeature().equals(targetReference); + } + + private static boolean isDiagramTargetReference(Setting setting) { + EReference targetReference = SiriusPackage.eINSTANCE.getDSemanticDecorator_Target(); + return setting.getEObject() instanceof DDiagram && setting.getEStructuralFeature().equals(targetReference); + } + + private static ECrossReferenceAdapter getCrossReferencer(EObject semanticElement) { + Session session = SessionManager.INSTANCE.getSession(semanticElement); + if (session != null) { + return session.getSemanticCrossReferencer(); + } else { + return null; + } + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/ISequenceEvent.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/ISequenceEvent.java new file mode 100644 index 0000000000..55ec6ff46b --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/ISequenceEvent.java @@ -0,0 +1,172 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.elements; + +import java.util.Collection; +import java.util.List; + +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * Common interface for all the elements of a sequence diagram which represent + * an event associated to a (logical) time interval and thus a range of vertical + * coordinates. This includes lifelines (considered as a special case of + * executions), executions and messages. + * + * @author mporhel + */ +public interface ISequenceEvent extends ISequenceElement { + + /** + * Predicate to test all notation predicate of existing sequence events. + */ + @SuppressWarnings("unchecked") + Predicate ISEQUENCEEVENT_NOTATION_PREDICATE = Predicates.or(AbstractNodeEvent.notationPredicate(), Message.notationPredicate(), InteractionUse.notationPredicate(), + CombinedFragment.notationPredicate(), Operand.notationPredicate()); + + /** + * A function to compute the vertical range a sequence event. + */ + Function VERTICAL_RANGE = new Function() { + public Range apply(ISequenceEvent from) { + return from.getVerticalRange(); + } + }; + + /** + * Tests whether this event should be considered logically to be + * instantaneous. Depending on its graphical representation, it may still + * cover a significant vertical space. + * + * @return true if this event should be considered + * instantaneous. + */ + boolean isLogicallyInstantaneous(); + + /** + * Returns the vertical range of coordinates this event covers. The + * coordinates are normalized y coordinates (relative to the origin of the + * logical plane, whatever the scroll state is, and independent of the zoom + * level). + * + * @return the vertical range of coordinates this event covers. + */ + Range getVerticalRange(); + + /** + * Set the vertical range of this sequence event. + * + * @param range + * the new vertical range. + * @throws IllegalStateException + * if range is not valid. + */ + void setVerticalRange(Range range) throws IllegalStateException; + + /** + * Returns the parent event of this event (from a business point of view), + * if any. Returns null for top-level events, i.e. lifelines. + * + * @return the parent event of this event, if any. + */ + ISequenceEvent getParentEvent(); + + /** + * Returns the hierarchical parent event of this event (from a Notation + * point of view), if any. Returns null for top-level events + * i.e. lifelines / frames / messages. + * + * @return the hierarchical parent event of this event, if any. + */ + ISequenceEvent getHierarchicalParentEvent(); + + /** + * Returns the list of direct sub-events of this event, in chronological + * (and thus also graphical) order. This includes both events which are + * directly owned by this event (e.g. the messages sent by an execution) and + * events not owned but connected to this event (e.g. the messages received + * by an execution). + * + * @return the list of direct sub-events of this event, in chronological + * order. + */ + List getSubEvents(); + + /** + * Returns the vertical range of coordinates inside which direct sub-events + * of this event can be. The coordinates are normalized y coordinates. The + * range returned is guaranteed to be a sub-range of + * {@link #getVerticalRange()} or the empty range for events which can not + * have children. + * + * @return the vertical range in + */ + Range getValidSubEventsRange(); + + /** + * Tests whether a given child can be placed anywhere in the specified + * vertical range. + * + * @param child + * the child. + * @param range + * the vertical range to test. + * @return true if the child can be placed anywhere inside the + * specified vertical range (including occupying the whole range). + */ + boolean canChildOccupy(ISequenceEvent child, Range range); + + /** + * Tests whether a given child can be placed anywhere in the specified + * vertical range. + * + * @param child + * the child. + * @param range + * the vertical range to test. + * @param eventsToIgnore + * the list of events to ignore while computing canChildOccupy. + * @param lifelines + * lifelines to inspect. + * @return true if the child can be placed anywhere inside the + * specified vertical range (including occupying the whole range). + */ + boolean canChildOccupy(ISequenceEvent child, Range range, List eventsToIgnore, Collection lifelines); + + /** + * Calculate the maximal occupied range of this event. + * + * @return the maximal range occupied by children of this event, from the + * beginning of the first sub-event to the end of the last one. + */ + Range getOccupiedRange(); + + /** + * Finds the deepest Operand container if existing. + * + * @return the deepest Operand container if existing. + */ + Option getParentOperand(); + + /** + * Elements whcih should move with current events. + * + * @return a collection of elements moved with the current event. + */ + Collection getEventsToMoveWith(); +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/ISequenceNode.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/ISequenceNode.java new file mode 100644 index 0000000000..3f0626416e --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/ISequenceNode.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.elements; + +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gmf.runtime.notation.Node; + +/** + * Common interface for all the elements of a sequence diagram which represent + * an event associated to a (logical) time interval and thus a range of vertical + * coordinates. This includes lifelines (considered as a special case of + * executions), executions and messages. + * + * @author mporhel + */ +public interface ISequenceNode extends ISequenceElement { + /** + * Convenience method to return the underlying GMF View as a Node. + * + * @return the GMF Node representing this element. + */ + Node getNotationNode(); + + /** + * . + * + * @return . + */ + Rectangle getBounds(); +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/InstanceRole.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/InstanceRole.java new file mode 100644 index 0000000000..59ae7a607e --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/InstanceRole.java @@ -0,0 +1,189 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.elements; + +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.notation.Bounds; +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.NotationPackage; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.DRepresentationElement; +import org.eclipse.sirius.RGBValues; +import org.eclipse.sirius.business.api.query.NodeStyleQuery; +import org.eclipse.sirius.diagram.sequence.description.DescriptionPackage; +import org.eclipse.sirius.diagram.sequence.util.NotationPredicate; + +/** + * Represents the instance role node at the top of a lifeline. + * + * @author mporhel, pcdavid + */ +public class InstanceRole extends AbstractSequenceNode { + + /** + * The visual ID. Same as a normal node. + */ + public static final int VISUAL_ID = 2001; + + /** + * Predicate to check whether a Sirius DDiagramElement represents an + * instance role. + */ + private static enum SiriusElementPredicate implements Predicate { + INSTANCE; + + public boolean apply(DDiagramElement input) { + return AbstractSequenceElement.isSequenceDiagramElement(input, DescriptionPackage.eINSTANCE.getInstanceRoleMapping()); + } + } + + /** + * Constructor. + * + * @param node + * the GMF Node representing this instance role. + */ + InstanceRole(Node node) { + super(node); + Preconditions.checkArgument(InstanceRole.notationPredicate().apply(node), "The node does not represent an instance role."); + + } + + /** + * Returns a predicate to check whether a GMF View represents an instance + * role. + * + * @return a predicate to check whether a GMF View represents an instance + * role. + */ + public static Predicate notationPredicate() { + return new NotationPredicate(NotationPackage.eINSTANCE.getNode(), VISUAL_ID, InstanceRole.viewpointElementPredicate()); + } + + /** + * Returns a predicate to check whether a Sirius DDiagramElement + * represents an instance role. + * + * @return a predicate to check whether a Sirius DDiagramElement + * represents an instance role. + */ + public static Predicate viewpointElementPredicate() { + return SiriusElementPredicate.INSTANCE; + } + + /** + * Tests whether the instance role is explicitly created by a creation + * message, or if it starts from the beginning of the sequence. + * + * @return true if the instance role is explicitly created by a + * creation message. + */ + public boolean isExplicitlyCreated() { + return getCreationMessage().some(); + } + + /** + * Locate the creation message which creates the instance role, if any. + * + * @return the creation message which creates the instance role, if any. + */ + public Option getCreationMessage() { + Node node = getNotationNode(); + for (Edge edge : Iterables.filter(node.getTargetEdges(), Edge.class)) { + Option message = ISequenceElementAccessor.getMessage(edge); + if (message.some() && message.get().getKind() == Message.Kind.CREATION) { + return message; + } + } + return Options.newNone(); + } + + /** + * {@inheritDoc} + */ + public Option getLifeline() { + for (View child : Iterables.filter(getNotationView().getChildren(), View.class)) { + Option lifeline = ISequenceElementAccessor.getLifeline(child); + if (lifeline.some()) { + return lifeline; + } + } + return Options.newNone(); + } + + /** + * {@inheritDoc} + */ + public Rectangle getProperLogicalBounds() { + if (getNotationNode().getLayoutConstraint() instanceof Bounds) { + Bounds bounds = (Bounds) getNotationNode().getLayoutConstraint(); + return new Rectangle(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight()); + } else { + throw new RuntimeException(); + } + } + + /** + * Return the name of the DRepresentationElement associated to this Instance + * role. + * + * @return the name of the DRepresentationElement associated to this + * Instance role + */ + public String getName() { + EObject targetElement = getNotationNode().getElement(); + if (targetElement instanceof DRepresentationElement) { + return ((DRepresentationElement) targetElement).getName(); + } + return ""; + } + + /** + * Return the background color of the style of the DRepresentationElement + * associated to this Instance role. + * + * @return the background color of the style of the DRepresentationElement + * associated to this Instance role. + */ + public Option getBackgroundColor() { + EObject targetElement = getNotationNode().getElement(); + if (targetElement instanceof DNode) { + return new NodeStyleQuery(((DNode) targetElement).getOwnedStyle()).getBackgroundColor(); + } + return Options.newNone(); + } + + /** + * Return the label color of the style of the DRepresentationElement + * associated to this Instance role. + * + * @return the label color of the style of the DRepresentationElement + * associated to this Instance role. + */ + public Option getLabelColor() { + EObject targetElement = getNotationNode().getElement(); + if (targetElement instanceof DNode) { + return new NodeStyleQuery(((DNode) targetElement).getOwnedStyle()).getLabelColor(); + } + return Options.newNone(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/InteractionUse.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/InteractionUse.java new file mode 100644 index 0000000000..1561d2449a --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/InteractionUse.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.elements; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.NotationPackage; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.diagram.sequence.business.internal.query.SequenceNodeQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.util.RangeSetter; +import org.eclipse.sirius.diagram.sequence.description.DescriptionPackage; +import org.eclipse.sirius.diagram.sequence.util.NotationPredicate; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * Represents an interaction use / reference. + * + * @author pcdavid + */ +public class InteractionUse extends AbstractFrame { + /** + * The visual ID. Same as a normal bordered node. + * + * see org.eclipse.sirius.diagram.internal.edit.parts. + * DNodeContainerEditPart.VISUAL_ID + */ + public static final int VISUAL_ID = 2002; + + /** + * Predicate to check whether a Sirius DDiagramElement represents an + * execution. + */ + private static enum SiriusElementPredicate implements Predicate { + INSTANCE; + + public boolean apply(DDiagramElement input) { + return AbstractSequenceElement.isSequenceDiagramElement(input, DescriptionPackage.eINSTANCE.getInteractionUseMapping()); + } + + } + + /** + * Constructor. + * + * @param node + * the GMF Node representing the interaction use. + */ + InteractionUse(Node node) { + super(node); + Preconditions.checkArgument(InteractionUse.notationPredicate().apply(node), "The node does not represent an interaction use."); + } + + /** + * Returns a predicate to check whether a GMF View represents an execution. + * + * @return a predicate to check whether a GMF View represents an execution. + */ + public static Predicate notationPredicate() { + return new NotationPredicate(NotationPackage.eINSTANCE.getNode(), VISUAL_ID, InteractionUse.viewpointElementPredicate()); + } + + /** + * Returns a predicate to check whether a Sirius DDiagramElement + * represents an execution. + * + * @return a predicate to check whether a Sirius DDiagramElement + * represents an execution. + */ + public static Predicate viewpointElementPredicate() { + return SiriusElementPredicate.INSTANCE; + } + + /** + * {@inheritDoc} + */ + public Range getVerticalRange() { + return new SequenceNodeQuery(getNotationNode()).getVerticalRange(); + } + + /** + * {@inheritDoc} + */ + public void setVerticalRange(Range range) throws IllegalStateException { + RangeSetter.setVerticalRange(this, range); + } + + /** + * {@inheritDoc} + */ + public List getSubEvents() { + return Collections.emptyList(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean canChildOccupy(ISequenceEvent child, Range range) { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean canChildOccupy(ISequenceEvent child, Range range, List eventsToIgnore, Collection lifelines) { + return false; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/Lifeline.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/Lifeline.java new file mode 100644 index 0000000000..a78772a674 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/Lifeline.java @@ -0,0 +1,331 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.elements; + +import java.util.Collection; +import java.util.List; + +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gmf.runtime.notation.Bounds; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.NotationPackage; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.business.internal.query.ISequenceEventQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.query.SequenceNodeQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.util.ParentOperandFinder; +import org.eclipse.sirius.diagram.sequence.business.internal.util.RangeSetter; +import org.eclipse.sirius.diagram.sequence.business.internal.util.SubEventsHelper; +import org.eclipse.sirius.diagram.sequence.description.DescriptionPackage; +import org.eclipse.sirius.diagram.sequence.util.NotationPredicate; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * Common interface for all the elements of a sequence diagram. + * + * @author mporhel + */ +public class Lifeline extends AbstractSequenceNode implements ISequenceEvent { + /** + * The visual id. + * + * @see DNode2EditPart.VISUAL_ID + */ + public static final int VISUAL_ID = 3001; + + /** + * Predicate to check whether a Sirius DDiagramElement represents a + * lifeline. + */ + private static enum SiriusElementPredicate implements Predicate { + INSTANCE; + + public boolean apply(DDiagramElement input) { + return AbstractSequenceElement.isSequenceDiagramElement(input, DescriptionPackage.eINSTANCE.getExecutionMapping()) + && InstanceRole.viewpointElementPredicate().apply((DDiagramElement) input.eContainer()); + } + } + + /** + * . + * + * @param node + * . + */ + Lifeline(Node node) { + super(node); + Preconditions.checkArgument(Lifeline.notationPredicate().apply(node), "The node does not represent a lifeline."); + } + + /** + * Returns a predicate to check whether a GMF View represents a lifeline. + * + * @return a predicate to check whether a GMF View represents a lifeline. + */ + public static Predicate notationPredicate() { + return new NotationPredicate(NotationPackage.eINSTANCE.getNode(), VISUAL_ID, Lifeline.viewpointElementPredicate()); + } + + /** + * Returns a predicate to check whether a Sirius DDiagramElement + * represents a lifeline. + * + * @return a predicate to check whether a Sirius DDiagramElement + * represents a lifeline. + */ + public static Predicate viewpointElementPredicate() { + return SiriusElementPredicate.INSTANCE; + } + + /** + * {@inheritDoc} + */ + public InstanceRole getInstanceRole() { + return ISequenceElementAccessor.getInstanceRole((View) getNotationNode().eContainer()).get(); + } + + /** + * {@inheritDoc} + */ + public Range getVerticalRange() { + return new SequenceNodeQuery(getNotationNode()).getVerticalRange(); + } + + /** + * {@inheritDoc} + */ + public boolean isLogicallyInstantaneous() { + return false; + } + + /** + * {@inheritDoc} + */ + public void setVerticalRange(Range range) throws IllegalStateException { + RangeSetter.setVerticalRange(this, range); + } + + /** + * Returns the0 EOL marker for this lifeline, if any. Note that the mere + * presence of an EOL does not mean this lifeline is explicitly destroyed, + * as EOLs can be used just to serve as visual hints of the end of lifelines + * and convenient resize handles. + * + * @return the EOL marker for this lifeline, if any. + */ + public Option getEndOfLife() { + for (View child : Iterables.filter(getNotationView().getChildren(), View.class)) { + if (EndOfLife.notationPredicate().apply(child)) { + return ISequenceElementAccessor.getEndOfLife(child); + } + } + return Options.newNone(); + } + + /** + * {@inheritDoc} + */ + public Option getLifeline() { + return Options.newSome(this); + } + + /** + * {@inheritDoc} + */ + public Rectangle getProperLogicalBounds() { + if (getNotationNode().getLayoutConstraint() instanceof Bounds) { + Bounds bounds = (Bounds) getNotationNode().getLayoutConstraint(); + Rectangle irBounds = getInstanceRole().getProperLogicalBounds(); + Point bottom = irBounds.getBottom(); + int width = bounds.getWidth(); + return new Rectangle(bottom.x - width / 2, bottom.y, bounds.getWidth(), bounds.getHeight()); + } else { + throw new RuntimeException(); + } + } + + /** + * Lifelines are the root of the events hierarchy and thus have not parent. + *

+ * {@inheritDoc} + */ + public ISequenceEvent getParentEvent() { + return null; + } + + /** + * {@inheritDoc} + */ + public List getSubEvents() { + return new SubEventsHelper(this).getSubEvents(); + } + + /** + * {@inheritDoc} + */ + public Collection getEventsToMoveWith() { + return getSubEvents(); + } + + /** + * {@inheritDoc} + */ + public boolean canChildOccupy(ISequenceEvent child, Range range) { + return new SubEventsHelper(this).canChildOccupy(child, range); + } + + /** + * {@inheritDoc} + */ + public boolean canChildOccupy(ISequenceEvent child, Range range, List eventsToIgnore, Collection lifelines) { + return new SubEventsHelper(this).canChildOccupy(child, range, eventsToIgnore, lifelines); + } + + /** + * {@inheritDoc} + */ + public Range getOccupiedRange() { + return new ISequenceEventQuery(this).getOccupiedRange(); + } + + /** + * {@inheritDoc} + */ + public Range getValidSubEventsRange() { + Range result = getVerticalRange(); + if (result.width() > 2 * LayoutConstants.EXECUTION_CHILDREN_MARGIN) { + result = result.shrinked(LayoutConstants.EXECUTION_CHILDREN_MARGIN); + } + return result; + } + + /** + * Locate the destruction message which destroys the lifeline, if any. + * + * @return the destruction message which destroys the lifeline, if any. + */ + public Option getDestructionMessage() { + Option optEOL = getEndOfLife(); + if (optEOL.some()) { + return optEOL.get().getDestructionMessage(); + } else { + return Options.newNone(); + } + } + + /** + * Tests whether the lifeline is explicitly destroyed by a destruction + * message, or if it goes until the end of the sequence. + * + * @return true if the lifeline is explicitly destroyed by a + * destruction message. + */ + public boolean isExplicitlyDestroyed() { + Option optEOL = getEndOfLife(); + if (optEOL.some()) { + return optEOL.get().isExplicitelyDestroyed(); + } else { + return false; + } + } + + /** + * Tests whether the lifeline is explicitly created by a creation message, + * or if it starts from the beginning of the sequence. + * + * @return true if the lifeline is explicitly created by a + * creation message. + */ + public boolean isExplicitlyCreated() { + InstanceRole opt = getInstanceRole(); + if (opt != null) { + return opt.isExplicitlyCreated(); + } else { + return false; + } + } + + /** + * Locate the destruction message which creates the lifeline, if any. + * + * @return the destruction message which creates the lifeline, if any. + */ + public Option getCreationMessage() { + InstanceRole opt = getInstanceRole(); + if (opt != null) { + return opt.getCreationMessage(); + } + return Options.newNone(); + } + + /** + * {@inheritDoc} + */ + public Option getParentOperand() { + return getParentOperand(getVerticalRange()); + } + + /** + * Finds the deepest Operand container including the position if existing. + * + * @param verticalPosition + * the position where to look for the deepest operand + * @return the deepest Operand convering this lifeline at this range + * @see ISequenceEvent#getParentOperand() + */ + public Option getParentOperand(final int verticalPosition) { + return new ParentOperandFinder(this).getParentOperand(new Range(verticalPosition, verticalPosition)); + } + + /** + * Finds the deepest Operand container convering the range if existing. + * + * @param range + * the range where to look for the deepest operand + * @return the deepest Operand convering this lifeline at this range + * @see ISequenceEvent#getParentOperand() + */ + public Option getParentOperand(final Range range) { + return new ParentOperandFinder(this).getParentOperand(range); + } + + /** + * {@inheritDoc} + */ + public ISequenceEvent getHierarchicalParentEvent() { + return null; + } + + /** + * Investigate recursively sub events to find all covering interaction uses. + * + * @return all interaction uses covering this lifeline recursively + */ + public Collection getAllCoveringInteractionUses() { + Predicate interactionUseCoveringLifeline = new Predicate() { + + public boolean apply(InteractionUse input) { + return input.computeCoveredLifelines().contains(Lifeline.this); + } + }; + return Lists.newArrayList(Iterables.filter(getDiagram().getAllInteractionUses(), interactionUseCoveringLifeline)); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/LostMessageEnd.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/LostMessageEnd.java new file mode 100644 index 0000000000..c61fcdac68 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/LostMessageEnd.java @@ -0,0 +1,147 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.elements; + +import java.util.List; + +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gmf.runtime.notation.Bounds; +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.NotationPackage; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.EdgeTarget; +import org.eclipse.sirius.description.DescriptionPackage; +import org.eclipse.sirius.diagram.sequence.util.NotationPredicate; + +/** + * Represents the LostMEssageEnd marker which can appear at at the end of a + * message. This element can be present if a message do not have a starting end + * or a finishing end. + * + * @author mporhel + */ +public class LostMessageEnd extends AbstractSequenceNode { + /** + * The visual ID. Same as a standard node. + * + * see org.eclipse.sirius.diagram.internal.edit.parts.DNodeEditPart. + * VISUAL_ID + */ + public static final int VISUAL_ID = 2001; + + /** + * Predicate to check whether a Sirius DDiagramElement represents an + * Lost Message End. + */ + private static enum SiriusElementPredicate implements Predicate { + INSTANCE; + + public boolean apply(DDiagramElement input) { + boolean result = AbstractSequenceElement.isSequenceDiagramElement(input, DescriptionPackage.eINSTANCE.getNodeMapping()); + if (input instanceof EdgeTarget) { + EdgeTarget et = (EdgeTarget) input; + result = result && Iterables.any(Iterables.concat(et.getIncomingEdges(), et.getOutgoingEdges()), Message.viewpointElementPredicate()); + } + + List> potentialMessageTarget = Lists.newArrayList(); + potentialMessageTarget.add(EndOfLife.viewpointElementPredicate()); + potentialMessageTarget.add(AbstractNodeEvent.viewpointElementPredicate()); + potentialMessageTarget.add(Lifeline.viewpointElementPredicate()); + potentialMessageTarget.add(InstanceRole.viewpointElementPredicate()); + + result = result && !Predicates.or(potentialMessageTarget).apply(input); + + return result; + } + } + + /** + * Constructor. + * + * @param node + * the GMF Node which represents this Lost Message End. + */ + LostMessageEnd(Node node) { + super(node); + Preconditions.checkArgument(LostMessageEnd.notationPredicate().apply(node), "The node does not represent an lost message end."); + } + + /** + * Returns a predicate to check whether a GMF View represents an Lost Message End. + * + * @return a predicate to check whether a GMF View represents an Lost Message End. + */ + public static Predicate notationPredicate() { + return new NotationPredicate(NotationPackage.eINSTANCE.getNode(), VISUAL_ID, LostMessageEnd.viewpointElementPredicate()); + } + + /** + * Returns a predicate to check whether a Sirius DDiagramElement + * represents an EndOfLife. + * + * @return a predicate to check whether a Sirius DDiagramElement + * represents an EndOfLife. + */ + public static Predicate viewpointElementPredicate() { + return SiriusElementPredicate.INSTANCE; + } + + /** + * Returns the message which references this lost end. + * + * @return the message which references this lost end. + */ + public Option getMessage() { + Message msg = null; + Node node = getNotationNode(); + Iterable srcEdges = Iterables.filter(node.getSourceEdges(), Edge.class); + Iterable tgtEdges = Iterables.filter(node.getTargetEdges(), Edge.class); + for (Edge edge : Iterables.concat(srcEdges, tgtEdges)) { + Option message = ISequenceElementAccessor.getMessage(edge); + if (message.some()) { + msg = message.get(); + break; + } + } + + return Options.newSome(msg); + } + + /** + * {@inheritDoc} + */ + public Option getLifeline() { + return Options.newNone(); + } + + /** + * {@inheritDoc} + */ + public Rectangle getProperLogicalBounds() { + if (getNotationNode().getLayoutConstraint() instanceof Bounds) { + Bounds bounds = (Bounds) getNotationNode().getLayoutConstraint(); + return new Rectangle(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight()); + } else { + throw new RuntimeException(); + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/Message.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/Message.java new file mode 100644 index 0000000000..098206e1a7 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/Message.java @@ -0,0 +1,552 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.elements; + +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.NotationPackage; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Ordering; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.business.internal.ordering.EventEndHelper; +import org.eclipse.sirius.diagram.sequence.business.internal.query.ISequenceEventQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.query.SequenceMessageViewQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.util.RangeSetter; +import org.eclipse.sirius.diagram.sequence.description.DescriptionPackage; +import org.eclipse.sirius.diagram.sequence.ordering.CompoundEventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.EventEndsOrdering; +import org.eclipse.sirius.diagram.sequence.ordering.SingleEventEnd; +import org.eclipse.sirius.diagram.sequence.util.NotationPredicate; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * Common interface for all the elements of a sequence diagram. + * + * @author mporhel + */ +public class Message extends AbstractSequenceElement implements ISequenceEvent { + /** + * Predicate to filter States, Frames and Operand from possible new source + * or target of a message reconnection. + */ + public static final Predicate NO_RECONNECTABLE_EVENTS = new Predicate() { + public boolean apply(ISequenceEvent input) { + return input instanceof AbstractFrame || input instanceof Operand || input instanceof State; + } + }; + + /** + * Function to get the Sirius DDiagramElement message kind. + */ + public static final Function VIEWPOINT_MESSAGE_KIND = new Function() { + public Kind apply(DEdge from) { + Kind result = null; + if (AbstractSequenceElement.isSequenceDiagramElement(from, DescriptionPackage.eINSTANCE.getBasicMessageMapping())) { + result = Kind.BASIC; + } else if (AbstractSequenceElement.isSequenceDiagramElement(from, DescriptionPackage.eINSTANCE.getReturnMessageMapping())) { + result = Kind.REPLY; + } else if (AbstractSequenceElement.isSequenceDiagramElement(from, DescriptionPackage.eINSTANCE.getCreationMessageMapping())) { + result = Kind.CREATION; + } else if (AbstractSequenceElement.isSequenceDiagramElement(from, DescriptionPackage.eINSTANCE.getDestructionMessageMapping())) { + result = Kind.DESTRUCTION; + } + assert result != null : "Unsupported kind of message detected"; + return result; + } + }; + + /** + * The visual ID. + * + * see org.eclipse.sirius.diagram.internal.edit.parts.DEdgeEditPart. + * VISUAL_ID + */ + public static final int VISUAL_ID = 4001; + + /** + * The different (exclusive) kinds of sequence messages. + */ + public enum Kind { + /** + * Normal, basic message. + */ + BASIC, + /** + * Reply message, associated to the basic message to which it replies. + */ + REPLY, + /** + * Creation message. + */ + CREATION, + /** + * Destruction message. + */ + DESTRUCTION; + } + + /** + * Predicate to check whether a Sirius DDiagramElement represents a + * message. + */ + private static enum SiriusElementPredicate implements Predicate { + INSTANCE; + + public boolean apply(DDiagramElement input) { + return AbstractSequenceElement.isSequenceDiagramElement(input, DescriptionPackage.eINSTANCE.getMessageMapping()); + } + } + + /** + * . + * + * @param edge + * . + */ + public Message(Edge edge) { + super(edge); + Preconditions.checkArgument(Message.notationPredicate().apply(edge), "The edge does not represent a sequence message."); + } + + /** + * Returns a predicate to check whether a GMF View represents a message. + * + * @return a predicate to check whether a GMF View represents a message. + */ + public static Predicate notationPredicate() { + return new NotationPredicate(NotationPackage.eINSTANCE.getEdge(), VISUAL_ID, Message.viewpointElementPredicate()); + } + + /** + * Returns a predicate to check whether a Sirius DDiagramElement + * represents a message. + * + * @return a predicate to check whether a Sirius DDiagramElement + * represents a message. + */ + public static Predicate viewpointElementPredicate() { + return SiriusElementPredicate.INSTANCE; + } + + /** + * {@inheritDoc} + */ + public Edge getNotationEdge() { + return (Edge) view; + } + + /** + * Returns the precise kind of this message, if this element is valid. + * + * @return the precise kind of this message, if this element is valid. + */ + public Kind getKind() { + EObject element = view.getElement(); + if (element instanceof DEdge) { + return VIEWPOINT_MESSAGE_KIND.apply((DEdge) element); + } else { + // Assume basic message + return Kind.BASIC; + } + } + + /** + * {@inheritDoc} + */ + public ISequenceNode getSourceElement() { + return ISequenceElementAccessor.getISequenceNode(getNotationEdge().getSource()).get(); + } + + /** + * {@inheritDoc} + */ + public ISequenceNode getTargetElement() { + return ISequenceElementAccessor.getISequenceNode(getNotationEdge().getTarget()).get(); + } + + /** + * {@inheritDoc} + */ + public Range getVerticalRange() { + return new SequenceMessageViewQuery(getNotationEdge()).getVerticalRange(); + } + + /** + * {@inheritDoc} + */ + public boolean isLogicallyInstantaneous() { + return !isReflective(); + } + + /** + * {@inheritDoc} + */ + public void setVerticalRange(Range range) throws IllegalStateException { + RangeSetter.setVerticalRange(this, range); + } + + /** + * {@inheritDoc} + */ + public Option getLifeline() { + if (isReflective()) { + return getSourceLifeline(); + } + return Options.newNone(); + } + + /** + * Tests whether this a reflective message, i.e. both its source and target + * are in the context of the same lifeline. + * + * @return true if this message is reflective. + */ + public boolean isReflective() { + Option sourceLifeline = getSourceLifeline(); + Option targetLifeline = getTargetLifeline(); + return sourceLifeline.some() && targetLifeline.some() && sourceLifeline.get() == targetLifeline.get(); + } + + /** + * Returns the lifeline in the context of which this message is sent. + * + * @return the lifeline in the context of which this message is sent. + */ + public Option getSourceLifeline() { + ISequenceNode sourceElement = getSourceElement(); + if (sourceElement != null) { + return sourceElement.getLifeline(); + } + return Options.newNone(); + } + + /** + * Returns the lifeline in the context of which this message is received. + * + * @return the lifeline in the context of which this message is received. + */ + public Option getTargetLifeline() { + ISequenceNode targetElement = getTargetElement(); + if (targetElement != null) { + return targetElement.getLifeline(); + } + return Options.newNone(); + } + + /** + * Returns the lifeline on "the other side" of the message, with respect to + * the specified lifeline. For reflective messages, this is the same as the + * local lifeline. The specified local lifeline must be either the + * source of target lifeline of this message. Otherwise the result is + * unspecified. + * + * @param local + * the lifeline to consider as "local", either the source or + * target lifeline of the message. + * @return the lifeline on "the other side" of the message, i.e. the the + * target lifeline is local it the source lifeline, and + * the source lifeline otherwise. + */ + public Option getRemoteLifeline(Lifeline local) { + Option sourceLifeline = getSourceLifeline(); + if (local == sourceLifeline.get()) { + return getTargetLifeline(); + } else { + return sourceLifeline; + } + } + + public boolean isCompoundMessage() { + return !Iterables.isEmpty(Iterables.filter(getDiagram().findEnds(this), CompoundEventEnd.class)); + } + + /** + * {@inheritDoc} + */ + public Rectangle getProperLogicalBounds() { + Range range = getVerticalRange(); + + ISequenceNode srcElement = getSourceElement(); + ISequenceNode tgtElement = getTargetElement(); + + Rectangle srcLogicalBounds = srcElement.getProperLogicalBounds().getCopy(); + Rectangle tgtLogicalBounds = tgtElement.getProperLogicalBounds().getCopy(); + + int srcCenterX = srcLogicalBounds.getCenter().x; + int tgtCenterX = tgtLogicalBounds.getCenter().x; + + int srcX = 0; + int tgtX = 0; + if (isReflective()) { + srcX = srcLogicalBounds.getRight().x; + tgtX = tgtLogicalBounds.getRight().x; + } else if (srcCenterX <= tgtCenterX) { + srcX = srcLogicalBounds.getRight().x; + tgtX = tgtLogicalBounds.getLeft().x; + } else { + srcX = srcLogicalBounds.getLeft().x; + tgtX = tgtLogicalBounds.getRight().x; + } + + if (srcElement instanceof Lifeline) { + srcX = srcLogicalBounds.getCenter().x; + } + + if (tgtElement instanceof Lifeline) { + tgtX = tgtLogicalBounds.getCenter().x; + } + + return new Rectangle(srcX, range.getLowerBound(), tgtX - srcX, range.width()); + } + + /** + * Messages have no sub-events. + *

+ * {@inheritDoc} + */ + public List getSubEvents() { + return Collections.emptyList(); + } + + /** + * {@inheritDoc} + */ + public Collection getEventsToMoveWith() { + return getSubEvents(); + } + + /** + * {@inheritDoc} + */ + public ISequenceEvent getParentEvent() { + ISequenceNode sourceElement = getSourceElement(); + if (sourceElement instanceof ISequenceEvent) { + return (ISequenceEvent) sourceElement; + } + return null; + } + + /** + * {@inheritDoc} + */ + public ISequenceEvent getHierarchicalParentEvent() { + return null; + } + + /** + * {@inheritDoc} + */ + public Range getOccupiedRange() { + return new ISequenceEventQuery(this).getOccupiedRange(); + } + + /** + * Messages have no sub-events. + *

+ * {@inheritDoc} + */ + public Range getValidSubEventsRange() { + return Range.emptyRange(); + } + + /** + * Messages have no sub-events. + *

+ * {@inheritDoc} + */ + public boolean canChildOccupy(ISequenceEvent child, Range range) { + return false; + } + + /** + * Messages have no sub-events. + *

+ * {@inheritDoc} + */ + public boolean canChildOccupy(ISequenceEvent child, Range range, List eventsToIgnore, Collection lifelines) { + return false; + } + + /** + * {@inheritDoc} + */ + public Option getParentOperand() { + Option sourceLifeline = getSourceLifeline(); + Option sourceParentOperand = Options.newNone(); + if (sourceLifeline.some()) { + sourceParentOperand = sourceLifeline.get().getParentOperand(getVerticalRange()); + } + + Option targetLifeline = getTargetLifeline(); + Option targetParentOperand = Options.newNone(); + if (targetLifeline.some()) { + targetParentOperand = targetLifeline.get().getParentOperand(getVerticalRange()); + } + + boolean noOperand = !sourceParentOperand.some() && !targetParentOperand.some(); + boolean lostEnd = sourceLifeline.some() && !targetLifeline.some() || !sourceLifeline.some() && targetLifeline.some(); + boolean sameOperand = lostEnd || noOperand || sourceParentOperand.get().equals(targetParentOperand.get()); + Preconditions.checkArgument(noOperand || sameOperand, "The source and target parent operand must be the same one or not existing."); + + Option parentOperand = sourceParentOperand; + if (!parentOperand.some()) { + parentOperand = targetParentOperand; + } + + return parentOperand; + } + + /** + * Check if the current message is reflexive and surrounds other events on + * the same lifeline. + * + * @return true if the current message is reflexive and surrounds other + * events on the same lifeline. + */ + public boolean surroundsEventOnSameLifeline() { + return !getSurroundedSameLifelineEvents().isEmpty(); + } + + /** + * Get the surrounded reflexives message depth. + * + * @return the surrounded reflexives message depth. + */ + public int getReflexiveMessageWidth() { + Collection events = getSurroundedSameLifelineEvents(); + final Range range = getVerticalRange(); + Predicate toConsider = new Predicate() { + public boolean apply(ISequenceEvent input) { + boolean toConsider = range.includes(input.getVerticalRange()); + if (input instanceof Message) { + toConsider = toConsider && ((Message) input).isReflective(); + } + return toConsider; + } + }; + + List impactingEvents = Lists.newArrayList(Iterables.filter(events, toConsider)); + Collections.sort(impactingEvents, Ordering.natural().onResultOf(Functions.compose(Range.lowerBoundFunction(), ISequenceEvent.VERTICAL_RANGE))); + int subMessagesMaxRight = 0; + for (Message msg : Iterables.filter(impactingEvents, Message.class)) { + int reflexiveMessageWidth = msg.getReflexiveMessageWidth(); + int origin = msg.getSourceElement().getProperLogicalBounds().right(); + origin = Math.max(origin, msg.getTargetElement().getProperLogicalBounds().right()); + subMessagesMaxRight = Math.max(subMessagesMaxRight, origin + reflexiveMessageWidth); + } + + int maxRight = 0; + for (AbstractNodeEvent node : Iterables.filter(impactingEvents, AbstractNodeEvent.class)) { + maxRight = Math.max(maxRight, node.getProperLogicalBounds().right()); + } + + int origin = getSourceElement().getProperLogicalBounds().right(); + origin = Math.max(origin, getTargetElement().getProperLogicalBounds().right()); + + int width = LayoutConstants.MESSAGE_TO_SELF_BENDPOINT_HORIZONTAL_GAP; + width = Math.max(width, maxRight - origin + LayoutConstants.MESSAGE_TO_SELF_HORIZONTAL_GAP); + width = Math.max(width, subMessagesMaxRight - origin + LayoutConstants.MESSAGE_TO_SELF_HORIZONTAL_GAP); + + return width; + } + + /** + * Get the surrounded events on the same lifeline. + * + * @return the surrounded events on the same lifeline. + */ + public Collection getSurroundedSameLifelineEvents() { + Set englobedEvents = new LinkedHashSet(); + if (isReflective()) { + Lifeline lifeline = getLifeline().get(); + SequenceDiagram diagram = getDiagram(); + EventEndsOrdering semanticOrdering = diagram.getSequenceDDiagram().getSemanticOrdering(); + List msgEnds = EventEndHelper.findEndsFromSemanticOrdering(this); + if (msgEnds.size() == 2) { + int start = semanticOrdering.getEventEnds().indexOf(msgEnds.get(0)); + int end = semanticOrdering.getEventEnds().indexOf(msgEnds.get(1)); + if (Math.abs(start - end) > 1) { + Collection sees = Sets.newHashSet(); + Collection interEvents = Sets.newHashSet(); + + for (int i = start + 1; i < end; i++) { + EventEnd eventEnd = semanticOrdering.getEventEnds().get(i); + if (eventEnd instanceof SingleEventEnd) { + sees.add((SingleEventEnd) eventEnd); + } else if (eventEnd instanceof CompoundEventEnd) { + sees.addAll(((CompoundEventEnd) eventEnd).getEventEnds()); + } + } + + for (SingleEventEnd see : sees) { + ISequenceEvent perturbing = EventEndHelper.findISequenceEvent(see, diagram); + if (perturbing != null) { + interEvents.add(perturbing); + } + } + + englobedEvents.addAll(getValidInterEvents(interEvents, lifeline)); + } + } + } + return englobedEvents; + } + + private Collection getValidInterEvents(Collection interEvents, Lifeline lifeline) { + Set englobedEvents = new LinkedHashSet(); + for (ISequenceEvent pertub : interEvents) { + if (pertub instanceof Message) { + Message msg = (Message) pertub; + Option sourceLifeline = msg.getSourceLifeline(); + Option targetLifeline = msg.getTargetLifeline(); + if (targetLifeline.some() && targetLifeline.get().equals(lifeline)) { + englobedEvents.add(pertub); + } else if (sourceLifeline.some() && sourceLifeline.get().equals(lifeline)) { + englobedEvents.add(pertub); + } + } else if (pertub instanceof CombinedFragment) { + CombinedFragment cf = (CombinedFragment) pertub; + Collection coverage = cf.computeCoveredLifelines(); + if (coverage.contains(lifeline)) { + englobedEvents.add(pertub); + } + } else { + Option pLif = pertub.getLifeline(); + if (pLif.some() && pLif.get().equals(lifeline)) { + englobedEvents.add(pertub); + } + } + } + return englobedEvents; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/ObservationPoint.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/ObservationPoint.java new file mode 100644 index 0000000000..6e6daa8282 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/ObservationPoint.java @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (c) 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.elements; + +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.notation.Bounds; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.NotationPackage; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.diagram.sequence.description.DescriptionPackage; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.util.NotationPredicate; + +/** + * Represents the ObservationPoint marker which can appear to represent a + * EventEnd location. + * + * @author mporhel + */ +public class ObservationPoint extends AbstractSequenceNode { + /** + * The visual ID. Same as a standard node. + * + * see org.eclipse.sirius.diagram.internal.edit.parts.DNodeEditPart. + * VISUAL_ID + */ + public static final int VISUAL_ID = 2001; + + /** + * Predicate to check whether a Sirius DDiagramElement represents an + * ObservationPoint. + */ + private static enum SiriusElementPredicate implements Predicate { + INSTANCE; + + public boolean apply(DDiagramElement input) { + return AbstractSequenceElement.isSequenceDiagramElement(input, DescriptionPackage.eINSTANCE.getObservationPointMapping()); + } + } + + /** + * Constructor. + * + * @param node + * the GMF Node which represents this ObservationPoint. + */ + ObservationPoint(Node node) { + super(node); + Preconditions.checkArgument(ObservationPoint.notationPredicate().apply(node), "The node does not represent an observation point."); + } + + /** + * Returns a predicate to check whether a GMF View represents an + * ObservationPoint. + * + * @return a predicate to check whether a GMF View represents an + * ObservationPoint. + */ + public static Predicate notationPredicate() { + return new NotationPredicate(NotationPackage.eINSTANCE.getNode(), VISUAL_ID, ObservationPoint.viewpointElementPredicate()); + } + + /** + * Returns a predicate to check whether a Sirius DDiagramElement + * represents an ObservationPoint. + * + * @return a predicate to check whether a Sirius DDiagramElement + * represents an ObservationPoint. + */ + public static Predicate viewpointElementPredicate() { + return SiriusElementPredicate.INSTANCE; + } + + /** + * {@inheritDoc} + */ + public Option getLifeline() { + return Options.newNone(); + } + + /** + * {@inheritDoc} + */ + public Rectangle getProperLogicalBounds() { + if (getNotationNode().getLayoutConstraint() instanceof Bounds) { + Bounds bounds = (Bounds) getNotationNode().getLayoutConstraint(); + return new Rectangle(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight()); + } else { + throw new RuntimeException(); + } + } + + /** + * Look in its diagram'semantic ordering for the observed eventEnd: and + * event end whose semantic end is the observation point semantic target + * element. + * + * @return the observed EventEnd if valid. + */ + public Option getObservedEventEnd() { + Option semanticTargetElement = getSemanticTargetElement(); + if (semanticTargetElement.some()) { + for (EventEnd eventEnd : getDiagram().getSequenceDDiagram().getSemanticOrdering().getEventEnds()) { + if (eventEnd.getSemanticEnd() == semanticTargetElement.get()) { + return Options.newSome(eventEnd); + } + } + } + return Options.newNone(); + } + + /** + * Get the observed logical location (ie: it corresponds to the center of + * the logical bounds). + * + * @return the observed logical location. + */ + public Point getObservedLogicalLocation() { + return getProperLogicalBounds().getCenter().getCopy(); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/Operand.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/Operand.java new file mode 100644 index 0000000000..ac085cd530 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/Operand.java @@ -0,0 +1,330 @@ +/******************************************************************************* + * Copyright (c) 2010, 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.elements; + +import java.util.Collection; +import java.util.List; + +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.notation.Bounds; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.NotationPackage; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.business.internal.ordering.EventEndHelper; +import org.eclipse.sirius.diagram.sequence.business.internal.query.ISequenceEventQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.query.SequenceNodeQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.util.RangeSetter; +import org.eclipse.sirius.diagram.sequence.business.internal.util.SubEventsHelper; +import org.eclipse.sirius.diagram.sequence.description.DescriptionPackage; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.util.NotationPredicate; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * Represents an operand inside a combined fragment. + * + * @author pcdavid + */ +public class Operand extends AbstractSequenceNode implements ISequenceEvent { + /** + * The visual ID. + * + * @see DNodeContainer2EditPart.VISUAL_ID. + */ + public static final int VISUAL_ID = 3008; + + /** + * The visual ID of the compartment contained by the operand. + * + * see org.eclipse.sirius.diagram.internal.edit.parts. + * DNodeContainerViewNodeContainerCompartment2EditPart.VISUAL_ID + */ + public static final int COMPARTMENT_VISUAL_ID = 7002; + + /** + * Predicate to check whether a Sirius DDiagramElement represents an + * execution. + */ + private static enum SiriusElementPredicate implements Predicate { + INSTANCE; + + public boolean apply(DDiagramElement input) { + return AbstractSequenceElement.isSequenceDiagramElement(input, DescriptionPackage.eINSTANCE.getOperandMapping()); + } + } + + /** + * Constructor. + * + * @param node + * the GMF Node representing the operand. + */ + Operand(Node node) { + super(node); + Preconditions.checkArgument(Operand.notationPredicate().apply(node), "The node does not represent an operand."); + } + + /** + * Returns a predicate to check whether a GMF View represents an execution. + * + * @return a predicate to check whether a GMF View represents an execution. + */ + public static Predicate notationPredicate() { + return new NotationPredicate(NotationPackage.eINSTANCE.getNode(), VISUAL_ID, Operand.viewpointElementPredicate()); + } + + /** + * Returns a predicate to check whether a GMF View represents an combined + * fragment compartment. + * + * @return a predicate to check whether a GMF View represents an combined + * fragment compartment. + */ + public static Predicate compartmentNotationPredicate() { + return new NotationPredicate(NotationPackage.eINSTANCE.getNode(), COMPARTMENT_VISUAL_ID, Operand.viewpointElementPredicate()); + } + + /** + * Returns a predicate to check whether a Sirius DDiagramElement + * represents an execution. + * + * @return a predicate to check whether a Sirius DDiagramElement + * represents an execution. + */ + public static Predicate viewpointElementPredicate() { + return SiriusElementPredicate.INSTANCE; + } + + /** + * Operands are not associated to a particular lifeline. + *

+ * {@inheritDoc} + */ + public Option getLifeline() { + return Options.newNone(); + } + + /** + * {@inheritDoc} + */ + public Rectangle getProperLogicalBounds() { + Rectangle cfBounds = getCombinedFragment().getProperLogicalBounds(); + if (getNotationNode().getLayoutConstraint() instanceof Bounds) { + Bounds bounds = (Bounds) getNotationNode().getLayoutConstraint(); + return new Rectangle(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight()).getTranslated(cfBounds.getLocation()); + } else { + throw new RuntimeException(); + } + } + + /** + * {@inheritDoc} + */ + public ISequenceEvent getParentEvent() { + return getCombinedFragment(); + } + + /** + * {@inheritDoc} + */ + public ISequenceEvent getHierarchicalParentEvent() { + return getCombinedFragment(); + } + + /** + * {@inheritDoc} + */ + public List getSubEvents() { + return new SubEventsHelper(this).getSubEvents(); + } + + /** + * {@inheritDoc} + */ + public Collection getEventsToMoveWith() { + return getSubEvents(); + } + + /** + * {@inheritDoc} + */ + public Range getVerticalRange() { + // Rectangle logicalBounds = getProperLogicalBounds(); + // return new Range(logicalBounds.y, logicalBounds.bottom()); + return new SequenceNodeQuery(getNotationNode()).getVerticalRange(); + } + + /** + * {@inheritDoc} + */ + public boolean isLogicallyInstantaneous() { + return false; + } + + /** + * {@inheritDoc} + */ + public void setVerticalRange(Range range) throws IllegalStateException { + RangeSetter.setVerticalRange(this, range); + } + + /** + * Finds the parent Combined fragment. + * + * @return the parent Combined fragment. + */ + public CombinedFragment getCombinedFragment() { + EObject viewContainer = this.view.eContainer(); + if (viewContainer instanceof View) { + View parentView = (View) viewContainer; + Option parentElement = ISequenceElementAccessor.getCombinedFragment(parentView); + // The parent should be the compartment of the Combined Fragment + if (parentElement.some()) { + return parentElement.get(); + } else { + // The grand parent should be the Combined Fragment we are + // looking for + View grandParentView = (View) viewContainer.eContainer(); + Option grandparentElement = ISequenceElementAccessor.getCombinedFragment(grandParentView); + if (grandparentElement.some()) { + return grandparentElement.get(); + } + } + } + throw new RuntimeException("Invalid context for operand " + this); + } + + /** + * Finds the index of this operand among the parent combined fragment. + * + * @return the index of this operand among the parent combined fragment. + */ + public int getIndex() { + CombinedFragment parentCombinedFragment = getCombinedFragment(); + return parentCombinedFragment.getIndexOfOperand(this); + } + + /** + * Check if this operand is the last in the parent {@link CombinedFragment}. + * + * @return if this operand is the last in the parent + * {@link CombinedFragment}. + */ + public boolean isLastOperand() { + return getIndex() == getCombinedFragment().getOperands().size() - 1; + } + + /** + * Check if this operand is the first in the parent {@link CombinedFragment} + * . + * + * @return if this operand is the first in the parent + * {@link CombinedFragment}. + */ + public boolean isFirstOperand() { + return getIndex() == 0; + } + + /** + * Finds the following operand. + * + * @return the following operand if existing, Options.newNone() otherwise + */ + public Option getFollowingOperand() { + CombinedFragment combinedFragment = getCombinedFragment(); + return combinedFragment.getOperand(combinedFragment.getIndexOfOperand(this) + 1); + } + + /** + * Finds the previous operand. + * + * @return the previous operand if existing, Options.newNone() otherwise + */ + public Option getPreviousOperand() { + CombinedFragment combinedFragment = getCombinedFragment(); + return combinedFragment.getOperand(combinedFragment.getIndexOfOperand(this) - 1); + } + + /** + * {@inheritDoc} + */ + public boolean canChildOccupy(ISequenceEvent child, Range range) { + return new SubEventsHelper(this).canChildOccupy(child, range); + } + + /** + * {@inheritDoc} + */ + public boolean canChildOccupy(ISequenceEvent child, Range range, List eventsToIgnore, Collection lifelines) { + return new SubEventsHelper(this).canChildOccupy(child, range, eventsToIgnore, lifelines); + } + + /** + * {@inheritDoc} + */ + public Range getOccupiedRange() { + return new ISequenceEventQuery(this).getOccupiedRange(); + } + + /** + * {@inheritDoc} + */ + public Range getValidSubEventsRange() { + return getVerticalRange().shrinked(LayoutConstants.EXECUTION_CHILDREN_MARGIN); + } + + /** + * Get the covered lifelines. + * + * @return the covered lifelines. + */ + public Collection computeCoveredLifelines() { + return getCombinedFragment().computeCoveredLifelines(); + } + + /** + * Returns the last event end (in semantic order) which is contained inside + * this operand, excluding the operand's finishing end. If the operand is + * empty, this is the starting end of the operand itself. + * + * @return the last event end which is contained inside this operand. + */ + public EventEnd getLastContainedEventEnd() { + List subEvents = getSubEvents(); + if (subEvents.isEmpty()) { + List ends = EventEndHelper.findEndsFromSemanticOrdering(this); + assert !ends.isEmpty(); + return ends.get(0); + } else { + ISequenceEvent lastEvent = subEvents.get(subEvents.size() - 1); + List ends = EventEndHelper.findEndsFromSemanticOrdering(lastEvent); + assert ends.size() == 2; + return ends.get(1); + } + } + + /** + * {@inheritDoc} + */ + public Option getParentOperand() { + return getCombinedFragment().getParentOperand(); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/SequenceDiagram.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/SequenceDiagram.java new file mode 100644 index 0000000000..9497c4a717 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/SequenceDiagram.java @@ -0,0 +1,463 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.elements; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.notation.Diagram; +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Ordering; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.common.tools.api.util.AllContents; +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.ordering.EventEndHelper; +import org.eclipse.sirius.diagram.sequence.description.DescriptionPackage; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * Represents a sequence diagram. This is the root of all sequence elements. + * + * @author mporhel, pcdavid + */ +public class SequenceDiagram extends AbstractSequenceElement { + + private static final String INTERNAL_ERROR = "Internal error."; + + /** + * Predicate to check whether a GMF View represents a sequence diagram. + */ + private static enum NotationPredicate implements Predicate { + INSTANCE; + + public boolean apply(View input) { + if (input instanceof Diagram) { + EObject element = input.getElement(); + return element instanceof DDiagram && SequenceDiagram.viewpointElementPredicate().apply((DDiagram) element); + } else { + return false; + } + } + + } + + /** + * Predicate to check whether a Sirius DDiagram represents a sequence + * diagram. + */ + private static enum SiriusElementPredicate implements Predicate { + INSTANCE; + + public boolean apply(DDiagram input) { + if (input == null) { + return false; + } else { + EClass sdDescClass = DescriptionPackage.eINSTANCE.getSequenceDiagramDescription(); + return input instanceof SequenceDDiagram && sdDescClass.isInstance(input.getDescription()); + } + } + } + + /** + * Constructor. + * + * @param diagram + * the GMF Diagram representing this sequence diagram. + */ + SequenceDiagram(Diagram diagram) { + super(diagram); + Preconditions.checkArgument(SequenceDiagram.notationPredicate().apply(diagram), "The diagram does not represent a sequence diagram."); + } + + /** + * Returns a predicate to check whether a GMF View represents a sequence + * diagram. + * + * @return a predicate to check whether a GMF View represents a sequence + * diagram. + */ + public static Predicate notationPredicate() { + return NotationPredicate.INSTANCE; + } + + /** + * Returns a predicate to check whether a Sirius DDiagram represents a + * sequence diagram. + * + * @return a predicate to check whether a Sirius DDiagram represents a + * sequence diagram. + */ + public static Predicate viewpointElementPredicate() { + return SiriusElementPredicate.INSTANCE; + } + + public Diagram getNotationDiagram() { + return (Diagram) view; + } + + public SequenceDDiagram getSequenceDDiagram() { + return (SequenceDDiagram) view.getElement(); + } + + /** + * Finds all the lifelines in this diagram which are at least partially + * covered by the specified rectangular area. + * + * @param area + * the rectangular area to check for lifelines (in logical + * coordinates). + * @return all the lifelines in this diagram which are at least partially + * covered by the area. + */ + public Set getGraphicallyCoveredLifelines(final Rectangle area) { + List result = Lists.newArrayList(); + Iterables.addAll(result, Iterables.filter(getAllLifelines(), new Predicate() { + public boolean apply(Lifeline input) { + return input.getProperLogicalBounds().intersects(area) && input.getVerticalRange().includes(area.getTop().y); + } + })); + Collections.sort(result, Range.lowerBoundOrdering().onResultOf(ISequenceEvent.VERTICAL_RANGE)); + return Sets.newLinkedHashSet(result); + } + + /** + * . + * + * @return . + */ + public List getSortedInstanceRole() { + Function xLocation = new Function() { + public Integer apply(InstanceRole from) { + Rectangle bounds = from.getBounds(); + return bounds.x; + } + }; + + List allInstanceRoles = Lists.newArrayList(getAllInstanceRoles()); + Collections.sort(allInstanceRoles, Ordering.natural().onResultOf(xLocation)); + return allInstanceRoles; + } + + /** + * . + * + * @return . + */ + public Collection getAllInstanceRoles() { + Collection result = Lists.newArrayList(); + for (View child : Iterables.filter(getNotationView().getChildren(), View.class)) { + if (InstanceRole.notationPredicate().apply(child)) { + Option instanceRole = ISequenceElementAccessor.getInstanceRole(child); + if (instanceRole.some()) { + result.add(instanceRole.get()); + } + } + } + return result; + } + + /** + * . + * + * @return . + */ + public List getAllLifelines() { + Collection allInstanceRoles = getAllInstanceRoles(); + Function lifelineFunction = new Function() { + public Lifeline apply(ISequenceNode from) { + return from.getLifeline().get(); + } + }; + List result = Lists.newArrayList(Iterables.transform(allInstanceRoles, lifelineFunction)); + Collections.sort(result, Range.lowerBoundOrdering().onResultOf(ISequenceEvent.VERTICAL_RANGE)); + return result; + } + + /** + * Returns all the {@link Node}s in the specified diagram which represent an + * ObservationPoint. + * + * @return the Nodes inside this diagram which represent sequence + * ObservationPoint. An empty iterator is returned if the diagram is + * not a sequence diagram. + */ + public Collection getAllObservationPoints() { + Collection result = Lists.newArrayList(); + for (View child : Iterables.filter(getNotationView().getChildren(), View.class)) { + if (ObservationPoint.notationPredicate().apply(child)) { + Option obsPoint = ISequenceElementAccessor.getObservationPoint(child); + if (obsPoint.some()) { + result.add(obsPoint.get()); + } + } + } + return result; + } + + /** + * Returns all the {@link Node}s in the specified diagram which represent a + * lost sequence message end. + * + * @return the Nodes inside this diagram which represent lost sequence + * messages end. An empty iterator is returned if the diagram is not + * a sequence diagram. + */ + public Collection getAllLostMessageEnds() { + Collection result = Lists.newArrayList(); + for (View child : Iterables.filter(getNotationView().getChildren(), View.class)) { + if (LostMessageEnd.notationPredicate().apply(child)) { + Option lostMessageEnd = ISequenceElementAccessor.getLostMessageEnd(child); + if (lostMessageEnd.some()) { + result.add(lostMessageEnd.get()); + } + } + } + return result; + } + + /** + * Returns all the {@link Edge}s in the specified diagram which represent a + * sequence message of any kind. + * + * @return the Edges inside this diagram which represent sequence messages. + * An empty iterator is returned if the diagram is not a sequence + * diagram. + */ + public Set getAllMessages() { + List result = Lists.newArrayList(); + for (Edge edge : Iterables.filter(Iterables.filter(getNotationDiagram().getEdges(), Edge.class), Message.notationPredicate())) { + Option message = ISequenceElementAccessor.getMessage(edge); + assert message.some() : INTERNAL_ERROR; + result.add(message.get()); + } + Collections.sort(result, Range.lowerBoundOrdering().onResultOf(ISequenceEvent.VERTICAL_RANGE)); + return Sets.newLinkedHashSet(result); + } + + /** + * Returns all AbstractNodeEvent in the given diagram. + * + * @return all AbstractNodeEvent on the given diagram. + */ + public Set getAllAbstractNodeEvents() { + List result = Lists.newArrayList(); + for (Node node : Iterables.filter(Iterables.filter(AllContents.of(getNotationDiagram()), Node.class), AbstractNodeEvent.notationPredicate())) { + Option exec = ISequenceElementAccessor.getAbstractNodeEvent(node); + assert exec.some() : INTERNAL_ERROR; + result.add(exec.get()); + } + Collections.sort(result, Range.lowerBoundOrdering().onResultOf(ISequenceEvent.VERTICAL_RANGE)); + return Sets.newLinkedHashSet(result); + } + + /** + * Returns all executions in the given diagram. + * + * @return all executions on the given diagram. + */ + public Set getAllExecutions() { + List result = Lists.newArrayList(); + for (Node node : Iterables.filter(Iterables.filter(AllContents.of(getNotationDiagram()), Node.class), Execution.notationPredicate())) { + Option exec = ISequenceElementAccessor.getExecution(node); + assert exec.some() : INTERNAL_ERROR; + result.add(exec.get()); + } + Collections.sort(result, Range.lowerBoundOrdering().onResultOf(ISequenceEvent.VERTICAL_RANGE)); + return Sets.newLinkedHashSet(result); + } + + /** + * Returns all executions in the given diagram. + * + * @return all executions on the given diagram. + */ + public Set getAllStates() { + List result = Lists.newArrayList(); + for (Node node : Iterables.filter(Iterables.filter(AllContents.of(getNotationDiagram()), Node.class), State.notationPredicate())) { + Option exec = ISequenceElementAccessor.getState(node); + assert exec.some() : INTERNAL_ERROR; + result.add(exec.get()); + } + Collections.sort(result, Range.lowerBoundOrdering().onResultOf(ISequenceEvent.VERTICAL_RANGE)); + return Sets.newLinkedHashSet(result); + } + + /** + * Returns all frames in the given diagram. + * + * @return all frames on the given diagram. + */ + public Collection getAllFrames() { + List result = Lists.newArrayList(); + for (Node node : Iterables.filter(Iterables.filter(getNotationDiagram().getChildren(), Node.class), AbstractFrame.notationPredicate())) { + Option exec = ISequenceElementAccessor.getISequenceEvent(node); + assert exec.some() : INTERNAL_ERROR; + if (exec.get() instanceof AbstractFrame) { + result.add((AbstractFrame) exec.get()); + } + } + Collections.sort(result, Range.lowerBoundOrdering().onResultOf(ISequenceEvent.VERTICAL_RANGE)); + return Sets.newLinkedHashSet(result); + } + + /** + * Returns all interaction uses in the given diagram. + * + * @return all interaction uses on the given diagram. + */ + public Set getAllInteractionUses() { + List result = Lists.newArrayList(); + for (Node node : Iterables.filter(Iterables.filter(getNotationDiagram().getChildren(), Node.class), InteractionUse.notationPredicate())) { + Option exec = ISequenceElementAccessor.getInteractionUse(node); + assert exec.some() : INTERNAL_ERROR; + result.add(exec.get()); + } + Collections.sort(result, Range.lowerBoundOrdering().onResultOf(ISequenceEvent.VERTICAL_RANGE)); + return Sets.newLinkedHashSet(result); + } + + /** + * Returns all combined fragments in the given diagram. + * + * @return all combined fragments on the given diagram. + */ + public Set getAllCombinedFragments() { + List result = Lists.newArrayList(); + for (Node node : Iterables.filter(Iterables.filter(getNotationDiagram().getChildren(), Node.class), CombinedFragment.notationPredicate())) { + Option exec = ISequenceElementAccessor.getCombinedFragment(node); + assert exec.some() : INTERNAL_ERROR; + result.add(exec.get()); + } + Collections.sort(result, Range.lowerBoundOrdering().onResultOf(ISequenceEvent.VERTICAL_RANGE)); + return Sets.newLinkedHashSet(result); + } + + /** + * Returns all operands in the given diagram. + * + * @return all operands on the given diagram. + */ + public Set getAllOperands() { + List result = Lists.newArrayList(); + for (Node node : Iterables.filter(Iterables.filter(AllContents.of(getNotationDiagram()), Node.class), Operand.notationPredicate())) { + Option exec = ISequenceElementAccessor.getOperand(node); + assert exec.some() : INTERNAL_ERROR; + result.add(exec.get()); + } + Collections.sort(result, Range.lowerBoundOrdering().onResultOf(ISequenceEvent.VERTICAL_RANGE)); + return Sets.newLinkedHashSet(result); + } + + /** + * Returns all endOfLifes in the given diagram. + * + * @return all endOfLifes on the given diagram. + */ + public Set getAllEndOfLifes() { + Set allEndOfLifes = new HashSet(); + for (Lifeline lifeline : getAllLifelines()) { + if (lifeline.getEndOfLife().some()) { + allEndOfLifes.add(lifeline.getEndOfLife().get()); + } + } + return allEndOfLifes; + } + + /** + * Returns all sequence events in the given diagram. The result is ordered + * regarding the lower bound ordering. + * + * @return all sequence events on the given diagram. + */ + public Set getAllOrderedDelimitedSequenceEvents() { + List result = Lists.newArrayList(); + Iterables.addAll(result, getAllDelimitedSequenceEvents()); + + Collections.sort(result, Range.lowerBoundOrdering().onResultOf(ISequenceEvent.VERTICAL_RANGE)); + return Sets.newLinkedHashSet(result); + } + + /** + * Returns all sequence events in the given diagram. The result is not + * ordered and will be computed on iteration. + * + * @return all sequence events on the given diagram. + */ + public Iterable getAllDelimitedSequenceEvents() { + Function getISE = new Function() { + public ISequenceEvent apply(View from) { + Option ise = ISequenceElementAccessor.getISequenceEvent(from); + assert ise.some() : INTERNAL_ERROR; + return ise.get(); + } + }; + return Iterables.transform(Iterables.filter(Iterables.filter(AllContents.of(getNotationDiagram()), View.class), ISequenceEvent.ISEQUENCEEVENT_NOTATION_PREDICATE), getISE); + } + + /** + * Finds and returns the EventEnds corresponding to the given part. + * + * @param event + * current event + * @return the EventEnds corresponding to the given part + */ + public List findEnds(ISequenceEvent event) { + List ends = Lists.newArrayList(); + EObject seqDiag = getNotationDiagram().getElement(); + Option semanticEvent = event.getSemanticTargetElement(); + if (seqDiag instanceof SequenceDDiagram && semanticEvent.some()) { + for (EventEnd ee : ((SequenceDDiagram) seqDiag).getGraphicalOrdering().getEventEnds()) { + if (EventEndHelper.getSemanticEvents(ee).contains(semanticEvent.get())) { + ends.add(ee); + } + } + } + return ends; + } + + /** + * Diagram are not associated to a particular lifeline. + *

+ * {@inheritDoc} + */ + public Option getLifeline() { + return Options.newNone(); + } + + /** + * The diagram itself has no significant bounds. + * + * @return the bounds of the diagram. + */ + public Rectangle getProperLogicalBounds() { + return new Rectangle(0, 0, 0, 0); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/State.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/State.java new file mode 100644 index 0000000000..2c01d0ba40 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/State.java @@ -0,0 +1,215 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.elements; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.NotationPackage; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.diagram.sequence.business.internal.ordering.EventEndHelper; +import org.eclipse.sirius.diagram.sequence.business.internal.query.SequenceNodeQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.util.ParentOperandFinder; +import org.eclipse.sirius.diagram.sequence.business.internal.util.RangeSetter; +import org.eclipse.sirius.diagram.sequence.description.DescriptionPackage; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.util.NotationPredicate; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * Represents a state on a lifeline or an execution. + * + * @author smonnier + */ +public class State extends AbstractNodeEvent { + + /** + * The visual ID. Same as a normal bordered node. + * + * @see DNode2EditPart.VISUAL_ID + */ + public static final int VISUAL_ID = 3001; + + /** + * Predicate to check whether a Sirius DDiagramElement represents a + * state. + */ + private static enum SiriusElementPredicate implements Predicate { + INSTANCE; + + public boolean apply(DDiagramElement input) { + return AbstractSequenceElement.isSequenceDiagramElement(input, DescriptionPackage.eINSTANCE.getStateMapping()) + && !InstanceRole.viewpointElementPredicate().apply((DDiagramElement) input.eContainer()); + } + } + + /** + * Constructor. + * + * @param node + * the GMF Node representing this state. + */ + State(Node node) { + super(node); + Preconditions.checkArgument(State.notationPredicate().apply(node), "The node does not represent an state."); + } + + /** + * Returns a predicate to check whether a GMF View represents a state. + * + * @return a predicate to check whether a GMF View represents a state. + */ + public static Predicate notationPredicate() { + return new NotationPredicate(NotationPackage.eINSTANCE.getNode(), VISUAL_ID, State.viewpointElementPredicate()); + } + + /** + * Returns a predicate to check whether a Sirius DDiagramElement + * represents a state. + * + * @return a predicate to check whether a Sirius DDiagramElement + * represents a state. + */ + public static Predicate viewpointElementPredicate() { + return SiriusElementPredicate.INSTANCE; + } + + /** + * {@inheritDoc} + */ + public boolean canChildOccupy(ISequenceEvent child, Range range) { + return false; + } + + /** + * {@inheritDoc} + */ + public boolean canChildOccupy(ISequenceEvent child, Range range, List eventsToIgnore, Collection lifelines) { + return false; + } + + /** + * {@inheritDoc} + */ + public Collection getEventsToMoveWith() { + return Collections.emptyList(); + } + + /** + * {@inheritDoc} + */ + @Override + public ISequenceEvent getHierarchicalParentEvent() { + EObject viewContainer = this.view.eContainer(); + if (viewContainer instanceof View) { + View parentView = (View) viewContainer; + Option parentElement = ISequenceElementAccessor.getISequenceEvent(parentView); + if (parentElement.some()) { + return parentElement.get(); + } + } + throw new RuntimeException("Invalid context for state " + this); + } + + /** + * {@inheritDoc} + */ + public Range getOccupiedRange() { + return Range.emptyRange(); + } + + /** + * {@inheritDoc} + */ + @Override + public ISequenceEvent getParentEvent() { + ISequenceEvent parent = getHierarchicalParentEvent(); + + List potentialSiblings = parent.getSubEvents(); + if (!potentialSiblings.contains(this)) { + // look for parentOperand + parent = getParentOperand().get(); + } + return parent; + } + + /** + * {@inheritDoc} + */ + @Override + public Option getParentOperand() { + return new ParentOperandFinder(this).getParentOperand(); + } + + /** + * {@inheritDoc} + */ + public List getSubEvents() { + return Collections.emptyList(); + } + + /** + * {@inheritDoc} + */ + public Range getValidSubEventsRange() { + return Range.emptyRange(); + } + + /** + * {@inheritDoc} + */ + @Override + public Range getVerticalRange() { + return new SequenceNodeQuery(getNotationNode()).getVerticalRange(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isLogicallyInstantaneous() { + List ends = EventEndHelper.findEndsFromSemanticOrdering(this); + return ends.size() == 1 && EventEndHelper.PUNCTUAL_COMPOUND_EVENT_END.apply(ends.iterator().next()); + } + + /** + * {@inheritDoc} + */ + @Override + public void setVerticalRange(Range range) throws IllegalStateException { + RangeSetter.setVerticalRange(this, range); + } + + /** + * {@inheritDoc} + */ + @Override + public Option getLifeline() { + return getParentLifeline(); + } + + /** + * {@inheritDoc} + */ + @Override + public List getLinkedMessages() { + return Collections.emptyList(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/package-info.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/package-info.java new file mode 100644 index 0000000000..310f17a23d --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/elements/package-info.java @@ -0,0 +1,12 @@ +/******************************************************************************* + * Copyright (c) 2013 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.elements; + diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/AbstractSequenceLayout.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/AbstractSequenceLayout.java new file mode 100644 index 0000000000..6ef85337cd --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/AbstractSequenceLayout.java @@ -0,0 +1,214 @@ +/******************************************************************************* + * Copyright (c) 2011, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.layout; + +import java.util.Map; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.EndOfLife; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElement; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.LostMessageEnd; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.query.ISequenceElementQuery; + +/** + * Computes the appropriate graphical locations of sequence events and lifelines + * on a sequence diagram to reflect the semantic order. + * + * @param + * the layouted element type. + * + * @param + * the layout data type. + * + * @author mporhel + */ +public abstract class AbstractSequenceLayout { + + /** + * The diagram to layout. + */ + protected final SequenceDiagram sequenceDiagram; + + /** + * A map to register old layout data. + */ + protected final Map oldLayoutData; + + /** + * Constructor. + * + * @param sequenceDiagram + * the sequence diagram to layout. + */ + public AbstractSequenceLayout(SequenceDiagram sequenceDiagram) { + this.sequenceDiagram = sequenceDiagram; + + this.oldLayoutData = Maps.newHashMap(); + } + + /** + * Compute and apply a specific layout. Should be use in a + * {@link org.eclipse.emf.transaction.RecordingCommand}. + * + * @param pack + * pack the space between instance roles. + * + * @return true if a layout was applied + */ + public final boolean layout(boolean pack) { + // initialisation + init(pack); + + // vertical range computation + Map finalRanges = computeLayout(pack); + + // Apply computed ranges + boolean applied = applyComputedLayout(finalRanges, pack); + + dispose(); + + return applied; + + } + + /** + * Init the needed context for layout computation. + * + * @param pack + * pack the diagram + */ + protected abstract void init(boolean pack); + + /** + * Get old layout data before layout application. + * + * @param ise + * the requested sequence element. + * @return the old layout data. + */ + protected abstract T getOldLayoutData(S ise); + + /** + * Computes the absolute vertical (Y) location for all the messages in the + * sequence diagram. + * + * @param pack + * pack the diagram + * @return a map associating each message edit part to the new absolute + * vertical location it should have. + */ + protected abstract Map computeLayout(boolean pack); + + /** + * Apply the computed layout. + * + * @param finalRanges + * a map associating each message edit part to the new absolute + * vertical location it should have. + * @param pack + * pack the diagram + * + * @return true if a layout was applied + */ + protected abstract boolean applyComputedLayout(Map finalRanges, boolean pack); + + /** + * Dispose the layout context after layout application. + */ + protected void dispose() { + oldLayoutData.clear(); + } + + /** + * Return the non-explicitely created lifelines. + * + * @return the non-explicitely created lifelines. + */ + protected Iterable getLifeLinesWithoutCreation() { + Predicate isMainLifeline = new Predicate() { + public boolean apply(Lifeline input) { + boolean main = true; + InstanceRole instanceRole = input.getInstanceRole(); + if (instanceRole != null) { + main = !instanceRole.isExplicitlyCreated(); + } + return main; + } + }; + return Iterables.filter(sequenceDiagram.getAllLifelines(), isMainLifeline); + } + + /** + * Return the non-explicitely destructed lifelines. + * + * @return the non-explicitely destructed lifelines. + */ + protected Iterable getLifeLinesWithoutDestruction() { + Predicate isLifelineWithoutDestruction = new Predicate() { + public boolean apply(Lifeline input) { + boolean result = true; + // filter lifeline with endOfLife + Option endOfLife = input.getEndOfLife(); + if (endOfLife.some()) { + result = !endOfLife.get().isExplicitelyDestroyed(); + } + return result; + } + }; + return Iterables.filter(sequenceDiagram.getAllLifelines(), isLifelineWithoutDestruction); + } + + /** + * Check if the current lost end has been created from a tool application. + * Tool creation flags will be erased after the first layout. + * + * @param lostEnd + * the current end. + * @return true if the end was created by a tool. + */ + public static boolean createdFromTool(LostMessageEnd lostEnd) { + boolean toolCreated = false; + ISequenceElementQuery query = new ISequenceElementQuery(lostEnd); + if (query.hasAbsoluteBoundsFlag() && query.getFlaggedAbsoluteBounds().x == LayoutConstants.TOOL_CREATION_FLAG_FROM_SEMANTIC.x) { + toolCreated = true; + } + return toolCreated; + } + + /** + * Check if the current lost end has been created from a tool application. + * Tool creation flags will be erased after the first layout. + * + * @param lostEnd + * the current end. + * @return true if the end was created by a tool. + */ + public static boolean createdFromExternalChange(LostMessageEnd lostEnd) { + boolean externalCreation = false; + Option message = lostEnd.getMessage(); + ISequenceElementQuery query = new ISequenceElementQuery(lostEnd); + if (query.hasAbsoluteBoundsFlag() && query.getFlaggedAbsoluteBounds().x == LayoutConstants.EXTERNAL_CHANGE_FLAG.x) { + externalCreation = true; + } else if (message.some()) { + query = new ISequenceElementQuery(message.get()); + externalCreation = query.hasAbsoluteBoundsFlag() && query.getFlaggedAbsoluteBounds().x == LayoutConstants.EXTERNAL_CHANGE_FLAG.x; + } + return externalCreation; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/AbstractSequenceOrderingLayout.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/AbstractSequenceOrderingLayout.java new file mode 100644 index 0000000000..85561687d0 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/AbstractSequenceOrderingLayout.java @@ -0,0 +1,168 @@ +/******************************************************************************* + * Copyright (c) 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.layout; + +import java.util.List; +import java.util.Map; + +import org.eclipse.draw2d.geometry.Rectangle; + +import com.google.common.base.Function; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; + +/** + * Computes the appropriate graphical locations of sequence events and lifelines + * on a sequence diagram to reflect the semantic order. + * + * @param + * the layouted element type. + * + * @param + * the layout data type. + * + * @param + * the ordering type. + * + * @author mporhel + */ +public abstract class AbstractSequenceOrderingLayout extends AbstractSequenceLayout { + + /** + * The semantic ordering. + */ + protected final List semanticOrdering; + + /** + * The graphical ordering. + */ + protected final List graphicalOrdering; + + /** + * Semantic flagged ordering elements. + */ + protected final List flaggedEnds; + + /** + * Old flagged absolute bounds. + */ + protected final Map oldFlaggedLayoutData; + + /** + * Constructor. + * + * @param sequenceDiagram + * the sequence diagram to layout. + */ + public AbstractSequenceOrderingLayout(SequenceDiagram sequenceDiagram) { + super(sequenceDiagram); + + this.semanticOrdering = Lists.newArrayList(); + this.graphicalOrdering = Lists.newArrayList(); + this.flaggedEnds = Lists.newArrayList(); + this.oldFlaggedLayoutData = Maps.newHashMap(); + } + + /** + * Dispose the layout context after layout application. + */ + protected void dispose() { + semanticOrdering.clear(); + graphicalOrdering.clear(); + flaggedEnds.clear(); + oldFlaggedLayoutData.clear(); + super.dispose(); + } + + /** + * + * Look in the semantic, graphical and flaggedEnds orderings to retrieve the + * safest predecessor and try to keep a stable delta regarding it. + * + * @param currentPos + * the current position (x or y) + * @param element + * the current ordering element + * @param alreadyComputedLocations + * the already computed locations + * @return the delta stable position. + */ + protected int getDeltaStablePosition(final int currentPos, final U element, Map alreadyComputedLocations) { + int deltaStablePos = currentPos; + int semanticIndex = semanticOrdering.indexOf(element); + int graphicalIndex = graphicalOrdering.indexOf(element); + int flaggedIndex = flaggedEnds.indexOf(element); + + if (flaggedIndex != -1 && semanticIndex != 0 && graphicalIndex != -1) { + List semanticPredecessors = Lists.newArrayList(semanticOrdering.subList(0, semanticIndex)); + List graphicalPredecessors = Lists.newArrayList(graphicalOrdering.subList(0, graphicalIndex)); + List flaggedPredecessors = Lists.newArrayList(flaggedEnds.subList(0, flaggedIndex)); + + // Intersection + semanticPredecessors.retainAll(flaggedEnds); + graphicalPredecessors.retainAll(flaggedEnds); + flaggedPredecessors.retainAll(semanticPredecessors); + + // Which is the safer position ? + Function oldPosition = getOldPosition(); + U flaggedPred = null; + + if (Iterables.elementsEqual(semanticPredecessors, graphicalPredecessors) && !graphicalPredecessors.isEmpty()) { + flaggedPred = graphicalPredecessors.get(graphicalPredecessors.size() - 1); + } else { + // Desynchronisation -> flagged position + oldPosition = getOldFlaggedPosition(); + + // Look for the last semantic predecessor with same index in + // semantic and flagged lists. + U potentialSafePred = null; + for (int i = 0; i < flaggedPredecessors.size(); i++) { + U semPot = semanticPredecessors.get(i); + U flaggedPot = flaggedPredecessors.get(i); + if (semPot != null && semPot.equals(flaggedPot)) { + potentialSafePred = semPot; + } + } + + if (potentialSafePred != null) { + flaggedPred = potentialSafePred; + } + } + + if (flaggedPred != null) { + Integer predY = oldPosition.apply(flaggedPred); + Integer flaggedY = oldPosition.apply(element); + int delta = flaggedY - predY; + if (delta >= 0) { + deltaStablePos = alreadyComputedLocations.get(flaggedPred) + delta; + } + } + } + return deltaStablePos; + } + + /** + * A function to retrieve the old positions. + * + * @return the function. + */ + protected abstract Function getOldPosition(); + + /** + * A function to retrieve the old flagged positions. + * + * @return the function. + */ + protected abstract Function getOldFlaggedPosition(); +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/EventEndToPositionFunction.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/EventEndToPositionFunction.java new file mode 100644 index 0000000000..0f77634cb8 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/EventEndToPositionFunction.java @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.layout; + +import java.util.Collection; +import java.util.Iterator; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractFrame; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.ordering.EventEndHelper; +import org.eclipse.sirius.diagram.sequence.business.internal.query.ISequenceElementQuery; +import org.eclipse.sirius.diagram.sequence.ordering.CompoundEventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.SingleEventEnd; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * Function to compute position of and EventEnd. + * + * @author pcmporhel + */ +public class EventEndToPositionFunction implements Function { + + private final Function> eventEndToSequenceEvents; + + private final Function> ranges; + + /** + * Constructor. + * + * @param eventEndToSequenceEvents + * function to get sequence event linked to the given event end. + * @param ranges + * ranges of sequence events. + */ + public EventEndToPositionFunction(Function> eventEndToSequenceEvents, Function> ranges) { + this.eventEndToSequenceEvents = eventEndToSequenceEvents; + this.ranges = ranges; + } + + /** + * {@inheritDoc} + */ + public Integer apply(EventEnd from) { + return getOldPosition(from, eventEndToSequenceEvents.apply(from)); + } + + private Integer getOldPosition(EventEnd end, Collection ises) { + SingleEventEnd see = null; + ISequenceEvent ise = null; + if (end instanceof SingleEventEnd && !ises.isEmpty()) { + see = (SingleEventEnd) end; + ise = ises.iterator().next(); + } else if (end instanceof CompoundEventEnd && !ises.isEmpty()) { + CompoundEventEnd cee = (CompoundEventEnd) end; + if (EventEndHelper.PUNCTUAL_COMPOUND_EVENT_END.apply(cee)) { + ise = getSafeEvent(ises); + } else { + ise = getSafeEvent(ises); + see = ise == null ? null : EventEndHelper.getSingleEventEnd(end, ((DDiagramElement) ise.getNotationView().getElement()).getTarget()); + } + } + return getOldPositionFromRange(see, ise); + } + + private ISequenceEvent getSafeEvent(Collection ises) { + ISequenceEvent ise = null; + Predicate safe = Predicates.or(Predicates.instanceOf(AbstractNodeEvent.class), Predicates.instanceOf(AbstractFrame.class)); + Collection safeEvents = Lists.newArrayList(Iterables.filter(ises, safe)); + + if (!safeEvents.isEmpty()) { + ise = safeEvents.iterator().next(); + } else if (Iterables.size(Iterables.filter(ises, Operand.class)) == 2) { + ise = getSafeOperandEnd(ises); + } else { + ise = ises.iterator().next(); + } + return ise; + } + + private ISequenceEvent getSafeOperandEnd(Collection ises) { + ISequenceEvent ise = null; + + Iterator iterator = ises.iterator(); + ISequenceEvent pot1 = iterator.next(); + ISequenceEvent pot2 = iterator.next(); + + if (new ISequenceElementQuery(pot1).hasAbsoluteBoundsFlag()) { + ise = pot1; + } else if (new ISequenceElementQuery(pot2).hasAbsoluteBoundsFlag()) { + ise = pot2; + } + return ise; + } + + /** + * Get the old position of the corresponding event end, regarding the given + * event old range. event. + * + * @param see + * event end + * @param ise + * corresponding event + * @return old position + */ + protected Integer getOldPositionFromRange(SingleEventEnd see, ISequenceEvent ise) { + Integer oldPosition = 0; + Option oldRange = ranges.apply(ise); + + if (ise != null && oldRange.some()) { + if (see != null) { + oldPosition = see.isStart() ? oldRange.get().getLowerBound() : oldRange.get().getUpperBound(); + } else if (see == null && ise.isLogicallyInstantaneous()) { + oldPosition = oldRange.get().middleValue(); + } + } + return oldPosition; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/LayoutConstants.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/LayoutConstants.java new file mode 100644 index 0000000000..347faa8070 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/LayoutConstants.java @@ -0,0 +1,193 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.layout; + +import org.eclipse.draw2d.geometry.Rectangle; + +/** + * A utility class to centralize all the dimensions ("magic number") used during + * the layout of sequence diagrams. Unless otherwise stated, all the dimensions + * and locations are in pixels. + * + * @author pcdavid, smonnier, mporhel, edugueperoux + */ +public final class LayoutConstants { + + /** + * This should be LayoutUtils.SCALE, but we do not want to depend here on + * the plug-in where LayoutUtils is defined. + */ + public static final int UNIT = 10; + + /** + * The minimal amount of vertical space between the bottom of an instance + * role and the first element (message or execution) on its root execution + * for packing layout. + */ + public static final int TIME_START_OFFSET = 3 * UNIT; + + /** + * The minimal amount of vertical space between the bottom of an instance + * role and the first element (message or execution) on its root execution + * for non-packing layout. + */ + public static final int TIME_START_MIN_OFFSET = 1 * UNIT; + + /** + * The minimal amount of vertical space between the last element (message or + * execution) on a root execution and the bottom of the root execution. + */ + public static final int TIME_STOP_OFFSET = 5 * UNIT; + + /** + * The x position of the first left lifeline. + */ + public static final int LIFELINES_START_X = 5 * UNIT; + + /** + * The lifeline top position. + */ + public static final int LIFELINES_START_Y = 5 * UNIT; + + /** + * The minimum horizontal gap to keep between two neighboring lifelines. + */ + public static final int LIFELINES_MIN_X_GAP = 1 * UNIT; + + /** + * The minimum horizontal gap to keep between two neighboring lifelines. + */ + public static final int LIFELINES_MIN_PACKED_X_GAP = 3 * UNIT; + + /** + * The minimum vertical range upper bound for lifelines. + */ + public static final int LIFELINES_MIN_Y = 20 * UNIT; + + /** + * The minimum vertical space to leave between two successive messages. + */ + public static final int MIN_INTER_SEQUENCE_EVENTS_VERTICAL_GAP = 2 * UNIT; + + /** + * The minimum margin between the start/finish of an execution and its + * first/last child. + */ + public static final int EXECUTION_CHILDREN_MARGIN = 5; + + /** + * The default width used for newly created interaction uses. + */ + public static final int DEFAULT_INTERACTION_USE_WIDTH = 6 * UNIT; + + /** + * The default width used for newly created combined fragments. + */ + public static final int DEFAULT_COMBINED_FRAGMENT_WIDTH = 10 * UNIT; + + /** + * The default width used for newly created operands. + */ + public static final int DEFAULT_OPERAND_WIDTH = 10 * UNIT; + + /** + * The default height used for newly created interaction uses. + */ + public static final int DEFAULT_INTERACTION_USE_HEIGHT = 5 * UNIT; + + /** + * How much space between the top of a combined fragment and the top of its + * first operand. + */ + public static final int COMBINED_FRAGMENT_TITLE_HEIGHT = 3 * UNIT; + + /** + * The default height used for newly created operand. + */ + public static final int DEFAULT_OPERAND_HEIGHT = 6 * UNIT; + + /** + * The default height used for newly created combined fragments. + */ + public static final int DEFAULT_COMBINED_FRAGMENT_HEIGHT = COMBINED_FRAGMENT_TITLE_HEIGHT + DEFAULT_OPERAND_HEIGHT; + + /** + * The default height used for newly created executions. + */ + public static final int DEFAULT_EXECUTION_HEIGHT = 3 * UNIT; + + /** + * The default width used for newly created executions. + */ + public static final int DEFAULT_EXECUTION_WIDTH = 2 * UNIT; + + /** + * How much space there is between bendpoints of a message to self. + */ + public static final int MESSAGE_TO_SELF_BENDPOINT_VERTICAL_GAP = UNIT; + + /** + * How much space there is between bendpoints of a message to self. + */ + public static final int MESSAGE_TO_SELF_BENDPOINT_HORIZONTAL_GAP = 3 * UNIT; + + /** + * How much space there is between bendpoints of a two included messages to + * self. + */ + public static final int MESSAGE_TO_SELF_HORIZONTAL_GAP = 2 * UNIT; + + /** + * The default width used for lost messages. + */ + public static final int LOST_MESSAGE_DEFAULT_WIDTH = 6 * UNIT; + + /** Defines the visual appearance of lifelines. */ + public static final int[] LIFELINE_DASH_STYLE = new int[] { 10, 10 }; + + /** Defines the visual appearance of operands. */ + public static final int[] OPERAND_DASH_STYLE = new int[] { 5, 5 }; + + /** + * Default height of execution after a layout, also the default height of + * syncCall/asyncCall execution after creation. + */ + public static final int INTERACTION_EXECUTION_MIN_HEIGHT_AFTER_LAYOUT = 50; + + /** + * Default height of execution after a layout. + */ + public static final int INTERACTION_STATE_MIN_HEIGHT_AFTER_LAYOUT = 30; + + /** + * Margin between a parent frame and its children. + */ + public static final int BORDER_FRAME_MARGIN = UNIT; + + /** + * Tool creation flag for directly created DDiagramElement. + */ + public static final Rectangle TOOL_CREATION_FLAG = new Rectangle(-1, -1, 0, 0); + + /** + * Tool creation flag for created DDiagramElement from semantic. + */ + public static final Rectangle TOOL_CREATION_FLAG_FROM_SEMANTIC = new Rectangle(-1, -2, 0, 0); + + /** + * External Reparent / Reconnect detection flag. + */ + public static final Rectangle EXTERNAL_CHANGE_FLAG = new Rectangle(-2, 0, 0, 0); + + private LayoutConstants() { + // Prevents instantiation. + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/SequenceLayout.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/SequenceLayout.java new file mode 100644 index 0000000000..eda7496903 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/SequenceLayout.java @@ -0,0 +1,138 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.layout; + +import org.eclipse.gmf.runtime.notation.Diagram; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.Size; + +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.CollapseFilter; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.diagram.business.api.query.NodeQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceNode; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.flag.SequenceDiagramAbsoluteBoundsFlagger; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.horizontal.SequenceHorizontalLayout; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.observation.SequenceObservationLayout; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.vertical.SequenceVerticalLayout; + +/** + * Computes the appropriate graphical locations of sequence events and lifelines + * on a sequence diagram to reflect the semantic order. + * + * @author pcdavid, mporhel + */ +public class SequenceLayout { + + private final Option sequenceDiagram; + + private SequenceHorizontalLayout sequenceHorizontalLayout; + + private SequenceVerticalLayout sequenceVerticalLayout; + + private SequenceObservationLayout sequenceObservationLayout; + + /** + * Constructor. + * + * @param diagram + * the sequence diagram for which to compute the messages + * locations. + */ + public SequenceLayout(Diagram diagram) { + this.sequenceDiagram = ISequenceElementAccessor.getSequenceDiagram(diagram); + this.sequenceHorizontalLayout = new SequenceHorizontalLayout(sequenceDiagram.get()); + this.sequenceVerticalLayout = new SequenceVerticalLayout(sequenceDiagram.get()); + this.sequenceObservationLayout = new SequenceObservationLayout(sequenceDiagram.get()); + } + + public Option getSequenceDiagram() { + return sequenceDiagram; + } + + /** + * Compute and apply horizontal layout. Should be use in a + * {@link org.eclipse.emf.transaction.RecordingCommand}. + * + * @param pack + * pack the space between instance roles. + * @return true if horizontal layout has been done + */ + public boolean horizontalLayout(boolean pack) { + if (this.sequenceHorizontalLayout != null && this.sequenceDiagram.some()) { + return this.sequenceHorizontalLayout.layout(pack); + } + return false; + } + + /** + * Compute and apply vertical layout. Should be use in a + * {@link org.eclipse.emf.transaction.RecordingCommand}. + * + * @param pack + * pack the space between sequence events + * @return true if vertical layout has been done + */ + public boolean verticalLayout(boolean pack) { + if (this.sequenceVerticalLayout != null && this.sequenceDiagram.some()) { + return this.sequenceVerticalLayout.layout(pack); + } + return false; + } + + /** + * Compute and apply observation layout. Should be use in a + * {@link org.eclipse.emf.transaction.RecordingCommand} and after vertical + * and horizontal layout. + * + * It will place the ObservationPoint. + * + * @param pack + * pack the space between sequence events + * @return true if horizontal layout has been done + */ + public boolean observationLayout(boolean pack) { + if (this.sequenceObservationLayout != null && this.sequenceDiagram.some()) { + return this.sequenceObservationLayout.layout(pack); + } + return false; + } + + /** + * Flag DDiagramElement with their absolute bounds. + */ + public void flagSequenceEvents() { + // Flag event with their new position + if (this.sequenceDiagram.some()) { + SequenceDiagramAbsoluteBoundsFlagger flagHelper = new SequenceDiagramAbsoluteBoundsFlagger(sequenceDiagram.get()); + flagHelper.flag(); + + updateCollapseFilters(); + } + } + + private void updateCollapseFilters() { + for (ISequenceNode isn : Iterables.concat(sequenceDiagram.get().getAllAbstractNodeEvents(), sequenceDiagram.get().getAllObservationPoints(), sequenceDiagram.get().getAllLifelines())) { + Node node = isn.getNotationNode(); + if (new NodeQuery(node).isCollapsed() && node.getElement() instanceof DDiagramElement && node.getLayoutConstraint() instanceof Size) { + Size size = (Size) node.getLayoutConstraint(); + DDiagramElement dde = (DDiagramElement) node.getElement(); + for (CollapseFilter collapseFilter : Iterables.filter(dde.getGraphicalFilters(), CollapseFilter.class)) { + collapseFilter.setHeight(size.getHeight()); + } + } + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/flag/AbstractSequenceAbsoluteBoundsFlagger.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/flag/AbstractSequenceAbsoluteBoundsFlagger.java new file mode 100644 index 0000000000..41dfc0c4fb --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/flag/AbstractSequenceAbsoluteBoundsFlagger.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.layout.flag; + +import java.util.Collection; + +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.ecore.EObject; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.AbsoluteBoundsFilter; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.SiriusFactory; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElement; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.LostMessageEnd; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; + +/** + * Helper to compute and attach absolute bounds flag for sequence events. + * + * @author mporhel + */ +public abstract class AbstractSequenceAbsoluteBoundsFlagger { + + /** + * Compute absolute bounds flags for each delimited sequence events of the + * current diagram. + */ + public final void flag() { + for (ISequenceElement ise : getEventsToFlag()) { + flag(ise); + } + } + + private void flag(ISequenceElement ise) { + // add flag + EObject element = ise.getNotationView().getElement(); + if (element instanceof DDiagramElement) { + DDiagramElement dde = (DDiagramElement) element; + Rectangle absBounds = ise.getProperLogicalBounds(); + + AbsoluteBoundsFilter flag = getOrCreateFlag(dde); + + if (ise instanceof LostMessageEnd && !((LostMessageEnd) ise).getMessage().some()) { + flag.setX(LayoutConstants.TOOL_CREATION_FLAG_FROM_SEMANTIC.x); + } + + // Update flag + if (flag != null && absBounds != null) { + if (!(ise instanceof Message)) { + flag.setX(absBounds.x); + flag.setWidth(absBounds.width); + } + flag.setY(absBounds.y); + flag.setHeight(absBounds.height); + } + } + } + + /** + * Gets events to flag. + * + * @return a collection of events to flag. + */ + protected abstract Collection getEventsToFlag(); + + private AbsoluteBoundsFilter getOrCreateFlag(DDiagramElement dde) { + AbsoluteBoundsFilter flag = null; + Collection flags = Lists.newArrayList(Iterables.filter(dde.getGraphicalFilters(), AbsoluteBoundsFilter.class)); + for (AbsoluteBoundsFilter prevFlag : flags) { + flag = prevFlag; + break; + } + + if (flag == null) { + flag = SiriusFactory.eINSTANCE.createAbsoluteBoundsFilter(); + dde.getGraphicalFilters().add(flag); + } + return flag; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/flag/SequenceDiagramAbsoluteBoundsFlagger.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/flag/SequenceDiagramAbsoluteBoundsFlagger.java new file mode 100644 index 0000000000..30ca92ada5 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/flag/SequenceDiagramAbsoluteBoundsFlagger.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.layout.flag; + +import java.util.Collection; +import java.util.List; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElement; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; + +/** + * Helper to compute and attach absolute bounds flag for sequence events. + * + * @author mporhel + */ +public class SequenceDiagramAbsoluteBoundsFlagger extends AbstractSequenceAbsoluteBoundsFlagger { + + private SequenceDiagram diagram; + + /** + * Constructor. + * + * @param sequenceDiagram + * the sequence diagram to flag. + */ + public SequenceDiagramAbsoluteBoundsFlagger(SequenceDiagram sequenceDiagram) { + this.diagram = sequenceDiagram; + } + + /** + * {@inheritDoc} + */ + @Override + protected Collection getEventsToFlag() { + List eventsToFlag = Lists.newArrayList(); + if (diagram != null) { + Iterables.addAll(eventsToFlag, diagram.getAllDelimitedSequenceEvents()); + Iterables.addAll(eventsToFlag, diagram.getAllLostMessageEnds()); + Iterables.addAll(eventsToFlag, diagram.getAllInstanceRoles()); + } + return eventsToFlag; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/flag/SequenceEventAbsoluteBoundsFlagger.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/flag/SequenceEventAbsoluteBoundsFlagger.java new file mode 100644 index 0000000000..201dfdab79 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/flag/SequenceEventAbsoluteBoundsFlagger.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.layout.flag; + +import java.util.Collection; +import java.util.List; + +import com.google.common.collect.Lists; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElement; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; + +/** + * Helper to compute and attach absolute bounds flag for sequence events. + * + * @author mporhel + */ +public class SequenceEventAbsoluteBoundsFlagger extends AbstractSequenceAbsoluteBoundsFlagger { + + private ISequenceEvent ise; + + /** + * Constructor. + * + * @param ise + * the sequence event to flag. + */ + public SequenceEventAbsoluteBoundsFlagger(ISequenceEvent ise) { + this.ise = ise; + } + + /** + * {@inheritDoc} + */ + @Override + protected Collection getEventsToFlag() { + List eventsToFlag = Lists.newArrayList(); + if (ise != null) { + eventsToFlag.add(ise); + } + return eventsToFlag; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/horizontal/LostMessageEndHorizontalLayoutHelper.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/horizontal/LostMessageEndHorizontalLayoutHelper.java new file mode 100644 index 0000000000..de4e392692 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/horizontal/LostMessageEndHorizontalLayoutHelper.java @@ -0,0 +1,451 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.layout.horizontal; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.ecore.EObject; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.EdgeTarget; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceNode; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.LostMessageEnd; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message.Kind; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.AbstractSequenceLayout; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * Computes the appropriate graphical locations of sequence events and lifelines + * on a sequence diagram to reflect the semantic order. + * + * @author pcdavid, mporhel + */ +public class LostMessageEndHorizontalLayoutHelper { + + private final Map lostMessages = Maps.newHashMap(); + + private final Multimap lostSources = HashMultimap.create(); + + private final Multimap lostTargets = HashMultimap.create(); + + private Map operands = Maps.newHashMap(); + + private Multimap operands2lostEnds = HashMultimap.create(); + + private Set diagramLostEnds = Sets.newHashSet(); + + private SequenceDiagram sequenceDiagram; + + private Set unconnectedLostEnds = Sets.newHashSet(); + + /** + * Constructor. + * + * @param diagram + * the sequence diagram for which to compute the horizontal + * locations. + */ + public LostMessageEndHorizontalLayoutHelper(SequenceDiagram diagram) { + this.sequenceDiagram = diagram; + } + + /** + * Populate lost message ends and helper context. + */ + public void populateLostMessageEnds() { + for (Message msg : sequenceDiagram.getAllMessages()) { + populateLostEnds(msg); + } + + registerUnconnectedLostEnds(); + } + + private void registerUnconnectedLostEnds() { + Predicate unConnectedEnds = Predicates.not(Predicates.in(lostMessages.keySet())); + for (LostMessageEnd lme : Iterables.filter(sequenceDiagram.getAllLostMessageEnds(), unConnectedEnds)) { + unconnectedLostEnds.add(lme); + + // look viewpoint edges + if (lme.getNotationNode().getElement() instanceof EdgeTarget) { + EdgeTarget targetNode = getKnownTargetNode(lme); + if (targetNode != null) { + ISequenceEvent ise = getISequenceEvent(targetNode); + if (ise != null && ise.getLifeline().some()) { + lostSources.put(ise.getLifeline().get(), lme); + Option parentOperand = ise.getParentOperand(); + if (parentOperand.some()) { + operands.put(lme, parentOperand.get()); + operands2lostEnds.put(parentOperand.get(), lme); + } else { + diagramLostEnds.add(lme); + } + } + } + EdgeTarget sourceNode = getKnownSourceNode(lme); + if (sourceNode != null) { + ISequenceEvent ise = getISequenceEvent(sourceNode); + if (ise != null && ise.getLifeline().some()) { + lostTargets.put(ise.getLifeline().get(), lme); + Option parentOperand = ise.getParentOperand(); + if (parentOperand.some()) { + operands.put(lme, parentOperand.get()); + operands2lostEnds.put(parentOperand.get(), lme); + } else { + diagramLostEnds.add(lme); + } + } + } + } + } + } + + private void populateLostEnds(Message msg) { + ISequenceNode sourceElement = msg.getSourceElement(); + ISequenceNode targetElement = msg.getTargetElement(); + + Option parentOperand = msg.getParentOperand(); + if (sourceElement != null && targetElement != null) { + Option sourceLifeline = sourceElement.getLifeline(); + Option targetLifeline = targetElement.getLifeline(); + + // Only messages with one lost end are handled. + if (sourceElement instanceof LostMessageEnd && targetLifeline.some()) { + LostMessageEnd sourceLME = (LostMessageEnd) sourceElement; + lostSources.put(targetLifeline.get(), sourceLME); + lostMessages.put(sourceLME, msg); + if (parentOperand.some()) { + operands.put(sourceLME, parentOperand.get()); + operands2lostEnds.put(parentOperand.get(), sourceLME); + } else { + diagramLostEnds.add(sourceLME); + } + } else if (targetElement instanceof LostMessageEnd && sourceLifeline.some()) { + LostMessageEnd targetLME = (LostMessageEnd) targetElement; + lostTargets.put(sourceLifeline.get(), targetLME); + lostMessages.put(targetLME, msg); + if (parentOperand.some()) { + operands.put(targetLME, parentOperand.get()); + operands2lostEnds.put(parentOperand.get(), targetLME); + } else { + diagramLostEnds.add(targetLME); + } + } + } + } + + /** + * Computes the delta between lostEnds and their attached lifeline. + * + * @param pack + * pack the space between instance roles. + * @return computed deltas. + */ + public Map computeLostMessageEndDeltaWithLifeline(boolean pack) { + Map deltas = Maps.newHashMap(); + + for (Lifeline lifeline : sequenceDiagram.getAllLifelines()) { + int lifelineX = lifeline.getProperLogicalBounds().x; + + // Align sources on left + Map maxOpSourceDeltas = Maps.newHashMap(); + int maxLifelineSourceDelta = 0; + for (LostMessageEnd lostSource : lostSources.get(lifeline)) { + Rectangle bounds = lostSource.getProperLogicalBounds().getCopy(); + int delta = bounds.x - lifelineX; + if (pack || AbstractSequenceLayout.createdFromTool(lostSource) || AbstractSequenceLayout.createdFromExternalChange(lostSource)) { + delta = getFoundEndPackedX(lostSource, lifeline, lifelineX, bounds.width); + } + deltas.put(lostSource, delta); + + // Force align + if (operands.containsKey(lostSource)) { + Operand op = operands.get(lostSource); + int opMax = 0; + if (maxOpSourceDeltas.containsKey(op)) { + opMax = maxOpSourceDeltas.get(op); + } + opMax = Math.min(opMax, delta); + maxOpSourceDeltas.put(op, opMax); + } else { + Kind kind = getMessageKind(lostSource); + + if (!Message.Kind.CREATION.equals(kind) && !Message.Kind.DESTRUCTION.equals(kind)) { + maxLifelineSourceDelta = Math.min(maxLifelineSourceDelta, delta); + } + } + } + + // Align targets on right + Map maxOpTargetDeltas = Maps.newHashMap(); + int maxLifelineTargetDelta = 0; + for (LostMessageEnd lostTarget : lostTargets.get(lifeline)) { + Rectangle bounds = lostTarget.getProperLogicalBounds().getCopy(); + int delta = bounds.x - lifelineX; + if (pack || AbstractSequenceLayout.createdFromTool(lostTarget) || AbstractSequenceLayout.createdFromExternalChange(lostTarget)) { + delta = LayoutConstants.LOST_MESSAGE_DEFAULT_WIDTH; + if (lostMessages.containsKey(lostTarget)) { + Message message = lostMessages.get(lostTarget); + delta += message.getSourceElement().getProperLogicalBounds().right() - lifelineX; + } else if (unconnectedLostEnds.contains(lostTarget)) { + ISequenceEvent sourceEvent = getISequenceEvent(getKnownSourceNode(lostTarget)); + if (sourceEvent != null) { + delta += sourceEvent.getProperLogicalBounds().right() - lifelineX; + } + } + } + deltas.put(lostTarget, delta); + + // Force align + if (operands.containsKey(lostTarget)) { + Operand op = operands.get(lostTarget); + int opMax = 0; + if (maxOpTargetDeltas.containsKey(op)) { + opMax = maxOpTargetDeltas.get(op); + } + opMax = Math.max(opMax, delta); + maxOpTargetDeltas.put(op, opMax); + } else { + maxLifelineTargetDelta = Math.max(maxLifelineTargetDelta, delta); + } + } + + // Force align + if (pack) { + for (Map.Entry entry : maxOpSourceDeltas.entrySet()) { + Integer maxSourceDelta = entry.getValue(); + for (LostMessageEnd source : Iterables.filter(operands2lostEnds.get(entry.getKey()), Predicates.in(lostSources.get(lifeline)))) { + deltas.put(source, maxSourceDelta); + } + } + for (LostMessageEnd source : Iterables.filter(lostSources.get(lifeline), Predicates.not(Predicates.in(operands.keySet())))) { + Kind kind = getMessageKind(source); + + if (!Message.Kind.CREATION.equals(kind) && !Message.Kind.DESTRUCTION.equals(kind)) { + deltas.put(source, maxLifelineSourceDelta); + } + } + for (Map.Entry entry : maxOpTargetDeltas.entrySet()) { + Integer maxTargetDelta = entry.getValue(); + for (LostMessageEnd target : Iterables.filter(operands2lostEnds.get(entry.getKey()), Predicates.in(lostTargets.get(lifeline)))) { + deltas.put(target, maxTargetDelta); + } + } + for (LostMessageEnd target : Iterables.filter(lostTargets.get(lifeline), Predicates.not(Predicates.in(operands.keySet())))) { + deltas.put(target, maxLifelineTargetDelta); + } + } + } + return deltas; + } + + private int getFoundEndPackedX(LostMessageEnd lostSourceEnd, Lifeline lifeline, int lifelineX, int lostEndWidth) { + int refX = lifelineX; + Kind kind = getMessageKind(lostSourceEnd); + + if (Message.Kind.CREATION.equals(kind) && lifeline.getInstanceRole() != null) { + refX = lifeline.getInstanceRole().getProperLogicalBounds().x; + } else if (Message.Kind.DESTRUCTION.equals(kind) && lifeline.getEndOfLife().some()) { + refX = lifeline.getEndOfLife().get().getProperLogicalBounds().x; + } + return refX - lifelineX - LayoutConstants.LOST_MESSAGE_DEFAULT_WIDTH - lostEndWidth; + } + + /** + * Get the found ends gap. + * + * @param lifeline + * the current lifeline. + * @param zone + * zone to get look for lost messages, can be null. + * @param lostEndsDelta + * the computed deltas with lifeline. + * @return the gap. + */ + public int getLeftGap(Lifeline lifeline, Range zone, Map lostEndsDelta) { + int foundEndsGap = getLostMessageEndsGap(lifeline, lostSources, zone, lostEndsDelta, true); + int lostMessageEndsGap = getLostMessageEndsGap(lifeline, lostTargets, zone, lostEndsDelta, true); + return Math.max(lostMessageEndsGap, foundEndsGap); + } + + /** + * Get the lost ends gap. + * + * @param lifeline + * the current lifeline. + * @param zone + * zone to get look for lost messages, can be null. + * @param lostEndsDelta + * the computed deltas with lifeline. + * @return the gap. + */ + public int getRightEndsGap(Lifeline lifeline, Range zone, Map lostEndsDelta) { + int lostMessageEndsGap = getLostMessageEndsGap(lifeline, lostTargets, zone, lostEndsDelta, false); + int foundEndsGap = getLostMessageEndsGap(lifeline, lostSources, zone, lostEndsDelta, false); + return Math.max(lostMessageEndsGap, foundEndsGap); + } + + private EdgeTarget getKnownSourceNode(LostMessageEnd lme) { + EdgeTarget sourceNode = null; + EdgeTarget dde = (EdgeTarget) lme.getNotationNode().getElement(); + Iterable incomingEdges = Iterables.filter(dde.getIncomingEdges(), Message.viewpointElementPredicate()); + if (!Iterables.isEmpty(incomingEdges)) { + DEdge msg = incomingEdges.iterator().next(); + sourceNode = msg.getSourceNode(); + } + return sourceNode; + } + + private EdgeTarget getKnownTargetNode(LostMessageEnd lme) { + EdgeTarget targetNode = null; + EdgeTarget dde = (EdgeTarget) lme.getNotationNode().getElement(); + Iterable outgoingEdges = Iterables.filter(dde.getOutgoingEdges(), Message.viewpointElementPredicate()); + if (!Iterables.isEmpty(outgoingEdges)) { + DEdge msg = outgoingEdges.iterator().next(); + targetNode = msg.getTargetNode(); + } + return targetNode; + } + + // Get the first known event in hierarchy. + private ISequenceEvent getISequenceEvent(EdgeTarget lostNode) { + ISequenceEvent correspondingEvent = null; + List knownEnds = Lists.newArrayList(); + knownEnds.addAll(sequenceDiagram.getAllLifelines()); + knownEnds.addAll(sequenceDiagram.getAllExecutions()); + + for (ISequenceEvent ise : knownEnds) { + if (ise.getNotationView() != null && lostNode != null && lostNode.equals(ise.getNotationView().getElement())) { + correspondingEvent = ise; + break; + } + } + if (correspondingEvent == null) { + EObject eContainer = lostNode.eContainer(); + for (ISequenceEvent ise : knownEnds) { + if (ise.getNotationView() != null && eContainer != null && eContainer.equals(ise.getNotationView().getElement())) { + correspondingEvent = ise; + } + } + } + return correspondingEvent; + } + + private Kind getMessageKind(LostMessageEnd lostSourceEnd) { + Kind kind = Message.Kind.BASIC; + Message message = lostMessages.get(lostSourceEnd); + if (message != null) { + kind = message.getKind(); + } else if (unconnectedLostEnds.contains(lostSourceEnd) && lostSourceEnd.getNotationNode().getElement() instanceof EdgeTarget) { + EdgeTarget dde = (EdgeTarget) lostSourceEnd.getNotationNode().getElement(); + if (!dde.getOutgoingEdges().isEmpty()) { + kind = Message.VIEWPOINT_MESSAGE_KIND.apply(dde.getOutgoingEdges().iterator().next()); + } + } + return kind; + } + + private int getLostMessageEndsGap(Lifeline lifeline, Multimap lostEnds, Range zone, Map lostEndsDelta, boolean revertDelta) { + int gap = 0; + if (lostEnds.containsKey(lifeline)) { + int maxLostEndWidth = 0; + int maxDelta = 0; + int frameBorder = 0; + for (LostMessageEnd lme : lostEnds.get(lifeline)) { + // Message in zone ? + if (zone == null || lostMessages.containsKey(lme) && zone.includesAtLeastOneBound(lostMessages.get(lme).getVerticalRange())) { + int delta = lostEndsDelta.get(lme); + maxDelta = Math.max(maxDelta, revertDelta ? -delta : delta); + if (maxDelta > 0) { + frameBorder = LayoutConstants.BORDER_FRAME_MARGIN; + maxLostEndWidth = Math.max(maxLostEndWidth, lme.getProperLogicalBounds().width); + } + } + } + gap = maxDelta + maxLostEndWidth + frameBorder; + } + return gap; + } + + /** + * Dispose the helper context. + */ + public void dispose() { + operands.clear(); + operands2lostEnds.clear(); + diagramLostEnds.clear(); + lostMessages.clear(); + lostSources.clear(); + lostTargets.clear(); + unconnectedLostEnds.clear(); + } + + /** + * Compute the lost message ends futur locations. + * + * @param irMoves + * computed instance roles new locations. + * @param lostEndsDelta + * computed delats between lost message ends and lifelines. + * @return lost message ends locations. + */ + public Map computeLostMessageEndsHorizontalBounds(Map irMoves, Map lostEndsDelta) { + Map lostMessageEndMoves = Maps.newHashMap(); + + for (Lifeline lifeline : sequenceDiagram.getAllLifelines()) { + int newLifelineX = getNewLifelineX(lifeline, irMoves); + + // Compute new ends x. + for (LostMessageEnd lostSource : Iterables.concat(lostSources.get(lifeline), lostTargets.get(lifeline))) { + int deltaWithLifeline = lostEndsDelta.get(lostSource); + Rectangle bounds = lostSource.getProperLogicalBounds().getCopy(); + + bounds.x = newLifelineX + deltaWithLifeline; + lostMessageEndMoves.put(lostSource, bounds); + } + } + return lostMessageEndMoves; + } + + private int getNewLifelineX(Lifeline lifeline, Map irMoves) { + int lifelineOldX = lifeline.getProperLogicalBounds().x; + InstanceRole instanceRole = lifeline.getInstanceRole(); + + int deltaX = 0; + if (instanceRole != null && irMoves.containsKey(instanceRole)) { + Rectangle irBounds = instanceRole.getProperLogicalBounds().getCopy(); + Rectangle irFutureBounds = irMoves.get(instanceRole); + + deltaX = irFutureBounds.x - irBounds.x; + } + return lifelineOldX + deltaX; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/horizontal/SequenceHorizontalLayout.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/horizontal/SequenceHorizontalLayout.java new file mode 100644 index 0000000000..b9d75d0b76 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/horizontal/SequenceHorizontalLayout.java @@ -0,0 +1,656 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.layout.horizontal; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.draw2d.geometry.Insets; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.notation.Bendpoints; +import org.eclipse.gmf.runtime.notation.Bounds; +import org.eclipse.gmf.runtime.notation.LayoutConstraint; +import org.eclipse.gmf.runtime.notation.Location; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.RelativeBendpoints; +import org.eclipse.gmf.runtime.notation.datatype.RelativeBendpoint; + +import com.google.common.base.Function; +import com.google.common.base.Predicates; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; +import com.google.common.collect.Ordering; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractFrame; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.CombinedFragment; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElement; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.LostMessageEnd; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.AbstractSequenceOrderingLayout; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.business.internal.query.ISequenceElementQuery; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * Computes the appropriate graphical locations of sequence events and lifelines + * on a sequence diagram to reflect the semantic order. + * + * @author pcdavid, mporhel + */ +public class SequenceHorizontalLayout extends AbstractSequenceOrderingLayout { + + private static final Function RECT_TO_X = new Function() { + public Integer apply(Rectangle input) { + return input.x; + } + }; + + private final Insets padding = new Insets(LayoutConstants.LIFELINES_START_Y, LayoutConstants.LIFELINES_START_X, 0, LayoutConstants.LIFELINES_MIN_X_GAP - LayoutConstants.LIFELINES_START_X); + + private final Collection frames = Lists.newArrayList(); + + private final Multimap coverage = HashMultimap.create(); + + private final Multimap invCoverage = HashMultimap.create(); + + private final Map ranges = Maps.newHashMap(); + + private final Map frameChildrenDepths = Maps.newHashMap(); + + private final Map lifelineChildrenDepths = Maps.newHashMap(); + + private LostMessageEndHorizontalLayoutHelper lostMessageEndHorizontalLayoutHelper; + + private final Map reflexiveMessagesToLayout = Maps.newHashMap(); + + /** + * Constructor. + * + * @param diagram + * the sequence diagram for which to compute the horizontal + * locations. + */ + public SequenceHorizontalLayout(SequenceDiagram diagram) { + super(diagram); + } + + /** + * {@inheritDoc} + */ + @Override + protected void init(boolean pack) { + if (pack) { + this.padding.left = LayoutConstants.LIFELINES_START_X; + this.padding.right = LayoutConstants.LIFELINES_MIN_PACKED_X_GAP - LayoutConstants.LIFELINES_START_X; + } + + populateSortedIntanceRoles(); + + populateLifelineDepth(); + populateFrames(); + populateLostMessageEnds(); + + populateReflexiveMessages(); + } + + /** + * {@inheritDoc} + */ + @Override + protected Rectangle getOldLayoutData(ISequenceElement ise) { + return ise.getProperLogicalBounds(); + } + + /** + * {@inheritDoc} + */ + @Override + protected Map computeLayout(boolean pack) { + LinkedHashMap allMoves = Maps.newLinkedHashMap(); + + Map lostEndsDelta = lostMessageEndHorizontalLayoutHelper.computeLostMessageEndDeltaWithLifeline(pack); + Map reflexiveMessagesMoves = computeReflexiveMessagesHorizontalBounds(); + + Map irMoves = computeInstanceRoleHorizontalLocations(pack, lostEndsDelta); + Map lostMessageEndMoves = lostMessageEndHorizontalLayoutHelper.computeLostMessageEndsHorizontalBounds(irMoves, lostEndsDelta); + Map frameMoves = computeFrameHorizontalBounds(irMoves, lostEndsDelta); + + allMoves.putAll(irMoves); + allMoves.putAll(frameMoves); + allMoves.putAll(lostMessageEndMoves); + allMoves.putAll(reflexiveMessagesMoves); + return allMoves; + } + + /** + * {@inheritDoc} + */ + @Override + protected boolean applyComputedLayout(Map bounds, boolean pack) { + boolean applied = false; + + applied = layoutInstanceRoles(bounds, pack) || applied; + applied = layoutFrames(bounds, pack) || applied; + applied = layoutLostMessageEnds(bounds, pack) || applied; + applied = layoutReflexiveMessages(bounds, pack) || applied; + + // if (pack) { + // correctGmfLocations(); + // } + + return applied; + } + + /** + * {@inheritDoc} + */ + @Override + protected void dispose() { + frameChildrenDepths.clear(); + lifelineChildrenDepths.clear(); + coverage.clear(); + invCoverage.clear(); + ranges.clear(); + frames.clear(); + lostMessageEndHorizontalLayoutHelper.dispose(); + reflexiveMessagesToLayout.clear(); + + super.dispose(); + } + + @SuppressWarnings("unused") + private void correctGmfLocations() { + layoutAbstractNodes(); + layoutLifeline(); + } + + private void layoutLifeline() { + for (Lifeline lifeline : sequenceDiagram.getAllLifelines()) { + Rectangle properLogicalBounds = lifeline.getProperLogicalBounds(); + Rectangle parentLogicalBounds = lifeline.getInstanceRole().getProperLogicalBounds(); + LayoutConstraint layoutConstraint = lifeline.getNotationNode().getLayoutConstraint(); + if (layoutConstraint instanceof Location) { + Location loc = (Location) layoutConstraint; + loc.setX(properLogicalBounds.x - parentLogicalBounds.x); + } + } + } + + private void layoutAbstractNodes() { + for (AbstractNodeEvent exec : sequenceDiagram.getAllAbstractNodeEvents()) { + Rectangle properLogicalBounds = exec.getProperLogicalBounds(); + Rectangle parentLogicalBounds = exec.getHierarchicalParentEvent().getProperLogicalBounds(); + LayoutConstraint layoutConstraint = exec.getNotationNode().getLayoutConstraint(); + if (layoutConstraint instanceof Location) { + Location loc = (Location) layoutConstraint; + loc.setX(properLogicalBounds.x - parentLogicalBounds.x); + } + } + } + + private void populateSortedIntanceRoles() { + // Graphicall order + graphicalOrdering.addAll(sequenceDiagram.getSortedInstanceRole()); + + // If a semantic order is specified, sort the instance roles. + semanticOrdering.addAll(graphicalOrdering); + SequenceDDiagram sequenceDDiagram = sequenceDiagram.getSequenceDDiagram(); + if (sequenceDDiagram != null && !sequenceDDiagram.getInstanceRoleSemanticOrdering().getSemanticInstanceRoles().isEmpty()) { + final List semanticOrder = sequenceDDiagram.getInstanceRoleSemanticOrdering().getSemanticInstanceRoles(); + Function semanticIndex = new Function() { + public Integer apply(InstanceRole ir) { + Option semIr = ir.getSemanticTargetElement(); + return semIr.some() ? semanticOrder.indexOf(semIr.get()) : -1; + } + }; + Collections.sort(semanticOrdering, Ordering.natural().onResultOf(semanticIndex)); + } + + for (InstanceRole role : semanticOrdering) { + ISequenceElementQuery query = new ISequenceElementQuery(role); + if (query.hasAbsoluteBoundsFlag()) { + Rectangle flaggedAbsoluteBounds = query.getFlaggedAbsoluteBounds(); + oldFlaggedLayoutData.put(role, flaggedAbsoluteBounds); + flaggedEnds.add(role); + } + } + + Collections.sort(flaggedEnds, Ordering.natural().onResultOf(getOldFlaggedPosition())); + } + + private void populateLifelineDepth() { + List allLifelines = sequenceDiagram.getAllLifelines(); + for (Lifeline lifeline : allLifelines) { + int depth = getOrComputeMaxChildrenDepth(lifeline.getNotationNode(), lifeline.getVerticalRange()); + lifelineChildrenDepths.put(lifeline, depth); + } + } + + private void populateLostMessageEnds() { + lostMessageEndHorizontalLayoutHelper = new LostMessageEndHorizontalLayoutHelper(sequenceDiagram); + lostMessageEndHorizontalLayoutHelper.populateLostMessageEnds(); + } + + private void populateFrames() { + Collection allFrames = Lists.newArrayList(); + allFrames.addAll(sequenceDiagram.getAllInteractionUses()); + allFrames.addAll(sequenceDiagram.getAllCombinedFragments()); + + for (AbstractFrame frame : allFrames) { + Collection coveredLifelines = frame.computeCoveredLifelines(); + if (!coveredLifelines.isEmpty()) { + frames.add(frame); + coverage.putAll(frame, coveredLifelines); + ranges.put(frame, frame.getVerticalRange()); + } + } + + for (AbstractFrame frame : frames) { + getOrComputeMaxChildrenDepth(frame, Collections.singletonList(frame)); + } + + Multimaps.invertFrom(coverage, invCoverage); + } + + private void populateReflexiveMessages() { + for (Message msg : sequenceDiagram.getAllMessages()) { + if (msg.isReflective()) { + int width = msg.getReflexiveMessageWidth(); + if (width != 0) { + reflexiveMessagesToLayout.put(msg, width); + } + } + } + + } + + private int getOrComputeMaxChildrenDepth(Node node, Range range) { + int maxChildrenDepth = 0; + for (Node child : Iterables.filter(Iterables.filter(node.getChildren(), Node.class), AbstractNodeEvent.notationPredicate())) { + AbstractNodeEvent childExec = ISequenceElementAccessor.getAbstractNodeEvent(child).get(); + if (range == null || range.intersects(childExec.getVerticalRange())) { + int childDepth = 1 + getOrComputeMaxChildrenDepth(child, range); + maxChildrenDepth = Math.max(childDepth, maxChildrenDepth); + } + } + return maxChildrenDepth; + } + + private int getOrComputeMaxChildrenDepth(AbstractFrame frame, Collection framesToIgnore) { + int children = 0; + if (!frameChildrenDepths.containsKey(frame)) { + Collection frameCoverage = coverage.get(frame); + Range frameRange = ranges.get(frame); + for (AbstractFrame potentialChild : Iterables.filter(frames, Predicates.not(Predicates.in(framesToIgnore)))) { + Collection potentialCoverage = coverage.get(potentialChild); + Range potentialRange = ranges.get(potentialChild); + + if (frame != potentialChild && frameCoverage.containsAll(potentialCoverage) && frameRange.includes(potentialRange)) { + ArrayList newArrayList = Lists.newArrayList(framesToIgnore); + newArrayList.add(potentialChild); + children = Math.max(children, 1 + getOrComputeMaxChildrenDepth(potentialChild, newArrayList)); + } + } + frameChildrenDepths.put(frame, children); + } else { + children = frameChildrenDepths.get(frame); + } + return children; + } + + private Map computeFrameHorizontalBounds(Map irMoves, Map lostEndsDelta) { + Map frameMoves = Maps.newHashMap(); + + for (AbstractFrame frame : frames) { + Rectangle newBounds = null; + Lifeline leftLifeline = null; + Lifeline rightLifeline = null; + for (Lifeline lifeline : coverage.get(frame)) { + Rectangle lBounds = getInstanceRoleBounds(lifeline.getInstanceRole(), irMoves); + if (newBounds == null) { + newBounds = lBounds.getCopy(); + } else { + newBounds.union(lBounds); + } + + // look for right lifeline + if (lBounds.right() == newBounds.right()) { + rightLifeline = lifeline; + } + + // look for left lifeline + if (lBounds.x == newBounds.x) { + leftLifeline = lifeline; + } + } + + Integer frameDepth = frameChildrenDepths.get(frame); + int frameDepthGap = frameDepth * LayoutConstants.BORDER_FRAME_MARGIN; + + if (rightLifeline != null) { + Range verticalRange = frame.getVerticalRange(); + int irWidth = getInstanceRoleBounds(rightLifeline.getInstanceRole(), irMoves).width; + int lifelineRightGap = getLifelineRightGap(rightLifeline, verticalRange, irWidth, lostEndsDelta); + newBounds.width += Math.max(lifelineRightGap, frameDepthGap); + } + + if (leftLifeline != null) { + Range verticalRange = frame.getVerticalRange(); + int irWidth = getInstanceRoleBounds(leftLifeline.getInstanceRole(), irMoves).width; + int lifelineLeftGap = getLifelineLeftGap(rightLifeline, verticalRange, irWidth, lostEndsDelta); + lifelineLeftGap = Math.max(lifelineLeftGap, frameDepthGap); + + newBounds.x -= lifelineLeftGap; + newBounds.width += lifelineLeftGap; + } + + frameMoves.put(frame, newBounds); + } + return frameMoves; + } + + private Rectangle getInstanceRoleBounds(InstanceRole instanceRole, Map irMoves) { + if (irMoves.containsKey(instanceRole)) { + return irMoves.get(instanceRole); + } + return instanceRole.getBounds(); + } + + /** + * Computes the horizontal absolute location of instance roles. + * + * @param pack + * pack the diagram + * @param lostEndsDelta + * @param reflexiveMessagesMoves + * + * @return a map associating each instance role edit part to the new + * absolute horizontal location it should have. + */ + private Map computeInstanceRoleHorizontalLocations(boolean pack, Map lostEndsDelta) { + final Map computedMoves = new HashMap(); + + // initial position + int currentX = padding.left; + for (InstanceRole instanceRole : semanticOrdering) { + currentX = computeLocation(currentX, instanceRole, pack, lostEndsDelta, computedMoves); + } + return computedMoves; + } + + /** + * Compute and store the new bounds of the instance roles, the x location + * will be the only modified value. Return the next minimum x. + */ + private int computeLocation(final int currentX, final InstanceRole instanceRole, boolean pack, Map lostEndsDelta, final Map computedMoves) { + final Rectangle oldBounds = instanceRole.getProperLogicalBounds(); + final Option lifeline = instanceRole.getLifeline(); + + int newMinX = currentX; + int rightComputedGap = 0; + if (lifeline.some()) { + int maxFrameDepth = getMaxFrameDepth(lifeline.get()); + int foundMessagesGap = getLifelineLeftGap(lifeline.get(), null, oldBounds.width, lostEndsDelta); + int frameGap = maxFrameDepth * LayoutConstants.BORDER_FRAME_MARGIN; + int rightGap = getLifelineRightGap(lifeline.get(), null, oldBounds.width, lostEndsDelta); + + // Make space for frame and found messages. + newMinX += frameGap + foundMessagesGap; + // Update computed gap + rightComputedGap = frameGap + rightGap; + } + + Rectangle newBounds = oldBounds.getCopy(); + if (pack) { + newBounds.x = newMinX; + } else { + // shift the current instancerole to the right ? + // don't reduce previous delta with known/flagged predecessor + int deltaStablePosition = getDeltaStablePosition(newMinX, instanceRole, Maps.transformValues(computedMoves, RECT_TO_X)); + + newBounds.x = Math.max(newMinX, Math.max(newBounds.x, deltaStablePosition)); + } + + // Store computed move + computedMoves.put(instanceRole, newBounds); + + // Return the next minX : right of the current instance role + minimum + // gap + place for frames and found messages + return newBounds.right() + getMinInstanceRoleGap() + rightComputedGap; + } + + private int getMaxReflexiveDepth(Lifeline lifeline, Range zone) { + int maxWidth = 0; + for (Message msg : reflexiveMessagesToLayout.keySet()) { + if (lifeline.equals(msg.getLifeline().get()) && (zone == null || zone.includes(msg.getVerticalRange()))) { + maxWidth = Math.max(maxWidth, reflexiveMessagesToLayout.get(msg)); + } + } + return maxWidth; + } + + private Map computeReflexiveMessagesHorizontalBounds() { + final Map computedMoves = new HashMap(); + for (Message msg : reflexiveMessagesToLayout.keySet()) { + Rectangle properLogicalBounds = msg.getProperLogicalBounds(); + properLogicalBounds.width = reflexiveMessagesToLayout.get(msg); + computedMoves.put(msg, properLogicalBounds); + } + return computedMoves; + } + + private int getMinInstanceRoleGap() { + return padding.right + padding.left; + } + + private int getMaxExecDepth(Lifeline lifeline) { + int depth = 0; + if (lifelineChildrenDepths.containsKey(lifeline)) { + depth = lifelineChildrenDepths.get(lifeline); + } + return depth; + } + + private int getMaxFrameDepth(Lifeline lifeline) { + int depth = 0; + Collection collection = invCoverage.get(lifeline); + if (collection != null && !collection.isEmpty()) { + for (AbstractFrame abstractFrame : collection) { + Integer integer = frameChildrenDepths.get(abstractFrame); + depth = Math.max(integer, depth); + } + } + return depth; + } + + /** + * Compute the right gap for the given lifeline and range. + * + * @param lifeline + * the current lifeline. + * @param zone + * if not null, the restricted vertical range to look for + * execution and lost ends. + * @param irWidth + * the instance role width. + * @param lostEndsDelta + * @return the right gap for the current lifeline. + */ + private int getLifelineRightGap(Lifeline lifeline, Range zone, int irWidth, Map lostEndsDelta) { + int rightGap = 0; + int execDepth = zone == null ? getMaxExecDepth(lifeline) : getOrComputeMaxChildrenDepth(lifeline.getNotationNode(), zone); + + // handle Execution and State hierarchy + if (execDepth != 0) { + // the first execution is centered on the lifeline. + int gap = LayoutConstants.UNIT; + // TODO get the execution vsm size. + gap += (execDepth - 1) * 15; + // Remove the mid instance role size to get the gap relative to its + // right and ad the minimum gap between last execution and frame + // border. + gap += -irWidth / 2 + LayoutConstants.UNIT; + rightGap = Math.max(0, gap); + } + + // Make space for reflexive messages + int reflexiveGap = getMaxReflexiveDepth(lifeline, zone) - irWidth / 2; + rightGap = Math.max(rightGap, reflexiveGap); + + // handle lost message ends + rightGap = Math.max(rightGap, lostMessageEndHorizontalLayoutHelper.getRightEndsGap(lifeline, zone, lostEndsDelta) - irWidth / 2); + + return rightGap; + } + + private int getLifelineLeftGap(Lifeline lifeline, Range zone, int irWidth, Map lostEndsDelta) { + int leftGap = 0; + + // handle lost message ends + leftGap = Math.max(leftGap, lostMessageEndHorizontalLayoutHelper.getLeftGap(lifeline, zone, lostEndsDelta) - irWidth / 2); + + return leftGap; + } + + private boolean layoutInstanceRoles(Map bounds, boolean pack) { + boolean applied = false; + for (InstanceRole instanceRole : Iterables.filter(bounds.keySet(), InstanceRole.class)) { + final Node node = instanceRole.getNotationNode(); + final Integer computedX = bounds.get(instanceRole).x; + + LayoutConstraint layoutConstraint = node.getLayoutConstraint(); + if (layoutConstraint instanceof Location) { + Location location = (Location) layoutConstraint; + location.setX(computedX); + } + applied = true; + } + return applied; + } + + private boolean layoutFrames(Map bounds, boolean pack) { + boolean applied = false; + for (AbstractFrame frame : Iterables.filter(bounds.keySet(), AbstractFrame.class)) { + Rectangle newBounds = bounds.get(frame); + Node notationNode = frame.getNotationNode(); + LayoutConstraint layoutConstraint = notationNode.getLayoutConstraint(); + if (layoutConstraint instanceof Bounds && newBounds != null) { + Bounds b = (Bounds) layoutConstraint; + b.setWidth(newBounds.width); + b.setX(newBounds.x); + applied = true; + } + + if (frame instanceof CombinedFragment) { + CombinedFragment cf = (CombinedFragment) frame; + for (Operand op : cf.getOperands()) { + Node opNode = op.getNotationNode(); + LayoutConstraint opLC = opNode.getLayoutConstraint(); + if (opLC instanceof Bounds && newBounds != null) { + Bounds opBounds = (Bounds) opLC; + opBounds.setWidth(newBounds.width); + opBounds.setX(0); + applied = true; + } + } + } + } + return applied; + } + + private boolean layoutLostMessageEnds(Map bounds, boolean pack) { + boolean applied = false; + for (LostMessageEnd lostMessageEnd : Iterables.filter(bounds.keySet(), LostMessageEnd.class)) { + final Node node = lostMessageEnd.getNotationNode(); + final Integer computedX = bounds.get(lostMessageEnd).x; + + LayoutConstraint layoutConstraint = node.getLayoutConstraint(); + if (layoutConstraint instanceof Location) { + Location location = (Location) layoutConstraint; + location.setX(computedX); + applied = true; + } + } + return applied; + } + + private boolean layoutReflexiveMessages(Map bounds, boolean pack) { + boolean applied = false; + for (Message msg : Iterables.filter(bounds.keySet(), Message.class)) { + Bendpoints bendpoints = msg.getNotationEdge().getBendpoints(); + if (msg.isReflective() && bendpoints instanceof RelativeBendpoints) { + RelativeBendpoints relativeBendpoints = (RelativeBendpoints) bendpoints; + Iterable points = Iterables.filter(relativeBendpoints.getPoints(), RelativeBendpoint.class); + if (Iterables.size(points) == 4) { + RelativeBendpoint p0 = Iterables.get(points, 0); + RelativeBendpoint p1 = Iterables.get(points, 1); + RelativeBendpoint p2 = Iterables.get(points, 2); + RelativeBendpoint p3 = Iterables.get(points, 3); + + int deltaX = bounds.get(msg).width - p1.getSourceX(); + RelativeBendpoint newP1 = new RelativeBendpoint(p1.getSourceX() + deltaX, p1.getSourceY(), p1.getTargetX() + deltaX, p1.getTargetY()); + RelativeBendpoint newP2 = new RelativeBendpoint(p2.getSourceX() + deltaX, p2.getSourceY(), p2.getTargetX() + deltaX, p2.getTargetY()); + + List newPoints = Lists.newArrayList(); + newPoints.add(p0); + newPoints.add(newP1); + newPoints.add(newP2); + newPoints.add(p3); + + relativeBendpoints.setPoints(newPoints); + applied = true; + } + } + } + return applied; + } + + @Override + protected Function getOldPosition() { + return new Function() { + public Integer apply(InstanceRole input) { + return input.getProperLogicalBounds().x; + } + }; + } + + @Override + protected Function getOldFlaggedPosition() { + return new Function() { + public Integer apply(InstanceRole input) { + int oldFlaggedPosition = Integer.MIN_VALUE; + Rectangle flaggedData = oldFlaggedLayoutData.get(input); + if (flaggedData != null) { + oldFlaggedPosition = flaggedData.x; + } + return oldFlaggedPosition; + } + }; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/observation/SequenceObservationLayout.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/observation/SequenceObservationLayout.java new file mode 100644 index 0000000000..f390fc1778 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/observation/SequenceObservationLayout.java @@ -0,0 +1,165 @@ +/******************************************************************************* + * Copyright (c) 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.layout.observation; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gmf.runtime.notation.Bounds; +import org.eclipse.gmf.runtime.notation.Node; + +import com.google.common.collect.Maps; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.diagram.business.internal.query.DNodeQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractFrame; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Execution; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ObservationPoint; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.State; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.AbstractSequenceLayout; +import org.eclipse.sirius.diagram.sequence.business.internal.ordering.EventEndHelper; +import org.eclipse.sirius.diagram.sequence.ordering.CompoundEventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.SingleEventEnd; + +/** + * Computes the appropriate graphical locations of observation points on a + * sequence diagram. + * + * @author mporhel + */ +public class SequenceObservationLayout extends AbstractSequenceLayout { + + private Map endToObservationPoint; + + /** + * Constructor. + * + * @param sequenceDiagram + * the sequence diagram for which to compute the observation + * point locations. + */ + public SequenceObservationLayout(SequenceDiagram sequenceDiagram) { + super(sequenceDiagram); + this.endToObservationPoint = Maps.newHashMap(); + } + + @Override + protected void init(boolean pack) { + for (ObservationPoint point : sequenceDiagram.getAllObservationPoints()) { + Option observedEventEnd = point.getObservedEventEnd(); + + if (observedEventEnd.some()) { + endToObservationPoint.put(observedEventEnd.get(), point); + } + } + + } + + @Override + protected Map computeLayout(boolean pack) { + HashMap computedLayout = Maps.newHashMap(); + + for (ISequenceEvent ise : sequenceDiagram.getAllDelimitedSequenceEvents()) { + Rectangle bounds = ise.getProperLogicalBounds(); + + List foundEnds = sequenceDiagram.findEnds(ise); + for (EventEnd eventEnd : foundEnds) { + Point refPoint = null; + ObservationPoint observationPoint = endToObservationPoint.get(eventEnd); + if (observationPoint != null) { + if (eventEnd instanceof SingleEventEnd) { + SingleEventEnd see = (SingleEventEnd) eventEnd; + if (ise instanceof Message) { + Message msg = (Message) ise; + if (msg.isReflective()) { + refPoint = see.isStart() ? bounds.getTopLeft().getCopy() : bounds.getBottomRight().getCopy(); + } else { + refPoint = see.isStart() ? bounds.getTopLeft().getCopy() : bounds.getBottomRight().getCopy(); + } + } else if (ise instanceof AbstractFrame || ise instanceof Operand) { + refPoint = see.isStart() ? bounds.getTopLeft().getCopy() : bounds.getBottomLeft().getCopy(); + } else { + refPoint = see.isStart() ? bounds.getTop().getCopy() : bounds.getBottom().getCopy(); + } + + } else if (eventEnd instanceof CompoundEventEnd) { + if (ise instanceof State && ise.isLogicallyInstantaneous()) { + // getcenter ? + refPoint = bounds.getLeft().getCopy(); + } else if (ise instanceof Execution) { + SingleEventEnd see = EventEndHelper.getSingleEventEnd(eventEnd, ise.getSemanticTargetElement().get()); + refPoint = see.isStart() ? bounds.getTop().getCopy() : bounds.getBottom().getCopy(); + } else if (ise instanceof Operand) { + SingleEventEnd see = EventEndHelper.getSingleEventEnd(eventEnd, ise.getSemanticTargetElement().get()); + refPoint = see.isStart() ? bounds.getTopLeft().getCopy() : bounds.getBottomLeft().getCopy(); + } + } + if (refPoint != null) { + computedLayout.put(observationPoint, refPoint); + } + } + } + } + + return computedLayout; + } + + @Override + protected boolean applyComputedLayout(Map finalLocations, boolean pack) { + boolean applied = false; + for (ObservationPoint point : sequenceDiagram.getAllObservationPoints()) { + Point computedCenter = finalLocations.get(point); + if (computedCenter != null) { + Node notationNode = point.getNotationNode(); + Bounds bounds = (Bounds) notationNode.getLayoutConstraint(); + + // Center the node on the computed location. + int midWidth = bounds.getWidth() / 2; + if (bounds.getWidth() == -1) { + midWidth = new DNodeQuery((DNode) point.getNotationNode().getElement()).getDefaultDimension().width / 2; + } + int midHeight = bounds.getHeight() / 2; + if (bounds.getHeight() == -1) { + midHeight = new DNodeQuery((DNode) point.getNotationNode().getElement()).getDefaultDimension().height / 2; + } + + int x = computedCenter.x - midWidth; + int y = computedCenter.y - midHeight; + + bounds.setX(x); + bounds.setY(y); + applied = true; + } + } + return applied; + } + + @Override + protected Point getOldLayoutData(ObservationPoint obsPoint) { + return obsPoint.getObservedLogicalLocation(); + } + + @Override + protected void dispose() { + this.endToObservationPoint.clear(); + + super.dispose(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/vertical/SequenceVerticalLayout.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/vertical/SequenceVerticalLayout.java new file mode 100644 index 0000000000..9e3b48f756 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/layout/vertical/SequenceVerticalLayout.java @@ -0,0 +1,1080 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.layout.vertical; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.LayoutConstraint; +import org.eclipse.gmf.runtime.notation.Location; +import org.eclipse.gmf.runtime.notation.Size; + +import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Iterables; +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Ordering; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.diagram.business.internal.query.DNodeQuery; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.CombinedFragment; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.EndOfLife; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElement; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceNode; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InteractionUse; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.LostMessageEnd; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.State; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.AbstractSequenceLayout; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.AbstractSequenceOrderingLayout; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.EventEndToPositionFunction; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.business.internal.ordering.EventEndHelper; +import org.eclipse.sirius.diagram.sequence.business.internal.query.ISequenceElementQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.query.SequenceMessageViewQuery; +import org.eclipse.sirius.diagram.sequence.ordering.CompoundEventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.SingleEventEnd; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * Computes the appropriate graphical locations of sequence events and lifelines + * on a sequence diagram to reflect the semantic order. + * + * @author pcdavid, mporhel + */ +public class SequenceVerticalLayout extends AbstractSequenceOrderingLayout { + + /** + * A map to link an {@link EventEnd} to the attached {@link ISequenceEvent}. + */ + protected final Map creators; + + /** + * A map to link an {@link EventEnd} to the attached {@link ISequenceEvent}. + */ + protected final Map destructors; + + /** + * A map to link an {@link EventEnd} to an attached {@link LostMessageEnd}. + */ + protected final Map losts; + + /** + * Unconnected lostMessageEnds. + */ + protected final List unconnectedLostEnds; + + /** + * Semantic flagged event ends at creation. + */ + protected final List toolCreatedEnds = Lists.newArrayList(); + + /** + * A map to link an {@link EventEnd} to the attached {@link ISequenceEvent}. + */ + protected final Multimap endToISequencEvents; + + /** + * A map to link an {@link ISequenceEvent} to its starting and ending + * {@link EventEnd}. + */ + protected final Multimap iSequenceEventsToEventEnds; + + /** + * A function to compute the sequence events corresponding to an event end. + */ + protected final Function> eventEndToSequenceEvents = new Function>() { + public Collection apply(EventEnd from) { + return endToISequencEvents.get(from); + } + }; + + /** + * The global time range of the diagram. Can be udpated during layout + * computation. + */ + protected Range timeRange; + + /** + * A function to get the instance role height of a lifeline. + */ + private final Function instanceRoleHeight = new Function() { + public Integer apply(Lifeline from) { + InstanceRole irep = from.getInstanceRole(); + if (irep != null) { + return ((Size) irep.getNotationNode().getLayoutConstraint()).getHeight(); + } + return 0; + } + }; + + /** + * An ordering to sort {@link Lifeline} regarding the height of their + * {@link InstanceRole}. + */ + private final Ordering heightOrdering = Ordering.natural().onResultOf(instanceRoleHeight); + + private final Function> oldRangeFunction = new Function>() { + public Option apply(ISequenceEvent from) { + Range range = oldLayoutData.get(from); + if (range == null) { + range = Range.emptyRange(); + } + return Options.newSome(range); + } + }; + + private final Function> oldFlaggedRange = new Function>() { + public Option apply(ISequenceEvent from) { + Rectangle rect = oldFlaggedLayoutData.get(from); + Range result = null; + if (rect != null) { + result = Range.verticalRange(rect); + } + return Options.newSome(result); + } + }; + + private final Function eventEndOldPosition = new EventEndToPositionFunction(eventEndToSequenceEvents, oldRangeFunction) { + + @Override + protected Integer getOldPositionFromRange(SingleEventEnd see, ISequenceEvent ise) { + Integer oldPos = super.getOldPositionFromRange(see, ise); + + if (ise instanceof Message && !ise.isLogicallyInstantaneous() && see != null) { + // Real position (diagram initialization, message creation) + Message smep = (Message) ise; + Edge notationView = smep.getNotationEdge(); + SequenceMessageViewQuery query = new SequenceMessageViewQuery(notationView); + oldPos = see.isStart() ? query.getFirstPointVerticalPosition(true) : query.getLastPointVerticalPosition(true); + } + + return oldPos; + } + + }; + + private final Function eventEndOldFlaggedPosition = new EventEndToPositionFunction(eventEndToSequenceEvents, oldFlaggedRange); + + /** + * Constructor. + * + * @param sequenceDiagram + * the sequence diagram for which to compute the messages + * locations. + */ + public SequenceVerticalLayout(SequenceDiagram sequenceDiagram) { + super(sequenceDiagram); + + this.iSequenceEventsToEventEnds = LinkedHashMultimap.create(); + this.endToISequencEvents = HashMultimap.create(); + + this.creators = Maps.newHashMap(); + this.destructors = Maps.newHashMap(); + this.losts = Maps.newHashMap(); + this.unconnectedLostEnds = Lists.newArrayList(); + } + + /** + * {@inheritDoc} + */ + @Override + protected void init(boolean pack) { + initSortedEventEnds(pack); + initLifelinesOldLayoutData(); + initTimeBounds(pack); + registerEventEnds(); + + lookForUnconnectedLostEnd(); + } + + /** + * {@inheritDoc} + */ + @Override + protected Range getOldLayoutData(ISequenceElement ise) { + Range verticalRange = Range.emptyRange(); + + if (ise instanceof ISequenceEvent) { + verticalRange = ((ISequenceEvent) ise).getVerticalRange(); + + if (ise instanceof Message) { + Message msg = (Message) ise; + ISequenceElementQuery query = null; + ISequenceNode sourceElement = msg.getSourceElement(); + ISequenceNode targetElement = msg.getTargetElement(); + if (sourceElement instanceof LostMessageEnd && AbstractSequenceLayout.createdFromTool((LostMessageEnd) sourceElement)) { + query = new ISequenceElementQuery(sourceElement); + } else if (targetElement instanceof LostMessageEnd && AbstractSequenceLayout.createdFromTool((LostMessageEnd) targetElement)) { + query = new ISequenceElementQuery(targetElement); + } + + if (query != null && query.hasAbsoluteBoundsFlag()) { + Rectangle flag = query.getFlaggedAbsoluteBounds(); + verticalRange = new Range(flag.y, flag.y); + } + } + } + + return verticalRange; + } + + /** + * {@inheritDoc} + */ + @Override + protected boolean applyComputedLayout(Map finalRanges, boolean pack) { + boolean applied = false; + Iterable keySet = Iterables.filter(finalRanges.keySet(), ISequenceEvent.class); + + // Begin with lifelines and executions (anchor positions move) + for (ISequenceEvent ise : Iterables.filter(keySet, Predicates.not(Predicates.instanceOf(Message.class)))) { + final Range newRange = finalRanges.get(ise); + ise.setVerticalRange(newRange); + applied = true; + } + + // Then apply computed vertical range on messages + for (Message smep : Iterables.filter(keySet, Message.class)) { + final Range newRange = finalRanges.get(smep); + smep.setVerticalRange(newRange); + applied = true; + } + + applied = layoutUnconnectedLostMessageEnd() || applied; + + return applied; + } + + /** + * {@inheritDoc} + */ + @Override + protected Map computeLayout(boolean pack) { + LinkedHashMap sequenceEventRanges = new LinkedHashMap(); + + // Compute the position of each event end. + Map endLocations = computeEndBounds(pack); + + // Compute ISequenceEvent vertical ranges from event end locations. + Map basicRanges = computeBasicRanges(endLocations); + + // Compute punctual States vertical range + Map punctualEventRanges = computePunctualEventsGraphicalRanges(endLocations, pack); + + // Update lifeline size. + Map lifelinesRanges = computeLifelineRanges(endLocations); + + sequenceEventRanges.putAll(lifelinesRanges); + sequenceEventRanges.putAll(basicRanges); + sequenceEventRanges.putAll(punctualEventRanges); + + return sequenceEventRanges; + } + + /** + * {@inheritDoc} + */ + @Override + protected void dispose() { + creators.clear(); + destructors.clear(); + losts.clear(); + unconnectedLostEnds.clear(); + toolCreatedEnds.clear(); + + endToISequencEvents.clear(); + iSequenceEventsToEventEnds.clear(); + + super.dispose(); + } + + private Map computePunctualEventsGraphicalRanges(Map endLocations, boolean pack) { + final Map sequenceEventsToRange = new LinkedHashMap(); + if (pack) { + for (EventEnd cee : Iterables.filter(semanticOrdering, EventEndHelper.PUNCTUAL_COMPOUND_EVENT_END)) { + if (endLocations.containsKey(cee) && endToISequencEvents.containsKey(cee)) { + int loc = endLocations.get(cee); + Collection ises = endToISequencEvents.get(cee); + + if (Iterables.any(ises, Predicates.instanceOf(State.class)) && ises.size() == 1) { + State ise = (State) ises.iterator().next(); + int midSize = getAbstractNodeEventVerticalSize(cee, ise, ises, pack) / 2; + sequenceEventsToRange.put(ise, new Range(loc - midSize, loc + midSize)); + } + } + } + } + return sequenceEventsToRange; + } + + /** + * Computes the absolute vertical (Y) location for all the messages in the + * sequence diagram. + * + * @return a map associating each message edit part to the new absolute + * vertical location it should have. + */ + private Map computeEndBounds(boolean pack) { + final Map result = Maps.newLinkedHashMap(); + + if (semanticOrdering == null || semanticOrdering.isEmpty()) { + return result; + } + + // current y location + int currentY = this.timeRange.getLowerBound(); + + EventEnd endBefore = null; + for (EventEnd end : semanticOrdering) { + currentY = computeLocation(currentY, end, endBefore, pack, result); + result.put(end, currentY); + endBefore = end; + } + return result; + } + + private int getGapFromCommonSequenceEvent(EventEnd end, Collection commonIses, boolean pack, int genericGap) { + int beforeGap = genericGap; + + if (commonIses.isEmpty()) { + return beforeGap; + } + + ISequenceEvent commonIse = commonIses.iterator().next(); + if (commonIse instanceof Message && ((Message) commonIse).isReflective()) { + beforeGap = LayoutConstants.MESSAGE_TO_SELF_BENDPOINT_VERTICAL_GAP; + } else if (commonIse instanceof AbstractNodeEvent) { + beforeGap = Math.max(genericGap, getAbstractNodeEventVerticalSize(end, (AbstractNodeEvent) commonIse, commonIses, pack)); + } else if (commonIse instanceof InteractionUse) { + beforeGap = LayoutConstants.DEFAULT_INTERACTION_USE_HEIGHT; + } else if (commonIse instanceof Operand) { + beforeGap = LayoutConstants.DEFAULT_OPERAND_HEIGHT; + } + return beforeGap; + } + + private int getAbstractNodeEventVerticalSize(EventEnd end, AbstractNodeEvent ise, Collection commonIses, boolean pack) { + int vSize = 0; + if (pack) { + DNode execution = (DNode) ise.getNotationView().getElement(); + int specifiedVSize = getSpecifiedVSize(execution); + if (specifiedVSize != 0) { + vSize = specifiedVSize; + } + } else if (isFlagguedByRefreshExtension(end, commonIses)) { + Rectangle rect = oldFlaggedLayoutData.get(ise); + vSize = rect != null ? rect.height : LayoutConstants.DEFAULT_EXECUTION_HEIGHT; + } else { + Range range = oldLayoutData.get(ise); + vSize = range != null ? range.width() : LayoutConstants.DEFAULT_EXECUTION_HEIGHT; + } + + return vSize; + } + + private Map computeBasicRanges(Map endLocations) { + final Map sequenceEventsToRange = new LinkedHashMap(); + Predicate notMoved = Predicates.not(Predicates.in(sequenceEventsToRange.keySet())); + + // CombinedFragments + for (EventEnd sortedEnd : semanticOrdering) { + Predicate frames = Predicates.and(notMoved, Predicates.or(Predicates.instanceOf(CombinedFragment.class), Predicates.instanceOf(InteractionUse.class))); + for (ISequenceEvent ise : Iterables.filter(endToISequencEvents.get(sortedEnd), frames)) { + computeFinalRange(endLocations, sequenceEventsToRange, ise); + } + } + + // Operands + for (EventEnd sortedEnd : semanticOrdering) { + Predicate operands = Predicates.and(notMoved, Predicates.instanceOf(Operand.class)); + for (ISequenceEvent ise : Iterables.filter(endToISequencEvents.get(sortedEnd), operands)) { + computeFinalRange(endLocations, sequenceEventsToRange, ise); + } + } + + // Other sequence events + for (EventEnd sortedEnd : semanticOrdering) { + for (ISequenceEvent ise : Iterables.filter(endToISequencEvents.get(sortedEnd), notMoved)) { + computeFinalRange(endLocations, sequenceEventsToRange, ise); + } + } + return sequenceEventsToRange; + } + + private void computeFinalRange(Map endLocations, final Map sequenceEventsToRange, ISequenceEvent ise) { + Collection ends = iSequenceEventsToEventEnds.get(ise); + if (ends.size() == 2) { + Iterator it = ends.iterator(); + EventEnd start = it.next(); + EventEnd finish = it.next(); + + Range newRange = getNewRange(ise, start, finish, endLocations); + sequenceEventsToRange.put(ise, newRange); + } else if (ends.size() == 1 && ise.isLogicallyInstantaneous() && (ise instanceof Message || EventEndHelper.PUNCTUAL_COMPOUND_EVENT_END.apply(ends.iterator().next()))) { + Iterator it = ends.iterator(); + EventEnd middle = it.next(); + + Range newRange = getNewRange(ise, middle, middle, endLocations); + sequenceEventsToRange.put(ise, newRange); + } + } + + private Range getNewRange(final ISequenceEvent event, final EventEnd start, final EventEnd end, final Map endLocations) { + Range oldRange = oldLayoutData.containsKey(event) ? oldLayoutData.get(event) : Range.emptyRange(); + int lowerBound = endLocations.containsKey(start) ? endLocations.get(start) : oldRange.getLowerBound(); + int upperBound = endLocations.containsKey(end) ? endLocations.get(end) : oldRange.getUpperBound(); + + if (event.isLogicallyInstantaneous() && start == end) { + lowerBound = lowerBound - oldRange.width() / 2; + upperBound = lowerBound + oldRange.width(); + } + + updateTimerange(upperBound); + return new Range(lowerBound, upperBound); + } + + private Map computeLifelineRanges(Map endLocations) { + final Map sequenceEventsToRange = new LinkedHashMap(); + int endOfLife = timeRange.getUpperBound() + LayoutConstants.TIME_STOP_OFFSET; + + layoutLifelinesWithoutCreation(sequenceEventsToRange); + layoutCreatedLifelines(endLocations, sequenceEventsToRange); + layoutDestructedLifelines(endLocations, sequenceEventsToRange); + layoutNonDestructedLifelines(sequenceEventsToRange, Math.max(endOfLife, LayoutConstants.LIFELINES_MIN_Y)); + + return sequenceEventsToRange; + } + + private void layoutLifelinesWithoutCreation(final Map sequenceEventsToRange) { + for (ISequenceEvent event : getLifeLinesWithoutCreation()) { + Range oldRange = oldLayoutData.get(event); + Option parentLifeline = event.getLifeline(); + if (parentLifeline.some()) { + InstanceRole instanceRole = parentLifeline.get().getInstanceRole(); + if (instanceRole != null) { + int newLBound = getLifelineMinLowerBound(instanceRole); + if (newLBound != oldRange.getLowerBound()) { + sequenceEventsToRange.put(event, new Range(newLBound, oldRange.getUpperBound())); + } + } + } + } + } + + private void layoutCreatedLifelines(Map endLocations, final Map sequenceEventsToRange) { + for (Message smep : creators.values()) { + Collection ends = iSequenceEventsToEventEnds.get(smep); + if (!ends.isEmpty()) { + Iterator it = ends.iterator(); + EventEnd first = it.next(); + if (endLocations.containsKey(first)) { + int endMove = endLocations.get(first); + int vGap = getTargetFigureMidHeight(smep); + Lifeline lep = smep.getTargetElement().getLifeline().get(); + Range oldRange = sequenceEventsToRange.containsKey(lep) ? sequenceEventsToRange.get(lep) : oldLayoutData.get(lep); + sequenceEventsToRange.put(lep, new Range(endMove + vGap, endMove + vGap + oldRange.width())); + } + } + } + } + + private void layoutDestructedLifelines(Map endLocations, final Map sequenceEventsToRange) { + for (Message smep : destructors.values()) { + Collection ends = iSequenceEventsToEventEnds.get(smep); + if (!ends.isEmpty()) { + Iterator it = ends.iterator(); + EventEnd first = it.next(); + int endMove = endLocations.get(first); + int vGap = getTargetFigureMidHeight(smep); + int newY = endMove - vGap; + Lifeline lep = ((EndOfLife) smep.getTargetElement()).getLifeline().get(); + Range oldRange = sequenceEventsToRange.containsKey(lep) ? sequenceEventsToRange.get(lep) : oldLayoutData.get(lep); + sequenceEventsToRange.put(lep, new Range(oldRange.getLowerBound(), newY)); + } + } + } + + private void layoutNonDestructedLifelines(final Map sequenceEventsToRange, int endOfLife) { + // update lifeline ranges + for (ISequenceEvent event : getLifeLinesWithoutDestruction()) { + Range currentRange = sequenceEventsToRange.containsKey(event) ? sequenceEventsToRange.get(event) : oldLayoutData.get(event); + if (currentRange.getUpperBound() != endOfLife) { + sequenceEventsToRange.put(event, new Range(currentRange.getLowerBound(), endOfLife)); + } + } + } + + private int computeLocation(final int currentY, final EventEnd end, final EventEnd endBefore, final boolean pack, Map alreadyComputedLocations) { + int location = currentY; + Collection commonIses = getCommonISequenceEvent(endBefore, end); + + if (shouldMove(commonIses)) { + int newMinY = getMinY(endBefore, end, commonIses, pack, location, alreadyComputedLocations); + if (pack) { + location = newMinY; + } else { + // try to save position + int oldPosition = getOldStablePosition(currentY, end); + + // don't minimize previous range + int rangeStableY = getRangeStablePosition(currentY, end, alreadyComputedLocations); + + // don't reduce previous delta with known/flagged predecessor + int deltaStableY = getDeltaStablePosition(currentY, end, alreadyComputedLocations); + + location = Math.max(newMinY, Math.max(oldPosition, Math.max(deltaStableY, rangeStableY))); + } + } + return location; + } + + private int getOldStablePosition(final int currentY, final EventEnd end) { + int oldPosition = currentY; + + // Should we trust GMF positions ? + if (flaggedEnds.contains(end) || toolCreatedEnds.contains(end)) { + oldPosition = eventEndOldPosition.apply(end); + } + + if (isFlagguedByRefreshExtension(end, endToISequencEvents.get(end))) { + oldPosition = eventEndOldFlaggedPosition.apply(end); + } + + return oldPosition; + } + + private int getRangeStablePosition(final int currentY, final EventEnd end, Map alreadyComputedLocations) { + int rangeStabilityPos = currentY; + Collection ises = endToISequencEvents.get(end); + for (ISequenceEvent ise : ises) { + if (!ise.isLogicallyInstantaneous()) { + SingleEventEnd see = EventEndHelper.getSingleEventEnd(end, ise.getSemanticTargetElement().get()); + if (!see.isStart() && !(ise instanceof Message && !Iterables.isEmpty(Iterables.filter(iSequenceEventsToEventEnds.get(ise), CompoundEventEnd.class)))) { + int startLocation = getStartLocation(ise, alreadyComputedLocations); + Option oldRange = oldRangeFunction.apply(ise); + + if (isFlagguedByRefreshExtension(end, Collections.singleton(ise))) { + oldRange = oldFlaggedRange.apply(ise); + } + + int width = oldRange.some() ? oldRange.get().width() : 0; + rangeStabilityPos = Math.max(rangeStabilityPos, startLocation + width); + } + } + } + return rangeStabilityPos; + } + + private boolean isFlagguedByRefreshExtension(EventEnd end, Collection ises) { + if (flaggedEnds.contains(end)) { + for (ISequenceEvent ise : ises) { + Rectangle flaggedAbsoluteBounds = new ISequenceElementQuery(ise).getFlaggedAbsoluteBounds(); + if (flaggedAbsoluteBounds.x == LayoutConstants.EXTERNAL_CHANGE_FLAG.x) { + return true; + } + } + } + return false; + } + + private boolean shouldMove(Collection commonIses) { + boolean shouldMove = true; + if (!commonIses.isEmpty()) { + ISequenceEvent commonIse = commonIses.iterator().next(); + shouldMove = !commonIse.isLogicallyInstantaneous(); + } + + return shouldMove; + } + + private int getMinY(EventEnd endBefore, EventEnd end, Collection commonIses, boolean pack, int currentLocation, Map alreadyComputedLocations) { + int genericGap = getGenericGap(endBefore, end, pack); + int minGap = genericGap; + + if (!commonIses.isEmpty()) { + int commonIseGap = getGapFromCommonSequenceEvent(end, commonIses, pack, genericGap); + minGap = commonIseGap; + } else { + boolean operands = Iterables.any(eventEndToSequenceEvents.apply(end), Predicates.instanceOf(Operand.class)); + if (operands) { + minGap = getGapBeforeOperandEnd(endBefore, end, currentLocation, genericGap, alreadyComputedLocations); + } + } + + return currentLocation + minGap; + } + + private int getGenericGap(EventEnd endBefore, EventEnd end, boolean pack) { + int beforeGap = 0; + + if (endBefore != null) { + Collection endBeforeEvents = eventEndToSequenceEvents.apply(endBefore); + beforeGap = pack ? LayoutConstants.MIN_INTER_SEQUENCE_EVENTS_VERTICAL_GAP : LayoutConstants.EXECUTION_CHILDREN_MARGIN; + + // Predecessor : Logically instantaneouse States + Iterable predStates = Iterables.filter(endBeforeEvents, State.class); + if (EventEndHelper.PUNCTUAL_COMPOUND_EVENT_END.apply(endBefore) && endBeforeEvents.size() == 1 && Iterables.size(predStates) == 1) { + State predState = Iterables.getOnlyElement(predStates); + if (predState.isLogicallyInstantaneous()) { + beforeGap += getAbstractNodeEventVerticalSize(endBefore, predState, endBeforeEvents, pack) / 2; + } + } + + if (Iterables.any(endBeforeEvents, Predicates.instanceOf(InteractionUse.class)) && endBefore instanceof SingleEventEnd && ((SingleEventEnd) endBefore).isStart()) { + beforeGap = LayoutConstants.DEFAULT_INTERACTION_USE_HEIGHT / 2; + } + + if (Iterables.any(eventEndToSequenceEvents.apply(end), Predicates.instanceOf(InteractionUse.class)) && end instanceof SingleEventEnd && !((SingleEventEnd) end).isStart()) { + beforeGap = LayoutConstants.DEFAULT_INTERACTION_USE_HEIGHT / 2; + } + + if (creators.keySet().contains(endBefore)) { + if (pack) { + beforeGap += getTargetFigureMidHeight(creators.get(endBefore)) + LayoutConstants.TIME_START_OFFSET - LayoutConstants.MIN_INTER_SEQUENCE_EVENTS_VERTICAL_GAP; + } else { + beforeGap += getTargetFigureMidHeight(creators.get(endBefore)); + } + } else if (losts.containsKey(endBefore)) { + beforeGap += losts.get(endBefore).getBounds().height / 2; + } + } else { + beforeGap = pack ? LayoutConstants.TIME_START_OFFSET : LayoutConstants.TIME_START_MIN_OFFSET; + } + + if (destructors.keySet().contains(end)) { + beforeGap += getTargetFigureMidHeight(destructors.get(end)); + } else if (losts.containsKey(end)) { + beforeGap += losts.get(end).getBounds().height / 2; + } + + // current event : Logically instantaneouse States + Collection endEvents = eventEndToSequenceEvents.apply(end); + Iterable states = Iterables.filter(endEvents, State.class); + if (EventEndHelper.PUNCTUAL_COMPOUND_EVENT_END.apply(end) && endEvents.size() == 1 && Iterables.size(states) == 1) { + State state = Iterables.getOnlyElement(states); + if (state.isLogicallyInstantaneous()) { + beforeGap += getAbstractNodeEventVerticalSize(endBefore, state, endEvents, pack) / 2; + } + } + return beforeGap; + } + + private int getGapBeforeOperandEnd(EventEnd endBefore, EventEnd end, int currentLocation, int genericGap, Map alreadyComputedLocations) { + int beforeGap = genericGap; + Iterable operands = Iterables.filter(eventEndToSequenceEvents.apply(end), Operand.class); + if (!Iterables.isEmpty(operands) && endBefore instanceof SingleEventEnd) { + if (Iterables.any(eventEndToSequenceEvents.apply(endBefore), Predicates.instanceOf(CombinedFragment.class)) && ((SingleEventEnd) endBefore).isStart()) { + beforeGap = LayoutConstants.COMBINED_FRAGMENT_TITLE_HEIGHT; + } else { + Operand op = selectEndedOperand(end, operands); + if (op != null) { + int startLoc = getStartLocation(op, alreadyComputedLocations); + int minEndLoc = startLoc + LayoutConstants.DEFAULT_OPERAND_HEIGHT; + beforeGap = Math.max(minEndLoc - currentLocation, genericGap); + } + } + } + return beforeGap; + } + + private Operand selectEndedOperand(EventEnd end, Iterable operands) { + Operand op = null; + if (end instanceof CompoundEventEnd) { + for (SingleEventEnd see : ((CompoundEventEnd) end).getEventEnds()) { + if (!see.isStart()) { + EObject semanticEvent = see.getSemanticEvent(); + for (Operand opp : operands) { + EObject eObject = opp.getSemanticTargetElement().get(); + if (semanticEvent != null && semanticEvent.equals(eObject)) { + op = opp; + } + } + } + } + } + return op; + } + + private int getStartLocation(ISequenceEvent ise, Map alreadyComputedLocations) { + Collection ends = iSequenceEventsToEventEnds.get(ise); + for (EventEnd end : ends) { + SingleEventEnd see = EventEndHelper.getSingleEventEnd(end, ise.getSemanticTargetElement().get()); + if (see.isStart() && alreadyComputedLocations.containsKey(end)) { + return alreadyComputedLocations.get(end); + } + } + return 0; + } + + private boolean layoutUnconnectedLostMessageEnd() { + boolean applied = false; + for (LostMessageEnd lme : unconnectedLostEnds) { + if (createdFromTool(lme)) { + ISequenceElementQuery query = new ISequenceElementQuery(lme); + int y = query.getFlaggedAbsoluteBounds().y; + if (y != -1) { + LayoutConstraint layoutConstraint = lme.getNotationNode().getLayoutConstraint(); + if (layoutConstraint instanceof Location) { + Rectangle bounds = lme.getProperLogicalBounds(); + ((Location) layoutConstraint).setY(y - bounds.height / 2); + applied = true; + } + } + } + } + return applied; + } + + private void initSortedEventEnds(boolean pack) { + SequenceDDiagram sequenceDDiagram = (SequenceDDiagram) sequenceDiagram.getNotationDiagram().getElement(); + graphicalOrdering.addAll(sequenceDDiagram.getGraphicalOrdering().getEventEnds()); + semanticOrdering.addAll(sequenceDDiagram.getSemanticOrdering().getEventEnds()); + } + + private void initLifelinesOldLayoutData() { + Collection lifelines = new ArrayList(); + lifelines.addAll(sequenceDiagram.getAllLifelines()); + for (ISequenceEvent ise : lifelines) { + oldLayoutData.put(ise, getOldLayoutData(ise)); + } + } + + private void lookForUnconnectedLostEnd() { + Collection allLostMessageEnds = sequenceDiagram.getAllLostMessageEnds(); + Collection discoveredLostEnds = Lists.newArrayList(); + for (Message knownMsgs : Iterables.filter(iSequenceEventsToEventEnds.keySet(), Message.class)) { + ISequenceNode sourceElement = knownMsgs.getSourceElement(); + if (sourceElement instanceof LostMessageEnd) { + discoveredLostEnds.add((LostMessageEnd) sourceElement); + } + + ISequenceNode targetElement = knownMsgs.getTargetElement(); + if (targetElement instanceof LostMessageEnd) { + discoveredLostEnds.add((LostMessageEnd) targetElement); + } + } + + Iterables.removeAll(allLostMessageEnds, discoveredLostEnds); + + unconnectedLostEnds.addAll(allLostMessageEnds); + } + + /** + * Determines the range of absolute Y locations in which the messages can be + * laid out. + * + * @param pack + * packing layout if true. + * @return + */ + protected void initTimeBounds(boolean pack) { + int minTimeBounds = getMinTimeBounds(); + int startTime = minTimeBounds; + int endTime = getMaxTimeBounds(pack, minTimeBounds) - LayoutConstants.TIME_STOP_OFFSET; + this.timeRange = new Range(startTime, endTime); + } + + private int getMaxTimeBounds(boolean pack, int minTimeBounds) { + int max = getSpecifiedMaxTimeBounds(minTimeBounds); + + if (!pack) { + Iterable lifelinesWithoutDestruction = getLifeLinesWithoutDestruction(); + + // Avoid to handle lifelines to move up for max computation. + Predicate isMaxRangeCandidate = new Predicate() { + public boolean apply(Lifeline input) { + InstanceRole irep = input.getInstanceRole(); + if (irep != null) { + return irep.getBounds().getLocation().y <= LayoutConstants.LIFELINES_START_Y; + } + return false; + } + }; + + Collection lifelinesToConsider = Lists.newArrayList(Iterables.filter(lifelinesWithoutDestruction, isMaxRangeCandidate)); + Ordering maxOrdering = Ordering.natural().onResultOf(Functions.compose(Range.upperBoundFunction(), ISequenceEvent.VERTICAL_RANGE)); + if (!lifelinesToConsider.isEmpty()) { + Lifeline lep = maxOrdering.max(lifelinesToConsider); + max = lep.getVerticalRange().getUpperBound(); + } + } + + return max; + } + + private int getSpecifiedMaxTimeBounds(int minTimeBounds) { + List allLifelines = sequenceDiagram.getAllLifelines(); + int timeBounds = LayoutConstants.LIFELINES_MIN_Y; + + for (Lifeline lep : allLifelines) { + DDiagramElement dde = (DDiagramElement) lep.getNotationNode().getElement(); + if (dde instanceof DNode && Lifeline.viewpointElementPredicate().apply(dde)) { + DNode node = (DNode) dde; + int specifiedVSize = getSpecifiedVSize(node); + int endOfLifeVsize = getSpecifiedEndOfLifeVSize(node); + timeBounds = Math.max(LayoutConstants.LIFELINES_MIN_Y, minTimeBounds + specifiedVSize - endOfLifeVsize / 2); + } + } + + return timeBounds; + } + + private int getSpecifiedEndOfLifeVSize(DNode node) { + int endOfLifeVsize = 0; + + List endOfLifes = Lists.newArrayList(Iterables.filter(node.getOwnedBorderedNodes(), new Predicate() { + public boolean apply(DNode input) { + return input.isVisible() && EndOfLife.viewpointElementPredicate().apply(input); + } + })); + + if (!endOfLifes.isEmpty()) { + endOfLifeVsize = getSpecifiedVSize(endOfLifes.iterator().next()); + } + return endOfLifeVsize; + } + + /** + * Return the specified size of the given node. + * + * @param node + * the given node. + * @return the specified height of a {@link DNode}. + */ + protected int getSpecifiedVSize(DNode node) { + return new DNodeQuery(node).getDefaultDimension().height; + } + + private int getMinTimeBounds() { + int min = 2 * LayoutConstants.LIFELINES_START_Y; + Iterable lifelinesWithoutCreation = getLifeLinesWithoutCreation(); + + // Avoid to handle lifelines to move up for min computation. + Predicate isMinRangeCandidate = new Predicate() { + public boolean apply(Lifeline input) { + InstanceRole irep = input.getInstanceRole(); + if (irep != null) { + return irep.getBounds().getLocation().y <= LayoutConstants.LIFELINES_START_Y; + } + return false; + } + }; + + Collection lifelinesToConsider = Lists.newArrayList(Iterables.filter(lifelinesWithoutCreation, isMinRangeCandidate)); + if (!lifelinesToConsider.isEmpty()) { + Lifeline lep = heightOrdering.max(lifelinesToConsider); + min = LayoutConstants.LIFELINES_START_Y + instanceRoleHeight.apply(lep); + } + return min; + } + + /** + * Get the middle height of the given message's targeted figure. + * + * @param mover + * the given message. + * @return the middle height. + */ + protected int getTargetFigureMidHeight(Message mover) { + int midHeight = 0; + if (mover != null && mover.getTargetElement() != null) { + midHeight = mover.getTargetElement().getBounds().height / 2; + } + return midHeight; + } + + /** + * Increase the time range to the given upperBound. If the current upper + * bounds is bigger than the parameter, it does nothing. + * + * @param upperBound + * the new minimum upperBound. + */ + protected void updateTimerange(int upperBound) { + if (upperBound > timeRange.getUpperBound()) { + timeRange = new Range(timeRange.getLowerBound(), upperBound); + } + } + + /** + * Return the minimum valid lower start time on the {@link Lifeline} of the + * given {@link InstanceRole}. + * + * @param irep + * the current {@link InstanceRole}. + * @return the lifeline minimum valid lower bound. + */ + protected int getLifelineMinLowerBound(final InstanceRole irep) { + int vGap = LayoutConstants.LIFELINES_START_Y; + vGap += irep.getBounds().height; + return vGap; + } + + /** + * Register event old and init context (ends, old layout data, previous + * bounds flag, creators, destructors, ...). + */ + protected void registerEventEnds() { + for (EventEnd end : Lists.newArrayList(semanticOrdering)) { + registerEventEnd(end); + } + Collections.sort(flaggedEnds, Ordering.natural().onResultOf(eventEndOldFlaggedPosition)); + } + + private void registerEventEnd(EventEnd end) { + Collection semanticEvents = EventEndHelper.getSemanticEvents(end); + Collection eventParts = Sets.newLinkedHashSet(); + for (EObject semanticEvent : semanticEvents) { + eventParts.addAll(ISequenceElementAccessor.getEventsForSemanticElement(sequenceDiagram, semanticEvent)); + } + + // ISequenceEvent has not been created + if (eventParts.isEmpty()) { + Collection ddes = Sets.newLinkedHashSet(); + for (EObject semanticEvent : semanticEvents) { + ddes.addAll(ISequenceElementAccessor.getDiagramElementsForSemanticElement(sequenceDiagram, semanticEvent)); + } + + // No ISequenceEvent has been created but DDiagramElement exists, + // gmf refresh did not occurs, abort + // current layout. + if (!ddes.isEmpty()) { + semanticOrdering.clear(); + graphicalOrdering.clear(); + } + } + + boolean flagged = false; + boolean toolCreated = false; + boolean toolSemanticCreated = false; + boolean lost = false; + for (ISequenceEvent ise : eventParts) { + Range oldData = getOldLayoutData(ise); + oldLayoutData.put(ise, oldData); + endToISequencEvents.put(end, ise); + iSequenceEventsToEventEnds.put(ise, end); + + ISequenceElementQuery query = new ISequenceElementQuery(ise); + if (query.hasAbsoluteBoundsFlag()) { + Rectangle flaggedAbsoluteBounds = query.getFlaggedAbsoluteBounds(); + if (LayoutConstants.TOOL_CREATION_FLAG.equals(flaggedAbsoluteBounds)) { + toolCreated = true; + } else if (LayoutConstants.TOOL_CREATION_FLAG_FROM_SEMANTIC.equals(flaggedAbsoluteBounds)) { + toolSemanticCreated = true; + } else { + if (flaggedAbsoluteBounds.height == -1) { + // Correct auto-size + flaggedAbsoluteBounds.height = 0; + } + oldFlaggedLayoutData.put(ise, flaggedAbsoluteBounds); + flagged = true; + } + } + + if (ise instanceof Message) { + Message smep = (Message) ise; + ISequenceNode targetElement = smep.getTargetElement(); + if (targetElement instanceof InstanceRole) { + creators.put(end, smep); + } else if (targetElement instanceof EndOfLife) { + destructors.put(end, smep); + } else if (targetElement instanceof LostMessageEnd) { + lost = true; + losts.put(end, (LostMessageEnd) targetElement); + } + + ISequenceNode sourceElement = smep.getSourceElement(); + if (sourceElement instanceof LostMessageEnd) { + lost = true; + losts.put(end, (LostMessageEnd) sourceElement); + } + } + } + + if (flagged && !toolCreated) { + flaggedEnds.add(end); + } else if (isSafeToolCreation(end)) { + if (toolCreated) { + toolCreatedEnds.add(end); + } else if (toolSemanticCreated && ((end instanceof SingleEventEnd && ((SingleEventEnd) end).isStart()) || lost)) { + toolCreatedEnds.add(end); + } + } + } + + private boolean isSafeToolCreation(EventEnd end) { + boolean safe = !(end instanceof CompoundEventEnd); + safe = safe || EventEndHelper.PUNCTUAL_COMPOUND_EVENT_END.apply(end); + for (Message msg : Iterables.filter(endToISequencEvents.get(end), Message.class)) { + safe = safe || msg.getSourceElement() instanceof LostMessageEnd || msg.getTargetElement() instanceof LostMessageEnd; + } + + return safe; + } + + /** + * Get the common {@link ISequenceEvent} between the given ends. + * + * @param end1 + * a first {@link EventEnd} + * @param end2 + * a second {@link EventEnd} + * @return the common events between given ends + */ + protected Collection getCommonISequenceEvent(EventEnd end1, EventEnd end2) { + if (end1 == null || end2 == null) { + return Collections. emptyList(); + } + Collection ises1 = endToISequencEvents.get(end1); + Collection ises2 = endToISequencEvents.get(end2); + Collection commonIses = Lists.newArrayList(ises2); + Iterables.retainAll(commonIses, ises1); + return commonIses; + } + + @Override + protected Function getOldPosition() { + return eventEndOldPosition; + } + + @Override + protected Function getOldFlaggedPosition() { + return eventEndOldFlaggedPosition; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/SequenceDDiagramSpec.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/SequenceDDiagramSpec.java new file mode 100644 index 0000000000..568fc955cc --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/SequenceDDiagramSpec.java @@ -0,0 +1,286 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.metamodel; + +import java.util.Collection; + +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.EcoreEList; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DDiagramElementContainer; +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.DNodeListElement; +import org.eclipse.sirius.DRepresentationElement; +import org.eclipse.sirius.SiriusPackage; +import org.eclipse.sirius.business.api.dialect.DialectManager; +import org.eclipse.sirius.business.internal.metamodel.helper.DSemanticDiagramHelper; +import org.eclipse.sirius.business.internal.metamodel.operations.DDiagramSpecOperations; +import org.eclipse.sirius.business.internal.query.DDiagramInternalQuery; +import org.eclipse.sirius.business.internal.query.DModelElementInternalQuery; +import org.eclipse.sirius.description.ContainerMapping; +import org.eclipse.sirius.description.DAnnotation; +import org.eclipse.sirius.description.DragAndDropTargetDescription; +import org.eclipse.sirius.description.EdgeMapping; +import org.eclipse.sirius.description.NodeMapping; +import org.eclipse.sirius.description.concern.ConcernDescription; +import org.eclipse.sirius.description.filter.FilterDescription; +import org.eclipse.sirius.diagram.sequence.impl.SequenceDDiagramImpl; + +/** + * Implementation of SequenceDDiagram. + * + * @author pcdavid + */ +public class SequenceDDiagramSpec extends SequenceDDiagramImpl { + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.impl.DRepresentationImpl#getOwnedRepresentationElements() + */ + @Override + public EList getOwnedRepresentationElements() { + final Collection result = getOwnedDiagramElements(); + return new EcoreEList.UnmodifiableEList(eInternalContainer(), SiriusPackage.eINSTANCE.getDRepresentation_OwnedRepresentationElements(), result.size(), + result.toArray()); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.impl.DRepresentationImpl#getRepresentationElements() + */ + @Override + public EList getRepresentationElements() { + final Collection result = getDiagramElements(); + return new EcoreEList.UnmodifiableEList(eInternalContainer(), SiriusPackage.eINSTANCE.getDRepresentation_RepresentationElements(), result.size(), result.toArray()); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.impl.DDiagramImpl#getDiagramElements() + */ + @Override + public EList getDiagramElements() { + final Collection result = new DDiagramInternalQuery(this).getDiagramElements(); + return new EcoreEList.UnmodifiableEList(eInternalContainer(), SiriusPackage.eINSTANCE.getDDiagram_DiagramElements(), result.size(), result.toArray()); + } + + /** + * Create the contents of the viewpoint. + * + * @see org.eclipse.sirius.impl.DDiagramImpl#createContents() + */ + @Override + public void createContents() { + refresh(); + } + + /** + * Create the contents of the viewpoint with the specified root element. + * + * @param rootElement + * the root element. + * @see org.eclipse.sirius.impl.DDiagramImpl#createContents(org.eclipse.emf.ecore.EObject) + */ + @Override + public void createContents(final EObject rootElement) { + refresh(); + } + + /** + * Update the content of the viewpoint. + * + * @see org.eclipse.sirius.impl.DDiagramImpl#updateContent() + */ + @Override + public void updateContent() { + refresh(); + } + + /** + * Clean the viewpoint, delete all elements that are obsolete. + * + * @see org.eclipse.sirius.impl.DDiagramImpl#clean() + */ + @Override + public void clean() { + DSemanticDiagramHelper.clean(this); + } + + /** + * Refresh the viewpoint. + * + * @see org.eclipse.sirius.impl.DDiagramImpl#refresh() + */ + @Override + public void refresh() { + DialectManager.INSTANCE.refresh(this, new NullProgressMonitor()); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.impl.DSemanticDiagramImpl#getRootContent() + */ + @Override + public EObject getRootContent() { + return DSemanticDiagramHelper.getRootContent(this); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.impl.DRepresentationImpl#getDAnnotation(String) + */ + @Override + public DAnnotation getDAnnotation(String source) { + return new DModelElementInternalQuery(this).getDAnnotation(source); + } + + /* + * Behavior inherited from DDiagramSpec + */ + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.impl.DDiagramImpl#getEdges() + */ + @Override + public EList getEdges() { + final Collection result = new DDiagramInternalQuery(this).getEdges(); + return new EcoreEList.UnmodifiableEList(eInternalContainer(), SiriusPackage.eINSTANCE.getDDiagram_Edges(), result.size(), result.toArray()); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.impl.DDiagramImpl#getNodes() + */ + @Override + public EList getNodes() { + final Collection result = new DDiagramInternalQuery(this).getNodes(); + return new EcoreEList.UnmodifiableEList(eInternalContainer(), SiriusPackage.eINSTANCE.getDDiagram_Nodes(), result.size(), result.toArray()); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.impl.DDiagramImpl#getNodeListElements() + */ + @Override + public EList getNodeListElements() { + final Collection result = new DDiagramInternalQuery(this).getNodeListElements(); + return new EcoreEList.UnmodifiableEList(eInternalContainer(), SiriusPackage.eINSTANCE.getDDiagram_NodeListElements(), result.size(), result.toArray()); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.impl.DDiagramImpl#getContainers() + */ + @Override + public EList getContainers() { + final Collection result = new DDiagramInternalQuery(this).getContainers(); + return new EcoreEList.UnmodifiableEList(eInternalContainer(), SiriusPackage.eINSTANCE.getDDiagram_Containers(), result.size(), result.toArray()); + + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.impl.DDiagramImpl#getNodesFromMapping(org.eclipse.sirius.description.NodeMapping) + */ + @Override + public EList getNodesFromMapping(final NodeMapping mapping) { + return DDiagramSpecOperations.getNodesFromMapping(this, mapping); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.impl.DDiagramImpl#getEdgesFromMapping(org.eclipse.sirius.description.EdgeMapping) + */ + @Override + public EList getEdgesFromMapping(final EdgeMapping mapping) { + return DDiagramSpecOperations.getEdgesFromMapping(this, mapping); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.impl.DDiagramImpl#getContainersFromMapping(org.eclipse.sirius.description.ContainerMapping) + */ + @Override + public EList getContainersFromMapping(final ContainerMapping mapping) { + return DDiagramSpecOperations.getContainersFromMapping(this, mapping); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.impl.DDiagramImpl#validate() + */ + @Override + public boolean validate() { + return DDiagramSpecOperations.validate(this); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.impl.DDiagramImpl#getAllFilters() + */ + @Override + public EList getAllFilters() { + final Collection result = new DDiagramInternalQuery(this).getAllFilters(); + return new EcoreEList.UnmodifiableEList(eInternalContainer(), SiriusPackage.eINSTANCE.getDDiagram_AllFilters(), result.size(), result.toArray()); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.impl.DDiagramImpl#setCurrentConcern(org.eclipse.sirius.description.concern.ConcernDescription) + */ + @Override + public void setCurrentConcern(final ConcernDescription newCurrentConcern) { + super.setCurrentConcern(newCurrentConcern); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.impl.DDiagramImpl#getDragAndDropDescription() + */ + @Override + public DragAndDropTargetDescription getDragAndDropDescription() { + return new DDiagramInternalQuery(this).getDragAndDropDescription(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.impl.DDiagramImpl#findSiriusElements(org.eclipse.emf.ecore.EObject, + * org.eclipse.emf.ecore.EClass) + */ + @Override + public EList findDiagramElements(final EObject semanticElement, final EClass type) { + return DDiagramSpecOperations.findDiagramElements(this, semanticElement, type); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/BasicMessageMappingSpec.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/BasicMessageMappingSpec.java new file mode 100644 index 0000000000..205897c7db --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/BasicMessageMappingSpec.java @@ -0,0 +1,153 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.metamodel.description; + +import org.eclipse.emf.common.util.BasicEList; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; + +import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.DMappingBased; +import org.eclipse.sirius.EdgeStyle; +import org.eclipse.sirius.EdgeTarget; +import org.eclipse.sirius.SiriusPlugin; +import org.eclipse.sirius.business.api.query.EObjectQuery; +import org.eclipse.sirius.business.internal.metamodel.description.operations.SiriusElementMappingSpecOperations; +import org.eclipse.sirius.business.internal.metamodel.helper.EdgeMappingHelper; +import org.eclipse.sirius.business.internal.metamodel.helper.MappingHelper; +import org.eclipse.sirius.description.DiagramElementMapping; +import org.eclipse.sirius.diagram.sequence.description.impl.BasicMessageMappingImpl; + +/** + * Implementation of BasicMessageMapping. + * + * @author pcdavid + */ +public class BasicMessageMappingSpec extends BasicMessageMappingImpl { + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#createEdge(org.eclipse.sirius.EdgeTarget, + * org.eclipse.sirius.EdgeTarget, org.eclipse.emf.ecore.EObject) + */ + @Override + public DEdge createEdge(final EdgeTarget source, final EdgeTarget target, final EObject semanticTarget) { + return createEdge(source, target, null, semanticTarget); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#createEdge(org.eclipse.sirius.EdgeTarget, + * org.eclipse.sirius.EdgeTarget, org.eclipse.emf.ecore.EObject, + * org.eclipse.emf.ecore.EObject) + */ + @Override + public DEdge createEdge(final EdgeTarget source, final EdgeTarget target, final EObject container, final EObject semanticTarget) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(container); + return new EdgeMappingHelper(interpreter).createEdge(this, source, target, container, semanticTarget); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#getBestStyle(org.eclipse.emf.ecore.EObject, + * org.eclipse.emf.ecore.EObject, org.eclipse.emf.ecore.EObject) + */ + @Override + public EdgeStyle getBestStyle(final EObject modelElement, final EObject viewVariable, final EObject containerVariable) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(modelElement); + return (EdgeStyle) new MappingHelper(interpreter).getBestStyle(this, modelElement, viewVariable, containerVariable, new EObjectQuery(containerVariable).getParentDiagram().get()); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#updateEdge(org.eclipse.sirius.DEdge) + */ + @Override + public void updateEdge(final DEdge dEdge) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(dEdge); + new EdgeMappingHelper(interpreter).updateEdge(this, dEdge); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#getEdgeTargetCandidates(org.eclipse.emf.ecore.EObject, + * org.eclipse.sirius.DDiagram) + */ + @Override + public EList getEdgeTargetCandidates(final EObject semanticOrigin, final DDiagram diagram) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(semanticOrigin); + return new EdgeMappingHelper(interpreter).getEdgeTargetCandidates(this, semanticOrigin, diagram); + + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#getEdgeSourceCandidates(org.eclipse.emf.ecore.EObject, + * org.eclipse.sirius.DDiagram) + */ + @Override + public EList getEdgeSourceCandidates(final EObject semanticOrigin, final DDiagram diagram) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(semanticOrigin); + return new EdgeMappingHelper(interpreter).getEdgeSourceCandidates(this, semanticOrigin, diagram); + } + + /* + * Behavior inherited from DiagramElementMapping + */ + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.DiagramElementMappingImpl#checkPrecondition(org.eclipse.emf.ecore.EObject, + * org.eclipse.emf.ecore.EObject, org.eclipse.emf.ecore.EObject) + */ + @Override + public boolean checkPrecondition(final EObject modelElement, final EObject container, final EObject containerView) { + return SiriusElementMappingSpecOperations.checkPrecondition(this, modelElement, container, containerView); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.DiagramElementMappingImpl#getAllMappings() + */ + @Override + public EList getAllMappings() { + return new BasicEList.UnmodifiableEList(0, new Object[0]); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.DiagramElementMappingImpl#isFrom(org.eclipse.sirius.DMappingBased) + */ + @Override + public boolean isFrom(final DMappingBased element) { + return SiriusElementMappingSpecOperations.isFrom(this, element); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#toString() + */ + @Override + public String toString() { + return getClass().getName() + " " + getName(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/CombinedFragmentMappingSpec.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/CombinedFragmentMappingSpec.java new file mode 100644 index 0000000000..f7593f0e36 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/CombinedFragmentMappingSpec.java @@ -0,0 +1,209 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.metamodel.description; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.EcoreEList; + +import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; +import org.eclipse.sirius.common.tools.api.util.EObjectCouple; +import org.eclipse.sirius.ContainerStyle; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DDiagramElementContainer; +import org.eclipse.sirius.DMappingBased; +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.SiriusPlugin; +import org.eclipse.sirius.business.api.query.EObjectQuery; +import org.eclipse.sirius.business.internal.metamodel.description.extensions.IContainerMappingExt; +import org.eclipse.sirius.business.internal.metamodel.description.operations.AbstractNodeMappingSpecOperations; +import org.eclipse.sirius.business.internal.metamodel.description.operations.SiriusElementMappingSpecOperations; +import org.eclipse.sirius.business.internal.metamodel.helper.ContainerMappingHelper; +import org.eclipse.sirius.business.internal.metamodel.helper.MappingHelper; +import org.eclipse.sirius.description.ContainerMapping; +import org.eclipse.sirius.description.DescriptionPackage; +import org.eclipse.sirius.description.DiagramElementMapping; +import org.eclipse.sirius.description.NodeMapping; +import org.eclipse.sirius.diagram.sequence.description.impl.CombinedFragmentMappingImpl; + +/** + * Implementation of CombinedFragmentMapping. + * + * @author pcdavid + */ +public class CombinedFragmentMappingSpec extends CombinedFragmentMappingImpl implements IContainerMappingExt { + + private final Map> viewContainerDone = new HashMap>(); + + private final Map> candidatesCache = new WeakHashMap>(); + + /** + * {@inheritDoc} + */ + public Map> getViewContainerDone() { + return viewContainerDone; + } + + /** + * {@inheritDoc} + */ + public Map> getCandidatesCache() { + return candidatesCache; + } + + /** + * {@inheritDoc} + */ + @Override + public EList getAllNodeMappings() { + final Collection result = ContainerMappingHelper.getAllNodeMappings(this); + return new EcoreEList.UnmodifiableEList(eInternalContainer(), DescriptionPackage.eINSTANCE.getContainerMapping_AllNodeMappings(), result.size(), result.toArray()); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getAllContainerMappings() { + final Collection result = ContainerMappingHelper.getAllContainerMappings(this); + return new EcoreEList.UnmodifiableEList(eInternalContainer(), DescriptionPackage.eINSTANCE.getContainerMapping_AllContainerMappings(), result.size(), result.toArray()); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getNodesCandidates(final EObject semanticOrigin, final EObject container, final EObject containerView) { + return ContainerMappingHelper.getNodesCandidates(this, semanticOrigin, container, containerView); + } + + /** + * {@inheritDoc} + */ + @Override + public void clearDNodesDone() { + ContainerMappingHelper.clearDNodesDone(this); + } + + /** + * {@inheritDoc} + */ + @Override + public EList findDNodeFromEObject(final EObject object) { + return ContainerMappingHelper.findDNodeFromEObject(this, object); + } + + /** + * {@inheritDoc} + */ + @Override + public DDiagramElementContainer createContainer(final EObject modelElement, final EObject container, final DDiagram viewPoint) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(container); + return new ContainerMappingHelper(interpreter).createContainer(this, modelElement, container, viewPoint); + } + + /** + * {@inheritDoc} + */ + @Override + public void updateContainer(final DDiagramElementContainer container) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(container); + new ContainerMappingHelper(interpreter).updateContainer(this, container); + } + + /** + * {@inheritDoc} + */ + @Override + public void addDoneNode(final DSemanticDecorator node) { + ContainerMappingHelper.addDoneNode(this, node); + } + + /** + * {@inheritDoc} + */ + @Override + public ContainerStyle getBestStyle(final EObject modelElement, final EObject viewVariable, final EObject containerVariable) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(modelElement); + return (ContainerStyle) new MappingHelper(interpreter).getBestStyle(this, modelElement, viewVariable, containerVariable, new EObjectQuery(containerVariable).getParentDiagram().get()); + } + + /* + * Here we add the behavior we should inherit from AbstractNodeMapping + */ + + /** + * {@inheritDoc} + */ + public void createBorderingNodes(EObject modelElement, DDiagramElement vpElement, @SuppressWarnings("rawtypes") + Collection filterSemantic, DDiagram viewPoint) { + AbstractNodeMappingSpecOperations.createBorderingNodes(this, modelElement, vpElement, filterSemantic, viewPoint); + } + + /* + * Behavior inherited from DiagramElementMapping + */ + + /** + * {@inheritDoc} + */ + @Override + public boolean checkPrecondition(final EObject modelElement, final EObject container, final EObject containerView) { + return SiriusElementMappingSpecOperations.checkPrecondition(this, modelElement, container, containerView); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getAllMappings() { + return ContainerMappingHelper.getAllMappings(this); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isFrom(final DMappingBased element) { + return SiriusElementMappingSpecOperations.isFrom(this, element); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return new StringBuffer(getClass().getName()).append(" ").append(getName()).toString(); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getAllBorderedNodeMappings() { + return AbstractNodeMappingSpecOperations.getAllBorderedNodeMappings(this); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getDNodesDone() { + return ContainerMappingHelper.getDNodesDone(this); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/CreationMessageMappingSpec.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/CreationMessageMappingSpec.java new file mode 100644 index 0000000000..503ac9cb37 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/CreationMessageMappingSpec.java @@ -0,0 +1,154 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.metamodel.description; + +import org.eclipse.emf.common.util.BasicEList; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; + +import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.DMappingBased; +import org.eclipse.sirius.EdgeStyle; +import org.eclipse.sirius.EdgeTarget; +import org.eclipse.sirius.SiriusPlugin; +import org.eclipse.sirius.business.api.query.EObjectQuery; +import org.eclipse.sirius.business.internal.metamodel.description.operations.SiriusElementMappingSpecOperations; +import org.eclipse.sirius.business.internal.metamodel.helper.EdgeMappingHelper; +import org.eclipse.sirius.business.internal.metamodel.helper.MappingHelper; +import org.eclipse.sirius.description.DiagramElementMapping; +import org.eclipse.sirius.diagram.sequence.description.impl.CreationMessageMappingImpl; + +/** + * Implementation of CreationMessageMapping. + * + * @author pcdavid + */ +public class CreationMessageMappingSpec extends CreationMessageMappingImpl { + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#createEdge(org.eclipse.sirius.EdgeTarget, + * org.eclipse.sirius.EdgeTarget, org.eclipse.emf.ecore.EObject) + */ + @Override + public DEdge createEdge(final EdgeTarget source, final EdgeTarget target, final EObject semanticTarget) { + return createEdge(source, target, null, semanticTarget); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#createEdge(org.eclipse.sirius.EdgeTarget, + * org.eclipse.sirius.EdgeTarget, org.eclipse.emf.ecore.EObject, + * org.eclipse.emf.ecore.EObject) + */ + @Override + public DEdge createEdge(final EdgeTarget source, final EdgeTarget target, final EObject container, final EObject semanticTarget) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(container); + return new EdgeMappingHelper(interpreter).createEdge(this, source, target, container, semanticTarget); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#getBestStyle(org.eclipse.emf.ecore.EObject, + * org.eclipse.emf.ecore.EObject, org.eclipse.emf.ecore.EObject) + */ + @Override + public EdgeStyle getBestStyle(final EObject modelElement, final EObject viewVariable, final EObject containerVariable) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(modelElement); + return (EdgeStyle) new MappingHelper(interpreter).getBestStyle(this, modelElement, viewVariable, containerVariable, new EObjectQuery(containerVariable).getParentDiagram().get()); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#updateEdge(org.eclipse.sirius.DEdge) + */ + @Override + public void updateEdge(final DEdge dEdge) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(dEdge); + new EdgeMappingHelper(interpreter).updateEdge(this, dEdge); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#getEdgeTargetCandidates(org.eclipse.emf.ecore.EObject, + * org.eclipse.sirius.DDiagram) + */ + @Override + public EList getEdgeTargetCandidates(final EObject semanticOrigin, final DDiagram diagram) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(semanticOrigin); + return new EdgeMappingHelper(interpreter).getEdgeTargetCandidates(this, semanticOrigin, diagram); + + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#getEdgeSourceCandidates(org.eclipse.emf.ecore.EObject, + * org.eclipse.sirius.DDiagram) + */ + @Override + public EList getEdgeSourceCandidates(final EObject semanticOrigin, final DDiagram diagram) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(semanticOrigin); + return new EdgeMappingHelper(interpreter).getEdgeSourceCandidates(this, semanticOrigin, diagram); + } + + /* + * Behavior inherited from DiagramElementMapping + */ + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.DiagramElementMappingImpl#checkPrecondition(org.eclipse.emf.ecore.EObject, + * org.eclipse.emf.ecore.EObject, org.eclipse.emf.ecore.EObject) + */ + @Override + public boolean checkPrecondition(final EObject modelElement, final EObject container, final EObject containerView) { + return SiriusElementMappingSpecOperations.checkPrecondition(this, modelElement, container, containerView); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.DiagramElementMappingImpl#getAllMappings() + */ + @Override + public EList getAllMappings() { + return new BasicEList.UnmodifiableEList(0, new Object[0]); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.DiagramElementMappingImpl#isFrom(org.eclipse.sirius.DMappingBased) + */ + @Override + public boolean isFrom(final DMappingBased element) { + return SiriusElementMappingSpecOperations.isFrom(this, element); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#toString() + */ + @Override + public String toString() { + return getClass().getName() + " " + getName(); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/DestructionMessageMappingSpec.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/DestructionMessageMappingSpec.java new file mode 100644 index 0000000000..0a6ee8faa5 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/DestructionMessageMappingSpec.java @@ -0,0 +1,154 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.metamodel.description; + +import org.eclipse.emf.common.util.BasicEList; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; + +import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.DMappingBased; +import org.eclipse.sirius.EdgeStyle; +import org.eclipse.sirius.EdgeTarget; +import org.eclipse.sirius.SiriusPlugin; +import org.eclipse.sirius.business.api.query.EObjectQuery; +import org.eclipse.sirius.business.internal.metamodel.description.operations.SiriusElementMappingSpecOperations; +import org.eclipse.sirius.business.internal.metamodel.helper.EdgeMappingHelper; +import org.eclipse.sirius.business.internal.metamodel.helper.MappingHelper; +import org.eclipse.sirius.description.DiagramElementMapping; +import org.eclipse.sirius.diagram.sequence.description.impl.DestructionMessageMappingImpl; + +/** + * Implementation of DestructionMessageMapping. + * + * @author pcdavid + */ +public class DestructionMessageMappingSpec extends DestructionMessageMappingImpl { + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#createEdge(org.eclipse.sirius.EdgeTarget, + * org.eclipse.sirius.EdgeTarget, org.eclipse.emf.ecore.EObject) + */ + @Override + public DEdge createEdge(final EdgeTarget source, final EdgeTarget target, final EObject semanticTarget) { + return createEdge(source, target, null, semanticTarget); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#createEdge(org.eclipse.sirius.EdgeTarget, + * org.eclipse.sirius.EdgeTarget, org.eclipse.emf.ecore.EObject, + * org.eclipse.emf.ecore.EObject) + */ + @Override + public DEdge createEdge(final EdgeTarget source, final EdgeTarget target, final EObject container, final EObject semanticTarget) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(container); + return new EdgeMappingHelper(interpreter).createEdge(this, source, target, container, semanticTarget); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#getBestStyle(org.eclipse.emf.ecore.EObject, + * org.eclipse.emf.ecore.EObject, org.eclipse.emf.ecore.EObject) + */ + @Override + public EdgeStyle getBestStyle(final EObject modelElement, final EObject viewVariable, final EObject containerVariable) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(modelElement); + return (EdgeStyle) new MappingHelper(interpreter).getBestStyle(this, modelElement, viewVariable, containerVariable, new EObjectQuery(containerVariable).getParentDiagram().get()); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#updateEdge(org.eclipse.sirius.DEdge) + */ + @Override + public void updateEdge(final DEdge dEdge) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(dEdge); + new EdgeMappingHelper(interpreter).updateEdge(this, dEdge); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#getEdgeTargetCandidates(org.eclipse.emf.ecore.EObject, + * org.eclipse.sirius.DDiagram) + */ + @Override + public EList getEdgeTargetCandidates(final EObject semanticOrigin, final DDiagram diagram) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(semanticOrigin); + return new EdgeMappingHelper(interpreter).getEdgeTargetCandidates(this, semanticOrigin, diagram); + + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#getEdgeSourceCandidates(org.eclipse.emf.ecore.EObject, + * org.eclipse.sirius.DDiagram) + */ + @Override + public EList getEdgeSourceCandidates(final EObject semanticOrigin, final DDiagram diagram) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(semanticOrigin); + return new EdgeMappingHelper(interpreter).getEdgeSourceCandidates(this, semanticOrigin, diagram); + } + + /* + * Behavior inherited from DiagramElementMapping + */ + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.DiagramElementMappingImpl#checkPrecondition(org.eclipse.emf.ecore.EObject, + * org.eclipse.emf.ecore.EObject, org.eclipse.emf.ecore.EObject) + */ + @Override + public boolean checkPrecondition(final EObject modelElement, final EObject container, final EObject containerView) { + return SiriusElementMappingSpecOperations.checkPrecondition(this, modelElement, container, containerView); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.DiagramElementMappingImpl#getAllMappings() + */ + @Override + public EList getAllMappings() { + return new BasicEList.UnmodifiableEList(0, new Object[0]); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.DiagramElementMappingImpl#isFrom(org.eclipse.sirius.DMappingBased) + */ + @Override + public boolean isFrom(final DMappingBased element) { + return SiriusElementMappingSpecOperations.isFrom(this, element); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#toString() + */ + @Override + public String toString() { + return getClass().getName() + " " + getName(); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/EndOfLifeMappingSpec.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/EndOfLifeMappingSpec.java new file mode 100644 index 0000000000..80d0d4d325 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/EndOfLifeMappingSpec.java @@ -0,0 +1,211 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.metamodel.description; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; + +import org.eclipse.emf.common.util.BasicEList; +import org.eclipse.emf.common.util.BasicEList.UnmodifiableEList; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; + +import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; +import org.eclipse.sirius.common.tools.api.util.EObjectCouple; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DMappingBased; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.DNodeListElement; +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.NodeStyle; +import org.eclipse.sirius.SiriusPlugin; +import org.eclipse.sirius.business.api.query.EObjectQuery; +import org.eclipse.sirius.business.internal.metamodel.description.extensions.INodeMappingExt; +import org.eclipse.sirius.business.internal.metamodel.description.operations.AbstractNodeMappingSpecOperations; +import org.eclipse.sirius.business.internal.metamodel.description.operations.SiriusElementMappingSpecOperations; +import org.eclipse.sirius.business.internal.metamodel.helper.MappingHelper; +import org.eclipse.sirius.business.internal.metamodel.helper.NodeMappingHelper; +import org.eclipse.sirius.description.DiagramElementMapping; +import org.eclipse.sirius.description.NodeMapping; +import org.eclipse.sirius.diagram.sequence.description.impl.EndOfLifeMappingImpl; + +/** + * Implementation of EndOfLifeMapping. + * + * @author pcdavid + */ +public class EndOfLifeMappingSpec extends EndOfLifeMappingImpl implements INodeMappingExt { + + private final Map> viewNodesDone = new HashMap>(); + + private final Map> candidatesCache = new WeakHashMap>(); + + /** + * {@inheritDoc} + */ + public Map> getViewNodesDone() { + return viewNodesDone; + } + + /** + * {@inheritDoc} + */ + public Map> getCandidatesCache() { + return candidatesCache; + } + + /** + * {@inheritDoc} + */ + @Override + public EList getNodesCandidates(final EObject semanticOrigin, final EObject container) { + return NodeMappingHelper.getNodesCandidates(this, semanticOrigin, container); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getNodesCandidates(final EObject semanticOrigin, final EObject container, final EObject containerView) { + return NodeMappingHelper.getNodesCandidates(this, semanticOrigin, container, containerView); + } + + /** + * {@inheritDoc} + */ + @Override + public DNode createNode(final EObject modelElement, final EObject container, final DDiagram diagram) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(modelElement); + return new NodeMappingHelper(interpreter).createNode(this, modelElement, container, diagram); + } + + /** + * {@inheritDoc} + */ + @Override + public void updateNode(final DNode node) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(node); + new NodeMappingHelper(interpreter).updateNode(this, node); + } + + /** + * {@inheritDoc} + */ + @Override + public NodeStyle getBestStyle(final EObject modelElement, final EObject viewVariable, final EObject containerVariable) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(modelElement); + return (NodeStyle) new MappingHelper(interpreter).getBestStyle(this, modelElement, viewVariable, containerVariable, new EObjectQuery(containerVariable).getParentDiagram().get()); + } + + /** + * {@inheritDoc} + */ + @Override + public DNodeListElement createListElement(final EObject modelElement, final DDiagram diagram) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(modelElement); + return new NodeMappingHelper(interpreter).createListElement(this, modelElement, diagram); + } + + /** + * {@inheritDoc} + */ + @Override + public void updateListElement(final DNodeListElement listElement) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(listElement); + new NodeMappingHelper(interpreter).updateListElement(this, listElement); + } + + /** + * {@inheritDoc} + */ + @Override + public void clearDNodesDone() { + NodeMappingHelper.clearDNodesDone(this); + } + + /** + * {@inheritDoc} + */ + @Override + public EList findDNodeFromEObject(final EObject object) { + return NodeMappingHelper.findDNodeFromEObject(this, object); + } + + /* + * Here we add the behavior we should inherit from AbstractNodeMapping + */ + + /** + * {@inheritDoc} + */ + public void createBorderingNodes(final EObject modelElement, final DDiagramElement vpElement, @SuppressWarnings("rawtypes") + final Collection filterSemantic, final DDiagram viewPoint) { + AbstractNodeMappingSpecOperations.createBorderingNodes(this, modelElement, vpElement, filterSemantic, viewPoint); + } + + /* + * Behavior inherited from DiagramElementMapping + */ + + /** + * {@inheritDoc} + */ + @Override + public boolean checkPrecondition(final EObject modelElement, final EObject container, final EObject containerView) { + return SiriusElementMappingSpecOperations.checkPrecondition(this, modelElement, container, containerView); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getAllMappings() { + final BasicEList allMappings = new BasicEList(); + allMappings.addAll(this.getAllBorderedNodeMappings()); + return new UnmodifiableEList(allMappings.size(), allMappings.toArray()); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isFrom(final DMappingBased element) { + return SiriusElementMappingSpecOperations.isFrom(this, element); + } + + /** + * {@inheritDoc} + */ + @Override + public void addDoneNode(final DSemanticDecorator node) { + NodeMappingHelper.addDoneNode(this, node); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return new StringBuffer(getClass().getName()).append(" ").append(getName()).toString(); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getAllBorderedNodeMappings() { + return AbstractNodeMappingSpecOperations.getAllBorderedNodeMappings(this); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/ExecutionMappingSpec.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/ExecutionMappingSpec.java new file mode 100644 index 0000000000..876d42f4f6 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/ExecutionMappingSpec.java @@ -0,0 +1,211 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.metamodel.description; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; + +import org.eclipse.emf.common.util.BasicEList; +import org.eclipse.emf.common.util.BasicEList.UnmodifiableEList; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; + +import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; +import org.eclipse.sirius.common.tools.api.util.EObjectCouple; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DMappingBased; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.DNodeListElement; +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.NodeStyle; +import org.eclipse.sirius.SiriusPlugin; +import org.eclipse.sirius.business.api.query.EObjectQuery; +import org.eclipse.sirius.business.internal.metamodel.description.extensions.INodeMappingExt; +import org.eclipse.sirius.business.internal.metamodel.description.operations.AbstractNodeMappingSpecOperations; +import org.eclipse.sirius.business.internal.metamodel.description.operations.SiriusElementMappingSpecOperations; +import org.eclipse.sirius.business.internal.metamodel.helper.MappingHelper; +import org.eclipse.sirius.business.internal.metamodel.helper.NodeMappingHelper; +import org.eclipse.sirius.description.DiagramElementMapping; +import org.eclipse.sirius.description.NodeMapping; +import org.eclipse.sirius.diagram.sequence.description.impl.ExecutionMappingImpl; + +/** + * Implementation of ExecutionMapping. + * + * @author pcdavid + */ +public class ExecutionMappingSpec extends ExecutionMappingImpl implements INodeMappingExt { + + private final Map> viewNodesDone = new HashMap>(); + + private final Map> candidatesCache = new WeakHashMap>(); + + /** + * {@inheritDoc} + */ + public Map> getViewNodesDone() { + return viewNodesDone; + } + + /** + * {@inheritDoc} + */ + public Map> getCandidatesCache() { + return candidatesCache; + } + + /** + * {@inheritDoc} + */ + @Override + public EList getNodesCandidates(final EObject semanticOrigin, final EObject container) { + return NodeMappingHelper.getNodesCandidates(this, semanticOrigin, container); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getNodesCandidates(final EObject semanticOrigin, final EObject container, final EObject containerView) { + return NodeMappingHelper.getNodesCandidates(this, semanticOrigin, container, containerView); + } + + /** + * {@inheritDoc} + */ + @Override + public DNode createNode(final EObject modelElement, final EObject container, final DDiagram diagram) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(modelElement); + return new NodeMappingHelper(interpreter).createNode(this, modelElement, container, diagram); + } + + /** + * {@inheritDoc} + */ + @Override + public void updateNode(final DNode node) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(node); + new NodeMappingHelper(interpreter).updateNode(this, node); + } + + /** + * {@inheritDoc} + */ + @Override + public NodeStyle getBestStyle(final EObject modelElement, final EObject viewVariable, final EObject containerVariable) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(modelElement); + return (NodeStyle) new MappingHelper(interpreter).getBestStyle(this, modelElement, viewVariable, containerVariable, new EObjectQuery(containerVariable).getParentDiagram().get()); + } + + /** + * {@inheritDoc} + */ + @Override + public DNodeListElement createListElement(final EObject modelElement, final DDiagram diagram) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(modelElement); + return new NodeMappingHelper(interpreter).createListElement(this, modelElement, diagram); + } + + /** + * {@inheritDoc} + */ + @Override + public void updateListElement(final DNodeListElement listElement) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(listElement); + new NodeMappingHelper(interpreter).updateListElement(this, listElement); + } + + /** + * {@inheritDoc} + */ + @Override + public void clearDNodesDone() { + NodeMappingHelper.clearDNodesDone(this); + } + + /** + * {@inheritDoc} + */ + @Override + public EList findDNodeFromEObject(final EObject object) { + return NodeMappingHelper.findDNodeFromEObject(this, object); + } + + /* + * Here we add the behavior we should inherit from AbstractNodeMapping + */ + + /** + * {@inheritDoc} + */ + public void createBorderingNodes(final EObject modelElement, final DDiagramElement vpElement, @SuppressWarnings("rawtypes") + final Collection filterSemantic, final DDiagram viewPoint) { + AbstractNodeMappingSpecOperations.createBorderingNodes(this, modelElement, vpElement, filterSemantic, viewPoint); + } + + /* + * Behavior inherited from DiagramElementMapping + */ + + /** + * {@inheritDoc} + */ + @Override + public boolean checkPrecondition(final EObject modelElement, final EObject container, final EObject containerView) { + return SiriusElementMappingSpecOperations.checkPrecondition(this, modelElement, container, containerView); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getAllMappings() { + final BasicEList allMappings = new BasicEList(); + allMappings.addAll(this.getAllBorderedNodeMappings()); + return new UnmodifiableEList(allMappings.size(), allMappings.toArray()); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isFrom(final DMappingBased element) { + return SiriusElementMappingSpecOperations.isFrom(this, element); + } + + /** + * {@inheritDoc} + */ + @Override + public void addDoneNode(final DSemanticDecorator node) { + NodeMappingHelper.addDoneNode(this, node); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return new StringBuffer(getClass().getName()).append(" ").append(getName()).toString(); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getAllBorderedNodeMappings() { + return AbstractNodeMappingSpecOperations.getAllBorderedNodeMappings(this); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/InstanceRoleMappingSpec.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/InstanceRoleMappingSpec.java new file mode 100644 index 0000000000..98aa2dfa5b --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/InstanceRoleMappingSpec.java @@ -0,0 +1,211 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.metamodel.description; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; + +import org.eclipse.emf.common.util.BasicEList; +import org.eclipse.emf.common.util.BasicEList.UnmodifiableEList; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; + +import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; +import org.eclipse.sirius.common.tools.api.util.EObjectCouple; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DMappingBased; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.DNodeListElement; +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.NodeStyle; +import org.eclipse.sirius.SiriusPlugin; +import org.eclipse.sirius.business.api.query.EObjectQuery; +import org.eclipse.sirius.business.internal.metamodel.description.extensions.INodeMappingExt; +import org.eclipse.sirius.business.internal.metamodel.description.operations.AbstractNodeMappingSpecOperations; +import org.eclipse.sirius.business.internal.metamodel.description.operations.SiriusElementMappingSpecOperations; +import org.eclipse.sirius.business.internal.metamodel.helper.MappingHelper; +import org.eclipse.sirius.business.internal.metamodel.helper.NodeMappingHelper; +import org.eclipse.sirius.description.DiagramElementMapping; +import org.eclipse.sirius.description.NodeMapping; +import org.eclipse.sirius.diagram.sequence.description.impl.InstanceRoleMappingImpl; + +/** + * Implementation of InstanceRoleMapping. + * + * @author pcdavid + */ +public class InstanceRoleMappingSpec extends InstanceRoleMappingImpl implements INodeMappingExt { + + private final Map> viewNodesDone = new HashMap>(); + + private final Map> candidatesCache = new WeakHashMap>(); + + /** + * {@inheritDoc} + */ + public Map> getViewNodesDone() { + return viewNodesDone; + } + + /** + * {@inheritDoc} + */ + public Map> getCandidatesCache() { + return candidatesCache; + } + + /** + * {@inheritDoc} + */ + @Override + public EList getNodesCandidates(final EObject semanticOrigin, final EObject container) { + return NodeMappingHelper.getNodesCandidates(this, semanticOrigin, container); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getNodesCandidates(final EObject semanticOrigin, final EObject container, final EObject containerView) { + return NodeMappingHelper.getNodesCandidates(this, semanticOrigin, container, containerView); + } + + /** + * {@inheritDoc} + */ + @Override + public DNode createNode(final EObject modelElement, final EObject container, final DDiagram diagram) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(modelElement); + return new NodeMappingHelper(interpreter).createNode(this, modelElement, container, diagram); + } + + /** + * {@inheritDoc} + */ + @Override + public void updateNode(final DNode node) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(node); + new NodeMappingHelper(interpreter).updateNode(this, node); + } + + /** + * {@inheritDoc} + */ + @Override + public NodeStyle getBestStyle(final EObject modelElement, final EObject viewVariable, final EObject containerVariable) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(modelElement); + return (NodeStyle) new MappingHelper(interpreter).getBestStyle(this, modelElement, viewVariable, containerVariable, new EObjectQuery(containerVariable).getParentDiagram().get()); + } + + /** + * {@inheritDoc} + */ + @Override + public DNodeListElement createListElement(final EObject modelElement, final DDiagram diagram) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(modelElement); + return new NodeMappingHelper(interpreter).createListElement(this, modelElement, diagram); + } + + /** + * {@inheritDoc} + */ + @Override + public void updateListElement(final DNodeListElement listElement) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(listElement); + new NodeMappingHelper(interpreter).updateListElement(this, listElement); + } + + /** + * {@inheritDoc} + */ + @Override + public void clearDNodesDone() { + NodeMappingHelper.clearDNodesDone(this); + } + + /** + * {@inheritDoc} + */ + @Override + public EList findDNodeFromEObject(final EObject object) { + return NodeMappingHelper.findDNodeFromEObject(this, object); + } + + /* + * Here we add the behavior we should inherit from AbstractNodeMapping + */ + + /** + * {@inheritDoc} + */ + public void createBorderingNodes(final EObject modelElement, final DDiagramElement vpElement, @SuppressWarnings("rawtypes") + final Collection filterSemantic, final DDiagram viewPoint) { + AbstractNodeMappingSpecOperations.createBorderingNodes(this, modelElement, vpElement, filterSemantic, viewPoint); + } + + /* + * Behavior inherited from DiagramElementMapping + */ + + /** + * {@inheritDoc} + */ + @Override + public boolean checkPrecondition(final EObject modelElement, final EObject container, final EObject containerView) { + return SiriusElementMappingSpecOperations.checkPrecondition(this, modelElement, container, containerView); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getAllMappings() { + final BasicEList allMappings = new BasicEList(); + allMappings.addAll(this.getAllBorderedNodeMappings()); + return new UnmodifiableEList(allMappings.size(), allMappings.toArray()); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isFrom(final DMappingBased element) { + return SiriusElementMappingSpecOperations.isFrom(this, element); + } + + /** + * {@inheritDoc} + */ + @Override + public void addDoneNode(final DSemanticDecorator node) { + NodeMappingHelper.addDoneNode(this, node); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return new StringBuffer(getClass().getName()).append(" ").append(getName()).toString(); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getAllBorderedNodeMappings() { + return AbstractNodeMappingSpecOperations.getAllBorderedNodeMappings(this); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/InteractionUseMappingSpec.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/InteractionUseMappingSpec.java new file mode 100644 index 0000000000..f9f38ccb74 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/InteractionUseMappingSpec.java @@ -0,0 +1,209 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.metamodel.description; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.EcoreEList; + +import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; +import org.eclipse.sirius.common.tools.api.util.EObjectCouple; +import org.eclipse.sirius.ContainerStyle; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DDiagramElementContainer; +import org.eclipse.sirius.DMappingBased; +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.SiriusPlugin; +import org.eclipse.sirius.business.api.query.EObjectQuery; +import org.eclipse.sirius.business.internal.metamodel.description.extensions.IContainerMappingExt; +import org.eclipse.sirius.business.internal.metamodel.description.operations.AbstractNodeMappingSpecOperations; +import org.eclipse.sirius.business.internal.metamodel.description.operations.SiriusElementMappingSpecOperations; +import org.eclipse.sirius.business.internal.metamodel.helper.ContainerMappingHelper; +import org.eclipse.sirius.business.internal.metamodel.helper.MappingHelper; +import org.eclipse.sirius.description.ContainerMapping; +import org.eclipse.sirius.description.DescriptionPackage; +import org.eclipse.sirius.description.DiagramElementMapping; +import org.eclipse.sirius.description.NodeMapping; +import org.eclipse.sirius.diagram.sequence.description.impl.InteractionUseMappingImpl; + +/** + * Implementation of InteractionUseMapping. + * + * @author pcdavid + */ +public class InteractionUseMappingSpec extends InteractionUseMappingImpl implements IContainerMappingExt { + + private final Map> viewContainerDone = new HashMap>(); + + private final Map> candidatesCache = new WeakHashMap>(); + + /** + * {@inheritDoc} + */ + public Map> getViewContainerDone() { + return viewContainerDone; + } + + /** + * {@inheritDoc} + */ + public Map> getCandidatesCache() { + return candidatesCache; + } + + /** + * {@inheritDoc} + */ + @Override + public EList getAllNodeMappings() { + final Collection result = ContainerMappingHelper.getAllNodeMappings(this); + return new EcoreEList.UnmodifiableEList(eInternalContainer(), DescriptionPackage.eINSTANCE.getContainerMapping_AllNodeMappings(), result.size(), result.toArray()); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getAllContainerMappings() { + final Collection result = ContainerMappingHelper.getAllContainerMappings(this); + return new EcoreEList.UnmodifiableEList(eInternalContainer(), DescriptionPackage.eINSTANCE.getContainerMapping_AllContainerMappings(), result.size(), result.toArray()); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getNodesCandidates(final EObject semanticOrigin, final EObject container, final EObject containerView) { + return ContainerMappingHelper.getNodesCandidates(this, semanticOrigin, container, containerView); + } + + /** + * {@inheritDoc} + */ + @Override + public void clearDNodesDone() { + ContainerMappingHelper.clearDNodesDone(this); + } + + /** + * {@inheritDoc} + */ + @Override + public EList findDNodeFromEObject(final EObject object) { + return ContainerMappingHelper.findDNodeFromEObject(this, object); + } + + /** + * {@inheritDoc} + */ + @Override + public DDiagramElementContainer createContainer(final EObject modelElement, final EObject container, final DDiagram viewPoint) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(container); + return new ContainerMappingHelper(interpreter).createContainer(this, modelElement, container, viewPoint); + } + + /** + * {@inheritDoc} + */ + @Override + public void updateContainer(final DDiagramElementContainer container) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(container); + new ContainerMappingHelper(interpreter).updateContainer(this, container); + } + + /** + * {@inheritDoc} + */ + @Override + public void addDoneNode(final DSemanticDecorator node) { + ContainerMappingHelper.addDoneNode(this, node); + } + + /** + * {@inheritDoc} + */ + @Override + public ContainerStyle getBestStyle(final EObject modelElement, final EObject viewVariable, final EObject containerVariable) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(modelElement); + return (ContainerStyle) new MappingHelper(interpreter).getBestStyle(this, modelElement, viewVariable, containerVariable, new EObjectQuery(containerVariable).getParentDiagram().get()); + } + + /* + * Here we add the behavior we should inherit from AbstractNodeMapping + */ + + /** + * {@inheritDoc} + */ + public void createBorderingNodes(EObject modelElement, DDiagramElement vpElement, @SuppressWarnings("rawtypes") + Collection filterSemantic, DDiagram viewPoint) { + AbstractNodeMappingSpecOperations.createBorderingNodes(this, modelElement, vpElement, filterSemantic, viewPoint); + } + + /* + * Behavior inherited from DiagramElementMapping + */ + + /** + * {@inheritDoc} + */ + @Override + public boolean checkPrecondition(final EObject modelElement, final EObject container, final EObject containerView) { + return SiriusElementMappingSpecOperations.checkPrecondition(this, modelElement, container, containerView); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getAllMappings() { + return ContainerMappingHelper.getAllMappings(this); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isFrom(final DMappingBased element) { + return SiriusElementMappingSpecOperations.isFrom(this, element); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return new StringBuffer(getClass().getName()).append(" ").append(getName()).toString(); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getAllBorderedNodeMappings() { + return AbstractNodeMappingSpecOperations.getAllBorderedNodeMappings(this); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getDNodesDone() { + return ContainerMappingHelper.getDNodesDone(this); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/ObservationPointMappingSpec.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/ObservationPointMappingSpec.java new file mode 100644 index 0000000000..92c5386cc3 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/ObservationPointMappingSpec.java @@ -0,0 +1,211 @@ +/******************************************************************************* + * Copyright (c) 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.metamodel.description; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; + +import org.eclipse.emf.common.util.BasicEList; +import org.eclipse.emf.common.util.BasicEList.UnmodifiableEList; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; + +import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; +import org.eclipse.sirius.common.tools.api.util.EObjectCouple; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DMappingBased; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.DNodeListElement; +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.NodeStyle; +import org.eclipse.sirius.SiriusPlugin; +import org.eclipse.sirius.business.api.query.EObjectQuery; +import org.eclipse.sirius.business.internal.metamodel.description.extensions.INodeMappingExt; +import org.eclipse.sirius.business.internal.metamodel.description.operations.AbstractNodeMappingSpecOperations; +import org.eclipse.sirius.business.internal.metamodel.description.operations.SiriusElementMappingSpecOperations; +import org.eclipse.sirius.business.internal.metamodel.helper.MappingHelper; +import org.eclipse.sirius.business.internal.metamodel.helper.NodeMappingHelper; +import org.eclipse.sirius.description.DiagramElementMapping; +import org.eclipse.sirius.description.NodeMapping; +import org.eclipse.sirius.diagram.sequence.description.impl.ObservationPointMappingImpl; + +/** + * Implementation of ObservationPointMapping. + * + * @author mporhel + */ +public class ObservationPointMappingSpec extends ObservationPointMappingImpl implements INodeMappingExt { + + private final Map> viewNodesDone = new HashMap>(); + + private final Map> candidatesCache = new WeakHashMap>(); + + /** + * {@inheritDoc} + */ + public Map> getViewNodesDone() { + return viewNodesDone; + } + + /** + * {@inheritDoc} + */ + public Map> getCandidatesCache() { + return candidatesCache; + } + + /** + * {@inheritDoc} + */ + @Override + public EList getNodesCandidates(final EObject semanticOrigin, final EObject container) { + return NodeMappingHelper.getNodesCandidates(this, semanticOrigin, container); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getNodesCandidates(final EObject semanticOrigin, final EObject container, final EObject containerView) { + return NodeMappingHelper.getNodesCandidates(this, semanticOrigin, container, containerView); + } + + /** + * {@inheritDoc} + */ + @Override + public DNode createNode(final EObject modelElement, final EObject container, final DDiagram diagram) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(modelElement); + return new NodeMappingHelper(interpreter).createNode(this, modelElement, container, diagram); + } + + /** + * {@inheritDoc} + */ + @Override + public void updateNode(final DNode node) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(node); + new NodeMappingHelper(interpreter).updateNode(this, node); + } + + /** + * {@inheritDoc} + */ + @Override + public NodeStyle getBestStyle(final EObject modelElement, final EObject viewVariable, final EObject containerVariable) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(modelElement); + return (NodeStyle) new MappingHelper(interpreter).getBestStyle(this, modelElement, viewVariable, containerVariable, new EObjectQuery(containerVariable).getParentDiagram().get()); + } + + /** + * {@inheritDoc} + */ + @Override + public DNodeListElement createListElement(final EObject modelElement, final DDiagram diagram) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(modelElement); + return new NodeMappingHelper(interpreter).createListElement(this, modelElement, diagram); + } + + /** + * {@inheritDoc} + */ + @Override + public void updateListElement(final DNodeListElement listElement) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(listElement); + new NodeMappingHelper(interpreter).updateListElement(this, listElement); + } + + /** + * {@inheritDoc} + */ + @Override + public void clearDNodesDone() { + NodeMappingHelper.clearDNodesDone(this); + } + + /** + * {@inheritDoc} + */ + @Override + public EList findDNodeFromEObject(final EObject object) { + return NodeMappingHelper.findDNodeFromEObject(this, object); + } + + /* + * Here we add the behavior we should inherit from AbstractNodeMapping + */ + + /** + * {@inheritDoc} + */ + public void createBorderingNodes(final EObject modelElement, final DDiagramElement vpElement, @SuppressWarnings("rawtypes") + final Collection filterSemantic, final DDiagram viewPoint) { + AbstractNodeMappingSpecOperations.createBorderingNodes(this, modelElement, vpElement, filterSemantic, viewPoint); + } + + /* + * Behavior inherited from DiagramElementMapping + */ + + /** + * {@inheritDoc} + */ + @Override + public boolean checkPrecondition(final EObject modelElement, final EObject container, final EObject containerView) { + return SiriusElementMappingSpecOperations.checkPrecondition(this, modelElement, container, containerView); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getAllMappings() { + final BasicEList allMappings = new BasicEList(); + allMappings.addAll(this.getAllBorderedNodeMappings()); + return new UnmodifiableEList(allMappings.size(), allMappings.toArray()); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isFrom(final DMappingBased element) { + return SiriusElementMappingSpecOperations.isFrom(this, element); + } + + /** + * {@inheritDoc} + */ + @Override + public void addDoneNode(final DSemanticDecorator node) { + NodeMappingHelper.addDoneNode(this, node); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return new StringBuffer(getClass().getName()).append(" ").append(getName()).toString(); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getAllBorderedNodeMappings() { + return AbstractNodeMappingSpecOperations.getAllBorderedNodeMappings(this); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/OperandMappingSpec.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/OperandMappingSpec.java new file mode 100644 index 0000000000..4533cf835d --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/OperandMappingSpec.java @@ -0,0 +1,209 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.metamodel.description; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.EcoreEList; + +import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; +import org.eclipse.sirius.common.tools.api.util.EObjectCouple; +import org.eclipse.sirius.ContainerStyle; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DDiagramElementContainer; +import org.eclipse.sirius.DMappingBased; +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.SiriusPlugin; +import org.eclipse.sirius.business.api.query.EObjectQuery; +import org.eclipse.sirius.business.internal.metamodel.description.extensions.IContainerMappingExt; +import org.eclipse.sirius.business.internal.metamodel.description.operations.AbstractNodeMappingSpecOperations; +import org.eclipse.sirius.business.internal.metamodel.description.operations.SiriusElementMappingSpecOperations; +import org.eclipse.sirius.business.internal.metamodel.helper.ContainerMappingHelper; +import org.eclipse.sirius.business.internal.metamodel.helper.MappingHelper; +import org.eclipse.sirius.description.ContainerMapping; +import org.eclipse.sirius.description.DescriptionPackage; +import org.eclipse.sirius.description.DiagramElementMapping; +import org.eclipse.sirius.description.NodeMapping; +import org.eclipse.sirius.diagram.sequence.description.impl.OperandMappingImpl; + +/** + * Implementation of OperandMapping. + * + * @author pcdavid + */ +public class OperandMappingSpec extends OperandMappingImpl implements IContainerMappingExt { + + private final Map> viewContainerDone = new HashMap>(); + + private final Map> candidatesCache = new WeakHashMap>(); + + /** + * {@inheritDoc} + */ + public Map> getViewContainerDone() { + return viewContainerDone; + } + + /** + * {@inheritDoc} + */ + public Map> getCandidatesCache() { + return candidatesCache; + } + + /** + * {@inheritDoc} + */ + @Override + public EList getAllNodeMappings() { + final Collection result = ContainerMappingHelper.getAllNodeMappings(this); + return new EcoreEList.UnmodifiableEList(eInternalContainer(), DescriptionPackage.eINSTANCE.getContainerMapping_AllNodeMappings(), result.size(), result.toArray()); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getAllContainerMappings() { + final Collection result = ContainerMappingHelper.getAllContainerMappings(this); + return new EcoreEList.UnmodifiableEList(eInternalContainer(), DescriptionPackage.eINSTANCE.getContainerMapping_AllContainerMappings(), result.size(), result.toArray()); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getNodesCandidates(final EObject semanticOrigin, final EObject container, final EObject containerView) { + return ContainerMappingHelper.getNodesCandidates(this, semanticOrigin, container, containerView); + } + + /** + * {@inheritDoc} + */ + @Override + public void clearDNodesDone() { + ContainerMappingHelper.clearDNodesDone(this); + } + + /** + * {@inheritDoc} + */ + @Override + public EList findDNodeFromEObject(final EObject object) { + return ContainerMappingHelper.findDNodeFromEObject(this, object); + } + + /** + * {@inheritDoc} + */ + @Override + public DDiagramElementContainer createContainer(final EObject modelElement, final EObject container, final DDiagram viewPoint) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(container); + return new ContainerMappingHelper(interpreter).createContainer(this, modelElement, container, viewPoint); + } + + /** + * {@inheritDoc} + */ + @Override + public void updateContainer(final DDiagramElementContainer container) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(container); + new ContainerMappingHelper(interpreter).updateContainer(this, container); + } + + /** + * {@inheritDoc} + */ + @Override + public void addDoneNode(final DSemanticDecorator node) { + ContainerMappingHelper.addDoneNode(this, node); + } + + /** + * {@inheritDoc} + */ + @Override + public ContainerStyle getBestStyle(final EObject modelElement, final EObject viewVariable, final EObject containerVariable) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(modelElement); + return (ContainerStyle) new MappingHelper(interpreter).getBestStyle(this, modelElement, viewVariable, containerVariable, new EObjectQuery(containerVariable).getParentDiagram().get()); + } + + /* + * Here we add the behavior we should inherit from AbstractNodeMapping + */ + + /** + * {@inheritDoc} + */ + public void createBorderingNodes(final EObject modelElement, final DDiagramElement vpElement, @SuppressWarnings("rawtypes") + final Collection filterSemantic, final DDiagram viewPoint) { + AbstractNodeMappingSpecOperations.createBorderingNodes(this, modelElement, vpElement, filterSemantic, viewPoint); + } + + /* + * Behavior inherited from DiagramElementMapping + */ + + /** + * {@inheritDoc} + */ + @Override + public boolean checkPrecondition(final EObject modelElement, final EObject container, final EObject containerView) { + return SiriusElementMappingSpecOperations.checkPrecondition(this, modelElement, container, containerView); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getAllMappings() { + return ContainerMappingHelper.getAllMappings(this); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isFrom(final DMappingBased element) { + return SiriusElementMappingSpecOperations.isFrom(this, element); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return new StringBuffer(getClass().getName()).append(" ").append(getName()).toString(); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getAllBorderedNodeMappings() { + return AbstractNodeMappingSpecOperations.getAllBorderedNodeMappings(this); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getDNodesDone() { + return ContainerMappingHelper.getDNodesDone(this); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/ReturnMessageMappingSpec.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/ReturnMessageMappingSpec.java new file mode 100644 index 0000000000..1c389c3c92 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/ReturnMessageMappingSpec.java @@ -0,0 +1,154 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.metamodel.description; + +import org.eclipse.emf.common.util.BasicEList; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; + +import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.DMappingBased; +import org.eclipse.sirius.EdgeStyle; +import org.eclipse.sirius.EdgeTarget; +import org.eclipse.sirius.SiriusPlugin; +import org.eclipse.sirius.business.api.query.EObjectQuery; +import org.eclipse.sirius.business.internal.metamodel.description.operations.SiriusElementMappingSpecOperations; +import org.eclipse.sirius.business.internal.metamodel.helper.EdgeMappingHelper; +import org.eclipse.sirius.business.internal.metamodel.helper.MappingHelper; +import org.eclipse.sirius.description.DiagramElementMapping; +import org.eclipse.sirius.diagram.sequence.description.impl.ReturnMessageMappingImpl; + +/** + * Implementation of ReturnMessageMapping. + * + * @author pcdavid + */ +public class ReturnMessageMappingSpec extends ReturnMessageMappingImpl { + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#createEdge(org.eclipse.sirius.EdgeTarget, + * org.eclipse.sirius.EdgeTarget, org.eclipse.emf.ecore.EObject) + */ + @Override + public DEdge createEdge(final EdgeTarget source, final EdgeTarget target, final EObject semanticTarget) { + return createEdge(source, target, null, semanticTarget); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#createEdge(org.eclipse.sirius.EdgeTarget, + * org.eclipse.sirius.EdgeTarget, org.eclipse.emf.ecore.EObject, + * org.eclipse.emf.ecore.EObject) + */ + @Override + public DEdge createEdge(final EdgeTarget source, final EdgeTarget target, final EObject container, final EObject semanticTarget) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(container); + return new EdgeMappingHelper(interpreter).createEdge(this, source, target, container, semanticTarget); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#getBestStyle(org.eclipse.emf.ecore.EObject, + * org.eclipse.emf.ecore.EObject, org.eclipse.emf.ecore.EObject) + */ + @Override + public EdgeStyle getBestStyle(final EObject modelElement, final EObject viewVariable, final EObject containerVariable) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(modelElement); + return (EdgeStyle) new MappingHelper(interpreter).getBestStyle(this, modelElement, viewVariable, containerVariable, new EObjectQuery(containerVariable).getParentDiagram().get()); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#updateEdge(org.eclipse.sirius.DEdge) + */ + @Override + public void updateEdge(final DEdge dEdge) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(dEdge); + new EdgeMappingHelper(interpreter).updateEdge(this, dEdge); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#getEdgeTargetCandidates(org.eclipse.emf.ecore.EObject, + * org.eclipse.sirius.DDiagram) + */ + @Override + public EList getEdgeTargetCandidates(final EObject semanticOrigin, final DDiagram diagram) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(semanticOrigin); + return new EdgeMappingHelper(interpreter).getEdgeTargetCandidates(this, semanticOrigin, diagram); + + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#getEdgeSourceCandidates(org.eclipse.emf.ecore.EObject, + * org.eclipse.sirius.DDiagram) + */ + @Override + public EList getEdgeSourceCandidates(final EObject semanticOrigin, final DDiagram diagram) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(semanticOrigin); + return new EdgeMappingHelper(interpreter).getEdgeSourceCandidates(this, semanticOrigin, diagram); + } + + /* + * Behavior inherited from DiagramElementMapping + */ + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.DiagramElementMappingImpl#checkPrecondition(org.eclipse.emf.ecore.EObject, + * org.eclipse.emf.ecore.EObject, org.eclipse.emf.ecore.EObject) + */ + @Override + public boolean checkPrecondition(final EObject modelElement, final EObject container, final EObject containerView) { + return SiriusElementMappingSpecOperations.checkPrecondition(this, modelElement, container, containerView); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.DiagramElementMappingImpl#getAllMappings() + */ + @Override + public EList getAllMappings() { + return new BasicEList.UnmodifiableEList(0, new Object[0]); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.DiagramElementMappingImpl#isFrom(org.eclipse.sirius.DMappingBased) + */ + @Override + public boolean isFrom(final DMappingBased element) { + return SiriusElementMappingSpecOperations.isFrom(this, element); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.EdgeMappingImpl#toString() + */ + @Override + public String toString() { + return getClass().getName() + " " + getName(); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/SequenceDiagramDescriptionSpec.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/SequenceDiagramDescriptionSpec.java new file mode 100644 index 0000000000..ea69459391 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/SequenceDiagramDescriptionSpec.java @@ -0,0 +1,120 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.metamodel.description; + +import java.util.Set; + +import org.eclipse.emf.common.util.BasicEList; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.util.EcoreEList; + +import org.eclipse.sirius.DSemanticDiagram; +import org.eclipse.sirius.business.internal.metamodel.helper.ContentHelper; +import org.eclipse.sirius.business.internal.metamodel.helper.DiagramDescriptionHelper; +import org.eclipse.sirius.business.internal.metamodel.helper.LayerHelper; +import org.eclipse.sirius.description.ContainerMapping; +import org.eclipse.sirius.description.DescriptionPackage; +import org.eclipse.sirius.description.EdgeMapping; +import org.eclipse.sirius.description.Layer; +import org.eclipse.sirius.description.NodeMapping; +import org.eclipse.sirius.description.tool.AbstractToolDescription; +import org.eclipse.sirius.diagram.sequence.SequenceFactory; +import org.eclipse.sirius.diagram.sequence.description.impl.SequenceDiagramDescriptionImpl; + +/** + * Specialized version of SequenceDiagramDescriptionImpl. + * + * @author pcdavid + */ +public class SequenceDiagramDescriptionSpec extends SequenceDiagramDescriptionImpl { + /** + * {@inheritDoc} + * + * SequenceDiagramDescription produce SequenceDDiagrams at + * runtime. + */ + @Override + public DSemanticDiagram createDiagram() { + return SequenceFactory.eINSTANCE.createSequenceDDiagram(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.DiagramDescriptionImpl#getAllTools() + */ + @Override + public EList getAllTools() { + final Set result = DiagramDescriptionHelper.getAllTools(this); + return new EcoreEList.UnmodifiableEList(eInternalContainer(), DescriptionPackage.eINSTANCE.getDiagramDescription_AllTools(), result.size(), result.toArray()); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.DiagramDescriptionImpl#getAllActivatedTools() + */ + @Override + public EList getAllActivatedTools() { + return new BasicEList(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.DiagramDescriptionImpl#getAllLayers() + */ + @Override + public EList getAllLayers() { + return LayerHelper.getAllLayers(this); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.DiagramDescriptionImpl#getAllEdgeMappings() + */ + @Override + public EList getAllEdgeMappings() { + return ContentHelper.getAllEdgeMappings(this, false); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.DiagramDescriptionImpl#getAllActivatedEdgeMappings() + */ + @SuppressWarnings("deprecation") + @Override + public EList getAllActivatedEdgeMappings() { + return ContentHelper.getAllActivatedEdgeMappings(this); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.DiagramDescriptionImpl#getAllContainerMappings() + */ + @Override + public EList getAllContainerMappings() { + return ContentHelper.getAllContainerMappings(this, false); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.impl.DiagramDescriptionImpl#getAllNodeMappings() + */ + @Override + public EList getAllNodeMappings() { + return ContentHelper.getAllNodeMappings(this, false); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/StateMappingSpec.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/StateMappingSpec.java new file mode 100644 index 0000000000..93c0402c63 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/StateMappingSpec.java @@ -0,0 +1,211 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.metamodel.description; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; + +import org.eclipse.emf.common.util.BasicEList; +import org.eclipse.emf.common.util.BasicEList.UnmodifiableEList; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; + +import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; +import org.eclipse.sirius.common.tools.api.util.EObjectCouple; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DMappingBased; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.DNodeListElement; +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.NodeStyle; +import org.eclipse.sirius.SiriusPlugin; +import org.eclipse.sirius.business.api.query.EObjectQuery; +import org.eclipse.sirius.business.internal.metamodel.description.extensions.INodeMappingExt; +import org.eclipse.sirius.business.internal.metamodel.description.operations.AbstractNodeMappingSpecOperations; +import org.eclipse.sirius.business.internal.metamodel.description.operations.SiriusElementMappingSpecOperations; +import org.eclipse.sirius.business.internal.metamodel.helper.MappingHelper; +import org.eclipse.sirius.business.internal.metamodel.helper.NodeMappingHelper; +import org.eclipse.sirius.description.DiagramElementMapping; +import org.eclipse.sirius.description.NodeMapping; +import org.eclipse.sirius.diagram.sequence.description.impl.StateMappingImpl; + +/** + * Implementation of StateMapping. + * + * @author smonnier + */ +public class StateMappingSpec extends StateMappingImpl implements INodeMappingExt { + + private final Map> viewNodesDone = new HashMap>(); + + private final Map> candidatesCache = new WeakHashMap>(); + + /** + * {@inheritDoc} + */ + public Map> getViewNodesDone() { + return viewNodesDone; + } + + /** + * {@inheritDoc} + */ + public Map> getCandidatesCache() { + return candidatesCache; + } + + /** + * {@inheritDoc} + */ + @Override + public EList getNodesCandidates(final EObject semanticOrigin, final EObject container) { + return NodeMappingHelper.getNodesCandidates(this, semanticOrigin, container); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getNodesCandidates(final EObject semanticOrigin, final EObject container, final EObject containerView) { + return NodeMappingHelper.getNodesCandidates(this, semanticOrigin, container, containerView); + } + + /** + * {@inheritDoc} + */ + @Override + public DNode createNode(final EObject modelElement, final EObject container, final DDiagram diagram) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(modelElement); + return new NodeMappingHelper(interpreter).createNode(this, modelElement, container, diagram); + } + + /** + * {@inheritDoc} + */ + @Override + public void updateNode(final DNode node) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(node); + new NodeMappingHelper(interpreter).updateNode(this, node); + } + + /** + * {@inheritDoc} + */ + @Override + public NodeStyle getBestStyle(final EObject modelElement, final EObject viewVariable, final EObject containerVariable) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(modelElement); + return (NodeStyle) new MappingHelper(interpreter).getBestStyle(this, modelElement, viewVariable, containerVariable, new EObjectQuery(containerVariable).getParentDiagram().get()); + } + + /** + * {@inheritDoc} + */ + @Override + public DNodeListElement createListElement(final EObject modelElement, final DDiagram diagram) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(modelElement); + return new NodeMappingHelper(interpreter).createListElement(this, modelElement, diagram); + } + + /** + * {@inheritDoc} + */ + @Override + public void updateListElement(final DNodeListElement listElement) { + IInterpreter interpreter = SiriusPlugin.getDefault().getInterpreterRegistry().getInterpreter(listElement); + new NodeMappingHelper(interpreter).updateListElement(this, listElement); + } + + /** + * {@inheritDoc} + */ + @Override + public void clearDNodesDone() { + NodeMappingHelper.clearDNodesDone(this); + } + + /** + * {@inheritDoc} + */ + @Override + public EList findDNodeFromEObject(final EObject object) { + return NodeMappingHelper.findDNodeFromEObject(this, object); + } + + /* + * Here we add the behavior we should inherit from AbstractNodeMapping + */ + + /** + * {@inheritDoc} + */ + public void createBorderingNodes(final EObject modelElement, final DDiagramElement vpElement, @SuppressWarnings("rawtypes") + final Collection filterSemantic, final DDiagram viewPoint) { + AbstractNodeMappingSpecOperations.createBorderingNodes(this, modelElement, vpElement, filterSemantic, viewPoint); + } + + /* + * Behavior inherited from DiagramElementMapping + */ + + /** + * {@inheritDoc} + */ + @Override + public boolean checkPrecondition(final EObject modelElement, final EObject container, final EObject containerView) { + return SiriusElementMappingSpecOperations.checkPrecondition(this, modelElement, container, containerView); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getAllMappings() { + final BasicEList allMappings = new BasicEList(); + allMappings.addAll(this.getAllBorderedNodeMappings()); + return new UnmodifiableEList(allMappings.size(), allMappings.toArray()); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isFrom(final DMappingBased element) { + return SiriusElementMappingSpecOperations.isFrom(this, element); + } + + /** + * {@inheritDoc} + */ + @Override + public void addDoneNode(final DSemanticDecorator node) { + NodeMappingHelper.addDoneNode(this, node); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return new StringBuffer(getClass().getName()).append(" ").append(getName()).toString(); + } + + /** + * {@inheritDoc} + */ + @Override + public EList getAllBorderedNodeMappings() { + return AbstractNodeMappingSpecOperations.getAllBorderedNodeMappings(this); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/tool/MessageCreationToolSpec.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/tool/MessageCreationToolSpec.java new file mode 100644 index 0000000000..e816179ca9 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/description/tool/MessageCreationToolSpec.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.metamodel.description.tool; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; + +import org.eclipse.sirius.EdgeTarget; +import org.eclipse.sirius.description.EdgeMapping; +import org.eclipse.sirius.diagram.sequence.description.tool.impl.MessageCreationToolImpl; +import org.eclipse.sirius.tools.api.refresh.BestMappingGetter; + +/** + * Implementation of MessageCreationTool. Uses the same code as the + * generic EdgeCreationDescription. + * + * @author pcdavid + */ +public class MessageCreationToolSpec extends MessageCreationToolImpl { + /** + * {@inheritDoc} + */ + @Override + public EdgeMapping getBestMapping(final EdgeTarget source, final EdgeTarget target, final EList createdElements) { + EdgeMapping edgeMapping = null; + if (!getEdgeMappings().isEmpty()) { + edgeMapping = getEdgeMappings().get(0); + if (!createdElements.isEmpty()) { + edgeMapping = new BestMappingGetter(source, target, createdElements.get(0)).getBestEdgeMapping(getEdgeMappings()); + } + } + return edgeMapping; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/ordering/CompoundEventEndSpec.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/ordering/CompoundEventEndSpec.java new file mode 100644 index 0000000000..f6daff3ec9 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/ordering/CompoundEventEndSpec.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.metamodel.ordering; + +import java.util.Set; + +import org.eclipse.emf.common.util.BasicEList; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; + +import com.google.common.collect.Sets; + +import org.eclipse.sirius.diagram.sequence.ordering.CompoundEventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.SingleEventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.impl.CompoundEventEndImpl; + +/** + * Implementation of CompoundEventEnd. + * + * @author mporhel + */ +public class CompoundEventEndSpec extends CompoundEventEndImpl { + + /** + * {@inheritDoc} + */ + @Override + public EList getSemanticEvents() { + EList result = new BasicEList(); + Set semantics = Sets.newHashSet(); + for (SingleEventEnd see : getEventEnds()) { + semantics.add(see.getSemanticEvent()); + } + result.addAll(semantics); + return result; + } + + /** + * {@inheritDoc} + * + * @not-generated + */ + @Override + public boolean equals(Object obj) { + boolean areEquals = false; + if (this == obj) { + areEquals = true; + } else if (obj instanceof CompoundEventEnd) { + CompoundEventEnd that = (CompoundEventEnd) obj; + areEquals = this.getSemanticEnd().equals(that.getSemanticEnd()) && this.getEventEnds().size() == that.getEventEnds().size() + && this.getSemanticEvents().containsAll(that.getSemanticEvents()); + } + return areEquals; + } + + /** + * {@inheritDoc} + * + * @not-generated + */ + @Override + public int hashCode() { + int subHash = 1; + for (EObject obj : this.getSemanticEvents()) { + subHash *= obj.hashCode(); + } + return 31 * this.getSemanticEnd().hashCode() * subHash; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/ordering/SingleEventEndSpec.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/ordering/SingleEventEndSpec.java new file mode 100644 index 0000000000..f3adfa014a --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/metamodel/ordering/SingleEventEndSpec.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.metamodel.ordering; + +import org.eclipse.sirius.diagram.sequence.ordering.SingleEventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.impl.SingleEventEndImpl; + +/** + * Customization of the default {@link SingleEventEndImpl} implementation. + * + * @author Alex Lagarde + * + */ +public class SingleEventEndSpec extends SingleEventEndImpl { + + /** + * {@inheritDoc} + * + * @not-generated + */ + @Override + public boolean equals(Object obj) { + boolean equals = false; + if (this == obj) { + equals = true; + } else if (obj instanceof SingleEventEnd) { + SingleEventEnd that = (SingleEventEnd) obj; + equals = this.start == that.isStart() && this.getSemanticEnd().equals(that.getSemanticEnd()) && this.getSemanticEvent().equals(that.getSemanticEvent()); + } + return equals; + } + + /** + * {@inheritDoc} + * + * @not-generated + */ + @Override + public int hashCode() { + return 17 * this.getSemanticEnd().hashCode() * this.getSemanticEvent().hashCode(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/EndOfLifeMoveOperation.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/EndOfLifeMoveOperation.java new file mode 100644 index 0000000000..f620a5f0b5 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/EndOfLifeMoveOperation.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.operation; + +import com.google.common.base.Preconditions; + +import org.eclipse.sirius.diagram.business.internal.operation.AbstractModelChangeOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * Modifies the vertical range of a lifeline edit part to move the end of life + * edit part. + * + * @author mporhel, smonnier + */ +public class EndOfLifeMoveOperation extends AbstractModelChangeOperation { + private final Lifeline lifeline; + + private final int rangeDeltaWidth; + + /** + * Constructor. + * + * @param lifeline + * the lifeline whose range to modify. + * @param rangeDeltaWidth + * the rangeDeltaWidth. + */ + public EndOfLifeMoveOperation(Lifeline lifeline, int rangeDeltaWidth) { + super("Move EOL"); + this.lifeline = Preconditions.checkNotNull(lifeline); + this.rangeDeltaWidth = rangeDeltaWidth; + } + + /** + * {@inheritDoc} + */ + @Override + public Void execute() { + Range movedRange = Range.verticalRange(lifeline.getProperLogicalBounds()); + lifeline.setVerticalRange(new Range(movedRange.getLowerBound(), movedRange.getUpperBound() + rangeDeltaWidth)); + return null; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/FixGraphicalOrderingOperation.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/FixGraphicalOrderingOperation.java new file mode 100644 index 0000000000..1bfcfb02ae --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/FixGraphicalOrderingOperation.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2013 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.operation; + +import java.util.List; + +import com.google.common.collect.Lists; + +import org.eclipse.sirius.diagram.business.internal.operation.AbstractModelChangeOperation; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.EventEndsOrdering; + +/** + * An operation to remove the invalid ends from the graphical ordering of a + * sequence diagram. + * + * @author pcdavid + */ +public final class FixGraphicalOrderingOperation extends AbstractModelChangeOperation { + private final SequenceDDiagram diagram; + + /** + * Constructor. + * + * @param diagram + * the diagram whos graphical ordering to fix. + */ + public FixGraphicalOrderingOperation(SequenceDDiagram diagram) { + super("Remove invalid ends from graphical ordering"); + this.diagram = diagram; + } + + /** + * {@inheritDoc} + */ + @Override + public Void execute() { + EventEndsOrdering graphical = diagram.getGraphicalOrdering(); + List invalids = findInvalidEnds(graphical); + removeEnds(graphical, invalids); + return null; + } + + private List findInvalidEnds(EventEndsOrdering graphical) { + List invalids = Lists.newArrayList(); + for (EventEnd end : graphical.getEventEnds()) { + if (!isValidEnd(end)) { + invalids.add(end); + } + } + return invalids; + } + + private boolean isValidEnd(EventEnd end) { + return end.getSemanticEnd() != null && end.getSemanticEnd().eContainer() != null; + } + + private void removeEnds(EventEndsOrdering graphical, List invalids) { + for (EventEnd invalidEnd : invalids) { + graphical.getEventEnds().remove(invalidEnd); + } + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/ISequenceNodeMoveOperation.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/ISequenceNodeMoveOperation.java new file mode 100644 index 0000000000..f29c1360a0 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/ISequenceNodeMoveOperation.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.operation; + +import java.util.Collection; + +import org.eclipse.gmf.runtime.notation.LayoutConstraint; +import org.eclipse.gmf.runtime.notation.Location; +import org.eclipse.gmf.runtime.notation.Node; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.diagram.business.internal.operation.AbstractModelChangeOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceNode; + +/** + * Move an execution, interaction use of combined fragment vertically. + * + * @author pcdavid + */ +public class ISequenceNodeMoveOperation extends AbstractModelChangeOperation { + + private final Collection seqNodes = Sets.newHashSet(); + + private final int logicalShift; + + /** + * Constructor. + * + * @param node + * the sequence node to move. + * @param logicalShift + * the logical shift. + */ + public ISequenceNodeMoveOperation(ISequenceNode node, int logicalShift) { + super("Move sequence node"); + this.seqNodes.add(Preconditions.checkNotNull(node)); + this.logicalShift = logicalShift; + } + + /** + * Constructor. + * + * @param nodes + * the sequence nodes to move. + * @param logicalShift + * the logical shift. + */ + public ISequenceNodeMoveOperation(Collection nodes, int logicalShift) { + super("Move sequence node"); + Preconditions.checkNotNull(nodes); + this.seqNodes.addAll(nodes); + this.logicalShift = logicalShift; + } + + /** + * {@inheritDoc} + */ + @Override + public Void execute() { + for (ISequenceNode seqNode : seqNodes) { + Node node = seqNode.getNotationNode(); + LayoutConstraint layoutConstraint = node.getLayoutConstraint(); + if (layoutConstraint instanceof Location && logicalShift != 0) { + Location location = (Location) layoutConstraint; + location.setY(location.getY() + logicalShift); + } + } + return null; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/InverseRelativeNodePositionOperation.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/InverseRelativeNodePositionOperation.java new file mode 100644 index 0000000000..8766654810 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/InverseRelativeNodePositionOperation.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.operation; + +import org.eclipse.gmf.runtime.notation.Location; +import org.eclipse.gmf.runtime.notation.Node; + +import com.google.common.base.Preconditions; + +import org.eclipse.sirius.diagram.business.internal.operation.AbstractModelChangeOperation; + +/** + * An operation to inverse the relative position of a node on both axes. This is + * used for labels on messages to make sure they are always above the message, + * regardless of its direction (left-to-right or right-to-left). + * + * @author pcdavid, smonnier + */ +public class InverseRelativeNodePositionOperation extends AbstractModelChangeOperation { + private final Node node; + + /** + * Constructor. + * + * @param node + * the node whose position to inverse. + */ + public InverseRelativeNodePositionOperation(Node node) { + super("Inverse Relative Node Position"); + this.node = Preconditions.checkNotNull(node); + } + + /** + * {@inheritDoc} + */ + @Override + public Void execute() { + if (node.getLayoutConstraint() instanceof Location) { + Location l = (Location) node.getLayoutConstraint(); + l.setY(-l.getY()); + l.setX(-l.getX()); + } + return null; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/RefreshGraphicalOrderingOperation.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/RefreshGraphicalOrderingOperation.java new file mode 100644 index 0000000000..590aa0ed67 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/RefreshGraphicalOrderingOperation.java @@ -0,0 +1,144 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.operation; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import com.google.common.base.Functions; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.MapMaker; +import com.google.common.collect.Ordering; + +import org.eclipse.sirius.diagram.business.internal.operation.AbstractModelChangeOperation; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.VerticalPositionFunction; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.ordering.RefreshOrderingHelper; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.EventEndsOrdering; +import org.eclipse.sirius.diagram.sequence.ordering.SingleEventEnd; + +/** + * An operation to re-compute the graphical ordering in which events appear on a + * diagram. + * + *
+ * GMF View Model ------> GraphicalMessageOrdering
+ * 
+ * + * @author pcdavid, smonnier + */ +public class RefreshGraphicalOrderingOperation extends AbstractModelChangeOperation { + private final SequenceDDiagram sequenceDiagram; + + /** + * Creates a command which updates the graphical ordering of events store in + * the model using the latest available graphical informations. + * + * @param sequenceDiagram + * the diagram whose graphical ordering should be refreshed. + */ + public RefreshGraphicalOrderingOperation(SequenceDiagram sequenceDiagram) { + super("Refresh graphical ordering"); + this.sequenceDiagram = sequenceDiagram.getSequenceDDiagram(); + } + + /** + * {@inheritDoc} + */ + @Override + public Void execute() { + EventEndsOrdering graphicalOrdering = sequenceDiagram.getGraphicalOrdering(); + if (graphicalOrdering != null) { + refreshGlobalOrdering(graphicalOrdering); + } + return null; + } + + private void refreshGlobalOrdering(EventEndsOrdering graphicalOrdering) { + if (graphicalOrdering.eContainer() instanceof SequenceDDiagram) { + refreshGlobalOrdering(graphicalOrdering, new CustomVerticalPositionFunction(sequenceDiagram)); + } + } + + /** + * Recomputes the graphical ordering of events. + * + * @param sequenceDiagram + * the diagram. + * + * @param verticalPosition + * the function to use to obtain the vertical position of the + * event ends. + */ + private void refreshGlobalOrdering(EventEndsOrdering graphicalOrdering, VerticalPositionFunction verticalPosition) { + final Map positions = new MapMaker().makeComputingMap(verticalPosition); + Predicate isValidEnd = new Predicate() { + public boolean apply(EventEnd input) { + Integer pos = positions.get(input); + return pos != VerticalPositionFunction.INVALID_POSITION && pos != -VerticalPositionFunction.INVALID_POSITION; + } + }; + List allEnds = Lists.newArrayList(Iterables.filter(RefreshOrderingHelper.getAllEventEnds(sequenceDiagram), isValidEnd)); + Collections.sort(allEnds, Ordering.natural().onResultOf(Functions.forMap(positions))); + RefreshOrderingHelper.updateIfNeeded(graphicalOrdering.getEventEnds(), allEnds); + } + + /** + * Custom vertical function which do not return the real location of an + * event end but allow to correctly order event end from logically + * instantaneous ISequenceEvent. + * + * @author mPorhel + */ + private static class CustomVerticalPositionFunction extends VerticalPositionFunction { + + /** + * {@inheritDoc} + */ + public CustomVerticalPositionFunction(SequenceDDiagram diagram) { + super(diagram); + } + + /** + * {@inheritDoc} + */ + @Override + public Integer apply(EventEnd end) { + Integer customPos = super.apply(end); + if (customPos != INVALID_POSITION || customPos != -INVALID_POSITION) { + /* + * Simulates a 10x zoom so that we can adjust the + * SingleEventEnds position to get the proper ordering. This + * manipulation gives us the right ordering, but the actual + * Integer values returned by the function should not be used + * for anything else than comparing relative positions. + */ + customPos *= 10; + + if (end instanceof SingleEventEnd) { + SingleEventEnd see = (SingleEventEnd) end; + if (see.isStart()) { + customPos -= 1; + } else { + customPos += 1; + } + + } + } + return customPos; + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/RefreshSemanticOrderingsOperation.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/RefreshSemanticOrderingsOperation.java new file mode 100644 index 0000000000..42da3b6ce9 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/RefreshSemanticOrderingsOperation.java @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.operation; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EObject; + +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; +import org.eclipse.sirius.common.tools.api.util.StringUtil; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.business.api.logger.RuntimeLoggerInterpreter; +import org.eclipse.sirius.business.api.logger.RuntimeLoggerManager; +import org.eclipse.sirius.description.DiagramDescription; +import org.eclipse.sirius.diagram.business.internal.operation.AbstractModelChangeOperation; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; +import org.eclipse.sirius.diagram.sequence.business.internal.ordering.RefreshOrderingHelper; +import org.eclipse.sirius.diagram.sequence.description.DescriptionPackage; +import org.eclipse.sirius.diagram.sequence.description.SequenceDiagramDescription; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.EventEndsOrdering; +import org.eclipse.sirius.diagram.sequence.ordering.InstanceRolesOrdering; +import org.eclipse.sirius.tools.api.interpreter.InterpreterUtil; + +/** + * An operation to re-compute the global semantic orderings of events and + * instance roles in a sequence diagram according to the user-specified + * criteria. + * + *
+ * Semantic Model + User-specified Ordering Expression ---> SemanticMessageOrdering
+ * 
+ * + * @author pcdavid, smonnier + */ +public class RefreshSemanticOrderingsOperation extends AbstractModelChangeOperation { + /** + * The name of the variable used to pass event ends to sort to + * user-specified expressions. + */ + private static final String EVENT_ENDS_TO_SORT_VARIABLE = "eventEnds"; + + private final SequenceDDiagram sequenceDDiagram; + + /** + * Constructor. + * + * @param diagram + * the diagram whose semantic ordering should be refreshed. + */ + public RefreshSemanticOrderingsOperation(SequenceDDiagram diagram) { + super("Refresh semantic ordering"); + this.sequenceDDiagram = Preconditions.checkNotNull(diagram); + } + + /** + * {@inheritDoc} + */ + @Override + public Void execute() { + EventEndsOrdering semanticOrdering = sequenceDDiagram.getSemanticOrdering(); + if (semanticOrdering != null) { + refreshGlobalOrdering(semanticOrdering); + } + + InstanceRolesOrdering irSemanticOrdering = sequenceDDiagram.getInstanceRoleSemanticOrdering(); + if (semanticOrdering != null) { + refreshGlobalOrdering(irSemanticOrdering); + } + return null; + } + + /** + * Refreshes the semantic ordering of all the events in the diagram. + *

+ * {@inheritDoc} + */ + private void refreshGlobalOrdering(EventEndsOrdering semanticOrdering) { + Iterable allEnds = RefreshOrderingHelper.getAllEventEnds(sequenceDDiagram); + RefreshOrderingHelper.updateIfNeeded(semanticOrdering.getEventEnds(), computeEventEndsOrdering(semanticOrdering, allEnds)); + } + + private List computeEventEndsOrdering(EventEndsOrdering semanticOrdering, Iterable allEnds) { + Map index = Maps.newHashMap(); + for (EventEnd eventEnd : allEnds) { + index.put(eventEnd.getSemanticEnd(), eventEnd); + } + + Iterable semanticEnds = Iterables.transform(allEnds, new Function() { + public EObject apply(EventEnd from) { + return from.getSemanticEnd(); + } + }); + List orderedSemanticEnds = computeOrdering(semanticEnds, DescriptionPackage.eINSTANCE.getSequenceDiagramDescription_EndsOrdering(), true); + + List result = new ArrayList(); + for (EObject semanticEnd : orderedSemanticEnds) { + EventEnd ee = index.get(semanticEnd); + if (ee != null) { + result.add(ee); + } + } + + return result; + } + + /** + * Refreshes the semantic ordering of all the instance roles in the diagram. + */ + private void refreshGlobalOrdering(InstanceRolesOrdering semanticOrdering) { + Iterable allInstanceRoles = Iterables.filter(sequenceDDiagram.getNodes(), InstanceRole.viewpointElementPredicate()); + RefreshOrderingHelper.updateIfNeeded(semanticOrdering.getSemanticInstanceRoles(), computeInstanceRolesOrdering(semanticOrdering, allInstanceRoles)); + } + + private List computeInstanceRolesOrdering(InstanceRolesOrdering semanticOrdering, Iterable allInstanceRoles) { + List semanticInstanceRoles = Lists.newArrayList(); + for (DNode node : allInstanceRoles) { + semanticInstanceRoles.add(node.getTarget()); + } + + List orderedSemanticEnds = computeOrdering(semanticInstanceRoles, DescriptionPackage.eINSTANCE.getSequenceDiagramDescription_InstanceRolesOrdering(), false); + return orderedSemanticEnds; + } + + private List computeOrdering(Iterable semanticEnds, EAttribute expressionAttribute, boolean declareEventEndsVariable) { + if (StringUtil.isEmpty((String) getSequenceDescription().eGet(expressionAttribute))) { + return Lists.newArrayList(semanticEnds); + } + + final IInterpreter interpreter = InterpreterUtil.getInterpreter(sequenceDDiagram); + if (declareEventEndsVariable) { + interpreter.setVariable(EVENT_ENDS_TO_SORT_VARIABLE, Lists.newArrayList(semanticEnds)); + } + + try { + RuntimeLoggerInterpreter loggerInterpreter = RuntimeLoggerManager.INSTANCE.decorate(interpreter); + Collection result = loggerInterpreter.evaluateCollection(sequenceDDiagram.getTarget(), getSequenceDescription(), expressionAttribute); + return Lists.newArrayList(result); + } finally { + if (declareEventEndsVariable) { + interpreter.unSetVariable(EVENT_ENDS_TO_SORT_VARIABLE); + } + } + } + + private SequenceDiagramDescription getSequenceDescription() { + if (sequenceDDiagram != null) { + DiagramDescription desc = sequenceDDiagram.getDescription(); + return SequenceDiagramDescription.class.cast(desc); + } else { + return null; + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/ReparentExecutionOperation.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/ReparentExecutionOperation.java new file mode 100644 index 0000000000..21c89a910c --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/ReparentExecutionOperation.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.operation; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Preconditions; + +import org.eclipse.sirius.AbstractDNode; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.diagram.business.internal.operation.AbstractModelChangeOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; + +/** + * Update the structure of DNodes and GMF Nodes to reflect that an execution has + * a new parent event. + * + * @author pcdavid, smonnier, mporhel + */ +public class ReparentExecutionOperation extends AbstractModelChangeOperation { + private final AbstractNodeEvent execution; + + private final ISequenceEvent finalParent; + + /** + * Constructor. + * + * @param execution + * the execution whose parent changed. + * @param finalParent + * the new parent event of the execution. + */ + public ReparentExecutionOperation(AbstractNodeEvent execution, ISequenceEvent finalParent) { + super("Reparent execution"); + this.execution = Preconditions.checkNotNull(execution); + this.finalParent = Preconditions.checkNotNull(finalParent); + Preconditions.checkArgument(execution.getNotationView().getElement() instanceof DNode); + Preconditions.checkArgument(finalParent.getNotationView().getElement() instanceof AbstractDNode); + } + + /** + * {@inheritDoc} + */ + @Override + public Void execute() { + DNode thisSem = (DNode) execution.getNotationView().getElement(); + Node thisNode = (Node) execution.getNotationView(); + + DNode newSem = (DNode) finalParent.getNotationView().getElement(); + Node newParentNode = (Node) finalParent.getNotationView(); + + // real reconnection ? + if (validate(thisSem, thisNode, newSem, newParentNode)) { + @SuppressWarnings("unchecked") + EList persistedChildren = newParentNode.getPersistedChildren(); + persistedChildren.add(thisNode); + newSem.getOwnedBorderedNodes().add(thisSem); + } + return null; + } + + private boolean validate(DNode thisSem, Node thisNode, DNode newSem, Node newParentNode) { + boolean validation = thisSem != null && newSem != null && newParentNode != null; + validation = validation && !newSem.equals(thisSem.eContainer()) && !newParentNode.equals(thisNode.eContainer()); + return validation; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/SequenceMessageRangeHelper.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/SequenceMessageRangeHelper.java new file mode 100644 index 0000000000..4db4da2dc6 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/SequenceMessageRangeHelper.java @@ -0,0 +1,176 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.operation; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.IdentityAnchor; +import org.eclipse.gmf.runtime.notation.NotationFactory; +import org.eclipse.gmf.runtime.notation.RelativeBendpoints; +import org.eclipse.gmf.runtime.notation.datatype.RelativeBendpoint; + +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * A helper to handle the change of vertical range for sequence messages. + * + * @author pcdavid + */ +public class SequenceMessageRangeHelper { + private static final String TOP_CENTER_TERMINAL = "(0.5, 0.0)"; + + /** + * Sets the range for a normal (non-reflective), horizontal message. + * + * @param edge + * the edge representing the message. + * @param range + * the final range of the message. In practice it is a range of + * width 1 for horizontal messages. + * @param sourceTop + * the logical vertical position of the top of the source element + * of the message. + * @param targetTop + * the logical vertical position of the top of the target element + * of the message. + */ + public void setMessageRangeForNormalMessage(Edge edge, Range range, final int sourceTop, final int targetTop) { + resetAnchors(edge); + + RelativeBendpoints bendpoints = (RelativeBendpoints) edge.getBendpoints(); + + int[] sourceX = getSourceX(bendpoints); + int[] targetX = getTargetX(bendpoints); + assert sourceX != null && sourceX.length == 2; + assert targetX != null && targetX.length == 2; + + /* + * The vertical offsets from the top of the source/target. + */ + int sourceDeltaY = range.getLowerBound() - sourceTop; + int targetDeltaY = range.getLowerBound() - targetTop; + + List newBendpoints = new ArrayList(); + newBendpoints.add(new RelativeBendpoint(sourceX[0], sourceDeltaY, targetX[0], targetDeltaY)); + newBendpoints.add(new RelativeBendpoint(sourceX[1], sourceDeltaY, targetX[1], targetDeltaY)); + + bendpoints.setPoints(newBendpoints); + } + + /** + * Sets the range for a reflective message. + * + * @param edge + * the edge representing the message. + * @param range + * the final range of the message. In practice it is a range of + * width 1 for horizontal messages. + * @param sourceTop + * the logical vertical position of the top of the source element + * of the message. + * @param targetTop + * the logical vertical position of the top of the target element + * of the message. + */ + public void setMessageRangeForMessageToSelf(Edge edge, Range range, final int sourceTop, final int targetTop) { + resetAnchors(edge); + + RelativeBendpoints bendpoints = (RelativeBendpoints) edge.getBendpoints(); + + if (bendpoints.getPoints().size() == 2) { + List newBendpoints = new ArrayList(); + RelativeBendpoint firstRB = (RelativeBendpoint) bendpoints.getPoints().get(0); + RelativeBendpoint secondRB = (RelativeBendpoint) bendpoints.getPoints().get(1); + newBendpoints.add(firstRB); + int hGap = LayoutConstants.MESSAGE_TO_SELF_BENDPOINT_HORIZONTAL_GAP; + Option msg = ISequenceElementAccessor.getMessage(edge); + if (msg.some()) { + hGap = msg.get().getReflexiveMessageWidth(); + } + + newBendpoints.add(new RelativeBendpoint(firstRB.getSourceX() + hGap, firstRB.getSourceY(), firstRB.getTargetX() + hGap, firstRB.getTargetY())); + newBendpoints.add(new RelativeBendpoint(firstRB.getSourceX() + hGap, secondRB.getSourceY(), firstRB.getTargetX() + hGap, secondRB.getTargetY())); + newBendpoints.add(secondRB); + bendpoints.setPoints(newBendpoints); + } + + int[] sourceX = getSourceX(bendpoints); + int[] targetX = getTargetX(bendpoints); + assert sourceX != null && sourceX.length == 4; + assert targetX != null && targetX.length == 4; + + /* + * The vertical offsets of the two first/top bendpoints from the top of + * the source/target. + */ + int topSourceDeltaY = range.getLowerBound() - sourceTop; + int topTargetDeltaY = range.getLowerBound() - targetTop; + + /* + * The vertical offsets of the two last/bottom bendpoints from the top + * of the source/target. + */ + int bottomSourceDeltaY = range.getUpperBound() - sourceTop; + int bottomTargetDeltaY = range.getUpperBound() - targetTop; + + List newBendpoints = new ArrayList(); + newBendpoints.add(new RelativeBendpoint(sourceX[0], topSourceDeltaY, targetX[0], topTargetDeltaY)); + newBendpoints.add(new RelativeBendpoint(sourceX[1], topSourceDeltaY, targetX[0], topTargetDeltaY)); + newBendpoints.add(new RelativeBendpoint(sourceX[2], bottomSourceDeltaY, targetX[0], bottomTargetDeltaY)); + newBendpoints.add(new RelativeBendpoint(sourceX[3], bottomSourceDeltaY, targetX[0], bottomTargetDeltaY)); + + bendpoints.setPoints(newBendpoints); + } + + private int[] getSourceX(final RelativeBendpoints bendpoints) { + int[] sourceXs = new int[bendpoints.getPoints().size()]; + int i = 0; + for (RelativeBendpoint rb : Iterables.filter(bendpoints.getPoints(), RelativeBendpoint.class)) { + sourceXs[i] = rb.getSourceX(); + i += 1; + } + return sourceXs; + } + + private int[] getTargetX(final RelativeBendpoints bendpoints) { + int[] targetXs = new int[bendpoints.getPoints().size()]; + int i = 0; + for (RelativeBendpoint rb : Iterables.filter(bendpoints.getPoints(), RelativeBendpoint.class)) { + targetXs[i] = rb.getTargetX(); + i += 1; + } + return targetXs; + } + + /** + * Reset the anchors of an edge to a known, easy to handle location: the + * center of the top side of the source or target element. + */ + private void resetAnchors(Edge edge) { + edge.setSourceAnchor(createCanonicalAnchor()); + edge.setTargetAnchor(createCanonicalAnchor()); + } + + private IdentityAnchor createCanonicalAnchor() { + IdentityAnchor anchor = NotationFactory.eINSTANCE.createIdentityAnchor(); + anchor.setId(TOP_CENTER_TERMINAL); + return anchor; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/SetMessageRangeOperation.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/SetMessageRangeOperation.java new file mode 100644 index 0000000000..26e4688dbc --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/SetMessageRangeOperation.java @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.operation; + +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Objects; + +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.EdgeTarget; +import org.eclipse.sirius.diagram.business.internal.operation.AbstractModelChangeOperation; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * An operation to set the vertical range of a sequence message. + * + * @author pcdavid, smonnier + */ +public class SetMessageRangeOperation extends AbstractModelChangeOperation { + + private static final String LABEL = "Set Sequence Message Vertical Range"; + + private Edge message; + + private final Range range; + + private View source; + + private Rectangle sourceBounds; + + private View target; + + private Rectangle targetBounds; + + /** + * . + * + * @param message + * . + * @param range + * . + */ + public SetMessageRangeOperation(Edge message, Range range) { + this(message, range, false); + } + + /** + * . + * + * @param message + * . + * @param range + * . + * @param copyEdge + * . + */ + public SetMessageRangeOperation(Edge message, Range range, boolean copyEdge) { + super(LABEL); + this.message = message; + this.range = range; + } + + /** + * . + * + * @param src + * . + * @param srcBounds + * . + */ + public void setSource(View src, Rectangle srcBounds) { + this.source = src; + this.sourceBounds = srcBounds.getCopy(); + } + + /** + * . + * + * @param tgt + * . + * @param tgtBounds + * . + */ + public void setTarget(View tgt, Rectangle tgtBounds) { + this.target = tgt; + this.targetBounds = tgtBounds.getCopy(); + } + + /** + * {@inheritDoc} + */ + @Override + public Void execute() { + if (message.getElement() != null && message.getElement().eContainer() != null) { + Edge currentMessage = message; + + currentMessage.setSource(source); + if (currentMessage.getElement() instanceof DEdge && source.getElement() instanceof EdgeTarget) { + ((DEdge) currentMessage.getElement()).setSourceNode((EdgeTarget) source.getElement()); + } + currentMessage.setTarget(target); + if (currentMessage.getElement() instanceof DEdge && target.getElement() instanceof EdgeTarget) { + ((DEdge) currentMessage.getElement()).setTargetNode((EdgeTarget) target.getElement()); + } + + int srcTop = sourceBounds.getTop().y; + int tgtTop = targetBounds.getTop().y; + SequenceMessageRangeHelper helper = new SequenceMessageRangeHelper(); + if (isMessageToSelf(currentMessage)) { + helper.setMessageRangeForMessageToSelf(currentMessage, range, srcTop, tgtTop); + } else { + helper.setMessageRangeForNormalMessage(currentMessage, range, srcTop, tgtTop); + } + } + return null; + } + + /** + * FIXME This method should be in an helper class. + * + * @param msg + * an Edge + * @return if the edge is reflexive. + */ + private boolean isMessageToSelf(Edge msg) { + View src = msg.getSource(); + View tgt = msg.getTarget(); + return Objects.equal(src, tgt) || Objects.equal(src.eContainer(), tgt) || Objects.equal(src, tgt.eContainer()); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/SetVerticalRangeOperation.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/SetVerticalRangeOperation.java new file mode 100644 index 0000000000..df61191868 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/SetVerticalRangeOperation.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.operation; + +import com.google.common.base.Preconditions; + +import org.eclipse.sirius.diagram.business.internal.operation.AbstractModelChangeOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * Modifies the vertical range of an event on a sequence diagram. + * + * @author pcdavid, smonnier + */ +public class SetVerticalRangeOperation extends AbstractModelChangeOperation { + private final ISequenceEvent ise; + + private final Range newRange; + + /** + * Constructor. + * + * @param ise + * the event whose range to modify. + * @param newRange + * the new vertical range for the event. + */ + public SetVerticalRangeOperation(ISequenceEvent ise, Range newRange) { + super("Set vertical range"); + this.ise = Preconditions.checkNotNull(ise); + this.newRange = Preconditions.checkNotNull(newRange); + Preconditions.checkArgument(!newRange.isEmpty()); + } + + /** + * {@inheritDoc} + */ + @Override + public Void execute() { + if (ise.getSemanticTargetElement().some()) { + ise.setVerticalRange(newRange); + } + return null; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/ShiftDirectSubExecutionsOperation.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/ShiftDirectSubExecutionsOperation.java new file mode 100644 index 0000000000..c4a790e66d --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/ShiftDirectSubExecutionsOperation.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.operation; + +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.business.internal.operation.AbstractModelChangeOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * This command moves all the direct sub-executions of a given ExecutionEdipart + * vertically. It is used when an execution is resized from the top to ensure + * the sub-executions stay at the same absolute position instead of moving along + * (as they are relative to the top of the parent). + * + * @author pcdavid, smonnier + */ +public class ShiftDirectSubExecutionsOperation extends AbstractModelChangeOperation { + private final int deltaY; + + private final ISequenceEvent parent; + + /** + * Constructor. + * + * @param parent + * the execution or lifeline whose direct sub-executions must be + * shifted. + * @param deltaY + * the vertical amount to shift the sub-executions (in logical + * space). + */ + public ShiftDirectSubExecutionsOperation(ISequenceEvent parent, int deltaY) { + super("Shift sub-executions' positions by " + deltaY); + this.parent = Preconditions.checkNotNull(parent); + this.deltaY = deltaY; + } + + /** + * {@inheritDoc} + */ + @Override + public Void execute() { + + for (View view : Iterables.filter(Iterables.filter(parent.getNotationView().getChildren(), View.class), AbstractNodeEvent.notationPredicate())) { + Option execution = ISequenceElementAccessor.getAbstractNodeEvent(view); + if (execution.some()) { + AbstractNodeEvent ise = execution.get(); + Range rg = ise.getVerticalRange(); + Range nrg = new Range(rg.getLowerBound() + deltaY, rg.getUpperBound() + deltaY); + ise.setVerticalRange(nrg); + } + } + return null; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/SynchronizeGraphicalOrderingOperation.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/SynchronizeGraphicalOrderingOperation.java new file mode 100644 index 0000000000..351e93e864 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/SynchronizeGraphicalOrderingOperation.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.operation; + +import org.eclipse.gmf.runtime.notation.Diagram; + +import com.google.common.base.Preconditions; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.business.internal.operation.AbstractModelChangeOperation; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.SequenceLayout; + +/** + * Refreshes the graphical locations of the elements in a sequence diagram to + * reflect the current semantic ordering. This command assumes that the + * {@link org.eclipse.sirius.diagram.sequence.ordering.GraphicalOrdering} and + * the {@link org.eclipse.sirius.diagram.sequence.ordering.SemanticOrdering} + * are up to date according to the current visual (resp. semantic) order but + * that when they do not match, the semantic ordering is the authoritative one + * and the graphical ordering should be changed to match it. + * + * @author pcdavid, smonnier + */ +public class SynchronizeGraphicalOrderingOperation extends AbstractModelChangeOperation { + + private final Diagram sequenceDiagram; + + private final boolean pack; + + /** + * Constructor. + * + * @param sequenceDiagram + * the sequence diagram to refresh. + * @param pack + * if true the diagram will be packed + */ + public SynchronizeGraphicalOrderingOperation(Diagram sequenceDiagram, boolean pack) { + super("Synchronize graphical ordering"); + this.sequenceDiagram = Preconditions.checkNotNull(sequenceDiagram); + this.pack = pack; + } + + /** + * {@inheritDoc} + */ + @Override + public Void execute() { + Preconditions.checkNotNull(sequenceDiagram); + SequenceLayout sequenceLayout = new SequenceLayout(sequenceDiagram); + Option sd = sequenceLayout.getSequenceDiagram(); + + if (sd.some()) { + SequenceDDiagram diagram = (SequenceDDiagram) sd.get().getNotationDiagram().getElement(); + + if (diagram != null && (diagram.getGraphicalOrdering().getEventEnds().size() == diagram.getSemanticOrdering().getEventEnds().size())) { + boolean verticalLayout = sequenceLayout.verticalLayout(pack); + boolean horizontalLayout = sequenceLayout.horizontalLayout(pack); + boolean observationLayout = sequenceLayout.observationLayout(pack); + + // Refresh flags when a layout made modifications. + if (verticalLayout || horizontalLayout || observationLayout) { + sequenceLayout.flagSequenceEvents(); + } + } + } + return null; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/SynchronizeISequenceEventsSemanticOrderingOperation.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/SynchronizeISequenceEventsSemanticOrderingOperation.java new file mode 100644 index 0000000000..400a2300a1 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/SynchronizeISequenceEventsSemanticOrderingOperation.java @@ -0,0 +1,330 @@ +/******************************************************************************* + * Copyright (c) 2010, 2013 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.operation; + +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; + +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.business.api.componentization.DiagramComponentizationManager; +import org.eclipse.sirius.business.api.query.DiagramElementMappingQuery; +import org.eclipse.sirius.business.api.session.Session; +import org.eclipse.sirius.business.api.session.SessionManager; +import org.eclipse.sirius.description.DiagramElementMapping; +import org.eclipse.sirius.description.tool.AbstractToolDescription; +import org.eclipse.sirius.diagram.business.internal.operation.AbstractModelChangeOperation; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElement; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.ordering.EventEndHelper; +import org.eclipse.sirius.diagram.sequence.business.internal.query.ISequenceEventQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.tool.ToolCommandBuilder; +import org.eclipse.sirius.diagram.sequence.description.tool.ReorderTool; +import org.eclipse.sirius.diagram.sequence.ordering.CompoundEventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.SingleEventEnd; +import org.eclipse.sirius.tools.api.command.SiriusCommand; + +/** + * Refreshes the semantic ordering of a an element of a sequence diagram to + * reflect the current graphical ordering. This command assumes that the + * GraphicalMessageOrdering and the + * SemanticMessageOrdering are up to date according to the current + * visual (resp. semantic) order but that when they do not match, the graphical + * ordering is the authoritative one and the semantic ordering should be changed + * to match it, through the appropriate use of the user-specified + * ReorderTool. + * + * @author pcdavid, smonnier + */ +public class SynchronizeISequenceEventsSemanticOrderingOperation extends AbstractModelChangeOperation { + /** + * The name of the command. + */ + public static final String COMMAND_NAME = "Synchronize semantic ordering"; + + private static final boolean STARTING_END = true; + + private static final boolean FINISHING_END = false; + + private ISequenceEvent event; + + private Set selection = Sets.newHashSet(); + + private final SequenceDDiagram sequenceDiagram; + + private final SequenceDiagram diagram; + + private final Set reordered = Sets.newHashSet(); + + private final Set allElementsToReorder = Sets.newHashSet(); + + /** + * Constructor. + * + * @param event + * the event to move to its new location in the semantic order. + */ + public SynchronizeISequenceEventsSemanticOrderingOperation(ISequenceEvent event) { + super("Synchronize semantic ordering"); + this.event = Preconditions.checkNotNull(event); + this.diagram = event.getDiagram(); + this.sequenceDiagram = this.diagram.getSequenceDDiagram(); + } + + /** + * Constructor. + * + * @param event + * the event to move to its new location in the semantic order. + * @param selection + * additional events to reorder + */ + public SynchronizeISequenceEventsSemanticOrderingOperation(ISequenceEvent event, Collection selection) { + this(event); + Preconditions.checkNotNull(selection); + this.selection.addAll(selection); + } + + /** + * {@inheritDoc} + */ + @Override + public Void execute() { + if (event instanceof AbstractNodeEvent && event.getParentEvent() == null) { + // The edit-part which requested the synchronize has been removed + // from the hierarchy. + // Look for its replacement edit part on the same semantic element + DDiagramElement dde = (DDiagramElement) event.getNotationView().getElement(); + Collection events = ISequenceElementAccessor.getEventsForSemanticElement(diagram, dde.getTarget()); + if (!events.isEmpty()) { + event = events.iterator().next(); + } + } + + Iterables.addAll(allElementsToReorder, new ISequenceEventQuery(event).getAllSequenceEventToMoveWith(selection)); + + updateSemanticPositions(); + return null; + } + + private void updateSemanticPositions() { + updateSemanticPosition(event); + for (ISequenceEvent selected : selection) { + updateSemanticPosition(selected); + } + } + + private void updateSemanticPosition(ISequenceEvent eventToUpdate) { + DDiagramElement dde = resolveDiagramElement(eventToUpdate); + if (dde == null || reordered.contains(eventToUpdate)) { + return; + } + + ReorderTool reorderTool = findReorderTool(dde); + if (reorderTool == null) { + return; + } + + EObject semanticElement = dde.getTarget(); + EList endsBySemanticOrder = sequenceDiagram.getSemanticOrdering().getEventEnds(); + EList endsByGraphicalOrder = sequenceDiagram.getGraphicalOrdering().getEventEnds(); + + List ends = EventEndHelper.findEndsFromSemanticOrdering(eventToUpdate); + List compoundEnds = getCompoundEnds(eventToUpdate, ends); + + // Ignore the ends of the descendants: they are treated by recursive + // calls. + Set toIgnore = selectEndsToIgnore(eventToUpdate, endsBySemanticOrder, ends, compoundEnds); + + // The semantic ordering contains the state before the move + EventEnd startingEndPredecessorBefore = findEndPredecessor(semanticElement, STARTING_END, endsBySemanticOrder, toIgnore); + EventEnd finishingEndPredecessorBefore = findEndPredecessor(semanticElement, FINISHING_END, endsBySemanticOrder, toIgnore); + + // The graphical ordering contains the state after + EventEnd startingEndPredecessorAfter = findEndPredecessor(semanticElement, STARTING_END, endsByGraphicalOrder, toIgnore); + EventEnd finishingEndPredecessorAfter = findEndPredecessor(semanticElement, FINISHING_END, endsByGraphicalOrder, toIgnore); + + // Handle lost messages + if (eventToUpdate.isLogicallyInstantaneous() && eventToUpdate instanceof Message && ends.size() == 1 && !Iterables.any(ends, Predicates.instanceOf(CompoundEventEnd.class))) { + SingleEventEnd see = (SingleEventEnd) ends.iterator().next(); + if (see.isStart()) { + finishingEndPredecessorBefore = startingEndPredecessorBefore; + finishingEndPredecessorAfter = startingEndPredecessorAfter; + } else { + startingEndPredecessorBefore = finishingEndPredecessorBefore; + startingEndPredecessorAfter = finishingEndPredecessorAfter; + } + + } + + if (!Objects.equal(startingEndPredecessorBefore, startingEndPredecessorAfter) || !Objects.equal(finishingEndPredecessorBefore, finishingEndPredecessorAfter) + || !Iterables.elementsEqual(endsByGraphicalOrder, endsBySemanticOrder)) { + applySemanticReordering(semanticElement, startingEndPredecessorAfter, finishingEndPredecessorAfter, reorderTool); + applyCompoundReordering(semanticElement, ends, compoundEnds, startingEndPredecessorAfter, finishingEndPredecessorAfter, reorderTool); + reordered.add(eventToUpdate); + + new RefreshSemanticOrderingsOperation(sequenceDiagram).execute(); + // updateLinkedEventsSemanticPositions(eventToUpdate); + updateSubEventsSemanticPositions(eventToUpdate); + } + } + + private DDiagramElement resolveDiagramElement(ISequenceEvent eventToUpdate) { + EObject element = eventToUpdate.getNotationView().getElement(); + if (element instanceof DDiagramElement) { + return (DDiagramElement) element; + } + throw new RuntimeException("Invalid context for ISequenceEvent " + eventToUpdate); + } + + private List getCompoundEnds(ISequenceEvent eventToUpdate, List ends) { + List compoundEvents = EventEndHelper.getCompoundEvents(eventToUpdate); + Predicate isLogicallyInstantaneous = new Predicate() { + public boolean apply(ISequenceEvent input) { + return input.isLogicallyInstantaneous(); + }; + }; + return Lists.newArrayList(Iterables.filter( + Iterables.filter(Iterables.concat(Iterables.transform(Iterables.filter(compoundEvents, isLogicallyInstantaneous), EventEndHelper.EVENT_ENDS)), + Predicates.instanceOf(SingleEventEnd.class)), Predicates.not(Predicates.in(ends)))); + } + + private void updateSubEventsSemanticPositions(ISequenceEvent eventToUpdate) { + for (ISequenceEvent subEvent : eventToUpdate.getEventsToMoveWith()) { + updateSemanticPosition(subEvent); + } + } + + private void applyCompoundReordering(EObject semanticElement, List ends, List compoundEnds, EventEnd startingEndPredecessor, EventEnd finishingEndPredecessor, + ReorderTool reorderTool) { + if (compoundEnds.isEmpty() || ends.size() != 2) { + return; + } + + EventEnd startEventEnd = ends.get(0); + List startSemanticEvents = EventEndHelper.getSemanticEvents(startEventEnd); + EventEnd endEventEnd = ends.get(1); + + for (EventEnd ee : compoundEnds) { + List eeSemElts = EventEndHelper.getSemanticEvents(ee); + EventEnd otherEnd = Iterables.any(eeSemElts, Predicates.in(startSemanticEvents)) ? startEventEnd : endEventEnd; + EventEnd predecessor = Iterables.any(eeSemElts, Predicates.in(startSemanticEvents)) ? startingEndPredecessor : finishingEndPredecessor; + + for (EObject elt : eeSemElts) { + SingleEventEnd singleEventEnd = EventEndHelper.getSingleEventEnd(otherEnd, elt); + ISequenceEvent ise = EventEndHelper.findISequenceEvent(singleEventEnd, diagram); + if (!singleEventEnd.isStart()) { + // SemanticEvent is before otherEnd + applySemanticReordering(elt, predecessor, ee, reorderTool); + } else { + // SemanticEvent is after otherEnd + applySemanticReordering(elt, predecessor, otherEnd, reorderTool); + } + reordered.add(ise); + } + } + } + + private Set selectEndsToIgnore(ISequenceEvent ise, List endsBySemanticOrder, final List iseEnds, final List compoundEnds) { + final Iterable movedElements = Iterables.filter(allElementsToReorder, Predicates.not(Predicates.in(reordered))); + final Set semanticLinked = Sets.newHashSet(Iterables.filter(Iterables.transform(movedElements, ISequenceElement.SEMANTIC_TARGET), Predicates.notNull())); + final Predicate isLinkedSubEventEnd = new Predicate() { + public boolean apply(EObject input) { + return semanticLinked.contains(input); + } + }; + + final Set semanticDescendants = Sets.newHashSet(Iterables.filter(Iterables.transform(new ISequenceEventQuery(ise).getAllDescendants(), ISequenceElement.SEMANTIC_TARGET), + Predicates.notNull())); + final Predicate isSemanticSubEventEnd = new Predicate() { + public boolean apply(EObject input) { + return semanticDescendants.contains(input); + } + }; + + Predicate toIgnore = new Predicate() { + public boolean apply(EventEnd input) { + return !iseEnds.contains(input) && (Iterables.any(EventEndHelper.getSemanticEvents(input), Predicates.or(isSemanticSubEventEnd, isLinkedSubEventEnd)) || compoundEnds.contains(input)); + } + }; + HashSet newHashSet = Sets.newHashSet(Iterables.filter(endsBySemanticOrder, toIgnore)); + return newHashSet; + } + + private EventEnd findEndPredecessor(EObject semanticElement, boolean startingEnd, List eventEnds, Set toIgnore) { + EventEnd result = null; + for (EventEnd end : Iterables.filter(eventEnds, Predicates.not(Predicates.in(toIgnore)))) { + if (isLookedEnd(semanticElement, startingEnd, end)) { + break; + } else { + result = end; + } + } + return result; + } + + private boolean isLookedEnd(EObject semanticElement, boolean startingEnd, EventEnd end) { + boolean currentMovedEnd = false; + List semanticEvents = EventEndHelper.getSemanticEvents(end); + if (semanticEvents.contains(semanticElement)) { + boolean lookedEventEnd = EventEndHelper.getSingleEventEnd(end, semanticElement).isStart() == startingEnd; + boolean punctualCompoundEvent = EventEndHelper.PUNCTUAL_COMPOUND_EVENT_END.apply(end); + currentMovedEnd = lookedEventEnd || punctualCompoundEvent; + } + return currentMovedEnd; + } + + private void applySemanticReordering(EObject semanticElement, EventEnd startingEndPredecessor, EventEnd finishingEndPredecessor, ReorderTool reorderTool) { + SiriusCommand cmd = ToolCommandBuilder.buildReorderCommand(sequenceDiagram, reorderTool, semanticElement, startingEndPredecessor, finishingEndPredecessor); + cmd.execute(); + } + + private ReorderTool findReorderTool(DDiagramElement diagramElement) { + if (diagramElement != null) { + List allTools; + Session session = SessionManager.INSTANCE.getSession(diagramElement); + if (session != null) { + allTools = new DiagramComponentizationManager().getAllTools(session.getSelectedSiriuss(false), sequenceDiagram.getDescription()); + } else { + allTools = sequenceDiagram.getDescription().getAllTools(); + } + + DiagramElementMapping mappingToCheck = new DiagramElementMappingQuery(diagramElement.getDiagramElementMapping()).getRootMapping(); + + // TODO Consider layers activation + for (ReorderTool toolDesc : Iterables.filter(allTools, ReorderTool.class)) { + if (toolDesc.getMappings().contains(mappingToCheck)) { + return toolDesc; + } + } + } + return null; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/SynchronizeInstanceRoleSemanticOrderingOperation.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/SynchronizeInstanceRoleSemanticOrderingOperation.java new file mode 100644 index 0000000000..51b1053e2e --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/SynchronizeInstanceRoleSemanticOrderingOperation.java @@ -0,0 +1,200 @@ +/******************************************************************************* + * Copyright (c) 2010, 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.operation; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.notation.Diagram; + +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.business.api.componentization.DiagramComponentizationManager; +import org.eclipse.sirius.business.api.session.Session; +import org.eclipse.sirius.business.api.session.SessionManager; +import org.eclipse.sirius.description.tool.AbstractToolDescription; +import org.eclipse.sirius.diagram.business.internal.operation.AbstractModelChangeOperation; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElement; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.tool.ToolCommandBuilder; +import org.eclipse.sirius.diagram.sequence.description.tool.InstanceRoleReorderTool; +import org.eclipse.sirius.tools.api.command.SiriusCommand; + +/** + * Refreshes the semantic ordering of a an element of a sequence diagram to + * reflect the current graphical ordering. This command assumes that the + * GraphicalMessageOrdering and the + * SemanticMessageOrdering are up to date according to the current + * visual (resp. semantic) order but that when they do not match, the graphical + * ordering is the authoritative one and the semantic ordering should be changed + * to match it, through the appropriate use of the user-specified + * ReorderTool. + * + * @author pcdavid, smonnier + */ +public class SynchronizeInstanceRoleSemanticOrderingOperation extends AbstractModelChangeOperation { + /** + * The name of the command. + */ + public static final String COMMAND_NAME = "Synchronize semantic ordering"; + + private final SequenceDDiagram sequenceDiagram; + + private final SequenceDiagram diagram; + + private final Set reordered = Sets.newLinkedHashSet(); + + private InstanceRole instanceRole; + + private Set selection = Sets.newLinkedHashSet(); + + /** + * Constructor. + * + * @param instanceRole + * the instance role to move to its new location in the semantic + * order. + */ + public SynchronizeInstanceRoleSemanticOrderingOperation(InstanceRole instanceRole) { + super(COMMAND_NAME); + this.instanceRole = Preconditions.checkNotNull(instanceRole); + this.diagram = instanceRole.getDiagram(); + this.sequenceDiagram = this.diagram.getSequenceDDiagram(); + } + + /** + * Constructor. + * + * @param instanceRole + * the instance role to move to its new location in the semantic + * order. + * @param selection + * additional events to reorder + */ + public SynchronizeInstanceRoleSemanticOrderingOperation(InstanceRole instanceRole, Collection selection) { + this(instanceRole); + Preconditions.checkNotNull(selection); + this.selection.addAll(selection); + } + + /** + * {@inheritDoc} + */ + @Override + public Void execute() { + updateSemanticPositions(); + return null; + } + + private void updateSemanticPositions() { + updateSemanticPosition(instanceRole); + for (InstanceRole selected : selection) { + updateSemanticPosition(selected); + } + } + + private void updateSemanticPosition(InstanceRole instanceRoleToUpdate) { + DDiagramElement dde = resolveDiagramElement(instanceRoleToUpdate); + if (dde == null || reordered.contains(instanceRoleToUpdate)) { + return; + } + + InstanceRoleReorderTool reorderTool = findReorderTool(dde); + if (reorderTool == null) { + return; + } + + EObject semanticElement = dde.getTarget(); + List rolesBySemanticOrder = sequenceDiagram.getInstanceRoleSemanticOrdering().getSemanticInstanceRoles(); + List rolesByGraphicalOrder = getSemanticInstanceRolesByGraphicalOrder(); + + // The semantic ordering contains the state before the move + EObject predecessorBefore = findEndPredecessor(semanticElement, rolesBySemanticOrder); + + // The graphical ordering contains the state after + EObject predecessorAfter = findEndPredecessor(semanticElement, rolesByGraphicalOrder); + + if (!rolesBySemanticOrder.isEmpty() && !Objects.equal(predecessorBefore, predecessorAfter) || !Iterables.elementsEqual(rolesBySemanticOrder, rolesByGraphicalOrder)) { + applySemanticReordering(semanticElement, predecessorBefore, predecessorAfter, reorderTool); + reordered.add(instanceRoleToUpdate); + + new RefreshSemanticOrderingsOperation(sequenceDiagram).execute(); + } + } + + private List getSemanticInstanceRolesByGraphicalOrder() { + Iterable diagramViews = Iterables.filter(ISequenceElementAccessor.getViewsForSemanticElement(sequenceDiagram, sequenceDiagram.getTarget()), Diagram.class); + if (!Iterables.isEmpty(diagramViews)) { + Option seqDiag = ISequenceElementAccessor.getSequenceDiagram(diagramViews.iterator().next()); + if (seqDiag.some()) { + return Lists.newArrayList(Iterables.transform(seqDiag.get().getSortedInstanceRole(), ISequenceElement.SEMANTIC_TARGET)); + } + } + return Collections.emptyList(); + } + + private DDiagramElement resolveDiagramElement(InstanceRole instanceRoleToUpdate) { + EObject element = instanceRoleToUpdate.getNotationView().getElement(); + if (element instanceof DDiagramElement) { + return (DDiagramElement) element; + } + throw new RuntimeException("Invalid context for InstanceRole " + instanceRoleToUpdate); + } + + private EObject findEndPredecessor(EObject semanticElement, List semanticInstanceRoles) { + EObject result = null; + for (EObject current : semanticInstanceRoles) { + if (semanticElement == current) { + break; + } else { + result = current; + } + } + return result; + } + + private void applySemanticReordering(EObject semanticElement, EObject predecessorBefore, EObject predecessorAfter, InstanceRoleReorderTool reorderTool) { + SiriusCommand cmd = ToolCommandBuilder.buildInstanceRoleReorderCommand(sequenceDiagram, reorderTool, semanticElement, predecessorBefore, predecessorAfter); + cmd.execute(); + } + + private InstanceRoleReorderTool findReorderTool(DDiagramElement diagramElement) { + if (diagramElement != null) { + List allTools; + Session session = SessionManager.INSTANCE.getSession(diagramElement); + if (session != null) { + allTools = new DiagramComponentizationManager().getAllTools(session.getSelectedSiriuss(false), sequenceDiagram.getDescription()); + } else { + allTools = sequenceDiagram.getDescription().getAllTools(); + } + // TODO Consider layers activation + for (InstanceRoleReorderTool toolDesc : Iterables.filter(allTools, InstanceRoleReorderTool.class)) { + if (toolDesc.getMappings().contains(diagramElement.getMapping())) { + return toolDesc; + } + } + } + return null; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/VerticalSpaceExpansion.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/VerticalSpaceExpansion.java new file mode 100644 index 0000000000..bc1ac9445e --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/operation/VerticalSpaceExpansion.java @@ -0,0 +1,339 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.operation; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.gmf.runtime.notation.LayoutConstraint; +import org.eclipse.gmf.runtime.notation.Location; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.Size; + +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.business.internal.operation.AbstractModelChangeOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceNode; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.query.ISequenceEventQuery; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * An operation to shift all the atomic events on a sequence diagram below a + * certain point to make room. + * + * @author pcdavid, smonnier + */ +public class VerticalSpaceExpansion extends AbstractModelChangeOperation { + + private final SequenceDiagram sequenceDiagram; + + private int insertionPoint; + + private int expansionSize; + + private Set eventsToIgnore; + + private Set eventsToResize; + + private Set eventsToShift; + + private Set messagesToResize; + + private Set messagesToShift; + + private Map finalMessagesRanges; + + private Integer move; + + /** + * Constructor. + * + * @param diagram + * the sequence diagram in which to make the change. + * @param shift + * the zone to expand. + * @param move + * how much the main execution which triggered this change is + * vertically moved. + * @param eventsToIgnore + * the events which should be ignored, as they will be moved into + * the new space. + */ + public VerticalSpaceExpansion(SequenceDiagram diagram, Range shift, Integer move, Collection eventsToIgnore) { + super("Auto-expand of " + shift); + this.sequenceDiagram = diagram; + this.move = move; + this.insertionPoint = shift.getLowerBound(); + this.expansionSize = shift.width(); + // Complete the specified eventsToIgnore with all their descendants. + this.eventsToIgnore = Sets.newHashSet(); + for (ISequenceEvent evt : eventsToIgnore) { + this.eventsToIgnore.add(evt); + this.eventsToIgnore.addAll(new ISequenceEventQuery(evt).getAllDescendants()); + this.eventsToIgnore.addAll(new ISequenceEventQuery(evt).getAllMessages()); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Void execute() { + categorizeSequenceNodes(findAllSequenceNodesToConsider()); + categorizeMessages(findAllMessagesToConsider()); + computeFinalMessageRanges(); + + expandLifelines(); + shiftSequenceNodes(); + resizeSequenceNodes(); + setFinalMessagesRanges(); + + return null; + } + + private void setFinalMessagesRanges() { + for (Message smep : finalMessagesRanges.keySet()) { + smep.setVerticalRange(finalMessagesRanges.get(smep)); + // setMessageRange(smep, finalMessagesRanges.get(smep)); + } + } + + private void computeFinalMessageRanges() { + finalMessagesRanges = Maps.newHashMap(); + Set messages = sequenceDiagram.getAllMessages(); + for (Message msg : messages) { + if (messagesToShift.contains(msg)) { + if (!isConnectedToAMovedExecutionByASingleEnd(msg) && !isContainedReflexiveMessage(msg)) { + finalMessagesRanges.put(msg, msg.getVerticalRange().shifted(expansionSize)); + } else { + finalMessagesRanges.put(msg, msg.getVerticalRange().shifted(move)); + } + } else if (messagesToResize.contains(msg)) { + final Range rangeBefore = msg.getVerticalRange(); + finalMessagesRanges.put(msg, new Range(rangeBefore.getLowerBound(), rangeBefore.getUpperBound() + expansionSize)); + } else { + finalMessagesRanges.put(msg, msg.getVerticalRange()); + } + } + } + + private boolean isConnectedToAMovedExecutionByASingleEnd(Message msg) { + boolean onlySourceIsInMovedExecutions = eventsToIgnore.contains(msg.getSourceElement()) && !eventsToIgnore.contains(msg.getTargetElement()); + boolean onlyTargetIsInMovedExecutions = !eventsToIgnore.contains(msg.getSourceElement()) && eventsToIgnore.contains(msg.getTargetElement()); + return onlySourceIsInMovedExecutions || onlyTargetIsInMovedExecutions; + } + + /** + * Expand all the lifelines which do not have a destroy message, but keep + * the messages they contain stable. + */ + private void expandLifelines() { + List lifelines = sequenceDiagram.getAllLifelines(); + lifelines.removeAll(eventsToIgnore); + for (Lifeline lifeline : lifelines) { + Option cm = lifeline.getCreationMessage(); + if (cm.some() && isStrictlyBelowInsertionPoint(cm.get())) { + /* + * The whole lifeline is below the insertion point. + */ + InstanceRole irep = lifeline.getInstanceRole(); + Node node = (Node) irep.getNotationView(); + LayoutConstraint layoutConstraint = node.getLayoutConstraint(); + if (layoutConstraint instanceof Location) { + Location location = (Location) layoutConstraint; + location.setY(location.getY() + expansionSize); + } + } else { + /* + * Only the end of the lifeline is below the insertion point. + */ + Option dm = lifeline.getDestructionMessage(); + if (!dm.some() || isStrictlyBelowInsertionPoint(dm.get())) { + expandDown(lifeline, expansionSize); + } + } + } + } + + /** + * Find all the executions in the diagram which may be affected by the + * operation. This is all the executions except the ones we are + * explicitly told to ignore (and their descendants). + */ + private Set findAllSequenceNodesToConsider() { + Set sequenceNodes = Sets.newLinkedHashSet(); + sequenceNodes.addAll(sequenceDiagram.getAllAbstractNodeEvents()); + sequenceNodes.addAll(sequenceDiagram.getAllInteractionUses()); + sequenceNodes.addAll(sequenceDiagram.getAllCombinedFragments()); + sequenceNodes.addAll(sequenceDiagram.getAllOperands()); + sequenceNodes.removeAll(eventsToIgnore); + return sequenceNodes; + } + + private Set findAllMessagesToConsider() { + Set messages = Sets.newHashSet(); + for (Message msg : sequenceDiagram.getAllMessages()) { + if (!isBetweenTwoMovedEvents(msg) || isContainedReflexiveMessage(msg)) { + messages.add(msg); + } + } + return messages; + } + + private boolean isBetweenTwoMovedEvents(Message msg) { + return eventsToIgnore.contains(msg.getSourceElement()) && eventsToIgnore.contains(msg.getTargetElement()); + } + + /** + * Validate if the message is a reflexive message between two ignored + * executions + * + * @param msg + * a Message + * @return if the message "msg" is a reflexive message between two ignored + * executions + */ + private boolean isContainedReflexiveMessage(Message msg) { + return eventsToIgnore.contains(msg.getSourceElement()) && eventsToIgnore.contains(msg.getTargetElement()) && msg.isReflective(); + } + + private void categorizeMessages(Set messages) { + messagesToResize = Sets.newHashSet(); + messagesToShift = Sets.newHashSet(); + for (Message ise : Iterables.filter(messages, Predicates.not(Predicates.in(eventsToIgnore)))) { + if (containsInsertionPoint(ise)) { + messagesToResize.add(ise); + } else if (isStrictlyBelowInsertionPoint(ise) || isConnectedToAMovedExecutionByASingleEnd(ise)) { + messagesToShift.add(ise); + } + } + } + + /** + * Decide what needs to be done for each of the specified ISequenceNode: + *

    + *
  • nothing if it is above the expansion zone.
  • + *
  • a resize if it intersects the insertion point, i.e. its top is above + * the point but its bottom is below.
  • + *
  • a complete shift if it is completely below the insertion point.
  • + *
+ *

+ * After completion of this method, toResize contains all the + * executions which need to be resized and toShift all the + * executions which need to be shifted. + */ + private void categorizeSequenceNodes(Set sequenceNodes) { + eventsToResize = Sets.newHashSet(); + eventsToShift = Sets.newHashSet(); + for (ISequenceNode isn : sequenceNodes) { + if (isn instanceof ISequenceEvent) { + ISequenceEvent ise = (ISequenceEvent) isn; + if (containsInsertionPoint(ise)) { + eventsToResize.add(isn); + } else if (isStrictlyBelowInsertionPoint(ise) && !(isn instanceof Operand && eventsToShift.contains(((Operand) isn).getCombinedFragment()))) { + eventsToShift.add(isn); + } + } + } + } + + private void shiftSequenceNodes() { + + for (ISequenceNode nodes : Iterables.filter(eventsToShift, Predicates.not(Predicates.instanceOf(AbstractNodeEvent.class)))) { + shift(nodes, expansionSize); + } + + for (AbstractNodeEvent execution : Iterables.filter(eventsToShift, AbstractNodeEvent.class)) { + Lifeline lep = execution.getLifeline().get(); + Option cm = lep.getCreationMessage(); + if (cm.some() && isStrictlyBelowInsertionPoint(cm.get())) { + continue; + } + /* + * Only actually shift the "top-level" executions. The rest will be + * moved along with their shifted ancestor, as execution position is + * relative to its parent. + */ + + if (!containsAncestors(eventsToShift, execution)) { + shift(execution, expansionSize); + } + } + + for (AbstractNodeEvent execution : Iterables.filter(eventsToIgnore, AbstractNodeEvent.class)) { + /* + * Unshift events to ignore... + */ + if (eventsToShift.contains(execution.getHierarchicalParentEvent())) { + shift(execution, -expansionSize); + } + } + } + + private void resizeSequenceNodes() { + for (ISequenceNode ise : eventsToResize) { + expandDown(ise, expansionSize); + } + } + + private boolean containsAncestors(Set events, AbstractNodeEvent ise) { + ISequenceEvent parent = ise.getHierarchicalParentEvent(); + if (parent == null || !(parent instanceof AbstractNodeEvent)) { + return false; + } else { + return Iterables.contains(events, parent) || containsAncestors(events, (AbstractNodeEvent) parent); + } + } + + private boolean containsInsertionPoint(ISequenceEvent event) { + return event != null && event.getVerticalRange().includes(insertionPoint); + } + + private boolean isStrictlyBelowInsertionPoint(ISequenceEvent event) { + return event != null && event.getVerticalRange().getLowerBound() > insertionPoint; + } + + private void expandDown(Lifeline lifeline, int height) { + Range range = lifeline.getVerticalRange(); + lifeline.setVerticalRange(new Range(range.getLowerBound(), range.getUpperBound() + height)); + } + + private void expandDown(ISequenceNode isn, int height) { + Node node = isn.getNotationNode(); + LayoutConstraint layoutConstraint = node.getLayoutConstraint(); + if (layoutConstraint instanceof Size) { + Size s = (Size) layoutConstraint; + s.setHeight(s.getHeight() + height); + } + } + + private void shift(ISequenceNode isn, int height) { + Node node = (Node) isn.getNotationView(); + LayoutConstraint layoutConstraint = node.getLayoutConstraint(); + if (layoutConstraint instanceof Location && height != 0) { + Location location = (Location) layoutConstraint; + location.setY(location.getY() + height); + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/ordering/EventEndHelper.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/ordering/EventEndHelper.java new file mode 100644 index 0000000000..96deceb66b --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/ordering/EventEndHelper.java @@ -0,0 +1,220 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.ordering; + +import java.util.Collection; +import java.util.List; + +import org.eclipse.emf.ecore.EObject; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.ordering.CompoundEventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.SingleEventEnd; + +/** + * Helper class to factor common code for semantic and graphical orders + * refreshing. + * + * @author mporhel + */ +public final class EventEndHelper { + + /** + * Function to find and returns the EventEnds corresponding to the given + * part. + */ + public static final Function> EVENT_ENDS = new Function>() { + public List apply(ISequenceEvent from) { + return EventEndHelper.findEndsFromSemanticOrdering(from); + } + }; + + /** + * A function which compute the semantic end element from a event end. + */ + public static final Function SEMANTIC_END = new Function() { + public EObject apply(EventEnd from) { + return from.getSemanticEnd(); + } + }; + + /** + * A predicate which check that the given {@link SingleEventEnd} is a + * starting event. + */ + public static final Predicate IS_START = new Predicate() { + public boolean apply(SingleEventEnd from) { + return from.isStart(); + } + }; + + /** + * A predicate which check that the given {@link EventEnd} is a punctual + * compound event end. + */ + public static final Predicate PUNCTUAL_COMPOUND_EVENT_END = new Predicate() { + public boolean apply(EventEnd input) { + return input instanceof CompoundEventEnd && EventEndHelper.getSemanticEvents(input).size() == 1; + } + }; + + private EventEndHelper() { + // Prevent instantiation. + } + + /** + * Helper to get semanticEvents from an event end. + * + * @param eventEnd + * an EventEnd representing the end of an event. + * @return a list of semantic element representing the event itself. + */ + public static List getSemanticEvents(EventEnd eventEnd) { + List result = Lists.newArrayList(); + if (eventEnd instanceof SingleEventEnd) { + result.add(((SingleEventEnd) eventEnd).getSemanticEvent()); + } else if (eventEnd instanceof CompoundEventEnd) { + result.addAll(((CompoundEventEnd) eventEnd).getSemanticEvents()); + } + return result; + } + + /** + * Helper to get the correct SingleEventEnd from an event end. + * + * @param eventEnd + * an EventEnd representing the end of an event. + * @param semanticEvent + * the wanted semantic event. + * @return a list of semantic element representing the event itself. + */ + public static SingleEventEnd getSingleEventEnd(EventEnd eventEnd, EObject semanticEvent) { + SingleEventEnd result = null; + if (eventEnd instanceof SingleEventEnd) { + SingleEventEnd see = (SingleEventEnd) eventEnd; + if (semanticEvent != null && semanticEvent.equals(see.getSemanticEvent())) { + result = see; + } + } else if (eventEnd instanceof CompoundEventEnd) { + CompoundEventEnd cee = (CompoundEventEnd) eventEnd; + for (SingleEventEnd see : cee.getEventEnds()) { + if (semanticEvent != null && semanticEvent.equals(see.getSemanticEvent())) { + result = see; + break; + } + } + } + return result; + } + + /** + * Finds and returns the EventEnds corresponding to the given part, using + * the semantic ordering instead of the graphical ordering used by the plain + * {@link #findEnds(ISequenceEventEditPart)}. + * + * @param part + * the part to look for + * @return the EventEnds corresponding to the given part + */ + public static List findEndsFromSemanticOrdering(ISequenceEvent part) { + List ends = Lists.newArrayList(); + SequenceDiagram sdep = part.getDiagram(); + SequenceDDiagram seqDiag = (SequenceDDiagram) sdep.getNotationDiagram().getElement(); + Option semanticEvent = part.getSemanticTargetElement(); + for (EventEnd ee : seqDiag.getSemanticOrdering().getEventEnds()) { + if (semanticEvent.some() && EventEndHelper.getSemanticEvents(ee).contains(semanticEvent.get())) { + ends.add(ee); + } + } + return ends; + } + + /** + * Filter the given list : remove the events linked by compound events. + * + * @param self + * the parent event part. + * @param childrenEvents + * event to filter. + * @return a filtered Iterable. + * + */ + public static Iterable getIndependantEvents(ISequenceEvent self, Collection childrenEvents) { + final List parentEnds = EventEndHelper.findEndsFromSemanticOrdering(self); + Predicate isValidSubEvent = new Predicate() { + public boolean apply(ISequenceEvent input) { + List inputEnds = EventEndHelper.findEndsFromSemanticOrdering(input); + boolean res = inputEnds.removeAll(parentEnds); + return !res; + } + }; + return Iterables.filter(childrenEvents, isValidSubEvent); + } + + /** + * Returns the list of direct compound-events of this given event, in + * chronological (and thus also graphical) order. This includes events with + * same semantic ends + * + * @param self + * The given {@link ISequenceEventEditPart}. + * + * @return the list of direct compound-events of this event, in + * chronological order. + */ + public static List getCompoundEvents(ISequenceEvent self) { + List compoundEvents = Lists.newArrayList(); + SequenceDiagram sdep = self.getDiagram(); + EObject semanticEvent = self.getSemanticTargetElement().get(); + List ends = EventEndHelper.findEndsFromSemanticOrdering(self); + for (CompoundEventEnd cee : Iterables.filter(ends, CompoundEventEnd.class)) { + for (SingleEventEnd see : cee.getEventEnds()) { + if (see.getSemanticEvent() != semanticEvent) { + ISequenceEvent ise = EventEndHelper.findISequenceEvent(see, sdep); + // if (!isMessageToSelf(ise)) { + if (ise != null) { + compoundEvents.add(ise); + } + } + } + } + return compoundEvents; + } + + /** + * Finds and returns the ISequenceEvent corresponding to the given + * SingleEventEnd. + * + * @param end + * the end to look for + * @param sdep + * the SequenceDiagramEditPart to inspect + * @return the ISequenceEvent corresponding to the given part + */ + public static ISequenceEvent findISequenceEvent(SingleEventEnd end, SequenceDiagram sdep) { + for (ISequenceEvent ise : sdep.getAllDelimitedSequenceEvents()) { + Option semanticEvent = ise.getSemanticTargetElement(); + if (semanticEvent.some() && end.getSemanticEvent().equals(semanticEvent.get())) { + return ise; + } + } + return null; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/ordering/RefreshOrderingHelper.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/ordering/RefreshOrderingHelper.java new file mode 100644 index 0000000000..567916100d --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/ordering/RefreshOrderingHelper.java @@ -0,0 +1,245 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.ordering; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EObject; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; +import org.eclipse.sirius.common.tools.api.util.AllContents; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.business.api.logger.RuntimeLoggerInterpreter; +import org.eclipse.sirius.business.api.logger.RuntimeLoggerManager; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.CombinedFragment; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InteractionUse; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.State; +import org.eclipse.sirius.diagram.sequence.description.DescriptionPackage; +import org.eclipse.sirius.diagram.sequence.ordering.CompoundEventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.OrderingFactory; +import org.eclipse.sirius.diagram.sequence.ordering.SingleEventEnd; +import org.eclipse.sirius.tools.api.interpreter.InterpreterUtil; + +/** + * Helper class to factor common code for semantic and graphical orders + * refreshing. + * + * @author pcdavid + */ +public final class RefreshOrderingHelper { + @SuppressWarnings("unchecked") + private static final Predicate DELIMITED_EVENT_PREDICATE = Predicates.or(AbstractNodeEvent.viewpointElementPredicate(), State.viewpointElementPredicate(), InteractionUse.viewpointElementPredicate(), CombinedFragment.viewpointElementPredicate(), Operand.viewpointElementPredicate()); + + private RefreshOrderingHelper() { + // Prevent instantiation. + } + + /** + * Replaces the content of a list with new contents, but only if there are + * actual differences between the two versions. + * + * @param + * the type of elements in the lists. + * @param oldValue + * the old list of values. Modified in place if necessary. + * @param newValue + * the new list of values. Not modified. + */ + public static void updateIfNeeded(List oldValue, List newValue) { + if (RefreshOrderingHelper.differentContents(oldValue, newValue)) { + oldValue.clear(); + oldValue.addAll(newValue); + } + } + + /** + * Determines if two lists have the same contents in the same order or not. + * Elements are compared using {@link #equals(Object)}. + * + * @param + * the type of elements in the lists. + * @param oldValue + * the old list of values. + * @param newValue + * the new lists of values. + * @return true if the list of new values have different + * contents or the same elements but in a different order rom the + * old one. + */ + public static boolean differentContents(List oldValue, List newValue) { + if (oldValue.size() != newValue.size()) { + return true; + } else { + return !Iterables.elementsEqual(oldValue, newValue); + } + } + + /** + * Returns all the starting and finishing ends of all the events in the + * specified diagram. + * + * @param sequenceDiagram + * a sequence diagram. + * @return all the starting and finishing ends of all the events in the + * specified diagram. + */ + public static Iterable getAllEventEnds(SequenceDDiagram sequenceDiagram) { + Multimap semanticEndToSingleEventEnds = ArrayListMultimap. create(); + List result = Lists.newArrayList(); + + RefreshOrderingHelper.addAllSingleEventEnds(sequenceDiagram, semanticEndToSingleEventEnds); + + RefreshOrderingHelper.minimizeEventEnds(result, semanticEndToSingleEventEnds); + + return result; + } + + private static void minimizeEventEnds(List result, Multimap semanticEndToSingleEventEnds) { + for (EObject semanticEnd : semanticEndToSingleEventEnds.keySet()) { + Collection sees = semanticEndToSingleEventEnds.get(semanticEnd); + if (sees.isEmpty()) { + continue; + } + + if (sees.size() == 1) { + result.add(sees.iterator().next()); + } else { + CompoundEventEnd cee = OrderingFactory.eINSTANCE.createCompoundEventEnd(); + cee.setSemanticEnd(semanticEnd); + + if (sees.size() == 2 && RefreshOrderingHelper.countEvents(sees) == 1) { + // start first + Iterables.addAll(cee.getEventEnds(), Iterables.filter(sees, EventEndHelper.IS_START)); + Iterables.addAll(cee.getEventEnds(), Iterables.filter(sees, Predicates.not(EventEndHelper.IS_START))); + } else { + // end first + Iterables.addAll(cee.getEventEnds(), Iterables.filter(sees, Predicates.not(EventEndHelper.IS_START))); + Iterables.addAll(cee.getEventEnds(), Iterables.filter(sees, EventEndHelper.IS_START)); + } + result.add(cee); + } + } + } + + private static int countEvents(Collection sees) { + Set events = Sets.newHashSet(); + for (SingleEventEnd see : sees) { + events.add(see.getSemanticEvent()); + } + return events.size(); + } + + private static void addAllSingleEventEnds(SequenceDDiagram sequenceDiagram, Multimap semanticEndToSingleEventEnd) { + + // Messages + for (DEdge edge : Iterables.filter(sequenceDiagram.getEdges(), Message.viewpointElementPredicate())) { + /* + * Target may be null if the semantic element has been removed from + * the model but the canonical refresh has not happened yet. + */ + if (edge.getTarget() != null) { + RefreshOrderingHelper.add(RefreshOrderingHelper.getStartingEnd(edge, DescriptionPackage.eINSTANCE.getMessageMapping_SendingEndFinderExpression()), semanticEndToSingleEventEnd); + RefreshOrderingHelper.add(RefreshOrderingHelper.getFinishingEnd(edge, DescriptionPackage.eINSTANCE.getMessageMapping_ReceivingEndFinderExpression()), semanticEndToSingleEventEnd); + } + } + + // Delimited elements : Executions, States, InteractionUse, Frames, + // Operands + for (DDiagramElement node : Iterables.filter(Iterables.filter(AllContents.of(sequenceDiagram), DDiagramElement.class), RefreshOrderingHelper.DELIMITED_EVENT_PREDICATE)) { + /* + * Target may be null if the semantic element has been removed from + * the model but the canonical refresh has not happened yet. + */ + if (node.getTarget() != null) { + SingleEventEnd startingEnd = RefreshOrderingHelper.getStartingEnd(node, DescriptionPackage.eINSTANCE.getDelimitedEventMapping_StartingEndFinderExpression()); + SingleEventEnd finishingEnd = RefreshOrderingHelper.getFinishingEnd(node, DescriptionPackage.eINSTANCE.getDelimitedEventMapping_FinishingEndFinderExpression()); + + RefreshOrderingHelper.add(startingEnd, semanticEndToSingleEventEnd); + RefreshOrderingHelper.add(finishingEnd, semanticEndToSingleEventEnd); + } + } + } + + private static void add(SingleEventEnd see, Multimap endToEventEnds) { + if (see != null && see.getSemanticEnd() != null) { + endToEventEnds.put(see.getSemanticEnd(), see); + } + } + + /** + * Helper to create an event end representing the start of an event. + * + * @param semanticEvent + * the semantic element representing the event itself. + * @param semanticEnd + * the semantic element representing the starting end of the + * event. + * @return an EventEnd representing the starting end of the event. + */ + public static SingleEventEnd createStartingEventEnd(EObject semanticEvent, EObject semanticEnd) { + SingleEventEnd result = OrderingFactory.eINSTANCE.createSingleEventEnd(); + result.setStart(true); + result.setSemanticEvent(semanticEvent); + result.setSemanticEnd(semanticEnd); + return result; + } + + /** + * Helper to create an event end representing the finishing of an event. + * + * @param semanticEvent + * the semantic element representing the event itself. + * @param semanticEnd + * the semantic element representing the finishing end of the + * event. + * @return an EventEnd representing the finishing end of the event. + */ + public static SingleEventEnd createFinishingEventEnd(EObject semanticEvent, EObject semanticEnd) { + SingleEventEnd result = OrderingFactory.eINSTANCE.createSingleEventEnd(); + result.setStart(false); + result.setSemanticEvent(semanticEvent); + result.setSemanticEnd(semanticEnd); + return result; + } + + private static SingleEventEnd getStartingEnd(DDiagramElement dde, EAttribute startingEnd) { + EObject semanticEvent = dde.getTarget(); + IInterpreter interpreter = InterpreterUtil.getInterpreter(dde); + RuntimeLoggerInterpreter loggerInterpreter = RuntimeLoggerManager.INSTANCE.decorate(interpreter); + EObject sendingEnd = loggerInterpreter.evaluateEObject(semanticEvent, dde.getDiagramElementMapping(), startingEnd); + return RefreshOrderingHelper.createStartingEventEnd(semanticEvent, sendingEnd); + } + + private static SingleEventEnd getFinishingEnd(DDiagramElement dde, EAttribute finishingEnd) { + EObject semanticEvent = dde.getTarget(); + IInterpreter interpreter = InterpreterUtil.getInterpreter(dde); + RuntimeLoggerInterpreter loggerInterpreter = RuntimeLoggerManager.INSTANCE.decorate(interpreter); + EObject receivingEnd = loggerInterpreter.evaluateEObject(semanticEvent, dde.getDiagramElementMapping(), finishingEnd); + return RefreshOrderingHelper.createFinishingEventEnd(semanticEvent, receivingEnd); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/ISequenceElementQuery.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/ISequenceElementQuery.java new file mode 100644 index 0000000000..2a435466ad --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/ISequenceElementQuery.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2010, 2013 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.query; + +import org.eclipse.draw2d.geometry.Rectangle; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.AbsoluteBoundsFilter; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElement; + +/** + * General queries on {@link ISequenceElement}s. + * + * @author pcdavid + */ +public class ISequenceElementQuery { + /** + * The event to query. + */ + protected final ISequenceElement event; + + /** + * Constructor. + * + * @param event + * the event to query. + */ + public ISequenceElementQuery(ISequenceElement event) { + this.event = Preconditions.checkNotNull(event); + } + + /** + * Check the presence of the absolute bounds flag. + * + * @return true if the element is flagged. + */ + public boolean hasAbsoluteBoundsFlag() { + if (event.getNotationView() != null && event.getNotationView().getElement() instanceof DDiagramElement) { + DDiagramElement dde = (DDiagramElement) event.getNotationView().getElement(); + return !Iterables.isEmpty(Iterables.filter(dde.getGraphicalFilters(), AbsoluteBoundsFilter.class)); + } + return false; + } + + /** + * Return the flagged absolute bounds, ie the last known logical bounds. + * + * @return the flagged absolute bounds. + */ + public Rectangle getFlaggedAbsoluteBounds() { + Rectangle bounds = new Rectangle(); + if (event.getNotationView() != null && event.getNotationView().getElement() instanceof DDiagramElement) { + DDiagramElement dde = (DDiagramElement) event.getNotationView().getElement(); + Iterable flags = Iterables.filter(dde.getGraphicalFilters(), AbsoluteBoundsFilter.class); + if (!Iterables.isEmpty(flags)) { + AbsoluteBoundsFilter flag = flags.iterator().next(); + bounds.x = safeGetInt(flag.getX()); + bounds.y = safeGetInt(flag.getY()); + bounds.width = safeGetInt(flag.getWidth()); + bounds.height = safeGetInt(flag.getHeight()); + } + } + return bounds; + } + + private int safeGetInt(Integer i) { + return i == null ? 0 : i; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/ISequenceEventQuery.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/ISequenceEventQuery.java new file mode 100644 index 0000000000..67b7d6a6a3 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/ISequenceEventQuery.java @@ -0,0 +1,309 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.query; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Execution; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * General queries on {@link ISequenceEvent}s. + * + * @author pcdavid + */ +public class ISequenceEventQuery { + /** + * The event to query. + */ + protected final ISequenceEvent event; + + /** + * Constructor. + * + * @param event + * the event to query. + */ + public ISequenceEventQuery(ISequenceEvent event) { + this.event = Preconditions.checkNotNull(event); + } + + /** + * Tests whether this event is an ancestor of the specified child event. + * + * @param child + * the potential descendant. + * @return true if this event is identical to the + * child, the parent of the child or an indirect ancestor of the + * child. + */ + public boolean isAncestorOrSelf(ISequenceEvent child) { + ISequenceEvent iSequenceEvent = event; + final boolean result; + if (iSequenceEvent == null || child == null) { + result = false; + } else if (iSequenceEvent.equals(child)) { + result = true; + } else { + ISequenceEvent parentEvent = child.getParentEvent(); + if (iSequenceEvent.equals(parentEvent)) { + result = true; + } else { + result = parentEvent != null && isAncestorOrSelf(parentEvent); + } + } + return result; + } + + /** + * Tests whether this event is a reflective message. + * + * @return true if this event is a reflective message. + */ + public boolean isReflectiveMessage() { + return event instanceof Message && ((Message) event).isReflective(); + } + + /** + * Computes all the descendants of the specified execution, i.e. the + * recursive transitive closure on getSubEvents(). + * + * The current ise is not included. + * + * @return all the proper descendant events of the given sequence event. + */ + public Set getAllDescendants() { + return getAllDescendants(false, ISequenceEvent.ISEQUENCEEVENT_NOTATION_PREDICATE); + } + + /** + * Computes all the descendants of the specified execution, i.e. the + * recursive transitive closure on getSubEvents(). + * + * @param includeSelf + * whether or not to consider "self" as a descendant. + * @return all the proper descendant events of the given sequence event. + */ + public Set getAllDescendants(boolean includeSelf) { + return getAllDescendants(includeSelf, ISequenceEvent.ISEQUENCEEVENT_NOTATION_PREDICATE); + } + + /** + * Computes all the descendants of the specified execution, i.e. the + * recursive transitive closure on getSubEvents(). + * + * @param includeSelf + * whether or not to consider "self" as a descendant. + * @param predicate + * the predicate to select which descendants to include in the + * collection. + * @return all the proper descendant events of the given execution. + */ + public Set getAllDescendants(boolean includeSelf, Predicate predicate) { + Set result = Sets.newHashSet(); + addAllDescendants(predicate, result); + if (!includeSelf) { + result.remove(event); + } + return result; + } + + /** + * Adds all the descendants of the specified edit part which verify the + * predicate into a collection. Only children edit parts are considered, not + * source and target connections. + * + * @param predicate + * the predicate to select which descendants to include in the + * collection. + * @param parts + * the collection in which to add all the descendants of + * element which verify the predicate. + */ + private void addAllDescendants(Predicate predicate, Collection parts) { + addAllDescendants(event, predicate, parts); + } + + private void addAllDescendants(ISequenceEvent ise, Predicate predicate, Collection parts) { + View element = ise.getNotationView(); + if (predicate.apply(element)) { + Option iSequenceEvent = ISequenceElementAccessor.getISequenceEvent(element); + if (iSequenceEvent.some()) { + parts.add(iSequenceEvent.get()); + } + } + for (ISequenceEvent childEvent : ise.getSubEvents()) { + addAllDescendants(childEvent, predicate, parts); + } + } + + /** + * Finds all the executions without duplicates. + * + * @return the found executions found. + */ + public Collection getAllExecutions() { + return Lists.newArrayList(Iterables.filter(getAllDescendants(true, Execution.notationPredicate()), Execution.class)); + } + + /** + * Finds all the sequence messages whose source or target is the specified + * element or any of its descendant edit parts, without duplicates. + * + * @return the messages found without duplicates. + */ + public Set getAllMessages() { + Set allMessages = Sets.newHashSet(); + allMessages.addAll(getAllMessagesFrom()); + allMessages.addAll(getAllMessagesTo()); + return allMessages; + } + + /** + * Finds all the sequence messages whose source is the specified element or + * any of its descendant edit parts. + * + * @return the messages found. + */ + public List getAllMessagesFrom() { + List messagesParts = Lists.newArrayList(); + addAllMessagesFrom(event.getNotationView(), messagesParts); + return messagesParts; + } + + /** + * Finds all the sequence messages whose target is the specified element or + * any of its descendant edit parts. + * + * @return the messages found. + */ + public List getAllMessagesTo() { + List messagesParts = Lists.newArrayList(); + addAllMessagesTo(event.getNotationView(), messagesParts); + return messagesParts; + } + + /** + * Finds all the sequence messages whose target is the specified element or + * any of its descendant edit parts and add them to a collection. + * + * @param element + * the element from which to start the search for messages. + * @param messages + * the collection in which to add the messages. + */ + private void addAllMessagesTo(View element, Collection messages) { + for (Edge connectionPart : Iterables.filter(Iterables.filter(element.getTargetEdges(), Edge.class), Message.notationPredicate())) { + Option message = ISequenceElementAccessor.getMessage(connectionPart); + if (message.some()) { + messages.add(message.get()); + } + } + if (element instanceof Message) { + messages.add((Message) element); + } + for (View child : Iterables.filter(element.getChildren(), Node.class)) { + addAllMessagesFrom(child, messages); + } + } + + /** + * Finds all the sequence messages whose source is the specified element or + * any of its descendant edit parts and add them to a collection. + * + * @param element + * the element from which to start the search for messages. + * @param messages + * the collection in which to add the messages. + */ + private void addAllMessagesFrom(View element, Collection messages) { + for (Edge connectionPart : Iterables.filter(Iterables.filter(element.getSourceEdges(), Edge.class), Message.notationPredicate())) { + Option message = ISequenceElementAccessor.getMessage(connectionPart); + if (message.some()) { + messages.add(message.get()); + } + } + if (element instanceof Message) { + messages.add((Message) element); + } + for (View child : Iterables.filter(element.getChildren(), Node.class)) { + addAllMessagesFrom(child, messages); + } + } + + /** + * Common implementation of + * {@link ISequenceEventEditPart#getOccupiedRange()}. + * + * @return the maximal range occupied by children of the event. + */ + public Range getOccupiedRange() { + Range range = Range.emptyRange(); + for (ISequenceEvent child : event.getSubEvents()) { + range = range.union(child.getVerticalRange()); + } + return range; + } + + /** + * Return all moved elements from the context. + * + * @return the all moved elements. + */ + public Set getAllSequenceEventToMoveWith() { + Set entryPoints = Sets.newHashSet(); + return getAllSequenceEventToMoveWith(entryPoints); + } + + /** + * Return all moved elements from the context. + * + * @param additionnalEntryPoints + * additionnal entry points. + * @return the all moved elements. + */ + public Set getAllSequenceEventToMoveWith(Collection additionnalEntryPoints) { + Set entryPoints = Sets.newHashSet(); + entryPoints.add(event); + entryPoints.addAll(additionnalEntryPoints); + + Set moved = Sets.newHashSet(); + for (ISequenceEvent ise : entryPoints) { + populateMovedElements(ise, moved); + } + return moved; + } + + private void populateMovedElements(ISequenceEvent inspectedElement, Set moved) { + moved.add(inspectedElement); + for (ISequenceEvent subEvent : Iterables.filter(inspectedElement.getEventsToMoveWith(), Predicates.not(Predicates.in(moved)))) { + populateMovedElements(subEvent, moved); + } + + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/InstanceRoleQuery.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/InstanceRoleQuery.java new file mode 100644 index 0000000000..18a339ed71 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/InstanceRoleQuery.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.query; + +import com.google.common.base.Preconditions; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.RGBValues; +import org.eclipse.sirius.business.api.diagramtype.HeaderData; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; + +/** + * General queries on {@link InstanceRole}s. + * + * @author lredor + */ +public class InstanceRoleQuery { + /** + * The Instance role to query. + */ + protected final InstanceRole instanceRole; + + /** + * Constructor. + * + * @param instanceRole + * the instance role to query. + */ + public InstanceRoleQuery(InstanceRole instanceRole) { + this.instanceRole = Preconditions.checkNotNull(instanceRole); + } + + /** + * Get the header data corresponding to this instance role. + * + * @return the header data corresponding to this instance role. + */ + public HeaderData getHeaderData() { + Option optionalBackgroundColor = instanceRole.getBackgroundColor(); + Option optionalLabelColor = instanceRole.getLabelColor(); + return new HeaderData(instanceRole.getName(), instanceRole.getBounds().x, instanceRole.getBounds().width, optionalBackgroundColor.get(), optionalLabelColor.get()); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/RangeComparator.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/RangeComparator.java new file mode 100644 index 0000000000..c1efc1fb03 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/RangeComparator.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.query; + +import java.io.Serializable; +import java.util.Comparator; +import java.util.List; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElement; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * {@link Comparator} used to order Set of {@link ISequenceElement} according to + * their {@link Range#getLowerBound()}. + * + * @author Esteban Dugueperoux + */ +public class RangeComparator implements Comparator, Serializable { + + /** + * Generated SUID. + */ + private static final long serialVersionUID = -8968333614931292746L; + + /** + * Overridden to compare {@link ISequenceElement} order according to their + * {@link Range}. + * + * {@inheritDoc} + */ + public int compare(ISequenceElement sequenceElement1, ISequenceElement sequenceElement2) { + Range sequenceElement1Range = Range.verticalRange(sequenceElement1.getProperLogicalBounds()); + Range sequenceElement2Range = Range.verticalRange(sequenceElement2.getProperLogicalBounds()); + int comparison = sequenceElement1Range.getLowerBound() - sequenceElement2Range.getLowerBound(); + if (comparison == 0) { + if (sequenceElement1 instanceof ISequenceEvent && sequenceElement2 instanceof ISequenceEvent) { + ISequenceEvent sequenceEvent1 = (ISequenceEvent) sequenceElement1; + ISequenceEvent sequenceEvent2 = (ISequenceEvent) sequenceElement2; + + SequenceDiagram sequenceDiagram = sequenceElement1.getDiagram(); + + List sequenceEvent1EventEnds = sequenceDiagram.findEnds(sequenceEvent1); + List sequenceEvent2EventEnds = sequenceDiagram.findEnds(sequenceEvent2); + + if (!sequenceEvent1EventEnds.isEmpty() && !sequenceEvent2EventEnds.isEmpty()) { + EventEnd firstEventEndOfSequenceEvent1 = sequenceEvent1EventEnds.get(0); + EventEnd firstEventEndOfSequenceEvent2 = sequenceEvent2EventEnds.get(0); + List eventEnds = sequenceDiagram.getSequenceDDiagram().getGraphicalOrdering().getEventEnds(); + comparison = eventEnds.indexOf(firstEventEndOfSequenceEvent1) - eventEnds.indexOf(firstEventEndOfSequenceEvent2); + } + } + + } + return comparison; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/ReversedRangeComparator.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/ReversedRangeComparator.java new file mode 100644 index 0000000000..6d8261af82 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/ReversedRangeComparator.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.query; + +import java.io.Serializable; +import java.util.Comparator; +import java.util.List; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElement; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * {@link Comparator} used to order Set of {@link ISequenceElement} according to + * their {@link Range#getUpperBound()}. + * + * @author Esteban Dugueperoux + */ +public class ReversedRangeComparator implements Comparator, Serializable { + + /** + * Generated SUID. + */ + private static final long serialVersionUID = -962057468507177598L; + + /** + * Overridden to compare {@link ISequenceElement} order according to their + * range from upper to lower. + * + * {@inheritDoc} + */ + public int compare(ISequenceElement sequenceElement1, ISequenceElement sequenceElement2) { + Range sequenceElement1Range = Range.verticalRange(sequenceElement1.getProperLogicalBounds()); + Range sequenceElement2Range = Range.verticalRange(sequenceElement2.getProperLogicalBounds()); + int comparison = sequenceElement1Range.getUpperBound() - sequenceElement2Range.getUpperBound(); + if (comparison == 0) { + if (sequenceElement1 instanceof ISequenceEvent && sequenceElement2 instanceof ISequenceEvent) { + ISequenceEvent sequenceEvent1 = (ISequenceEvent) sequenceElement1; + ISequenceEvent sequenceEvent2 = (ISequenceEvent) sequenceElement2; + + SequenceDiagram sequenceDiagram = sequenceElement1.getDiagram(); + + List sequenceEvent1EventEnds = sequenceDiagram.findEnds(sequenceEvent1); + List sequenceEvent2EventEnds = sequenceDiagram.findEnds(sequenceEvent2); + + if (!sequenceEvent1EventEnds.isEmpty() && !sequenceEvent2EventEnds.isEmpty()) { + EventEnd lastEventEndOfSequenceEvent1 = sequenceEvent1EventEnds.get(sequenceEvent1EventEnds.size() - 1); + EventEnd lastEventEndOfSequenceEvent2 = sequenceEvent2EventEnds.get(sequenceEvent2EventEnds.size() - 1); + List eventEnds = sequenceDiagram.getSequenceDDiagram().getGraphicalOrdering().getEventEnds(); + comparison = eventEnds.indexOf(lastEventEndOfSequenceEvent2) - eventEnds.indexOf(lastEventEndOfSequenceEvent1); + } + } + + } + return comparison; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/SequenceDiagramDescriptionQuery.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/SequenceDiagramDescriptionQuery.java new file mode 100644 index 0000000000..55c43e9a92 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/SequenceDiagramDescriptionQuery.java @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.query; + +import java.util.Iterator; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterators; + +import org.eclipse.sirius.diagram.sequence.description.EndOfLifeMapping; +import org.eclipse.sirius.diagram.sequence.description.ExecutionMapping; +import org.eclipse.sirius.diagram.sequence.description.InstanceRoleMapping; +import org.eclipse.sirius.diagram.sequence.description.MessageMapping; +import org.eclipse.sirius.diagram.sequence.description.SequenceDiagramDescription; + +/** + * Queries from a {@link SequenceDiagramDescription}. + * + * @author cbrun + * + */ +public class SequenceDiagramDescriptionQuery { + + private SequenceDiagramDescription diag; + + /** + * Create the queries. + * + * @param diag + * the source of the queries. + */ + public SequenceDiagramDescriptionQuery(SequenceDiagramDescription diag) { + this.diag = diag; + } + + /** + * return any InstanceRoleMapping contained in the diagram having the given + * name. + * + * @param name + * name of the searched element. + * @return any InstanceRoleMapping contained in the diagram having the given + * name. + */ + public Iterator getInstanceRoleMappings(final String name) { + return Iterators.filter(Iterators.filter(diag.eAllContents(), InstanceRoleMapping.class), new Predicate() { + + public boolean apply(InstanceRoleMapping input) { + return name.equals(input.getName()); + } + + }); + } + + /** + * return any ExecutionMapping contained in the diagram having the given + * name. + * + * @param name + * name of the searched element. + * @return any ExecutionMapping contained in the diagram having the given + * name. + */ + public Iterator getExecutionMappings(final String name) { + return Iterators.filter(Iterators.filter(diag.eAllContents(), ExecutionMapping.class), new Predicate() { + + public boolean apply(ExecutionMapping input) { + return name.equals(input.getName()); + } + + }); + } + + /** + * return any {@link EndOfLifeMapping} contained in the diagram having the + * given name. + * + * @param name + * name of the searched element. + * @return any EndOfLifeMapping contained in the diagram having the given + * name. + */ + public Iterator getEndOfLifeMappings(final String name) { + return Iterators.filter(Iterators.filter(diag.eAllContents(), EndOfLifeMapping.class), new Predicate() { + + public boolean apply(EndOfLifeMapping input) { + return name.equals(input.getName()); + } + + }); + } + + /** + * return any {@link MessageMapping} contained in the diagram having the + * given name. + * + * @param name + * name of the searched element. + * @return any MessageMapping contained in the diagram having the given + * name. + */ + public Iterator getMessageMappings(final String name) { + return Iterators.filter(Iterators.filter(diag.eAllContents(), MessageMapping.class), new Predicate() { + + public boolean apply(MessageMapping input) { + return name.equals(input.getName()); + } + + }); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/SequenceDiagramQuery.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/SequenceDiagramQuery.java new file mode 100644 index 0000000000..d3edccffe6 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/SequenceDiagramQuery.java @@ -0,0 +1,342 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.query; + +import java.util.Collection; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractFrame; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceNode; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.util.Range; +import org.eclipse.sirius.diagram.tools.api.graphical.edit.styles.IBorderItemOffsets; + +/** + * Queries from a {@link SequenceDiagram}. + * + * @author edugueperoux + * + */ +public class SequenceDiagramQuery { + + private static final String ASSERT_MESSAGE_ON_SEQUENCE_DIAGRAM = "contextual SequenceDiagram can't be null"; + + private static final String ASSERT_MESSAGE_ON_TIME_POINT = "timePoint must be strictly positive"; + + private SequenceDiagram sequenceDiagram; + + /** + * Create the query. + * + * @param sequenceDiagram + * the source of the query. + */ + public SequenceDiagramQuery(SequenceDiagram sequenceDiagram) { + this.sequenceDiagram = sequenceDiagram; + } + + /** + * Get all {@link ISequenceEvent} of the current {@link SequenceDiagram} + * with range's lower bound strictly inferior to timePoint. + * + * @param timePoint + * the timePoint from which get all lower {@link ISequenceEvent} + * + * @return the set of {@link ISequenceEvent} lower timePoint, sorted by + * range from lower to upper + */ + public Set getAllSequenceEventsLowerThan(int timePoint) { + Preconditions.checkNotNull(sequenceDiagram, ASSERT_MESSAGE_ON_SEQUENCE_DIAGRAM); + Preconditions.checkArgument(timePoint > 0, ASSERT_MESSAGE_ON_TIME_POINT); + + Set sequenceEventLowers = new TreeSet(new RangeComparator()); + for (ISequenceEvent sequenceEvent : getAllSequenceEvents()) { + Range sequenceEventRange = sequenceEvent.getVerticalRange(); + if (sequenceEventRange.getLowerBound() < timePoint) { + sequenceEventLowers.add(sequenceEvent); + } + } + return sequenceEventLowers; + } + + /** + * Get all {@link ISequenceEvent} of the current {@link SequenceDiagram} + * with range's including strictly timePoint. + * + * @param timePoint + * the timePoint from which get all {@link ISequenceEvent} + * including it in their range + * + * @return the set of {@link ISequenceEvent} having timePoint in their + * range, sorted by range from lower to upper + */ + public Set getAllSequenceEventsOn(int timePoint) { + Preconditions.checkNotNull(sequenceDiagram, ASSERT_MESSAGE_ON_SEQUENCE_DIAGRAM); + Preconditions.checkArgument(timePoint > 0, ASSERT_MESSAGE_ON_TIME_POINT); + + Set sequenceEventOns = new TreeSet(new RangeComparator()); + for (ISequenceEvent sequenceEvent : getAllSequenceEvents()) { + Range sequenceEventRange = sequenceEvent.getVerticalRange(); + if (sequenceEventRange.includes(timePoint)) { + sequenceEventOns.add(sequenceEvent); + } + } + return sequenceEventOns; + } + + /** + * Get all {@link ISequenceEvent} of the current {@link SequenceDiagram} + * with range's lowerbound strictly upper to timePoint. + * + * @param timePoint + * the timePoint from which get all upper {@link ISequenceEvent} + * + * @return the set of {@link ISequenceEvent} upper timePoint, sorted by + * range from lower to upper + */ + public Set getAllSequenceEventsUpperThan(int timePoint) { + Preconditions.checkNotNull(sequenceDiagram, ASSERT_MESSAGE_ON_SEQUENCE_DIAGRAM); + Preconditions.checkArgument(timePoint > 0, ASSERT_MESSAGE_ON_TIME_POINT); + + Set sequenceEventUppers = new TreeSet(new RangeComparator()); + for (ISequenceEvent sequenceEvent : getAllSequenceEvents()) { + Range sequenceEventRange = sequenceEvent.getVerticalRange(); + if (sequenceEventRange.getLowerBound() > timePoint) { + sequenceEventUppers.add(sequenceEvent); + } + } + return sequenceEventUppers; + } + + /** + * Get all {@link ISequenceEvent} of the current {@link SequenceDiagram}. + * + * @return the set of {@link ISequenceEvent} of the current + * {@link SequenceDiagram}, sorted by range from lower to upper + */ + public Set getAllSequenceEvents() { + Preconditions.checkNotNull(sequenceDiagram, ASSERT_MESSAGE_ON_SEQUENCE_DIAGRAM); + + Set allSequenceEvents = new TreeSet(new RangeComparator()); + allSequenceEvents.addAll(sequenceDiagram.getAllLifelines()); + allSequenceEvents.addAll(sequenceDiagram.getAllAbstractNodeEvents()); + allSequenceEvents.addAll(sequenceDiagram.getAllMessages()); + allSequenceEvents.addAll(sequenceDiagram.getAllInteractionUses()); + allSequenceEvents.addAll(sequenceDiagram.getAllCombinedFragments()); + allSequenceEvents.addAll(sequenceDiagram.getAllOperands()); + allSequenceEvents.addAll(sequenceDiagram.getAllStates()); + return allSequenceEvents; + } + + /** + * Get all {@link ISequenceEvent} of the current {@link SequenceDiagram} of + * the specified {@link Lifeline}. + * + * @param lifeline + * the specified {@link Lifeline} + * + * @return the set of {@link ISequenceEvent} of the current + * {@link SequenceDiagram} of the specified {@link Lifeline}, sorted + * by range from lower to upper + */ + public Set getAllSequenceEventsOnLifeline(Lifeline lifeline) { + Set allSequenceEventsOnLifeline = new TreeSet(new RangeComparator()); + for (ISequenceEvent sequenceEvent : Iterables.filter(getAllSequenceEvents(), Predicates.not(Predicates.instanceOf(Lifeline.class)))) { + Option lifelineOfSequenceEventOption = sequenceEvent.getLifeline(); + if (lifelineOfSequenceEventOption.some() && lifelineOfSequenceEventOption.get().equals(lifeline)) { + allSequenceEventsOnLifeline.add(sequenceEvent); + } else if (!lifelineOfSequenceEventOption.some()) { + if (sequenceEvent instanceof Message) { + Message message = (Message) sequenceEvent; + Option sourceLifelineOption = message.getSourceLifeline(); + Option targetLifelineOption = message.getTargetLifeline(); + if (sourceLifelineOption.some() && sourceLifelineOption.get().equals(lifeline)) { + allSequenceEventsOnLifeline.add(message); + } else if (targetLifelineOption.some() && targetLifelineOption.get().equals(lifeline)) { + allSequenceEventsOnLifeline.add(message); + } + } else if (sequenceEvent instanceof AbstractFrame) { + AbstractFrame abstractFrame = (AbstractFrame) sequenceEvent; + Collection coveredLifelines = abstractFrame.computeCoveredLifelines(); + if (coveredLifelines.contains(lifeline)) { + allSequenceEventsOnLifeline.add(abstractFrame); + } + } + } + } + return allSequenceEventsOnLifeline; + } + + /** + * Get all events having at least one bound on the given {@link Lifeline} in + * the given range. + * + * @param lifeline + * the lifeline to check. + * @param inclusionRange + * the range to look in. + * @return events on the given lifeline and range. + */ + public SortedSet getAllSequenceEventsOnLifelineOnRange(Lifeline lifeline, Range inclusionRange) { + SortedSet allSequenceEventsOnLifelineStrictlyIncludedBetween = new TreeSet(new RangeComparator()); + for (ISequenceEvent sequenceEvent : getAllSequenceEventsOnLifeline(lifeline)) { + Range sequenceEventRange = sequenceEvent.getVerticalRange(); + if (inclusionRange.includesAtLeastOneBound(sequenceEventRange)) { + allSequenceEventsOnLifelineStrictlyIncludedBetween.add(sequenceEvent); + } + } + return allSequenceEventsOnLifelineStrictlyIncludedBetween; + } + + /** + * Get all {@link ISequenceNode} of the current {@link SequenceDiagram} with + * range's lowerbound strictly lower to timePoint. + * + * @param timePoint + * the timePoint from which get all lower {@link ISequenceNode} + * + * @return the set of {@link ISequenceNode} lower timePoint, sorted by range + * from lower to upper + */ + public Set getAllSequenceNodesLowerThan(int timePoint) { + Preconditions.checkNotNull(sequenceDiagram, ASSERT_MESSAGE_ON_SEQUENCE_DIAGRAM); + Preconditions.checkArgument(timePoint > 0, ASSERT_MESSAGE_ON_TIME_POINT); + + Set sequenceNodeLowers = new TreeSet(new RangeComparator()); + for (ISequenceNode sequenceNode : getAllSequenceNodes()) { + Range sequenceNodeRange = Range.verticalRange(sequenceNode.getBounds()); + if (sequenceNodeRange.getLowerBound() < timePoint) { + sequenceNodeLowers.add(sequenceNode); + } + } + return sequenceNodeLowers; + } + + /** + * Get all {@link ISequenceNode} of the current {@link SequenceDiagram} with + * range's including strictly timePoint. + * + * @param timePoint + * the timePoint from which get all {@link ISequenceNode} + * including it in their range + * + * @return the set of {@link ISequenceNode} having timePoint in their range, + * sorted by range from lower to upper + */ + public Set getAllSequenceNodesOn(int timePoint) { + Preconditions.checkNotNull(sequenceDiagram, ASSERT_MESSAGE_ON_SEQUENCE_DIAGRAM); + Preconditions.checkArgument(timePoint > 0, "timePoint must be strictly positive"); + + Set sequenceNodeUnders = new TreeSet(new RangeComparator()); + for (ISequenceNode sequenceNode : getAllSequenceNodes()) { + Range sequenceEventRange = Range.verticalRange(sequenceNode.getBounds()); + if (sequenceEventRange.includes(timePoint)) { + sequenceNodeUnders.add(sequenceNode); + } + } + return sequenceNodeUnders; + } + + /** + * Get all {@link ISequenceNode} of the current {@link SequenceDiagram} with + * range's lowerbound strictly upper to timePoint. + * + * @param timePoint + * the timePoint from which get all upper {@link ISequenceNode} + * + * @return the set of {@link ISequenceNode} upper timePoint, sorted by range + * from lower to upper + */ + public Set getAllSequenceNodesUpperThan(int timePoint) { + Preconditions.checkNotNull(sequenceDiagram, ASSERT_MESSAGE_ON_SEQUENCE_DIAGRAM); + Preconditions.checkArgument(timePoint > 0, ASSERT_MESSAGE_ON_TIME_POINT); + + Set sequenceNodeUnders = new TreeSet(new RangeComparator()); + for (ISequenceNode sequenceNode : getAllSequenceNodes()) { + Range sequenceNodeRange = Range.verticalRange(sequenceNode.getBounds()); + if (sequenceNodeRange.getLowerBound() > timePoint) { + sequenceNodeUnders.add(sequenceNode); + } + } + return sequenceNodeUnders; + } + + /** + * Get all {@link ISequenceNode} of the current {@link SequenceDiagram}. + * + * @return the set of {@link ISequenceNode} of the current + * {@link SequenceDiagram}, sorted by range from lower to upper + */ + public Set getAllSequenceNodes() { + Preconditions.checkNotNull(sequenceDiagram, "contextual SequenceDiagram can't be null"); + + Set allSequenceNodes = new TreeSet(new RangeComparator()); + allSequenceNodes.addAll(sequenceDiagram.getAllInstanceRoles()); + allSequenceNodes.addAll(sequenceDiagram.getAllLifelines()); + allSequenceNodes.addAll(sequenceDiagram.getAllAbstractNodeEvents()); + allSequenceNodes.addAll(sequenceDiagram.getAllInteractionUses()); + allSequenceNodes.addAll(sequenceDiagram.getAllCombinedFragments()); + allSequenceNodes.addAll(sequenceDiagram.getAllOperands()); + allSequenceNodes.addAll(sequenceDiagram.getAllEndOfLifes()); + return allSequenceNodes; + } + + /** + * Get the initial time from which event ends can graphically start. This + * initial time corresponds to y coordinate of the bottom of the biggest + * InstanceRole not connected to a Create message + + * {@link LayoutConstants#TIME_START_OFFSET}. + * + * @return the initial time if the current {@link SequenceDiagram} contains + * {@link Lifeline}, 0 else + */ + public int getInitialTime() { + Preconditions.checkNotNull(sequenceDiagram, ASSERT_MESSAGE_ON_SEQUENCE_DIAGRAM); + int initialTime = 0; + for (Lifeline lifeline : sequenceDiagram.getAllLifelines()) { + int initialTimeCandidate = lifeline.getVerticalRange().getLowerBound(); + if (initialTimeCandidate > initialTime && !lifeline.getInstanceRole().isExplicitlyCreated()) { + initialTime = initialTimeCandidate; + } + } + return initialTime + LayoutConstants.TIME_START_OFFSET + IBorderItemOffsets.DEFAULT_OFFSET.height; + } + + /** + * Get the final time until which event ends can graphically end. This final + * time corresponds to y coordinate of the bottom of {@link Lifeline} - + * {@link LayoutConstants#TIME_STOP_OFFSET}. + * + * @return the final time if the current {@link SequenceDiagram} contains + * {@link Lifeline}, 0 else + */ + public int getFinalTime() { + Preconditions.checkNotNull(sequenceDiagram, ASSERT_MESSAGE_ON_SEQUENCE_DIAGRAM); + int finalTime = 0; + if (!sequenceDiagram.getAllLifelines().isEmpty()) { + Lifeline lifeline = sequenceDiagram.getAllLifelines().iterator().next(); + finalTime = lifeline.getVerticalRange().getUpperBound() - LayoutConstants.TIME_STOP_OFFSET; + } + return finalTime; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/SequenceMessageViewQuery.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/SequenceMessageViewQuery.java new file mode 100644 index 0000000000..ad69802617 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/SequenceMessageViewQuery.java @@ -0,0 +1,292 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.query; + +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.IdentityAnchor; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.RelativeBendpoints; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.gmf.runtime.notation.datatype.RelativeBendpoint; + +import com.google.common.base.Preconditions; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.diagram.sequence.business.internal.VerticalRangeFunction; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElement; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * Queries on sequence messages (all kinds). + * + * @author pcdavid + */ +public class SequenceMessageViewQuery { + /** + * The message to query. + */ + private final Edge edge; + + /** + * Constructor. + * + * @param edge + * the message to query. + */ + public SequenceMessageViewQuery(Edge edge) { + Preconditions.checkArgument(Message.notationPredicate().apply(edge)); + this.edge = edge; + } + + /** + * Tests whether this a reflective message, i.e. both its source and target + * are in the context of the same lifeline. + * + * @return true if this message is reflective. + */ + public boolean isReflective() { + Option optSource = getSourceLifeline(); + Option optTarget = getTargetLifeline(); + return optSource.some() && optTarget.some() && optSource.get() == optTarget.get(); + } + + /** + * Returns the lifeline in the context of which this message is sent. + * + * @return the lifeline in the context of which this message is sent. + */ + public Option getSourceLifeline() { + Option result = Options.newNone(); + View source = edge.getSource(); + Option iSequenceElement = ISequenceElementAccessor.getISequenceElement(source); + if (iSequenceElement.some()) { + Option lifeline = iSequenceElement.get().getLifeline(); + if (lifeline.some()) { + result = Options.newSome(lifeline.get().getNotationNode()); + } + } + return result; + } + + /** + * Returns the lifeline in the context of which this message is received. + * + * @return the lifeline in the context of which this message is received. + */ + public Option getTargetLifeline() { + Option result = Options.newNone(); + View target = edge.getTarget(); + Option iSequenceElement = ISequenceElementAccessor.getISequenceElement(target); + if (iSequenceElement.some()) { + Option lifeline = iSequenceElement.get().getLifeline(); + if (lifeline.some()) { + result = Options.newSome(lifeline.get().getNotationNode()); + } + } + return result; + } + + /** + * Returns the vertical range of the message. + * + * @return the vertical range of the message. + */ + public Range getVerticalRange() { + Range result; + RelativeBendpoints bendpoints = (RelativeBendpoints) edge.getBendpoints(); + if (bendpoints == null || bendpoints.getPoints().isEmpty()) { + result = Range.emptyRange(); + } else { + int firstY = getFirstPointVerticalPosition(true); + if (isLogicallyInstantaneous()) { + if (validateFirstPointStability(firstY)) { + result = new Range(firstY, firstY); + } else { + int lastY = getLastPointVerticalPosition(false); + result = new Range(lastY, lastY); + } + } else { + int lastY = getLastPointVerticalPosition(false); + if (msgToSelfInvalidEndLocation(edge.getSource(), edge.getTarget())) { + firstY = VerticalRangeFunction.INSTANCE.apply(edge.getSource()).getUpperBound(); + lastY = firstY + LayoutConstants.MESSAGE_TO_SELF_BENDPOINT_VERTICAL_GAP; + } + result = new Range(Math.min(firstY, lastY), Math.max(firstY, lastY)); + } + } + return result; + } + + /** + * Use only to check stability. Returns the vertical range of the message + * calculated with anchor position from source or from target. + * + * @param source + * anchor selection for range computation. + * @return the vertical range of the message from source or target, as + * requested. + */ + public Range getVerticalRange(boolean source) { + Range result; + RelativeBendpoints bendpoints = (RelativeBendpoints) edge.getBendpoints(); + if (bendpoints == null || bendpoints.getPoints().isEmpty()) { + result = Range.emptyRange(); + } else { + int firstY = getFirstPointVerticalPosition(source); + int lastY = getLastPointVerticalPosition(source); + result = new Range(Math.min(firstY, lastY), Math.max(firstY, lastY)); + } + return result; + } + + /** + * FIXME this workaround should be remove when business rules will be move from + * sequence ui to sequence core. This workaround is needed when dropping an + * execution on messages that will be reconnected. + * + * @param firstY + * the vertical position of the first bendpoint + * @return if the range should be calculated with the other end of the + * message. + */ + private boolean validateFirstPointStability(int firstY) { + // if (getSource() instanceof ISequenceEvent) { + // ISequenceEvent ise = (ISequenceEvent) getSource(); + // Edge edge = (Edge) this.getNotationView(); + // // Validate that the edge notation source is the same as the SMEP + // source notation + // return + // edge.getSource().getElement().equals(ise.resolveSemanticElement()); + // } + return true; + } + + /** + * . + * + * @param source + * . + * @return . + */ + public int getFirstPointVerticalPosition(boolean source) { + RelativeBendpoints bendpoints = (RelativeBendpoints) edge.getBendpoints(); + RelativeBendpoint firstPoint = (RelativeBendpoint) bendpoints.getPoints().get(0); + return getPointVerticalPosition(firstPoint, source); + } + + /** + * + * @param otherEnd + * @return + */ + private boolean msgToSelfInvalidEndLocation(View end, View otherEnd) { + // FIXME + // if (SequenceDiagramNotationHelper.isSequenceEvent(end)) { + // if (isReflective() && otherEnd != null) { + // if (EcoreUtil.isAncestor(otherEnd, end)) { + // return true; + // + // } + // // Point location = ((ISequenceEvent) + // // end).getFigure().getBounds().getLocation(); + // // return location.x == 0 && location.y == 0; + // } + // } + return false; + } + + /** + * . + * + * @param source + * . + * @return . + */ + public int getLastPointVerticalPosition(boolean source) { + RelativeBendpoints bendpoints = (RelativeBendpoints) edge.getBendpoints(); + RelativeBendpoint lastPoint = (RelativeBendpoint) bendpoints.getPoints().get(bendpoints.getPoints().size() - 1); + return getPointVerticalPosition(lastPoint, source); + } + + /** + * . + * + * @param bendpoint + * . + * @param source + * . + * @return . + */ + public int getPointVerticalPosition(RelativeBendpoint bendpoint, boolean source) { + int verticalPosition = 0; + if (!source) { + int tgtRefY = getTargetAnchorVerticalPosition(); + verticalPosition = tgtRefY + bendpoint.getTargetY(); + } else { + int srcRefY = getSourceAnchorVerticalPosition(); + verticalPosition = srcRefY + bendpoint.getSourceY(); + } + return verticalPosition; + } + + /** + * . + * + * @return . + */ + public int getSourceAnchorVerticalPosition() { + IdentityAnchor srcAnchor = (IdentityAnchor) edge.getSourceAnchor(); + Range sourceRange = new SequenceNodeQuery((Node) edge.getSource()).getVerticalRange(); + return getAnchorAbsolutePosition(srcAnchor, sourceRange); + // could not return 0 : other utility methode take 0,5 precision point + // for null anchor. + // return 0; + } + + /** + * Should not be called if !(getTarget() instanceof ISequenceEvent). + * + * @return target anchor absolute position. + */ + public int getTargetAnchorVerticalPosition() { + IdentityAnchor tgtAnchor = (IdentityAnchor) edge.getTargetAnchor(); + if (edge.getTarget() instanceof Node) { + Range targetRange = new SequenceNodeQuery((Node) edge.getTarget()).getVerticalRange(); + return getAnchorAbsolutePosition(tgtAnchor, targetRange); + } + return getSourceAnchorVerticalPosition(); + // could not return 0 : other utility methode take 0,5 precision point + // for null anchor. + // return 0; + } + + private int getAnchorAbsolutePosition(IdentityAnchor anchor, Range range) { + double relY = anchor != null ? parseRelY(anchor.getId()) : 0.5; + return range.getLowerBound() + (int) Math.round(relY * range.width()); + } + + private double parseRelY(String terminal) { + try { + return Float.parseFloat(terminal.substring(terminal.indexOf(',') + 1, terminal.indexOf(')'))); + } catch (NumberFormatException e) { + return Double.NaN; + } + } + + private boolean isLogicallyInstantaneous() { + return !isReflective(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/SequenceNodeQuery.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/SequenceNodeQuery.java new file mode 100644 index 0000000000..67b551eeee --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/SequenceNodeQuery.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.query; + +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.Size; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.DNodeContainer; +import org.eclipse.sirius.WorkspaceImage; +import org.eclipse.sirius.diagram.business.internal.query.DNodeQuery; +import org.eclipse.sirius.diagram.internal.refresh.GMFHelper; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.EndOfLife; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.util.Range; +import org.eclipse.sirius.diagram.tools.api.graphical.edit.styles.IBorderItemOffsets; + +/** + * Queries on sequence elements which are represented by GMF nodes. + * + * @author pcdavid + */ +public class SequenceNodeQuery { + + private final Node node; + + /** + * Constructor. + * + * @param node + * the node to query. + */ + public SequenceNodeQuery(Node node) { + this.node = node; + } + + /** + * Returns the vertical range of a sequence diagram element from its GMF + * notation node. + * + * @return the vertical range of the element. + */ + public Range getVerticalRange() { + EObject element = node.getElement(); + if (!(element instanceof DDiagramElement)) { + return null; + } else { + Rectangle absoluteBounds = GMFHelper.getAbsoluteBounds(node); + int y = absoluteBounds.y; + int height = absoluteBounds.height; + // GMFHelper.getAbsoluteBounds() use default + // DDiagramElementContainer (DNodeContainer/DNodeList) dimension if + // size == (-1,-1) and here we need to have the real GMF size if == + // to (-1,-1) + if (node.getLayoutConstraint() instanceof Size) { + Size size = (Size) node.getLayoutConstraint(); + height = size.getHeight(); + } + // handle container auto size -> range.widht = 0, next layout will + // set the good value + // check in interaction use view factory, that it cannot be there + if (element instanceof DNodeContainer && height == -1) { + height = 0; + } + if (height == -1 && element instanceof DNode && ((DNode) element).getOwnedStyle() instanceof WorkspaceImage) { + height = new DNodeQuery((DNode) element).getDefaultDimension().height; + } + + Range result = new Range(y, y + height); + + if (isShifted()) { + result = result.shifted(IBorderItemOffsets.DEFAULT_OFFSET.height); + } + + return result; + } + } + + private boolean isShifted() { + return Lifeline.notationPredicate().apply(node) || AbstractNodeEvent.notationPredicate().apply(node) || EndOfLife.notationPredicate().apply(node); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/TSequenceDiagramQuery.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/TSequenceDiagramQuery.java new file mode 100644 index 0000000000..4f6b7672cb --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/query/TSequenceDiagramQuery.java @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.query; + +import java.util.Iterator; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterators; + +import org.eclipse.sirius.diagram.sequence.template.TBasicMessageMapping; +import org.eclipse.sirius.diagram.sequence.template.TExecutionMapping; +import org.eclipse.sirius.diagram.sequence.template.TLifelineMapping; +import org.eclipse.sirius.diagram.sequence.template.TSequenceDiagram; + +/** + * Queries starting from a {@link TSequenceDiagram}. + * + * @author cbrun + * + */ +public class TSequenceDiagramQuery { + + private TSequenceDiagram template; + + /** + * Create a new query. + * + * @param template + * starting point of the query. + */ + public TSequenceDiagramQuery(TSequenceDiagram template) { + this.template = template; + } + + /** + * return any Lifeline Mapping having the given name. + * + * @param name + * name of the element to find. + * @return any Lifeline Mapping having the given name. + */ + public Iterator getLifeLineMappings(final String name) { + return Iterators.filter(Iterators.filter(template.eAllContents(), TLifelineMapping.class), new Predicate() { + + public boolean apply(TLifelineMapping input) { + return name.equals(input.getName()); + } + + }); + } + + /** + * return any {@link TExecutionMapping} Mapping having the given name. + * + * @param name + * name of the element to find. + * @return any TExecutionMapping Mapping having the given name. + */ + public Iterator getExecutionMappings(final String name) { + return Iterators.filter(Iterators.filter(template.eAllContents(), TExecutionMapping.class), new Predicate() { + + public boolean apply(TExecutionMapping input) { + return name.equals(input.getName()); + } + + }); + } + + /** + * return any {@link TBasicMessageMapping} Mapping having the given name. + * + * @param name + * name of the element to find. + * @return any TBasicMessageMapping Mapping having the given name. + */ + public Iterator getBasicMessageMapping(final String name) { + return Iterators.filter(Iterators.filter(template.eAllContents(), TBasicMessageMapping.class), new Predicate() { + + public boolean apply(TBasicMessageMapping input) { + return name.equals(input.getName()); + } + + }); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/FixBendpointsOnCreationCommand.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/FixBendpointsOnCreationCommand.java new file mode 100644 index 0000000000..76d2020f69 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/FixBendpointsOnCreationCommand.java @@ -0,0 +1,266 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.refresh; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.PointList; +import org.eclipse.draw2d.geometry.PrecisionPoint; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.transaction.RecordingCommand; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.gmf.runtime.draw2d.ui.figures.BaseSlidableAnchor; +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.IdentityAnchor; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.NotationFactory; +import org.eclipse.gmf.runtime.notation.NotationPackage; +import org.eclipse.gmf.runtime.notation.RelativeBendpoints; +import org.eclipse.gmf.runtime.notation.Routing; +import org.eclipse.gmf.runtime.notation.Style; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.gmf.runtime.notation.datatype.RelativeBendpoint; + +import org.eclipse.sirius.AbstractDNode; +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.diagram.business.api.view.SiriusLayoutDataManager; +import org.eclipse.sirius.diagram.business.internal.view.EdgeLayoutData; +import org.eclipse.sirius.diagram.business.internal.view.LayoutData; +import org.eclipse.sirius.diagram.internal.refresh.GMFHelper; +import org.eclipse.sirius.diagram.internal.refresh.edge.SlidableAnchor; +import org.eclipse.sirius.diagram.tools.api.graphical.edit.styles.IBorderItemOffsets; + +/** + * A Command to update sequence message bendpoints after the + * CanonicalSynchronizer. + * + * @author Esteban Dugueperoux + */ +public class FixBendpointsOnCreationCommand extends RecordingCommand { + + private Edge newEdge; + + private View source; + + private View target; + + // Anchors + private String sourceTerminal; + + private String targetTerminal; + + // Bendpoints + private PointList pointList; + + // Routing + private Routing routing; + + /** + * Default constructor. + * + * @param domain + * the {@link TransactionalEditingDomain} on which execute this + * Command + * + * @param createdEdge + * the {@link Edge} for which update the bendpoints + */ + public FixBendpointsOnCreationCommand(TransactionalEditingDomain domain, Edge createdEdge) { + super(domain); + this.newEdge = createdEdge; + } + + /** + * Overridden to update the bendpoints of the specified {@link Edge}. + * + * {@inheritDoc} + */ + @Override + protected void doExecute() { + + this.source = newEdge.getSource(); + this.target = newEdge.getTarget(); + + if (source != null && target != null) { + + EObject element = newEdge.getElement(); + if (element instanceof DEdge) { + DEdge dEdge = (DEdge) element; + updateEdge(dEdge); + } + } + } + + private void updateEdge(DEdge dEdge) { + + // Bendpoints + pointList = new PointList(); + Point sourceRefPoint = new Point(0, 0); + Point targetRefPoint = new Point(0, 0); + + EdgeLayoutData egdeLayoutData = null; + SiriusLayoutDataManager.INSTANCE.setIgnoreConsumeState(true); + try { + egdeLayoutData = SiriusLayoutDataManager.INSTANCE.getData(dEdge, false); + } finally { + SiriusLayoutDataManager.INSTANCE.setIgnoreConsumeState(false); + } + if (egdeLayoutData != null) { + + sourceTerminal = egdeLayoutData.getSourceTerminal(); + targetTerminal = egdeLayoutData.getTargetTerminal(); + + // pointList, sourceRefPoint, targetRefPoint of + // the edgeLayoutData can be null if the edge is + // hide when the layout data is stored + if (egdeLayoutData.getPointList() != null) { + pointList = egdeLayoutData.getPointList(); + } + if (egdeLayoutData.getSourceRefPoint() != null) { + sourceRefPoint = egdeLayoutData.getSourceRefPoint(); + } + if (egdeLayoutData.getTargetRefPoint() != null) { + targetRefPoint = egdeLayoutData.getTargetRefPoint(); + } + + routing = egdeLayoutData.getRouting(); + + } else { + computeEdgeDataWithoutEdgeLayoutData(); + } + + // Set connection anchors : + updateConnectionAnchors(); + + // Set Bendpoints : + updateBendpoints(sourceRefPoint, targetRefPoint); + } + + private void computeEdgeDataWithoutEdgeLayoutData() { + Point firstClick = new Point(0, 0); + Rectangle sourceBounds = GMFHelper.getAbsoluteBounds((Node) source); + if (source.getElement() instanceof AbstractDNode) { + AbstractDNode sourceDNode = (AbstractDNode) source.getElement(); + if (sourceDNode.eContainer() instanceof AbstractDNode) { + AbstractDNode parentDNode = (AbstractDNode) sourceDNode.eContainer(); + if (parentDNode.getOwnedBorderedNodes().contains(sourceDNode)) { + sourceBounds.y += IBorderItemOffsets.DEFAULT_OFFSET.height; + sourceBounds.height += 5; + } + } + LayoutData sourceLayoutData = null; + SiriusLayoutDataManager.INSTANCE.setIgnoreConsumeState(true); + try { + sourceLayoutData = SiriusLayoutDataManager.INSTANCE.getData(sourceDNode); + } finally { + SiriusLayoutDataManager.INSTANCE.setIgnoreConsumeState(false); + } + if (sourceLayoutData == null) { + firstClick = sourceBounds.getCenter(); + } else { + Point sourceLocation = sourceLayoutData.getLocation(); + firstClick = sourceLocation; + } + } + + PrecisionPoint sourceRelativeLocation = BaseSlidableAnchor.getAnchorRelativeLocation(firstClick, sourceBounds); + sourceTerminal = "(" + sourceRelativeLocation.preciseX + "," + sourceRelativeLocation.preciseY + ")"; + + Point secondClick = new Point(0, 0); + Rectangle targetBounds = GMFHelper.getAbsoluteBounds((Node) target); + + if (target.getElement() instanceof AbstractDNode) { + AbstractDNode targetDNode = (AbstractDNode) target.getElement(); + if (targetDNode.eContainer() instanceof AbstractDNode) { + AbstractDNode parentDNode = (AbstractDNode) targetDNode.eContainer(); + if (parentDNode.getOwnedBorderedNodes().contains(targetDNode)) { + targetBounds.y += IBorderItemOffsets.DEFAULT_OFFSET.height; + targetBounds.height += 5; + } + } + LayoutData targetLayoutData = null; + SiriusLayoutDataManager.INSTANCE.setIgnoreConsumeState(true); + try { + targetLayoutData = SiriusLayoutDataManager.INSTANCE.getData(targetDNode); + } finally { + SiriusLayoutDataManager.INSTANCE.setIgnoreConsumeState(false); + } + + if (targetLayoutData == null) { + secondClick = targetBounds.getCenter(); + } else { + Point targetLocation = targetLayoutData.getLocation(); + secondClick = targetLocation; + } + } + + PrecisionPoint targetRelativeLocation = BaseSlidableAnchor.getAnchorRelativeLocation(secondClick, targetBounds); + targetTerminal = "(" + targetRelativeLocation.preciseX + "," + targetRelativeLocation.preciseY + ")"; + + // Computes pointList + PrecisionPoint sourceRelativeReference = SequenceSlidableAnchor.parseTerminalString(sourceTerminal); + SlidableAnchor sourceAnchor = new SequenceSlidableAnchor((Node) source, sourceRelativeReference); + pointList.addPoint(sourceAnchor.getLocation(sourceAnchor.getReferencePoint())); + + PrecisionPoint targetRelativeReference = SequenceSlidableAnchor.parseTerminalString(targetTerminal); + SlidableAnchor targetAnchor = new SequenceSlidableAnchor((Node) target, targetRelativeReference); + pointList.addPoint(targetAnchor.getLocation(targetAnchor.getReferencePoint())); + } + + private void updateConnectionAnchors() { + if (sourceTerminal != null) { + if (sourceTerminal.length() == 0) + newEdge.setSourceAnchor(null); + else { + IdentityAnchor a = (IdentityAnchor) newEdge.getSourceAnchor(); + if (a == null) + a = NotationFactory.eINSTANCE.createIdentityAnchor(); + a.setId(sourceTerminal); + newEdge.setSourceAnchor(a); + } + } + if (targetTerminal != null) { + if (targetTerminal.length() == 0) + newEdge.setTargetAnchor(null); + else { + IdentityAnchor a = (IdentityAnchor) newEdge.getTargetAnchor(); + if (a == null) + a = NotationFactory.eINSTANCE.createIdentityAnchor(); + a.setId(targetTerminal); + newEdge.setTargetAnchor(a); + } + + } + } + + private void updateBendpoints(Point sourceRefPoint, Point targetRefPoint) { + List newBendpoints = new ArrayList(); + int numOfPoints = pointList.size(); + for (short i = 0; i < numOfPoints; i++) { + Dimension s = pointList.getPoint(i).getDifference(sourceRefPoint); + Dimension t = pointList.getPoint(i).getDifference(targetRefPoint); + newBendpoints.add(new RelativeBendpoint(s.width, s.height, t.width, t.height)); + } + + RelativeBendpoints points = (RelativeBendpoints) newEdge.getBendpoints(); + points.setPoints(newBendpoints); + + // Routing + if (routing != null) { + Style routingStyle = newEdge.getStyle(NotationPackage.Literals.ROUTING_STYLE); + routingStyle.eSet(NotationPackage.Literals.ROUTING_STYLE__ROUTING, routing); + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/RefreshLayoutCommand.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/RefreshLayoutCommand.java new file mode 100644 index 0000000000..3dab7ec0df --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/RefreshLayoutCommand.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.refresh; + +import org.eclipse.emf.transaction.RecordingCommand; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.gmf.runtime.notation.Diagram; + +import org.eclipse.sirius.diagram.business.internal.operation.AbstractModelChangeOperation; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.RefreshGraphicalOrderingOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.RefreshSemanticOrderingsOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.SynchronizeGraphicalOrderingOperation; + +/** + * Command to refresh the graphical layout of the whole diagram. + * + * @author edugueperoux + */ +public class RefreshLayoutCommand extends RecordingCommand { + + private Diagram diagram; + + private boolean refreshDiagram; + + /** + * Default constructor. + * + * @param diagram + * {@link Diagram} to refresh, used also to access + * {@link SequenceDDiagram} & {@link SequenceDiagram} to refresh + * + * @param refreshDiagram + * true if we should actually update the GMF model + * + * {@inheritDoc} + */ + public RefreshLayoutCommand(TransactionalEditingDomain domain, Diagram diagram, boolean refreshDiagram) { + super(domain, "Refresh graphical layout"); + this.diagram = diagram; + this.refreshDiagram = refreshDiagram; + } + + /** + * Overridden to refresh the sequence layout. + * + * {@inheritDoc} + */ + @Override + protected void doExecute() { + SequenceDiagram sequenceDiagram = ISequenceElementAccessor.getSequenceDiagram(diagram).get(); + SequenceDDiagram sequenceDDiagram = sequenceDiagram.getSequenceDDiagram(); + + /* + * Everything has been committed, so we should be in a stable state + * where it is safe to refresh both orderings. + */ + AbstractModelChangeOperation refreshSemanticOrderingOperation = new RefreshSemanticOrderingsOperation(sequenceDDiagram); + refreshSemanticOrderingOperation.execute(); + AbstractModelChangeOperation refreshGraphicalOrderingOperation = new RefreshGraphicalOrderingOperation(sequenceDiagram); + refreshGraphicalOrderingOperation.execute(); + + if (refreshDiagram) { + /* + * Launch a non-packing layout + */ + AbstractModelChangeOperation synchronizeGraphicalOrderingOperation = new SynchronizeGraphicalOrderingOperation(diagram, false); + synchronizeGraphicalOrderingOperation.execute(); + /* + * The layout has probably changed graphical positions: re-compute + * the ordering to make sure it is up-to-date. + */ + refreshGraphicalOrderingOperation.execute(); + } + + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/RefreshLayoutScope.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/RefreshLayoutScope.java new file mode 100644 index 0000000000..f6af464111 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/RefreshLayoutScope.java @@ -0,0 +1,133 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.refresh; + +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.gmf.runtime.notation.NotationPackage; + +import com.google.common.base.Predicate; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.SiriusPackage; +import org.eclipse.sirius.WorkspaceImage; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.EndOfLife; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; +import org.eclipse.sirius.diagram.sequence.business.internal.util.BendpointsHelper; +import org.eclipse.sirius.diagram.sequence.util.NotificationQuery; + +/** + * Default refresh layout scope for sequence diagram. This predicate decides + * whether or not we need to refresh the graphical layout, i.e. launch a + * non-packing layout + * + * @author mporhel + */ +public class RefreshLayoutScope implements Predicate { + + private final Predicate isLayoutConstraintNotationChange = new Predicate() { + Object[] features = new Object[] { NotationPackage.eINSTANCE.getRelativeBendpoints_Points(), NotationPackage.eINSTANCE.getLocation_Y(), NotationPackage.eINSTANCE.getLocation_X(), + NotationPackage.eINSTANCE.getSize_Width(), NotationPackage.eINSTANCE.getSize_Height(), }; + + public boolean apply(Notification input) { + NotificationQuery nq = new NotificationQuery(input); + return nq.isNotationChange() && isLayout(input.getFeature()) && !input.isTouch(); + } + + private boolean isLayout(Object feature) { + for (Object feature2 : features) { + if (feature == feature2) { + return true; + } + } + return false; + } + }; + + private final Predicate isSructuralNotationChange = new Predicate() { + int[] types = new int[] { Notification.ADD, Notification.ADD_MANY, Notification.MOVE, Notification.REMOVE, Notification.REMOVE_MANY }; + + public boolean apply(Notification input) { + NotificationQuery nq = new NotificationQuery(input); + return nq.isNotationChange() && isStructural(input.getEventType()); + } + + private boolean isStructural(int eventType) { + for (int type : types) { + if (eventType == type) { + return true; + } + } + return false; + } + }; + + /** + * {@inheritDoc} + */ + public boolean apply(Notification input) { + return needsLayout(input); + } + + private boolean needsLayout(Notification notification) { + return !isLayoutTouch(notification) && (containsStructuralNotationChanges(notification) || containsLayoutConstraintNotationChanges(notification) || containsSetWkpImgApplication(notification)); + } + + private boolean containsLayoutConstraintNotationChanges(Notification notification) { + return isLayoutConstraintNotationChange.apply(notification); + } + + private boolean containsStructuralNotationChanges(Notification notification) { + + return isSructuralNotationChange.apply(notification); + } + + private boolean isLayoutTouch(Notification notification) { + boolean result = true; + + boolean isTouch = notification.isTouch(); + if (!isTouch) { + if (NotationPackage.eINSTANCE.getRelativeBendpoints_Points().equals(notification.getFeature())) { + isTouch = BendpointsHelper.areSameBendpoints(notification.getOldValue(), notification.getNewValue()); + } + } + + if (!isTouch) { + result = false; + } + + return result; + } + + private boolean containsSetWkpImgApplication(Notification notification) { + boolean newStyle = false; + boolean wkpImageCustomization = false; + boolean wkpImageDeCustomization = false; + + if (notification.getEventType() == Notification.SET && SiriusPackage.eINSTANCE.getDNode_OwnedStyle().equals(notification.getFeature()) && hasSequenceMapping(notification.getNotifier())) { + newStyle = true; + } else if (SiriusPackage.eINSTANCE.getCustomizable_CustomFeatures().equals(notification.getFeature()) && notification.getNotifier() instanceof WorkspaceImage) { + WorkspaceImage workspaceImage = (WorkspaceImage) notification.getNotifier(); + wkpImageCustomization = !workspaceImage.getCustomFeatures().isEmpty(); + wkpImageDeCustomization = !wkpImageCustomization; + } + + return newStyle && (wkpImageCustomization || wkpImageDeCustomization); + } + + private boolean hasSequenceMapping(Object notifier) { + if (notifier instanceof DDiagramElement) { + DDiagramElement dde = (DDiagramElement) notifier; + return AbstractNodeEvent.viewpointElementPredicate().apply(dde) || EndOfLife.viewpointElementPredicate().apply(dde) || InstanceRole.viewpointElementPredicate().apply(dde); + } + return false; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/RefreshLayoutTrigger.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/RefreshLayoutTrigger.java new file mode 100644 index 0000000000..e1e126789c --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/RefreshLayoutTrigger.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.refresh; + +import java.util.Collection; + +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.emf.transaction.util.TransactionUtil; +import org.eclipse.gmf.runtime.notation.Diagram; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.business.api.session.ModelChangeTrigger; + +/** + * Default refresh layout trigger for Sequence Diagram edit part. + * + * @author mporhel + */ +public class RefreshLayoutTrigger implements ModelChangeTrigger { + + /** + * Priority of this {@link ModelChangeTrigger}. + */ + private static final int REFRESH_LAYOUT_TRIGGER_PRIORITY = SequenceCanonicalSynchronizerAdapter.SEQUENCE_CANONICAL_REFRESH_PRIORITY + 1; + + private Diagram sequenceDiagram; + + /** + * Constructor. + * + * @param sequenceDiagram + * the sequence diagram to refresh. + */ + public RefreshLayoutTrigger(Diagram sequenceDiagram) { + this.sequenceDiagram = sequenceDiagram; + } + + /** + * {@inheritDoc} + */ + public int priority() { + return REFRESH_LAYOUT_TRIGGER_PRIORITY; + } + + /** + * {@inheritDoc} + */ + public Option localChangesAboutToCommit(Collection notifications) { + TransactionalEditingDomain domain = TransactionUtil.getEditingDomain(sequenceDiagram); + if (sequenceDiagram == null || domain == null) { + return Options.newNone(); + } + Command refreshLayoutCommand = new RefreshLayoutCommand(domain, sequenceDiagram, true); + return Options.newSome(refreshLayoutCommand); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/SequenceCanonicalSynchronizerAdapter.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/SequenceCanonicalSynchronizerAdapter.java new file mode 100644 index 0000000000..2b4c4568cf --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/SequenceCanonicalSynchronizerAdapter.java @@ -0,0 +1,357 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.refresh; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.common.command.CompoundCommand; +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.edit.command.SetCommand; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.emf.transaction.util.TransactionUtil; +import org.eclipse.gmf.runtime.notation.Bounds; +import org.eclipse.gmf.runtime.notation.Diagram; +import org.eclipse.gmf.runtime.notation.Edge; +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.Size; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.AbstractDNode; +import org.eclipse.sirius.CollapseFilter; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.SiriusPackage; +import org.eclipse.sirius.business.api.query.DDiagramElementQuery; +import org.eclipse.sirius.business.api.session.ModelChangeTrigger; +import org.eclipse.sirius.diagram.business.api.view.SiriusLayoutDataManager; +import org.eclipse.sirius.diagram.business.internal.view.LayoutData; +import org.eclipse.sirius.diagram.internal.refresh.listeners.FilterListener; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.VerticalPositionFunction; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.CombinedFragment; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Execution; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InteractionUse; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.SetVerticalRangeOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.query.ISequenceElementQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.query.RangeComparator; +import org.eclipse.sirius.diagram.sequence.business.internal.query.SequenceDiagramQuery; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.util.Range; +import org.eclipse.sirius.diagram.ui.tools.internal.edit.command.CommandFactory; + +/** + * {@link ModelChangeTrigger} which return a {@link Command} to be executed just + * a SynchronizeGMFModelCommand to correct y location of execution (Node) and + * bendpoints of message (Edge). + * + * @author edugueperoux + */ +public class SequenceCanonicalSynchronizerAdapter implements ModelChangeTrigger { + + /** + * Priority of this {@link ModelChangeTrigger}. + */ + public static final int SEQUENCE_CANONICAL_REFRESH_PRIORITY = FilterListener.COMPOSITE_FILTER_REFRESH_PRIORITY + 1; + + /** + * Overridden to return a Command to adapt CanonicalSynchronizer work for + * sequence. + * + * {@inheritDoc} + */ + public Option localChangesAboutToCommit(Collection notifications) { + Option result; + CompoundCommand compoundCommand = new CompoundCommand(); + for (Notification notification : notifications) { + Object newValue = notification.getNewValue(); + if (SequenceCanonicalSynchronizerAdapterScope.isNotificationForNodeAdding(notification)) { + Node newNode = (Node) newValue; + + cancelArrangeNewNodeCommandforSequenceDiagram(newNode); + + Command fixSequenceNodeLowerBoundCmd = getFixSequenceNodeCreationRangeCmd(newNode); + if (fixSequenceNodeLowerBoundCmd != null) { + compoundCommand.append(fixSequenceNodeLowerBoundCmd); + } + + } else if (SequenceCanonicalSynchronizerAdapterScope.isNotificationForEdgeAdding(notification)) { + Edge newEdge = (Edge) newValue; + Command fixBendpointsForSequenceMessageCmd = getFixBendpointsForSequenceMessageCmd(newEdge); + if (fixBendpointsForSequenceMessageCmd != null) { + compoundCommand.append(fixBendpointsForSequenceMessageCmd); + } + } + } + Command cmd = compoundCommand; + result = Options.newSome(cmd); + return result; + } + + private void cancelArrangeNewNodeCommandforSequenceDiagram(Node newNode) { + Diagram diagram = newNode.getDiagram(); + if (diagram != null && ISequenceElementAccessor.getSequenceDiagram(diagram).some()) { + Map> viewToArrangeCenter = SiriusLayoutDataManager.INSTANCE.getCreatedViewWithCenterLayout(); + Map> viewToArrange = SiriusLayoutDataManager.INSTANCE.getCreatedViewsToLayout(); + + Set set = viewToArrange.get(diagram); + if (set != null) { + set.clear(); + } + + Set set2 = viewToArrangeCenter.get(diagram); + if (set2 != null) { + set2.clear(); + } + } + } + + private Command getFixSequenceNodeCreationRangeCmd(Node newNode) { + Command fixSequenceNodeLowerBoundCmd = null; + EObject element = newNode.getElement(); + EObject eContainer = element.eContainer(); + TransactionalEditingDomain domain = TransactionUtil.getEditingDomain(newNode); + if (eContainer instanceof AbstractDNode && ISequenceElementAccessor.getISequenceNode(newNode).some()) { + AbstractDNode containerDNode = (AbstractDNode) eContainer; + if (containerDNode.getOwnedBorderedNodes().contains(element) && element instanceof AbstractDNode) { + AbstractDNode borderedDNode = (AbstractDNode) element; + LayoutData layoutData = null; + SiriusLayoutDataManager.INSTANCE.setIgnoreConsumeState(true); + try { + layoutData = SiriusLayoutDataManager.INSTANCE.getData(borderedDNode, true); + } finally { + SiriusLayoutDataManager.INSTANCE.setIgnoreConsumeState(false); + } + if (layoutData != null) { + Point location = layoutData.getLocation(); + int y = location.y; + + fixSequenceNodeLowerBoundCmd = SetCommand.create(domain, newNode.getLayoutConstraint(), NotationPackage.Literals.LOCATION__Y, y); + Dimension size = layoutData.getSize(); + if (size != null && newNode.getLayoutConstraint() instanceof Size) { + fixSequenceNodeLowerBoundCmd = addSetSizeCmd(fixSequenceNodeLowerBoundCmd, domain, size, newNode); + } + expandSimpleExecution(newNode, fixSequenceNodeLowerBoundCmd, domain, y); + } else { + fixSequenceNodeLowerBoundCmd = getFlaggedRangeApplicationCommand(newNode, domain); + } + } else { + // Set height of operand to -1 to have + // SequenceVerticalLayout set the size correctly + Option operandOption = ISequenceElementAccessor.getOperand(newNode); + if (operandOption.some()) { + Command resetOperandHeightCmd = SetCommand.create(domain, newNode.getLayoutConstraint(), NotationPackage.Literals.SIZE__HEIGHT, -1); + fixSequenceNodeLowerBoundCmd = resetOperandHeightCmd; + Command resetOperandWidthCmd = SetCommand.create(domain, newNode.getLayoutConstraint(), NotationPackage.Literals.SIZE__WIDTH, -1); + fixSequenceNodeLowerBoundCmd = fixSequenceNodeLowerBoundCmd.chain(resetOperandWidthCmd); + } + } + } else { + // Set height of combined fragment to -1 to have + // SequenceVerticalLayout set the size correctly + Option combinedFragmentOption = ISequenceElementAccessor.getCombinedFragment(newNode); + if (combinedFragmentOption.some() && ((Bounds) newNode.getLayoutConstraint()).getHeight() == LayoutConstants.DEFAULT_COMBINED_FRAGMENT_HEIGHT) { + Command resetCombinedFragmentHeightCmd = SetCommand.create(domain, newNode.getLayoutConstraint(), NotationPackage.Literals.SIZE__HEIGHT, -1); + fixSequenceNodeLowerBoundCmd = resetCombinedFragmentHeightCmd; + Command resetCombinedFragmentWidthCmd = SetCommand.create(domain, newNode.getLayoutConstraint(), NotationPackage.Literals.SIZE__WIDTH, -1); + fixSequenceNodeLowerBoundCmd = fixSequenceNodeLowerBoundCmd.chain(resetCombinedFragmentWidthCmd); + } else { + Option interactionUseOption = ISequenceElementAccessor.getInteractionUse(newNode); + if (interactionUseOption.some() && ((Bounds) newNode.getLayoutConstraint()).getHeight() == LayoutConstants.DEFAULT_INTERACTION_USE_HEIGHT) { + Command resetInteractionUseHeightCmd = SetCommand.create(domain, newNode.getLayoutConstraint(), NotationPackage.Literals.SIZE__HEIGHT, -1); + fixSequenceNodeLowerBoundCmd = resetInteractionUseHeightCmd; + Command resetInteractionUseWidthCmd = SetCommand.create(domain, newNode.getLayoutConstraint(), NotationPackage.Literals.SIZE__WIDTH, -1); + fixSequenceNodeLowerBoundCmd = fixSequenceNodeLowerBoundCmd.chain(resetInteractionUseWidthCmd); + } + } + } + return fixSequenceNodeLowerBoundCmd; + } + + private Command addSetSizeCmd(Command globalCmd, TransactionalEditingDomain domain, Dimension size, Node newNode) { + Command result = null; + LayoutConstraint createdNodeLayoutConstraint = newNode.getLayoutConstraint(); + if (createdNodeLayoutConstraint instanceof Size) { + int width = size.width; + int height = size.height; + + if (newNode.getElement() instanceof DDiagramElement && new DDiagramElementQuery((DDiagramElement) newNode.getElement()).isCollapsed()) { + DDiagramElement dde = (DDiagramElement) newNode.getElement(); + CollapseFilter filter = Iterables.filter(dde.getGraphicalFilters(), CollapseFilter.class).iterator().next(); + + Command setFilterHeightCmd = SetCommand.create(domain, filter, SiriusPackage.Literals.COLLAPSE_FILTER__HEIGHT, height); + Command setHeightCmd = SetCommand.create(domain, newNode.getLayoutConstraint(), NotationPackage.Literals.SIZE__HEIGHT, height); + result = globalCmd.chain(setFilterHeightCmd); + result = result.chain(setHeightCmd); + } else { + Command setWidthCmd = SetCommand.create(domain, newNode.getLayoutConstraint(), NotationPackage.Literals.SIZE__WIDTH, width); + Command setHeightCmd = SetCommand.create(domain, newNode.getLayoutConstraint(), NotationPackage.Literals.SIZE__HEIGHT, height); + result = globalCmd.chain(setWidthCmd); + result = result.chain(setHeightCmd); + } + } + return result; + } + + private Command getFlaggedRangeApplicationCommand(View newView, TransactionalEditingDomain domain) { + Command flagCommand = null; + Option ise = ISequenceElementAccessor.getISequenceEvent(newView); + + if (ise.some()) { + Rectangle flag = new ISequenceElementQuery(ise.get()).getFlaggedAbsoluteBounds(); + + // Sequence Refresh extension could report an exisitng flag + // (external reparent, reconnect, ...) + if (flag.x == LayoutConstants.EXTERNAL_CHANGE_FLAG.x) { + Range flaggedRange = new Range(flag.y, flag.bottom()); + flagCommand = CommandFactory.createRecordingCommand(domain, new SetVerticalRangeOperation(ise.get(), flaggedRange)); + + if (newView instanceof Node) { + LayoutConstraint newViewLC = ((Node) newView).getLayoutConstraint(); + if (newViewLC instanceof Size) { + Command setWidthCmd = SetCommand.create(domain, newViewLC, NotationPackage.Literals.SIZE__WIDTH, flag.width); + flagCommand = flagCommand.chain(setWidthCmd); + } + } + } + } + + return flagCommand; + } + + /* + * Simple Execution creation implies that GMF Node at creation have a + * LayoutConstants.DEFAULT_EXECUTION_HEIGHT as minimum height and tries to + * take the maximum available space up to the next event. + */ + private void expandSimpleExecution(Node newNode, Command fixSequenceNodeLowerBoundCmd, TransactionalEditingDomain domain, int y) { + Option execOption = ISequenceElementAccessor.getExecution(newNode); + if (execOption.some() && newNode.getSourceEdges().isEmpty() && newNode.getTargetEdges().isEmpty()) { + Execution execution = execOption.get(); + View parentView = (View) newNode.eContainer(); + Option parentSequenceEvent = ISequenceElementAccessor.getISequenceEvent(parentView); + Range parentEventRange = null; + if (parentSequenceEvent.some()) { + parentEventRange = parentSequenceEvent.get().getVerticalRange(); + } + + int finalHeight = LayoutConstants.DEFAULT_EXECUTION_HEIGHT; + + // At this step the location of the new create exection + // is not yet corrected, will be corrected by the above + // SetCommand + int lowerRange = y; + if (parentEventRange != null) { + lowerRange += parentEventRange.getLowerBound(); + } + int upperRange = lowerRange + execution.getProperLogicalBounds().height; + // Doesn't take newly created execution into account + if (hasEventEndsAfterUpperRange(execution, lowerRange, Collections. singleton(execution))) { + int lowerBoundOfNextEventEnd = getRangeLimitOfNextEventEndOf(execution, lowerRange, Collections. singleton(execution)); + int newUpperRange = lowerBoundOfNextEventEnd - LayoutConstants.EXECUTION_CHILDREN_MARGIN; + if (newUpperRange > upperRange) { + finalHeight = newUpperRange - lowerRange; + Command setExecHeightCmd = SetCommand.create(domain, newNode.getLayoutConstraint(), NotationPackage.Literals.SIZE__HEIGHT, finalHeight); + fixSequenceNodeLowerBoundCmd.chain(setExecHeightCmd); + } + } + } + } + + private int getRangeLimitOfNextEventEndOf(Execution execution, int upperRange, Set sequenceEventToIgnores) { + SequenceDiagram sequenceDiagram = execution.getDiagram(); + SequenceDDiagram sequenceDDiagram = sequenceDiagram.getSequenceDDiagram(); + int rangeLimitOfNextEventEnd = 0; + + EventEnd eventEndAfter = null; + VerticalPositionFunction vpf = new VerticalPositionFunction(sequenceDDiagram); + for (EventEnd end : sequenceDDiagram.getGraphicalOrdering().getEventEnds()) { + int pos = vpf.apply(end); + if (pos != VerticalPositionFunction.INVALID_POSITION && pos > upperRange) { + eventEndAfter = end; + break; + } + } + int parentBottomMargin = LayoutConstants.EXECUTION_CHILDREN_MARGIN; + if (eventEndAfter != null) { + rangeLimitOfNextEventEnd = new VerticalPositionFunction(sequenceDDiagram).apply(eventEndAfter); + } else { + rangeLimitOfNextEventEnd = upperRange + LayoutConstants.DEFAULT_EXECUTION_HEIGHT + parentBottomMargin; + } + return rangeLimitOfNextEventEnd; + } + + private boolean hasEventEndsAfterUpperRange(Execution execution, int upperRange, Set sequenceEventToIgnores) { + SequenceDiagram sequenceDiagram = execution.getDiagram(); + Lifeline lifeline = execution.getLifeline().get(); + Set eventEndsAfterUpperRange = getEventEndsAfterUpperRange(sequenceDiagram, lifeline, upperRange, sequenceEventToIgnores); + Set eventEndsOnUpperRange = getEventEndsOnUpperRange(sequenceDiagram, lifeline, upperRange, sequenceEventToIgnores); + return !eventEndsAfterUpperRange.isEmpty() || !eventEndsOnUpperRange.isEmpty(); + } + + private Set getEventEndsAfterUpperRange(SequenceDiagram sequenceDiagram, Lifeline lifeline, int upperRange, Set sequenceEventToIgnores) { + Set eventEndsAfterUpperRange = new TreeSet(new RangeComparator()); + Set allSequenceEventsInUpperRange = new SequenceDiagramQuery(sequenceDiagram).getAllSequenceEventsUpperThan(upperRange); + allSequenceEventsInUpperRange.removeAll(sequenceEventToIgnores); + eventEndsAfterUpperRange.addAll(Sets.filter(allSequenceEventsInUpperRange, Predicates.not(Predicates.instanceOf(Lifeline.class)))); + return eventEndsAfterUpperRange; + } + + private Set getEventEndsOnUpperRange(SequenceDiagram sequenceDiagram, Lifeline lifeline, int upperRange, Set sequenceEventToIgnores) { + Set eventEndsOnUpperRange = new TreeSet(new RangeComparator()); + Set allSequenceEventsOnRange = new SequenceDiagramQuery(sequenceDiagram).getAllSequenceEventsOn(upperRange); + allSequenceEventsOnRange.removeAll(sequenceEventToIgnores); + eventEndsOnUpperRange.addAll(Sets.filter(allSequenceEventsOnRange, Predicates.not(Predicates.instanceOf(Lifeline.class)))); + return eventEndsOnUpperRange; + } + + private Command getFixBendpointsForSequenceMessageCmd(final Edge newEdge) { + Command changeBendpointsCmd = null; + if (newEdge != null && ISequenceElementAccessor.getMessage(newEdge).some()) { + TransactionalEditingDomain domain = TransactionUtil.getEditingDomain(newEdge); + changeBendpointsCmd = new FixBendpointsOnCreationCommand(domain, newEdge); + changeBendpointsCmd = changeBendpointsCmd.chain(getFlaggedRangeApplicationCommand(newEdge, domain)); + } + return changeBendpointsCmd; + } + + /** + * Overridden to specify a priority upper than + * {@link CollapseFilterListener#COLLAPSE_FILTER_REFRESH_PRIORITY} to be + * executed after it. + * + * {@inheritDoc} + */ + public int priority() { + return SEQUENCE_CANONICAL_REFRESH_PRIORITY; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/SequenceCanonicalSynchronizerAdapterScope.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/SequenceCanonicalSynchronizerAdapterScope.java new file mode 100644 index 0000000000..fbec0abdaf --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/SequenceCanonicalSynchronizerAdapterScope.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.refresh; + +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.NotationPackage; + +import com.google.common.base.Predicate; + +/** + * Predicate to notify SequenceCanonicalSynchronizerAdapter only for adding of + * GMF View. + * + * @author edugueperoux + */ +public class SequenceCanonicalSynchronizerAdapterScope implements Predicate { + + /** + * {@inheritDoc} + */ + public boolean apply(Notification notification) { + return isNotificationForNodeAdding(notification) || isNotificationForEdgeAdding(notification); + } + + /** + * Checks if this notification is about a add of a {@link Node} to the + * parent {@link Node}. + * + * @param notification + * {@link Notification} to check + * + * @return true if this notification is about a add of a {@link Node} to the + * parent {@link Node} + */ + public static boolean isNotificationForNodeAdding(Notification notification) { + boolean isNotificationForNodeAdding = false; + Object newValue = notification.getNewValue(); + Object feature = notification.getFeature(); + isNotificationForNodeAdding = NotationPackage.Literals.VIEW__PERSISTED_CHILDREN.equals(feature) && Notification.ADD == notification.getEventType() && newValue instanceof Node + && isNotANote(newValue); + return isNotificationForNodeAdding; + } + + /** + * Test that newValue is not a GMF Shape which represents a Note. + * + * @param newValue + * the object to test + * + * @return true if newValue is not a GMF Shape which represents a Note, + * false else + */ + private static boolean isNotANote(Object newValue) { + return ((Node) newValue).getElement() != null; + } + + /** + * Checks if this notification is about a add of a {@link Edge} to the + * parent {@link org.eclipse.gmf.runtime.notation.Diagram.Diagram}. + * + * @param notification + * {@link Notification} to check + * + * @return true if this notification is about a add of a {@link Edge} to the + * parent {@link org.eclipse.gmf.runtime.notation.Diagram} + */ + public static boolean isNotificationForEdgeAdding(Notification notification) { + boolean isNotificationForEdgeAdding = false; + Object newValue = notification.getNewValue(); + Object feature = notification.getFeature(); + isNotificationForEdgeAdding = NotationPackage.Literals.DIAGRAM__PERSISTED_EDGES.equals(feature) && Notification.ADD == notification.getEventType() && newValue instanceof Edge; + return isNotificationForEdgeAdding; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/SequenceRefreshExtension.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/SequenceRefreshExtension.java new file mode 100644 index 0000000000..ca4d6cc31c --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/SequenceRefreshExtension.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.refresh; + +import java.util.Collection; +import java.util.Map; + +import org.eclipse.emf.ecore.EObject; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import org.eclipse.sirius.AbsoluteBoundsFilter; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.SiriusFactory; +import org.eclipse.sirius.business.api.refresh.IRefreshExtension; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; + +/** + * Specific refresh extension to handle structural changes in sequence diagrams. + * It allows to keep the last known position when the parent of an abstract node + * event changes. + * + * @author mporhel + * + */ +public class SequenceRefreshExtension implements IRefreshExtension { + + private SequenceDDiagram currentDiagram; + + private Map flags; + + /** + * {@inheritDoc} + */ + public void beforeRefresh(DDiagram dDiagram) { + if (dDiagram instanceof SequenceDDiagram) { + currentDiagram = (SequenceDDiagram) dDiagram; + + Collection nodeEvents = getEventsToSync(currentDiagram); + + if (nodeEvents.size() != 0) { + flags = Maps.newHashMapWithExpectedSize(nodeEvents.size()); + for (DDiagramElement elt : nodeEvents) { + Iterable flag = Iterables.filter(elt.getGraphicalFilters(), AbsoluteBoundsFilter.class); + EObject semanticTarget = elt.getTarget(); + if (semanticTarget != null && Iterables.size(flag) == 1) { + flags.put(semanticTarget, Iterables.getOnlyElement(flag)); + } + } + } + } + } + + /** + * {@inheritDoc} + */ + public void postRefresh(DDiagram dDiagram) { + if (currentDiagram != null && currentDiagram.equals(dDiagram) && flags != null) { + Collection nodeEvents = getEventsToSync((SequenceDDiagram) dDiagram); + + if (nodeEvents.size() != 0) { + for (DDiagramElement elt : nodeEvents) { + Iterable flag = Iterables.filter(elt.getGraphicalFilters(), AbsoluteBoundsFilter.class); + EObject semanticTarget = elt.getTarget(); + if (semanticTarget != null && Iterables.isEmpty(flag) && flags.containsKey(semanticTarget)) { + AbsoluteBoundsFilter prevFlag = flags.get(semanticTarget); + + AbsoluteBoundsFilter newFlag = SiriusFactory.eINSTANCE.createAbsoluteBoundsFilter(); + newFlag.setX(LayoutConstants.EXTERNAL_CHANGE_FLAG.x); + newFlag.setY(prevFlag.getY()); + newFlag.setHeight(prevFlag.getHeight()); + newFlag.setWidth(prevFlag.getWidth()); + + elt.getGraphicalFilters().add(newFlag); + } + } + } + } + if (flags != null) { + flags.clear(); + flags = null; + } + currentDiagram = null; + } + + private Collection getEventsToSync(SequenceDDiagram sdd) { + Collection diagramElements = Lists.newArrayList(sdd.getDiagramElements()); + Predicate eventsToSync = Predicates.or(AbstractNodeEvent.viewpointElementPredicate(), Message.viewpointElementPredicate()); + return Lists.newArrayList(Iterables.filter(diagramElements, eventsToSync)); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/SequenceRefreshExtensionProvider.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/SequenceRefreshExtensionProvider.java new file mode 100644 index 0000000000..91635915f4 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/SequenceRefreshExtensionProvider.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.refresh; + +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.business.api.refresh.IRefreshExtension; +import org.eclipse.sirius.business.api.refresh.IRefreshExtensionProvider; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; + +public class SequenceRefreshExtensionProvider implements IRefreshExtensionProvider { + + private IRefreshExtension sequenceRefresh; + + /** + * {@inheritDoc} + */ + public IRefreshExtension getRefreshExtension(DDiagram viewPoint) { + if (sequenceRefresh == null) { + sequenceRefresh = new SequenceRefreshExtension(); + } + return sequenceRefresh; + } + + /** + * {@inheritDoc} + */ + public boolean provides(DDiagram viewPoint) { + return viewPoint instanceof SequenceDDiagram; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/SequenceSlidableAnchor.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/SequenceSlidableAnchor.java new file mode 100644 index 0000000000..29578609d5 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/refresh/SequenceSlidableAnchor.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2013 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.refresh; + +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.PrecisionPoint; +import org.eclipse.gmf.runtime.notation.Node; + +import org.eclipse.sirius.diagram.internal.refresh.edge.SlidableAnchor; + +/** + * Specialized anchor with some customizations for sequence diagrams. + * + * @author mporhel + */ +public class SequenceSlidableAnchor extends SlidableAnchor { + + /** + * Constructor. + * + * @param owner + * the figure that this anchor is associated with. + * @param pp + * the PrecisionPoint that the anchor will initially attach to. + */ + public SequenceSlidableAnchor(Node owner, PrecisionPoint pp) { + super(owner, pp); + } + + /** + * {@inheritDoc} + */ + @Override + protected Point getLocation(Point ownReference, Point foreignReference) { + return ownReference.getCopy(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/tool/ToolCommandBuilder.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/tool/ToolCommandBuilder.java new file mode 100644 index 0000000000..e3bb822bd1 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/tool/ToolCommandBuilder.java @@ -0,0 +1,504 @@ +/******************************************************************************* + * Copyright (c) 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.tool; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.emf.transaction.util.TransactionUtil; +import org.eclipse.gmf.runtime.notation.Diagram; + +import com.google.common.collect.Maps; + +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DDiagramElementContainer; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.EdgeTarget; +import org.eclipse.sirius.SiriusPlugin; +import org.eclipse.sirius.business.api.helper.task.InitInterpreterVariablesTask; +import org.eclipse.sirius.business.api.helper.task.TaskHelper; +import org.eclipse.sirius.business.api.query.IdentifiedElementQuery; +import org.eclipse.sirius.business.api.session.Session; +import org.eclipse.sirius.business.api.session.SessionManager; +import org.eclipse.sirius.description.tool.AbstractVariable; +import org.eclipse.sirius.description.tool.ContainerCreationDescription; +import org.eclipse.sirius.description.tool.PaneBasedSelectionWizardDescription; +import org.eclipse.sirius.description.tool.SelectionWizardDescription; +import org.eclipse.sirius.description.tool.ToolDescription; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.CombinedFragment; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.ordering.EventEndHelper; +import org.eclipse.sirius.diagram.sequence.description.tool.CombinedFragmentCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.ExecutionCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.InstanceRoleCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.InstanceRoleReorderTool; +import org.eclipse.sirius.diagram.sequence.description.tool.InteractionUseCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.MessageCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.ObservationPointCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.OperandCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.ReorderTool; +import org.eclipse.sirius.diagram.sequence.description.tool.StateCreationTool; +import org.eclipse.sirius.diagram.sequence.internal.tool.command.builders.ExecutionCreationCommandBuilder; +import org.eclipse.sirius.diagram.sequence.internal.tool.command.builders.FrameCreationCommandBuilder; +import org.eclipse.sirius.diagram.sequence.internal.tool.command.builders.InstanceRoleCreationCommandBuilder; +import org.eclipse.sirius.diagram.sequence.internal.tool.command.builders.MessageCreationCommandBuilder; +import org.eclipse.sirius.diagram.sequence.internal.tool.command.builders.ObservationPointCreationCommandBuilder; +import org.eclipse.sirius.diagram.sequence.internal.tool.command.builders.OperandCreationCommandBuilder; +import org.eclipse.sirius.diagram.sequence.internal.tool.command.builders.SequenceGenericToolCommandBuilder; +import org.eclipse.sirius.diagram.sequence.internal.tool.command.builders.SequencePaneBasedSelectionWizardCommandBuilder; +import org.eclipse.sirius.diagram.sequence.internal.tool.command.builders.SequenceSelectionWizardCommandBuilder; +import org.eclipse.sirius.diagram.sequence.internal.tool.command.builders.StateCreationCommandBuilder; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.ordering.SingleEventEnd; +import org.eclipse.sirius.diagram.tools.internal.commands.emf.EMFCommandFactoryUI; +import org.eclipse.sirius.tools.api.command.SiriusCommand; +import org.eclipse.sirius.tools.api.interpreter.InterpreterUtil; +import org.eclipse.sirius.tools.internal.command.builders.CommandBuilder; +import org.eclipse.sirius.tools.internal.command.builders.NodeCreationCommandBuilder; +import org.eclipse.sirius.ecore.extender.business.api.accessor.ModelAccessor; + +/** + * Helper class to build the concrete commands executing the user-specified + * tools on specific parameters. + * + * @author pcdavid + */ +public final class ToolCommandBuilder { + + private ToolCommandBuilder() { + // Prevent instantiation. + } + + /** + * Builds the command which will execute the user-tasks specified operations + * to reorder an event. + * + * @param sequenceDDiagram + * the DRepresentation to use for the ModelOperations + * @param reorderTool + * the user-defined reordering tool. + * @param event + * the semantic element of the event which was moved. + * @param startingEndPredecessor + * the event end immediately preceding the reordered event's + * starting end after the move. + * @param finishingEndPredecessor + * the event end immediately preceding the reordered event's + * finishing end after the move. + * @return a command which executes the user-specified operations with the + * appropriate variables set. + */ + public static SiriusCommand buildReorderCommand(SequenceDDiagram sequenceDDiagram, ReorderTool reorderTool, EObject event, EventEnd startingEndPredecessor, EventEnd finishingEndPredecessor) { + ModelAccessor modelAccessor = SiriusPlugin.getDefault().getModelAccessorRegistry().getModelAccessor(event); + TransactionalEditingDomain domain = TransactionUtil.getEditingDomain(event); + + SiriusCommand result = new SiriusCommand(domain, new IdentifiedElementQuery(reorderTool).getLabel()); + if (reorderTool.getOnEventMovedOperation().getFirstModelOperations() != null) { + result.getTasks().add(ToolCommandBuilder.buildVariablesInitializationTask(reorderTool, event, startingEndPredecessor, finishingEndPredecessor)); + TaskHelper taskHelper = new TaskHelper(modelAccessor, new EMFCommandFactoryUI()); + + result.getTasks().add(taskHelper.buildTaskFromModelOperation(sequenceDDiagram, event, reorderTool.getOnEventMovedOperation().getFirstModelOperations())); + } + return result; + } + + private static InitInterpreterVariablesTask buildVariablesInitializationTask(ReorderTool reorderTool, EObject event, EventEnd startingEndPredecessor, EventEnd finishingEndPredecessor) { + Map variables = Maps.newHashMap(); + variables.put(reorderTool.getStartingEndPredecessorAfter(), startingEndPredecessor); + variables.put(reorderTool.getFinishingEndPredecessorAfter(), finishingEndPredecessor); + return new InitInterpreterVariablesTask(variables, InterpreterUtil.getInterpreter(event), null); + } + + /** + * Builds the command which will execute the user-tasks specified operations + * to reorder an instance role. + * + * @param sequenceDDiagram + * the DRepresentation to use for the ModelOperations + * @param reorderTool + * the user-defined instance role reordering tool. + * @param element + * the semantic element of the instance role which was moved. + * @param predecessorBefore + * the semantic element corresponding to the instance role + * immediately preceding the reordered instance role before the + * move. + * @param predecessorAfter + * the semantic element corresponding to the instance role + * immediately preceding the reordered instance role after the + * move. + * @return a command which executes the user-specified operations with the + * appropriate variables set. + */ + public static SiriusCommand buildInstanceRoleReorderCommand(SequenceDDiagram sequenceDDiagram, InstanceRoleReorderTool reorderTool, EObject element, EObject predecessorBefore, + EObject predecessorAfter) { + ModelAccessor modelAccessor = SiriusPlugin.getDefault().getModelAccessorRegistry().getModelAccessor(element); + TransactionalEditingDomain domain = TransactionUtil.getEditingDomain(element); + + SiriusCommand result = new SiriusCommand(domain, new IdentifiedElementQuery(reorderTool).getLabel()); + if (reorderTool.getInstanceRoleMoved().getFirstModelOperations() != null) { + result.getTasks().add(ToolCommandBuilder.buildVariablesInitializationTask(reorderTool, element, predecessorBefore, predecessorAfter)); + TaskHelper taskHelper = new TaskHelper(modelAccessor, new EMFCommandFactoryUI()); + result.getTasks().add(taskHelper.buildTaskFromModelOperation(sequenceDDiagram, element, reorderTool.getInstanceRoleMoved().getFirstModelOperations())); + } + return result; + } + + private static InitInterpreterVariablesTask buildVariablesInitializationTask(InstanceRoleReorderTool reorderTool, EObject element, EObject predecessorBefore, EObject predecessorAfter) { + Map variables = Maps.newHashMap(); + variables.put(reorderTool.getPredecessorBefore(), predecessorBefore); + variables.put(reorderTool.getPredecessorAfter(), predecessorAfter); + return new InitInterpreterVariablesTask(variables, InterpreterUtil.getInterpreter(element), null); + } + + /** + * Builds the command which will execute the user-specified operations to + * create a new message. + * + * @param source + * the source of the new message to create. + * @param target + * the target of the new message to create. + * @param tool + * the tool to use to create the message. + * @param startingEndBefore + * the event end which precedes graphically the source location + * of the new message, to be used by the tool do decide where to + * insert the message in the semantic model. + * @param finishingEndBefore + * the event end which precedes graphically the target location + * of the new message, to be used by the tool do decide where to + * insert the message in the semantic model. + * @return a command to create the message. + */ + public static Command buildCreateMessageCommand(final EdgeTarget source, final EdgeTarget target, final MessageCreationTool tool, final EventEnd startingEndBefore, + final EventEnd finishingEndBefore) { + CommandBuilder builder = new MessageCreationCommandBuilder(tool, source, target, startingEndBefore, finishingEndBefore); + return getCommand(builder, source); + } + + private static Command getCommand(CommandBuilder builder, EObject dObject) { + ModelAccessor accessor = SiriusPlugin.getDefault().getModelAccessorRegistry().getModelAccessor(dObject); + TransactionalEditingDomain domain = TransactionUtil.getEditingDomain(dObject); + EMFCommandFactoryUI uiCallback = new EMFCommandFactoryUI(); + builder.init(accessor, domain, uiCallback); + return builder.buildCommand(); + } + + private static Command getCommand(CommandBuilder builder, Session session) { + EMFCommandFactoryUI uiCallback = new EMFCommandFactoryUI(); + builder.init(session.getModelAccessor(), session.getTransactionalEditingDomain(), uiCallback); + return builder.buildCommand(); + } + + /** + * Builds the command which will execute the user-specified operations to + * create a new instance role. + * + * @param diagram + * the sequence diagram on which to create the new instance role. + * @param tool + * the tool to use to create the instance role. + * @param predecessor + * the semantic element corresponding to the instance role + * graphically preceding the x location of the new instance role. + * @param location + * the clicked location. + * @return a command to create the instance role. + */ + public static Command buildCreateInstanceRoleCommandFromTool(final DDiagram diagram, final InstanceRoleCreationTool tool, final EObject predecessor, Point location) { + CommandBuilder builder = new InstanceRoleCreationCommandBuilder(tool, diagram, predecessor, location); + return getCommand(builder, diagram); + } + + /** + * Builds the command which will execute the user-specified operations to + * create a new execution. + * + * @param node + * the node on which to create the new execution. + * @param tool + * the tool to use to create the execution. + * @param startingEndPredecessor + * the event end graphically preceding the starting position of + * the new execution. + * @param finishingEndPredecessor + * the event end graphically preceding the finishing position of + * the new execution. + * @param location + * the clicked location. + * @return a command to create the execution. + */ + public static Command buildCreateExecutionCommandFromTool(final DNode node, final ExecutionCreationTool tool, final EventEnd startingEndPredecessor, final EventEnd finishingEndPredecessor, + Point location) { + CommandBuilder builder = new ExecutionCreationCommandBuilder(tool, node, startingEndPredecessor, finishingEndPredecessor, location); + Session session = SessionManager.INSTANCE.getSession(node.getTarget()); + return getCommand(builder, session); + } + + /** + * Builds the command which will execute the user-specified operations to + * create a new observation point. + * + * @param diagramElement + * the clicked diagram element. + * @param tool + * the tool to use to create the execution. + * @param startingEndPredecessor + * the event end graphically preceding the starting position of + * the new execution. + * @param finishingEndPredecessor + * the event end graphically preceding the finishing position of + * the new execution. + * @return a command to create the execution. + */ + public static Command buildCreateObservationPointCommandFromTool(final DDiagramElement diagramElement, final ObservationPointCreationTool tool, final EventEnd startingEndPredecessor, + final EventEnd finishingEndPredecessor) { + NodeCreationCommandBuilder builder = new ObservationPointCreationCommandBuilder(tool, diagramElement, startingEndPredecessor, finishingEndPredecessor); + Session session = SessionManager.INSTANCE.getSession(diagramElement.getTarget()); + return getCommand(builder, session); + } + + /** + * Builds the command which will execute the user-specified operations to + * create a new observation point. + * + * @param diagram + * the diagram on which to create the new execution. + * @param tool + * the tool to use to create the execution. + * @param startingEndPredecessor + * the event end graphically preceding the starting position of + * the new execution. + * @param finishingEndPredecessor + * the event end graphically preceding the finishing position of + * the new execution. + * @return a command to create the execution. + */ + public static Command buildCreateObservationPointCommandFromTool(final DDiagram diagram, final ObservationPointCreationTool tool, final EventEnd startingEndPredecessor, + final EventEnd finishingEndPredecessor) { + CommandBuilder builder = new ObservationPointCreationCommandBuilder(tool, diagram, startingEndPredecessor, finishingEndPredecessor); + return getCommand(builder, diagram); + } + + /** + * Builds the command which will execute the user-specified operations to + * create a new state. + * + * @param node + * the node on which to create the new state. + * @param tool + * the tool to use to create the state. + * @param startingEndPredecessor + * the event end graphically preceding the starting position of + * the new state. + * @param finishingEndPredecessor + * the event end graphically preceding the finishing position of + * the new state. + * @return a command to create the state. + */ + public static Command buildCreateStateCommandFromTool(final DNode node, final StateCreationTool tool, final EventEnd startingEndPredecessor, final EventEnd finishingEndPredecessor) { + CommandBuilder builder = new StateCreationCommandBuilder(tool, node, startingEndPredecessor, finishingEndPredecessor); + Session session = SessionManager.INSTANCE.getSession(node.getTarget()); + return getCommand(builder, session); + } + + /** + * Builds the command which will execute the user-specified operations to + * create a new interaction use. + * + * @param diagram + * the diagram on which to create the new IU. + * @param tool + * the tool to use to create the IU. + * @param startingEndPredecessor + * the event end graphically preceding the starting position of + * the new IU. + * @param finishingEndPredecessor + * the event end graphically preceding the finishing position of + * the new IU. + * @param coverage + * the semantic elements representing the lifelines graphically + * covered by the initial area of the IU. + * @return a command to create the IU. + */ + public static Command buildCreateInteractionUseCommandFromTool(DDiagram diagram, InteractionUseCreationTool tool, EventEnd startingEndPredecessor, EventEnd finishingEndPredecessor, + List coverage) { + return ToolCommandBuilder.buildCreateFrameCommandFromTool(diagram, tool, startingEndPredecessor, finishingEndPredecessor, coverage); + } + + /** + * Builds the command which will execute the user-specified operations to + * create a new combined fragment. + * + * @param diagram + * the diagram on which to create the new CF. + * @param tool + * the tool to use to create the CF. + * @param startingEndPredecessor + * the event end graphically preceding the starting position of + * the new CF. + * @param finishingEndPredecessor + * the event end graphically preceding the finishing position of + * the new CF. + * @param coverage + * the semantic elements representing the lifelines graphically + * covered by the initial area of the CF. + * @return a command to create the CF. + */ + public static Command buildCreateCombinedFragmentCommandFromTool(DDiagram diagram, CombinedFragmentCreationTool tool, EventEnd startingEndPredecessor, EventEnd finishingEndPredecessor, + List coverage) { + return ToolCommandBuilder.buildCreateFrameCommandFromTool(diagram, tool, startingEndPredecessor, finishingEndPredecessor, coverage); + } + + /** + * Builds the command which will execute the user-specified operations to + * create a new combined fragment. + * + * @param diagram + * the diagram on which to create the new execution. + * @param tool + * the tool to use to create the combined fragment. + * @param startingEndPredecessor + * the event end graphically preceding the starting position of + * the new CF. + * @param finishingEndPredecessor + * the event end graphically preceding the finishing position of + * the new CF. + * @param coverage + * the semantic elements representing the lifelines graphically + * covered by the initial area of the CF. + * @return a command to create the execution. + */ + private static Command buildCreateFrameCommandFromTool(DDiagram diagram, final ContainerCreationDescription tool, final EventEnd startingEndPredecessor, final EventEnd finishingEndPredecessor, + final List coverage) { + CommandBuilder builder = new FrameCreationCommandBuilder(tool, diagram, startingEndPredecessor, finishingEndPredecessor, coverage); + return getCommand(builder, diagram); + } + + /** + * Builds the command which will execute the user-specified operations to + * create a new operand. + * + * @param nodeContainer + * the container in which to create the operand. + * @param tool + * the tool to use to create the operand. + * @param startingEndPredecessor + * the event end graphically preceding the starting position of + * the new Operand. + * @param finishingEndPredecessor + * the event end graphically preceding the finishing position of + * the new Operand. + * @return a command to create the execution. + */ + public static org.eclipse.emf.common.command.Command buildCreateOperantCommandFromTool(DDiagramElementContainer nodeContainer, final OperandCreationTool tool, + final EventEnd startingEndPredecessor, final EventEnd finishingEndPredecessor) { + CommandBuilder builder = new OperandCreationCommandBuilder(tool, nodeContainer, startingEndPredecessor, finishingEndPredecessor); + Session session = SessionManager.INSTANCE.getSession(nodeContainer.getTarget()); + return getCommand(builder, session); + } + + /** + * Check if startingEndPredecessor is a starting {@link EventEnd} of + * {@link CombinedFragment}. + * + * @param sequenceDiagram + * the {@link SequenceDiagram} on which checks the property + * @param startingEndPredecessor + * the {@link EventEnd} to evaluate + * @return true if startingEndPredecessor is a starting {@link EventEnd} of + * {@link CombinedFragment} + */ + public static boolean isStartingEventEndOfCombinedFragment(SequenceDiagram sequenceDiagram, EventEnd startingEndPredecessor) { + if (sequenceDiagram != null && startingEndPredecessor instanceof SingleEventEnd && ((SingleEventEnd) startingEndPredecessor).isStart()) { + ISequenceEvent ise = EventEndHelper.findISequenceEvent((SingleEventEnd) startingEndPredecessor, sequenceDiagram); + return ise instanceof CombinedFragment; + } + return false; + } + + /** + * Builds the command which will execute the user-specified operations with + * a generic tool. + * + * @param containerView + * the clicked diagram element. + * @param tool + * the generic tool. + * @param endBefore + * the event end graphically preceding the position + * @param location + * the clicked location. + * @param diagram + * the GMF {@link Diagram} + * @return a command to execute the tool. + */ + public static Command buildSequenceGenericToolCommandFromTool(EObject containerView, ToolDescription tool, EventEnd endBefore, Point location, Diagram diagram) { + CommandBuilder builder = new SequenceGenericToolCommandBuilder(tool, containerView, endBefore, location, diagram); + return getCommand(builder, containerView); + } + + /** + * Builds the command which will execute the user-specified operations with + * a pane based selection tool. + * + * @param tool + * the pane based selection tool. + * @param dContainer + * the clicked diagram element. + * @param selectedElement + * the selected elements + * @param endBefore + * the event end graphically preceding the position + * @param location + * the clicked location. + * @return a command to execute the tool. + */ + public static Command buildSequencePaneBasedSelectionWizardCommandFromTool(PaneBasedSelectionWizardDescription tool, DSemanticDecorator dContainer, Collection selectedElement, + EventEnd endBefore, Point location) { + CommandBuilder builder = new SequencePaneBasedSelectionWizardCommandBuilder(tool, dContainer, selectedElement, endBefore, location); + Session session = SessionManager.INSTANCE.getSession(dContainer.getTarget()); + return getCommand(builder, session); + } + + /** + * Builds the command which will execute the user-specified operations with + * a selection tool. + * + * @param tool + * the selection tool. + * @param dContainer + * the clicked diagram element. + * @param selectedElement + * the selected elements + * @param endBefore + * the event end graphically preceding the position + * @param location + * the clicked location. + * @return a command to execute the tool. + */ + public static Command buildSequenceSelectionWizardCommandFromTool(SelectionWizardDescription tool, DSemanticDecorator dContainer, Collection selectedElement, EventEnd endBefore, + Point location) { + CommandBuilder builder = new SequenceSelectionWizardCommandBuilder(tool, dContainer, selectedElement, endBefore, location); + Session session = SessionManager.INSTANCE.getSession(dContainer.getTarget()); + return getCommand(builder, session); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/BendpointsHelper.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/BendpointsHelper.java new file mode 100644 index 0000000000..5a40fff33e --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/BendpointsHelper.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.util; + +import java.util.Collection; +import java.util.List; + +import org.eclipse.gmf.runtime.notation.datatype.RelativeBendpoint; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +/** + * Helper to compare two lists of GMF RelativeBenpoint to help avoid re-setting + * the same values (which can trigger unwanted notifications). + * + * @author pcdavid + */ +public final class BendpointsHelper { + private BendpointsHelper() { + // Prevents instantiations. + } + + /** + * Checks whether two objects are lists of GMF Bendpoints which are + * equivalent (i.e. the same number of points with the same coordinates in + * the same order). + * + * @param oldValue + * the first (old) value. + * @param newValue + * the second (new) value. + * @return true if the two given values are equivalent lists of + * GMF Bendpoints. + */ + public static boolean areSameBendpoints(Object oldValue, Object newValue) { + boolean isTouch = false; + if (oldValue instanceof Collection && newValue instanceof Collection) { + List newPoints = Lists.newArrayList(Iterables.filter((Collection) newValue, RelativeBendpoint.class)); + List oldPoints = Lists.newArrayList(Iterables.filter((Collection) oldValue, RelativeBendpoint.class)); + if (newPoints.size() == oldPoints.size()) { + isTouch = true; + for (int i = 0; i < newPoints.size(); i++) { + RelativeBendpoint newPoint = newPoints.get(i); + RelativeBendpoint oldPoint = oldPoints.get(i); + + boolean sourceTouch = newPoint.getSourceX() == oldPoint.getSourceX() && newPoint.getSourceY() == oldPoint.getSourceY(); + boolean targetTouch = newPoint.getTargetX() == oldPoint.getTargetX() && newPoint.getTargetY() == oldPoint.getTargetY(); + + if (!(sourceTouch && targetTouch)) { + isTouch = false; + break; + } + } + } + } + return isTouch; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/EventFinder.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/EventFinder.java new file mode 100644 index 0000000000..768b29b8ed --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/EventFinder.java @@ -0,0 +1,239 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.util; + +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * Helper to locate sequence events inside a given context event. + * + * @author pcdavid + */ +public class EventFinder { + private final ISequenceEvent context; + + private final Lifeline lifeline; + + private Range expansionZone; + + private Predicate eventsToIgnore; + + private boolean reconnect; + + private boolean reparent; + + private Function verticalRangeFunction = new Function() { + public Range apply(ISequenceEvent from) { + return from.getVerticalRange(); + } + }; + + private Map reparented = Maps.newHashMap(); + + /** + * Constructor. + * + * @param context + * the event in which to look for. Only sub-events of the context + * are considered in the search. + */ + public EventFinder(Lifeline context) { + this.context = context; + this.lifeline = context; + } + + /** + * Constructor. + * + * @param context + * the event in which to look for. Only sub-events of the context + * are considered in the search. + */ + public EventFinder(AbstractNodeEvent context) { + this.context = context; + Preconditions.checkArgument(context.getLifeline().some()); + this.lifeline = context.getLifeline().get(); + } + + /** + * Constructor. + * + * @param context + * the event in which to look for. Only sub-events of the context + * are considered in the search. + * + * @param lifeline + * the lifeline on which to look for an event. + */ + public EventFinder(ISequenceEvent context, Lifeline lifeline) { + this.context = context; + this.lifeline = lifeline; + } + + /** + * Allow to use another way to compute the range of the context (iG range + * after move?..). + * + * @param rangeFunction + * the range function to use to compute the context range. + */ + public void setVerticalRangefunction(Function rangeFunction) { + if (verticalRangeFunction != null) { + this.verticalRangeFunction = rangeFunction; + } + } + + public void setReconnection(boolean mode) { + this.reconnect = mode; + } + + public boolean isReconnection() { + return reconnect; + } + + public void setReparent(boolean mode) { + this.reparent = mode; + } + + public boolean isReparent() { + return reparent; + } + + public void setExpansionZone(Range expansionZone) { + this.expansionZone = expansionZone; + } + + public void setEventsToIgnore(Predicate eventsToIgnore) { + this.eventsToIgnore = eventsToIgnore; + } + + /** + * Returns the deepest event in the hierarchy of sub-event starting from + * this finder's context which includes completely the specified range. + * + * @param range + * the range to look for. + * @return the deepest event, starting from this finder's context, which + * includes the specified range, or null. + */ + public ISequenceEvent findMostSpecificEvent(Range range) { + if (context instanceof Message) { + return null; + } + ISequenceEvent result = null; + if (contextIncludesRange(range)) { + if (context != null && !shouldIgnore().apply(context)) { + boolean okForReconnection = !isReconnection() || !Message.NO_RECONNECTABLE_EVENTS.apply(context); + boolean okForReparent = !isReparent() || !AbstractNodeEvent.NO_REPARENTABLE_EVENTS.apply(context); + if (okForReconnection && okForReparent) { + result = context; + } + } + } + if (contextIncludesRange(range) || isReparent()) { + Predicate sameLifeline = new SameLifelinePredicate(lifeline); + List eventsToInspect = Lists.newArrayList(); + if ((reconnect || reparent) && (context instanceof AbstractNodeEvent || context instanceof Lifeline)) { + for (View view : Iterables.filter(context.getNotationView().getChildren(), View.class)) { + Option ise = ISequenceElementAccessor.getISequenceEvent(view); + if (ise != null && ise.some() && !reparented.containsKey(ise.get())) { + eventsToInspect.add(ise.get()); + } + } + + // handle reparented events + // look for new child + for (Entry entry : reparented.entrySet()) { + if (entry.getValue() == context) { + eventsToInspect.add(entry.getKey()); + } + } + } else { + eventsToInspect.addAll(context.getSubEvents()); + } + for (ISequenceEvent child : Iterables.filter(eventsToInspect, Predicates.and(sameLifeline, Predicates.not(shouldIgnore())))) { + EventFinder childFinder = new EventFinder(child, lifeline); + childFinder.setReconnection(isReconnection()); + childFinder.setReparent(isReparent()); + childFinder.setEventsToIgnore(eventsToIgnore); + childFinder.setExpansionZone(expansionZone); + childFinder.setVerticalRangefunction(verticalRangeFunction); + childFinder.setReparented(reparented); + ISequenceEvent moreSpecific = childFinder.findMostSpecificEvent(range); + if (moreSpecific != null) { + result = moreSpecific; + break; + } + } + } + + return result; + } + + /** + * Tests whether this finder's context element includes the specified range, + * considering the optional expansion step. + */ + private boolean contextIncludesRange(Range range) { + Range currentContextRange = verticalRangeFunction.apply(context); + return getRangeAfterExpansion(currentContextRange).includes(range); + } + + /** + * Returns a predicate which tests whether an element should be ignored in + * the search for descendants. + */ + private Predicate shouldIgnore() { + if (eventsToIgnore == null) { + return Predicates.alwaysFalse(); + } else { + return eventsToIgnore; + } + } + + private Range getRangeAfterExpansion(Range range) { + if (expansionZone != null && range.includesAtLeastOneBound(expansionZone)) { + return range.union(expansionZone); + } else { + return range; + } + } + + /** + * Handle already reparented event. + * + * @param newParents + * key is the reparented exec, value is the new parent. + */ + public void setReparented(Map newParents) { + this.reparented.putAll(newParents); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/ISequenceElementSwitch.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/ISequenceElementSwitch.java new file mode 100644 index 0000000000..205d955741 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/ISequenceElementSwitch.java @@ -0,0 +1,579 @@ +/******************************************************************************* + * Copyright (c) 2010, 2013 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.util; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.business.api.query.DiagramElementMappingQuery; +import org.eclipse.sirius.description.DiagramElementMapping; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractFrame; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.CombinedFragment; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.EndOfLife; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Execution; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElement; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InteractionUse; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.State; +import org.eclipse.sirius.diagram.sequence.description.DescriptionPackage; + +/** + * The Switch for the sequence elements's + * inheritance hierarchy. It supports the call + * {@link #doSwitch(ISequenceElement) doSwitch(ise)} to invoke the + * caseXXX method for each class of the model, starting with the + * actual class of the object and proceeding up the inheritance hierarchy until + * a non-null result is returned, which is the result of the switch. + * + * + * @param + * the return type. + * + * @author mporhel + */ +public class ISequenceElementSwitch { + + /** + * Creates an instance of the switch. + */ + public ISequenceElementSwitch() { + + } + + /** + * Calls caseXXX for each class of the model until one returns + * a non null result; it yields that result. + * + * @param element + * the element on which to do the switch. + * @return the first non-null result returned by a caseXXX + * call. + */ + public T doSwitch(ISequenceElement element) { + DDiagramElement dde = null; + if (element.getNotationView().getElement() instanceof DDiagramElement) { + dde = (DDiagramElement) element.getNotationView().getElement(); + } + if (dde != null && dde.getDiagramElementMapping() != null) { + DiagramElementMapping mappingToCheck = new DiagramElementMappingQuery(dde.getDiagramElementMapping()).getRootMapping(); + return doSwitch(mappingToCheck, element); + } + return null; + } + + /** + * Calls caseXXX for each class of the model until one returns + * a non null result; it yields that result. + * + * @param mapping + * the considered mapping for the swith (mapping of the element). + * @param element + * the element on which to do the switch. + * @return the first non-null result returned by a caseXXX + * call. + */ + protected T doSwitch(DiagramElementMapping mapping, ISequenceElement element) { + if (mapping != null && mapping.eClass().getEPackage() == DescriptionPackage.eINSTANCE) { + return doSwitch(mapping.eClass().getClassifierID(), element); + } else { + return defaultCase(element); + } + } + + /** + * Calls caseXXX for each class of the model until one returns + * a non null result; it yields that result. + * + * @return the first non-null result returned by a caseXXX + * call. + * @generated + */ + // CHECKSTYLE:OFF + protected T doSwitch(int classifierID, ISequenceElement element) { + switch (classifierID) { + case DescriptionPackage.SEQUENCE_DIAGRAM_DESCRIPTION: { + SequenceDiagram diag = (SequenceDiagram) element; + T result = caseSequenceDiagram(diag); + if (result == null) { + result = defaultCase(element); + } + return result; + } + case DescriptionPackage.INSTANCE_ROLE_MAPPING: { + InstanceRole instanceRole = (InstanceRole) element; + T result = caseInstanceRole(instanceRole); + if (result == null) { + result = defaultCase(element); + } + return result; + } + case DescriptionPackage.EVENT_MAPPING: { + ISequenceEvent event = (ISequenceEvent) element; + T result = caseEvent(event); + if (result == null) { + result = defaultCase(element); + } + return result; + } + case DescriptionPackage.DELIMITED_EVENT_MAPPING: { + ISequenceEvent delimitedEvent = (ISequenceEvent) element; + T result = caseDelimitedEvent(delimitedEvent); + if (result == null) { + result = caseEvent(delimitedEvent); + } + if (result == null) { + result = defaultCase(element); + } + return result; + } + case DescriptionPackage.EXECUTION_MAPPING: { + Execution execution = (Execution) element; + T result = caseExecution(execution); + if (result == null) { + result = caseDelimitedEvent(execution); + } + if (result == null) { + result = caseEvent(execution); + } + if (result == null) { + result = defaultCase(element); + } + return result; + } + case DescriptionPackage.STATE_MAPPING: { + State state = (State) element; + T result = caseState(state); + if (result == null) { + result = caseDelimitedEvent(state); + } + if (result == null) { + result = caseEvent(state); + } + if (result == null) { + result = defaultCase(element); + } + return result; + } + case DescriptionPackage.END_OF_LIFE_MAPPING: { + EndOfLife endOfLife = (EndOfLife) element; + T result = caseEndOfLife(endOfLife); + if (result == null) { + result = defaultCase(element); + } + return result; + } + + case DescriptionPackage.BASIC_MESSAGE_MAPPING: { + Message basicMessage = (Message) element; + T result = caseBasicMessage(basicMessage); + if (result == null) { + result = caseMessage(basicMessage); + } + return result; + } + case DescriptionPackage.RETURN_MESSAGE_MAPPING: { + Message returnMessage = (Message) element; + T result = caseReturnMessage(returnMessage); + if (result == null) { + result = caseMessage(returnMessage); + } + if (result == null) { + result = defaultCase(element); + } + return result; + } + case DescriptionPackage.CREATION_MESSAGE_MAPPING: { + Message creationMessage = (Message) element; + T result = caseCreationMessage(creationMessage); + if (result == null) { + result = caseMessage(creationMessage); + } + if (result == null) { + result = defaultCase(element); + } + return result; + } + case DescriptionPackage.DESTRUCTION_MESSAGE_MAPPING: { + Message destructionMessage = (Message) element; + T result = caseDestructionMessage(destructionMessage); + if (result == null) { + result = caseMessage(destructionMessage); + } + if (result == null) { + result = defaultCase(element); + } + return result; + } + case DescriptionPackage.FRAME_MAPPING: { + AbstractFrame frame = (AbstractFrame) element; + T result = caseFrame(frame); + if (result == null) { + result = caseDelimitedEvent(frame); + } + if (result == null) { + result = caseEvent(frame); + } + if (result == null) { + result = defaultCase(element); + } + return result; + } + case DescriptionPackage.INTERACTION_USE_MAPPING: { + InteractionUse interactionUse = (InteractionUse) element; + T result = caseInteractionUse(interactionUse); + if (result == null) { + result = caseFrame(interactionUse); + } + if (result == null) { + result = caseDelimitedEvent(interactionUse); + } + if (result == null) { + result = caseEvent(interactionUse); + } + if (result == null) { + result = defaultCase(element); + } + return result; + } + case DescriptionPackage.COMBINED_FRAGMENT_MAPPING: { + CombinedFragment combinedFragment = (CombinedFragment) element; + T result = caseCombinedFragment(combinedFragment); + if (result == null) { + result = caseFrame(combinedFragment); + } + if (result == null) { + result = caseDelimitedEvent(combinedFragment); + } + if (result == null) { + result = caseEvent(combinedFragment); + } + if (result == null) { + result = defaultCase(element); + } + return result; + } + case DescriptionPackage.OPERAND_MAPPING: { + Operand operand = (Operand) element; + T result = caseOperand(operand); + if (result == null) { + result = caseDelimitedEvent(operand); + } + if (result == null) { + result = caseEvent(operand); + } + if (result == null) { + result = defaultCase(element); + } + return result; + } + default: + return defaultCase(element); + } + } + + // CHECKSTYLE:ON + + /** + * Returns the result of interpreting the object as an instance of ' + * Sequence Diagram Description'. This + * implementation returns null; returning a non-null result will terminate + * the switch. + * + * @param object + * the target of the switch. + * @return the result of interpreting the object as an instance of ' + * Sequence Diagram'. + * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) + * @generated + */ + public T caseSequenceDiagram(SequenceDiagram object) { + return null; + } + + /** + * Returns the result of interpreting the object as an instance of ' + * Instance Role '. This implementation + * returns null; returning a non-null result will terminate the switch. + * + * @param object + * the target of the switch. + * @return the result of interpreting the object as an instance of ' + * Instance Role '. + * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) + * @generated + */ + public T caseInstanceRole(InstanceRole object) { + return null; + } + + /** + * Returns the result of interpreting the object as an instance of ' + * Event '. This implementation returns + * null; returning a non-null result will terminate the switch. + * + * @param object + * the target of the switch. + * @return the result of interpreting the object as an instance of ' + * Event '. + * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) + * @generated + */ + public T caseEvent(ISequenceEvent object) { + return null; + } + + /** + * Returns the result of interpreting the object as an instance of ' + * Delimited Event '. This implementation + * returns null; returning a non-null result will terminate the switch. + * + * @param object + * the target of the switch. + * @return the result of interpreting the object as an instance of ' + * Delimited Event '. + * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) + * @generated + */ + public T caseDelimitedEvent(ISequenceEvent object) { + return null; + } + + /** + * Returns the result of interpreting the object as an instance of ' + * Execution '. This implementation returns + * null; returning a non-null result will terminate the switch. + * + * @param object + * the target of the switch. + * @return the result of interpreting the object as an instance of ' + * Execution '. + * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) + * @generated + */ + public T caseExecution(Execution object) { + return null; + } + + /** + * Returns the result of interpreting the object as an instance of ' + * State '. This implementation returns + * null; returning a non-null result will terminate the switch. + * + * @param object + * the target of the switch. + * @return the result of interpreting the object as an instance of ' + * State '. + * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) + * @generated + */ + public T caseState(State object) { + return null; + } + + /** + * Returns the result of interpreting the object as an instance of ' + * End Of Life '. This implementation + * returns null; returning a non-null result will terminate the switch. + * + * @param object + * the target of the switch. + * @return the result of interpreting the object as an instance of ' + * End Of Life '. + * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) + * @generated + */ + public T caseEndOfLife(EndOfLife object) { + return null; + } + + /** + * Returns the result of interpreting the object as an instance of ' + * Message '. This implementation returns + * null; returning a non-null result will terminate the switch. + * + * @param object + * the target of the switch. + * @return the result of interpreting the object as an instance of ' + * Message '. + * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) + * @generated + */ + public T caseMessage(Message object) { + return null; + } + + /** + * Returns the result of interpreting the object as an instance of ' + * Basic Message '. This implementation + * returns null; returning a non-null result will terminate the switch. + * + * @param object + * the target of the switch. + * @return the result of interpreting the object as an instance of ' + * Basic Message '. + * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) + * @generated + */ + public T caseBasicMessage(Message object) { + return null; + } + + /** + * Returns the result of interpreting the object as an instance of ' + * Return Message '. This implementation + * returns null; returning a non-null result will terminate the switch. + * + * @param object + * the target of the switch. + * @return the result of interpreting the object as an instance of ' + * Return Message '. + * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) + * @generated + */ + public T caseReturnMessage(Message object) { + return null; + } + + /** + * Returns the result of interpreting the object as an instance of ' + * Creation Message '. This implementation + * returns null; returning a non-null result will terminate the switch. + * + * @param object + * the target of the switch. + * @return the result of interpreting the object as an instance of ' + * Creation Message '. + * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) + * @generated + */ + public T caseCreationMessage(Message object) { + return null; + } + + /** + * Returns the result of interpreting the object as an instance of ' + * Destruction Message '. This + * implementation returns null; returning a non-null result will terminate + * the switch. + * + * @param object + * the target of the switch. + * @return the result of interpreting the object as an instance of ' + * Destruction Message '. + * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) + * @generated + */ + public T caseDestructionMessage(Message object) { + return null; + } + + /** + * Returns the result of interpreting the object as an instance of ' + * Frame '. This implementation returns + * null; returning a non-null result will terminate the switch. + * + * @param object + * the target of the switch. + * @return the result of interpreting the object as an instance of ' + * Frame '. + * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) + * @generated + */ + public T caseFrame(AbstractFrame object) { + return null; + } + + /** + * Returns the result of interpreting the object as an instance of ' + * Interaction Use '. This implementation + * returns null; returning a non-null result will terminate the switch. + * + * @param object + * the target of the switch. + * @return the result of interpreting the object as an instance of ' + * Interaction Use '. + * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) + * @generated + */ + public T caseInteractionUse(InteractionUse object) { + return null; + } + + /** + * Returns the result of interpreting the object as an instance of ' + * Combined Fragment '. This implementation + * returns null; returning a non-null result will terminate the switch. + * + * @param object + * the target of the switch. + * @return the result of interpreting the object as an instance of ' + * Combined Fragment '. + * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) + * @generated + */ + public T caseCombinedFragment(CombinedFragment object) { + return null; + } + + /** + * Returns the result of interpreting the object as an instance of ' + * Operand '. This implementation returns + * null; returning a non-null result will terminate the switch. + * + * @param object + * the target of the switch. + * @return the result of interpreting the object as an instance of ' + * Operand '. + * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) + * @generated + */ + public T caseOperand(Operand object) { + return null; + } + + /** + * Returns the result of interpreting the object as an instance of ' + * EObject'. This implementation returns + * null; returning a non-null result will terminate the switch, but this is + * the last case anyway. + * + * @param object + * the target of the switch. + * @return the result of interpreting the object as an instance of ' + * EObject'. + * @see #doSwitch(org.eclipse.emf.ecore.EObject) + * @generated + */ + public T defaultCase(ISequenceElement object) { + return null; + } + +} // DescriptionSwitch diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/ISequenceEventsTreeIterator.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/ISequenceEventsTreeIterator.java new file mode 100644 index 0000000000..872fffc492 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/ISequenceEventsTreeIterator.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2013 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.util; + +import java.util.Iterator; + +import org.eclipse.emf.common.util.AbstractTreeIterator; + +import com.google.common.collect.Iterators; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; + +/** + * A tree iterator to iterate on hierarchies of ISequenceEvent. + * + * @author pcdavid + */ +public class ISequenceEventsTreeIterator extends AbstractTreeIterator { + /** + * Generated serial version UID. + */ + private static final long serialVersionUID = 3234398995856675133L; + + /** + * Creates a new tree iterator on the specified event and all its descendant + * events. + * + * @param root + * the root event of the iteration. + */ + public ISequenceEventsTreeIterator(ISequenceEvent root) { + super(root); + } + + /** + * Creates a new tree iterator on the descendants of the specified event. + * + * @param root + * the root event of the iteration. + * @param includeRoot + * if true, the iterator includes the root element. + */ + public ISequenceEventsTreeIterator(ISequenceEvent root, boolean includeRoot) { + super(root, includeRoot); + } + + /** + * {@inheritDoc} + */ + @Override + protected Iterator getChildren(Object object) { + if (object instanceof ISequenceEvent) { + Iterable children = ((ISequenceEvent) object).getSubEvents(); + return children.iterator(); + } else { + return Iterators.emptyIterator(); + } + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/ParentOperandFinder.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/ParentOperandFinder.java new file mode 100644 index 0000000000..cbcdda5776 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/ParentOperandFinder.java @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Set; + +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.CombinedFragment; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Execution; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InteractionUse; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.State; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * . + * + * @author mporhel + * + */ +public final class ParentOperandFinder { + + private static Collection> types = new ArrayList>(); + { + types.add(Lifeline.class); + types.add(AbstractNodeEvent.class); + types.add(Execution.class); + types.add(State.class); + types.add(InteractionUse.class); + types.add(CombinedFragment.class); + types.add(Message.class); + } + + private Function rangeFunction = ISequenceEvent.VERTICAL_RANGE; + + private ISequenceEvent event; + + /** + * Default constructor. + * + * @param event + * a supported {@link ISequenceEvent} : {@linkLifeline}, + * {@link AbstractNodeEvent}, {@link Operand}. + */ + public ParentOperandFinder(ISequenceEvent event) { + Preconditions.checkArgument(types.contains(event.getClass())); + Preconditions.checkNotNull(event); + this.event = event; + } + + /** + * Default constructor. + * + * @param event + * a supported {@link ISequenceEvent} : {@linkLifeline}, + * {@link AbstractNodeEvent}, {@link Operand}. + * @param rangeFunction + * function to compute expected range. + */ + public ParentOperandFinder(ISequenceEvent event, Function rangeFunction) { + this(event); + Preconditions.checkNotNull(rangeFunction); + this.rangeFunction = rangeFunction; + } + + /** + * Find the parent operand if exists. + * + * @return the potential parent operand. + */ + public Option getParentOperand() { + Range verticalRange = rangeFunction.apply(event); + return getParentOperand(verticalRange); + } + + /** + * Finds the deepest Operand container convering the range if existing. + * + * @param verticalRange + * the range where to look for the deepest operand + * @return the deepest Operand convering this lifeline at this range + * @see ISequenceEvent#getParentOperand() + */ + public Option getParentOperand(final Range verticalRange) { + SequenceDiagram diagram = event.getDiagram(); + Set allOperands = diagram.getAllOperands(); + + Predicate coveredLifeline = new Predicate() { + // Filter the operands that cover the execution parent lifeline + public boolean apply(Operand input) { + Collection computeCoveredLifelines = input.computeCoveredLifelines(); + return computeCoveredLifelines != null && computeCoveredLifelines.contains(event.getLifeline().get()); + } + }; + + Predicate includingExecutionRange = new Predicate() { + // Filter the operands having a range that contains the execution + // range (we consider the insertion point : lowerbound of range) + public boolean apply(Operand input) { + return rangeFunction.apply(input).includes(new Range(verticalRange.getLowerBound(), verticalRange.getLowerBound())); + // return rangeFunction.apply(input).includes(verticalRange); + } + }; + + Operand deepestCoveringOperand = null; + + for (Operand operand : Iterables.filter(allOperands, Predicates.and(coveredLifeline, includingExecutionRange))) { + // Find the deepest operand among the filtered ones + if (deepestCoveringOperand == null || rangeFunction.apply(deepestCoveringOperand).includes(rangeFunction.apply(operand))) { + deepestCoveringOperand = operand; + } + } + if (deepestCoveringOperand != null) { + return Options.newSome(deepestCoveringOperand); + } else { + return Options.newNone(); + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/RangeSetter.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/RangeSetter.java new file mode 100644 index 0000000000..2a87b40a77 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/RangeSetter.java @@ -0,0 +1,314 @@ +/******************************************************************************* + * Copyright (c) 2010, 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.util; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gmf.runtime.notation.Bounds; +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.LayoutConstraint; +import org.eclipse.gmf.runtime.notation.Location; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.RelativeBendpoints; +import org.eclipse.gmf.runtime.notation.Size; +import org.eclipse.gmf.runtime.notation.datatype.RelativeBendpoint; + +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.CombinedFragment; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.EndOfLife; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceNode; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InteractionUse; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.LostMessageEnd; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.State; +import org.eclipse.sirius.diagram.sequence.business.internal.query.SequenceMessageViewQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.query.SequenceNodeQuery; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * . + * + * @author mporhel + * + */ +public final class RangeSetter { + + /** + * Avoid instanticiation. + */ + private RangeSetter() { + // Do nothing. + } + + /** + * Common implementation of + * {@link ISequenceEventEditPart#setVerticalRange(Range)} for lifelines and + * executions. Assumes the {@link ISequenceEventEditPart} is a Node. + * + * @param self + * the (root) execution edit part. + * @param range + * the vertical range of the given sequence event. + */ + public static void setVerticalRange(AbstractNodeEvent self, Range range) { + Range oldRange = self.getVerticalRange(); + int deltaY = range.getLowerBound() - oldRange.getLowerBound(); + int size = range.width(); + + RangeSetter.setVerticalRange(self.getNotationNode(), deltaY, size); + } + + /** + * Common implementation of + * {@link ISequenceEventEditPart#setVerticalRange(Range)} for states. + * Assumes the {@link ISequenceEventEditPart} is a Node. + * + * @param self + * the state edit part. + * @param range + * the vertical range of the given sequence event. + */ + public static void setVerticalRange(State self, Range range) { + Range oldRange = self.getVerticalRange(); + int deltaY = range.getLowerBound() - oldRange.getLowerBound(); + int size = range.width(); + + RangeSetter.setVerticalRange(self.getNotationNode(), deltaY, size); + } + + /** + * Common implementation of + * {@link ISequenceEventEditPart#setVerticalRange(Range)} for interaction + * uses. Assumes the {@link ISequenceEventEditPart} is a Node. + * + * @param self + * the InteractionUse. + * @param range + * the vertical range of the given sequence event. + */ + public static void setVerticalRange(InteractionUse self, Range range) { + Range oldRange = self.getVerticalRange(); + int deltaY = range.getLowerBound() - oldRange.getLowerBound(); + int size = range.width(); + + RangeSetter.setVerticalRange(self.getNotationNode(), deltaY, size); + } + + /** + * Common implementation of + * {@link ISequenceEventEditPart#setVerticalRange(Range)} for combined + * fragments. Assumes the {@link ISequenceEventEditPart} is a Node. + * + * @param self + * the CombinedFragment. + * @param range + * the vertical range of the given sequence event. + */ + public static void setVerticalRange(CombinedFragment self, Range range) { + Range oldRange = self.getVerticalRange(); + int deltaY = range.getLowerBound() - oldRange.getLowerBound(); + int size = range.width(); + + RangeSetter.setVerticalRange(self.getNotationNode(), deltaY, size); + } + + /** + * Common implementation of + * {@link ISequenceEventEditPart#setVerticalRange(Range)} for combined + * fragments. Assumes the {@link ISequenceEventEditPart} is a Node. + * + * @param self + * the CombinedFragment. + * @param range + * the vertical range of the given sequence event. + */ + public static void setVerticalRange(Operand self, Range range) { + Range oldRange = self.getVerticalRange(); + int deltaY = range.getLowerBound() - oldRange.getLowerBound(); + int size = range.width(); + + RangeSetter.setVerticalRange(self.getNotationNode(), deltaY, size); + } + + /** + * Common implementation of + * {@link ISequenceEventEditPart#setVerticalRange(Range)} for lifelines and + * executions. Assumes the {@link ISequenceEventEditPart} is a Node. + * + * @param self + * the (root) execution edit part. + * @param range + * the vertical range of the given sequence event. + */ + public static void setVerticalRange(Lifeline self, Range range) { + InstanceRole instanceRole = self.getInstanceRole(); + Rectangle irepBounds = instanceRole.getBounds(); + Range irRange = new Range(range.getLowerBound() - irepBounds.height, range.getUpperBound()); + RangeSetter.setVerticalRange(instanceRole, irRange); + + Range oldRange = self.getVerticalRange(); + int deltaY = range.getLowerBound() - oldRange.getLowerBound(); + int size = range.width(); + RangeSetter.setVerticalRange(self.getNotationNode(), deltaY, size); + + Option eol = self.getEndOfLife(); + if (eol.some()) { + EndOfLife endOfLife = eol.get(); + Rectangle eolBounds = endOfLife.getBounds(); + Range eolRange = new Range(range.getUpperBound(), range.getUpperBound() + eolBounds.height); + RangeSetter.setVerticalRange(endOfLife, eolRange); + } + } + + /** + * . + * + * @param self + * . + * @param range + * . + */ + private static void setVerticalRange(InstanceRole self, Range range) { + Node node = self.getNotationNode(); + Range oldRange = new SequenceNodeQuery(node).getVerticalRange(); + + int deltaY = range.getLowerBound() - oldRange.getLowerBound(); + int size = oldRange.width(); + + RangeSetter.setVerticalRange(node, deltaY, size); + } + + /** + * . + * + * @param self + * . + * @param range + * . + */ + private static void setVerticalRange(EndOfLife self, Range range) { + Node node = self.getNotationNode(); + Range oldRange = new SequenceNodeQuery(node).getVerticalRange(); + + int deltaY = range.getLowerBound() - oldRange.getLowerBound(); + int size = oldRange.width(); + + RangeSetter.setVerticalRange(node, deltaY, size); + } + + /** + * . + * + * @param node + * . + * @param deltaY + * . + * @param size + * . + */ + private static void setVerticalRange(Node node, int deltaY, int size) { + LayoutConstraint layoutConstraint = node.getLayoutConstraint(); + int realDeltaY = deltaY; + // if (realDeltaY > 0) { + // realDeltaY -= IBorderItemOffsets.DEFAULT_OFFSET.height; + // }else if(realDeltaY<0){ + // realDeltaY += IBorderItemOffsets.DEFAULT_OFFSET.height; + // } + if (layoutConstraint instanceof Location && realDeltaY != 0) { + Location location = (Location) layoutConstraint; + location.setY(location.getY() + realDeltaY); + } + if (layoutConstraint instanceof Size) { + Size s = (Size) layoutConstraint; + if (s.getHeight() != size) { + s.setHeight(size); + } + } + } + + /** + * Common implementation of + * {@link ISequenceEventEditPart#setVerticalRange(Range)} for lifelines and + * executions. Assumes the {@link ISequenceEventEditPart} is a Node. + * + * @param self + * the (root) execution edit part. + * @param range + * the vertical range of the given sequence event. + */ + public static void setVerticalRange(Message self, Range range) { + RangeSetter.handlePotentialLostEnd(self.getSourceElement(), range); + RangeSetter.handlePotentialLostEnd(self.getTargetElement(), range); + + Edge edge = self.getNotationEdge(); + SequenceMessageViewQuery query = new SequenceMessageViewQuery(edge); + + int firstPointVerticalPosition = query.getFirstPointVerticalPosition(true); + int lastPointVerticalPosition = query.getLastPointVerticalPosition(true); + + int firstPointVerticalPositionFromTarget = query.getFirstPointVerticalPosition(false); + int lastPointVerticalPositionFromTarget = query.getLastPointVerticalPosition(false); + + int deltaFirstPointY = range.getLowerBound() - firstPointVerticalPosition; + int deltaFirstPointYFromTarget = range.getLowerBound() - firstPointVerticalPositionFromTarget; + + int deltaLastPointY = range.getUpperBound() - lastPointVerticalPosition; + int deltaLastPointYFromTarget = range.getUpperBound() - lastPointVerticalPositionFromTarget; + + RangeSetter.updateBendpoints(edge, firstPointVerticalPosition, lastPointVerticalPosition, deltaFirstPointY, deltaFirstPointYFromTarget, deltaLastPointY, deltaLastPointYFromTarget); + } + + private static void handlePotentialLostEnd(ISequenceNode end, Range range) { + if (end instanceof LostMessageEnd) { + Node node = end.getNotationNode(); + LayoutConstraint layoutConstraint = node.getLayoutConstraint(); + if (layoutConstraint instanceof Bounds) { + Bounds bounds = (Bounds) layoutConstraint; + int middleValue = bounds.getY() + bounds.getHeight() / 2; + RangeSetter.setVerticalRange(node, range.middleValue() - middleValue, bounds.getHeight()); + } + } + } + + private static void updateBendpoints(Edge edge, int firstPointVerticalPosition, int lastPointVerticalPosition, int deltaFirstPointY, int deltaFirstPointYFromTarget, int deltaLastPointY, + int deltaLastPointYFromTarget) { + List newBendpoints = new ArrayList(); + RelativeBendpoints bendpoints = (RelativeBendpoints) edge.getBendpoints(); + + Iterable relPoints = Iterables.filter(bendpoints.getPoints(), RelativeBendpoint.class); + + for (int i = 0; i < Iterables.size(relPoints) / 2; i++) { + RelativeBendpoint p = Iterables.get(relPoints, i); + RangeSetter.addBendpoint(newBendpoints, p, deltaFirstPointY, deltaFirstPointYFromTarget); + } + + for (int i = Iterables.size(relPoints) / 2; i < Iterables.size(relPoints); i++) { + RelativeBendpoint p = Iterables.get(relPoints, i); + RangeSetter.addBendpoint(newBendpoints, p, deltaLastPointY, deltaLastPointYFromTarget); + } + + if (!BendpointsHelper.areSameBendpoints(bendpoints.getPoints(), newBendpoints)) { + bendpoints.setPoints(newBendpoints); + } + } + + private static void addBendpoint(List newBendpoints, RelativeBendpoint p, double deltaSourceY, double deltaTargetY) { + newBendpoints.add(new RelativeBendpoint(p.getSourceX(), (int) (p.getSourceY() + deltaSourceY), p.getTargetX(), (int) (p.getTargetY() + deltaTargetY))); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/SameLifelinePredicate.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/SameLifelinePredicate.java new file mode 100644 index 0000000000..321701389d --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/SameLifelinePredicate.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.util; + +import java.util.Collection; + +import com.google.common.base.Predicate; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractFrame; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceNode; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; + +/** + * Predicate used to filter {@link ISequenceEvent} owned by one specified + * {@link Lifeline}. + * + * @author edugueperoux + */ +public class SameLifelinePredicate implements Predicate { + + private Lifeline owner; + + /** + * Default constructor. + * + * @param owner + * the {@link Lifeline} owner + */ + public SameLifelinePredicate(Lifeline owner) { + assert owner != null; + this.owner = owner; + } + + /** + * Overridden to tells if the specified {@link ISequenceEvent} is owned by + * the current {@link Lifeline}. + * + * {@inheritDoc} + */ + public boolean apply(ISequenceEvent input) { + boolean result = false; + Option inputLifeline = input.getLifeline(); + if (inputLifeline.some()) { + result = inputLifeline.get().equals(owner); + } else if (input instanceof Message) { + Message message = (Message) input; + ISequenceNode sourceElt = message.getSourceElement(); + ISequenceNode targetElt = message.getSourceElement(); + Option sourceLifeline = sourceElt.getLifeline(); + Option targetLifeline = targetElt.getLifeline(); + result = sourceLifeline.some() && sourceLifeline.get().equals(owner) || targetLifeline.some() && targetLifeline.get().equals(owner); + } else if (input instanceof Operand) { + result = true; + } else if (input instanceof AbstractFrame) { + AbstractFrame frame = (AbstractFrame) input; + Collection coverage = frame.computeCoveredLifelines(); + result = coverage.contains(owner); + } + return result; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/SubEventsHelper.java b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/SubEventsHelper.java new file mode 100644 index 0000000000..3336a8ba04 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence/src/org/eclipse/sirius/diagram/sequence/business/internal/util/SubEventsHelper.java @@ -0,0 +1,385 @@ +/******************************************************************************* + * Copyright (c) 2010, 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.business.internal.util; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractFrame; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.CombinedFragment; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Execution; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceNode; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.State; +import org.eclipse.sirius.diagram.sequence.business.internal.ordering.EventEndHelper; +import org.eclipse.sirius.diagram.sequence.business.internal.query.ISequenceEventQuery; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * . + * + * @author mporhel + * + */ +public final class SubEventsHelper { + + private static Collection> types = Lists.newArrayList(); + { + types.add(Execution.class); + types.add(Lifeline.class); + types.add(Operand.class); + } + + private ISequenceEvent parentEvent; + + private Range parentRange; + + private final Multimap coverage = HashMultimap.create(); + + /** + * Default constructor. + * + * @param event + * a supported {@link ISequenceEvent} : {@linkLifeline}, + * {@link AbstractNodeEvent}, {@link Operand}. + */ + public SubEventsHelper(ISequenceEvent event) { + Preconditions.checkArgument(types.contains(event.getClass())); + Preconditions.checkNotNull(event); + this.parentEvent = event; + this.parentRange = event.getVerticalRange(); + } + + /** + * Common implementation of {@link ISequenceEvent#getSubEvents()}. + * + * @return the sub-events of the (root) execution, ordered by their starting + * position (graphically) from top to bottom. + */ + public List getSubEvents() { + List result = getValidSubEvents(); + Collections.sort(result, Range.lowerBoundOrdering().onResultOf(ISequenceEvent.VERTICAL_RANGE)); + return result; + } + + /** + * Compute childs of Execution/Operand/Lifeline. + * + * @return the sub events. + */ + private List getValidSubEvents() { + List childrenEvents = Lists.newArrayList(); + + Set localParents = Sets.newHashSet(); + Set coveredLifelines = Sets.newHashSet(); + if (parentEvent instanceof AbstractNodeEvent || parentEvent instanceof Lifeline) { + localParents.add(parentEvent); + coveredLifelines.add(parentEvent.getLifeline().get()); + } else if (parentEvent instanceof Operand) { + Operand op = (Operand) parentEvent; + CombinedFragment parentCombinedFragment = op.getCombinedFragment(); + coveredLifelines.addAll(getCoverage(parentCombinedFragment)); + localParents.addAll(getCarryingParents(parentCombinedFragment, coveredLifelines)); + } + + Set hierarchicalChildren = getNotationDirectChildrenInParentRange(localParents); + Set frameChildren = getFrameChildrenInParentRange(coveredLifelines); + + childrenEvents.addAll(getTopLevelEvents(hierarchicalChildren, frameChildren, coveredLifelines)); + childrenEvents.addAll(getTopLevelEvents(frameChildren, childrenEvents, coveredLifelines)); + + return childrenEvents; + } + + private Collection getCoverage(ISequenceEvent ise) { + if (coverage.containsKey(ise)) { + return coverage.get(ise); + } + + Option lifeline = ise.getLifeline(); + Collection coveredLifelines = Lists.newArrayList(); + if (lifeline.some()) { + coveredLifelines.add(lifeline.get()); + } else if (ise instanceof Operand) { + coveredLifelines.addAll(getCoverage(((Operand) ise).getCombinedFragment())); + } else if (ise instanceof AbstractFrame) { + coveredLifelines.addAll(((AbstractFrame) ise).computeCoveredLifelines()); + } else if (ise instanceof Message) { + Message msg = (Message) ise; + Option sourceLifeline = msg.getSourceLifeline(); + if (sourceLifeline.some()) { + coveredLifelines.add(sourceLifeline.get()); + } + Option targetLifeline = msg.getTargetLifeline(); + if (targetLifeline.some()) { + coveredLifelines.add(targetLifeline.get()); + } + } + coverage.putAll(ise, coveredLifelines); + return coveredLifelines; + } + + private Collection getCarryingParents(AbstractFrame frame, Set coveredLifelines) { + Set coveredEvents = Sets.newHashSet(); + for (Lifeline lifeline : coveredLifelines) { + EventFinder localParentFinder = new EventFinder(lifeline); + localParentFinder.setReparent(true); + localParentFinder.setEventsToIgnore(Predicates.equalTo((ISequenceEvent) frame)); + ISequenceEvent localParent = localParentFinder.findMostSpecificEvent(frame.getVerticalRange()); + if (localParent != null) { + coveredEvents.add(localParent); + } + } + return coveredEvents; + } + + /** + * Look for execution and lifeline + * + * @param localParents + * @return + */ + private Set getNotationDirectChildrenInParentRange(Set localParents) { + Collection childViews = Sets.newLinkedHashSet(); + Collection parentConnections = Sets.newHashSet(); + Set childrenEvents = Sets.newHashSet(); + + for (ISequenceEvent ise : localParents) { + View notationView = ise.getNotationView(); + childViews.addAll(Lists.newArrayList(Iterables.filter(notationView.getChildren(), View.class))); + + Collection sourceEdges = Lists.newArrayList(Iterables.filter(notationView.getSourceEdges(), View.class)); + Collection targetEdges = Lists.newArrayList(Iterables.filter(notationView.getTargetEdges(), View.class)); + + childViews.addAll(sourceEdges); + childViews.addAll(targetEdges); + + // In some resize cases, edges could appears to be out of their + // source/target ranges + if (ise == parentEvent) { + parentConnections.addAll(sourceEdges); + parentConnections.addAll(targetEdges); + } + } + + for (View view : childViews) { + Option iSequenceEvent = ISequenceElementAccessor.getISequenceEvent(view); + if (iSequenceEvent.some()) { + ISequenceEvent ise = iSequenceEvent.get(); + if (parentConnections.contains(view) || parentRange.includes(ise.getVerticalRange())) { + childrenEvents.add(iSequenceEvent.get()); + } + } + } + + return Sets.newHashSet(EventEndHelper.getIndependantEvents(parentEvent, childrenEvents)); + } + + private Set getFrameChildrenInParentRange(Set coveredLifelines) { + Set childrenEvents = Sets.newHashSet(); + SequenceDiagram diagram = parentEvent.getDiagram(); + Set frames = Sets.newHashSet(); + frames.addAll(diagram.getAllFrames()); + + for (AbstractFrame frame : frames) { + Range frameRange = frame.getVerticalRange(); + if (parentRange.includes(frameRange) && validCoverage(frame, coveredLifelines)) { + childrenEvents.add(frame); + } + } + return getTopLevelEvents(childrenEvents, childrenEvents, coveredLifelines); + } + + private Set getTopLevelEvents(Set events, Collection potentialParents, Set coveredLifelines) { + HashSet topLevel = Sets.newHashSet(); + boolean parentFrames = Iterables.size(Iterables.filter(potentialParents, AbstractFrame.class)) != 0; + + for (ISequenceEvent event : events) { + final Range verticalRange = event.getVerticalRange(); + final ISequenceEvent potentialChild = event; + + Predicate isParentOfCurrent = new Predicate() { + public boolean apply(ISequenceEvent input) { + Range inputRange = input.getVerticalRange(); + boolean isParent = inputRange.includes(verticalRange); + return isParent && input != potentialChild; + } + }; + + List parents = Lists.newArrayList(Iterables.filter(potentialParents, isParentOfCurrent)); + if (parents.isEmpty()) { + topLevel.add(potentialChild); + } else if (potentialChild instanceof AbstractFrame && !parentFrames) { + Collection carriers = Lists.newArrayList(getCarryingParents((AbstractFrame) potentialChild, coveredLifelines)); + Iterables.removeAll(carriers, parents); + if (!carriers.isEmpty()) { + topLevel.add(potentialChild); + } + } + } + return topLevel; + } + + private boolean validCoverage(AbstractFrame frame, Set parentCoveredLifelines) { + boolean result = false; + Collection coveredLifelines = getCoverage(frame); + if (parentEvent instanceof Operand) { + result = parentCoveredLifelines.containsAll(coveredLifelines); + } else { + result = coveredLifelines.contains(parentEvent.getLifeline().get()); + } + + return result; + } + + /** + * Implementation of + * {@link ISequenceEvent#canChildOccupy(ISequenceEvent, Range)} . + * + * @param child + * the child. + * @param range + * the vertical range to test. + * @return true if the child can be placed anywhere inside the + * specified vertical range (including occupying the whole range). + */ + public boolean canChildOccupy(ISequenceEvent child, Range range) { + return canChildOccupy(child, range, null, child == null ? getCoverage(parentEvent) : getCoverage(child)); + } + + /** + * Implementation of + * {@link ISequenceEvent#canChildOccupy(ISequenceEvent, Range)} . + * + * @param child + * the child, if child is null it means that it is a insertion + * point request from a CreationTool. + * @param range + * the vertical range to test. + * @param eventsToIgnore + * the list of events to ignore while compute canChildOccupy. + * @param lifelines + * lifelines to inspect + * @return true if the child can be placed anywhere inside the + * specified vertical range (including occupying the whole range). + */ + public boolean canChildOccupy(ISequenceEvent child, final Range range, List eventsToIgnore, Collection lifelines) { + boolean result = true; + if (!parentEvent.getValidSubEventsRange().includes(range)) { + result = false; + } else { + for (ISequenceEvent event : getSequenceEventsToFilter(parentEvent, child, range, lifelines)) { + Range eventRange = event.getVerticalRange(); + if (eventsToIgnore != null && eventsToIgnore.contains(event)) { + // do nothing + } else if (event instanceof Message) { + Message msg = (Message) event; + if (!checkOverlapWithSiblingMessage(child, range, msg, eventRange)) { + result = false; + break; + } + } else if (eventRange.intersects(range) || !range.validatesBoundsAreDifferent(eventRange)) { + result = false; + break; + } + } + } + return result; + } + + private boolean checkOverlapWithSiblingMessage(ISequenceEvent child, Range childRange, Message msg, Range msgRange) { + boolean result = true; + // if event is a SequenceMessageEditPart reconnect it to the + // moved child + // only if event is a simple message or if full range of its + // associated Execution + // with the associated messages (call and return) is included in + // the future range of the child being dropped + if (child instanceof State) { + // A state can not have an overlapping message + result = !childRange.includesAtLeastOneBound(msgRange); + } else if (msg.isReflective()) { + if (msgRange.intersects(childRange) && (childRange.getLowerBound() <= msgRange.getLowerBound() || childRange.getUpperBound() >= msgRange.getUpperBound())) { + result = false; + } + } else { + ISequenceNode sourceEvent = msg.getSourceElement(); + ISequenceNode targetEvent = msg.getTargetElement(); + Execution parentExecEvent = null; + if (sourceEvent instanceof Execution) { + Execution sourceExec = (Execution) sourceEvent; + if (msg.equals(sourceExec.getEndMessage().get())) { + parentExecEvent = sourceExec; + } + } + + if (targetEvent instanceof Execution) { + Execution targetExec = (Execution) targetEvent; + if (msg.equals(targetExec.getStartMessage().get())) { + parentExecEvent = targetExec; + } + } + + if (parentExecEvent != null) { + Range verticalRange = parentExecEvent.getVerticalRange(); + if (child != null && (!childRange.validatesBoundsAreDifferent(verticalRange) || !childRange.includes(verticalRange) && !verticalRange.includes(childRange))) { + result = false; + } + } + } + return result; + } + + private Iterable getSequenceEventsToFilter(ISequenceEvent self, ISequenceEvent child, final Range range, final Collection lifelines) { + Set result = Sets.newHashSet(self.getSubEvents()); + Predicate inRangePredicate = new Predicate() { + + public boolean apply(ISequenceEvent input) { + Range inputRange = input.getVerticalRange(); + return range.includesAtLeastOneBound(inputRange) || new ISequenceEventQuery(input).isReflectiveMessage() && inputRange.includesAtLeastOneBound(range); + } + + }; + Predicate inCoverage = new Predicate() { + + public boolean apply(ISequenceEvent input) { + Collection inputCoverage = Lists.newArrayList(getCoverage(input)); + return Iterables.removeAll(inputCoverage, lifelines); + } + + }; + + @SuppressWarnings("unchecked") + Predicate predicateFilter = Predicates.and(Predicates.not(Predicates.equalTo(child)), inRangePredicate, inCoverage); + return Iterables.filter(result, predicateFilter); + } +} -- cgit v1.2.3