package org.eclipse.papyrus.uml.diagram.sequence.apex.command;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.PositionConstants;
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.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.RequestConstants;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gmf.runtime.common.core.command.CommandResult;
import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy;
import org.eclipse.gmf.runtime.diagram.ui.commands.SetBoundsCommand;
import org.eclipse.gmf.runtime.diagram.ui.editparts.ConnectionNodeEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand;
import org.eclipse.papyrus.uml.diagram.sequence.apex.interfaces.IApexLifelineEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.apex.util.ApexOccurrenceSpecificationMoveHelper;
import org.eclipse.papyrus.uml.diagram.sequence.apex.util.ApexSequenceRequestConstants;
import org.eclipse.papyrus.uml.diagram.sequence.apex.util.ApexSequenceUtil;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.InteractionOperandEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.LifelineEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.edit.policies.InteractionCompartmentXYLayoutEditPolicy;
import org.eclipse.papyrus.uml.diagram.sequence.util.OperandBoundsComputeHelper;
import org.eclipse.papyrus.uml.diagram.sequence.util.SequenceRequestConstant;
import org.eclipse.papyrus.uml.diagram.sequence.util.SequenceUtil;
import org.eclipse.uml2.uml.CombinedFragment;
import org.eclipse.uml2.uml.ExecutionSpecification;
import org.eclipse.uml2.uml.Interaction;
import org.eclipse.uml2.uml.InteractionFragment;
import org.eclipse.uml2.uml.InteractionOperand;
import org.eclipse.uml2.uml.Lifeline;
import org.eclipse.uml2.uml.Message;
import org.eclipse.uml2.uml.MessageOccurrenceSpecification;
/**
* @author Jiho
*
*/
@SuppressWarnings("unchecked")
public class ApexMoveInteractionFragmentsCommand extends
AbstractTransactionalCommand {
protected final static String COMMAND_LABEL = "Move InteractionFragments";
protected EditPartViewer viewer;
/**
* container (Interaction, CombinedFragment)
*/
private InteractionFragment fragment;
/**
* 이동 전 기준이 되는 범위
*/
private Rectangle extent;
/**
* moveDelta, sizeDelta
*/
private Point moveDelta;
/**
* 상하 여백
*/
private int margin;
private boolean dontMoveOthers;
/**
* 이동이 무시되는 fragment 리스트
*/
protected Collection<EObject> notToMoveEObject;
private CompoundCommand command;
public ApexMoveInteractionFragmentsCommand(
TransactionalEditingDomain domain, EditPartViewer viewer, InteractionFragment fragment, Point location, Point moveDelta, int margin) {
this(domain, viewer, fragment, new Rectangle(location.x, location.y, 0, 0), moveDelta, margin, false);
}
public ApexMoveInteractionFragmentsCommand(
TransactionalEditingDomain domain, EditPartViewer viewer, InteractionFragment fragment, Point location, Point moveDelta, int margin, boolean dontMoveOthers) {
this(domain, viewer, fragment, new Rectangle(location.x, location.y, 0, 0), moveDelta, margin, dontMoveOthers);
}
public ApexMoveInteractionFragmentsCommand(
TransactionalEditingDomain domain, EditPartViewer viewer, InteractionFragment fragment, Rectangle extent, Point moveDelta, int margin) {
this(domain, viewer, fragment, extent, moveDelta, margin, false);
}
/**
*
* @param domain editing domain
* @param viewer EditPartViewer
* @param fragment container (Interaction or InteractionOperand)
* @param extent 직접적인 이동 영역, Message의 경우 y만 해당되고, CF의 경우 y~bottom영역이 해당 됨
* @param moveDelta move delta
* @param margin
* @param dontMoveOthers true 이면 extent 범위만 이동하고, false 이면 extent 하위가 모두 이동
*/
public ApexMoveInteractionFragmentsCommand(
TransactionalEditingDomain domain, EditPartViewer viewer, InteractionFragment fragment, Rectangle extent, Point moveDelta, int margin, boolean dontMoveOthers) {
super(domain, COMMAND_LABEL, null);
this.viewer = viewer;
this.fragment = fragment;
this.extent = extent;
this.moveDelta = moveDelta;
this.margin = margin;
this.dontMoveOthers = dontMoveOthers;
command = new CompoundCommand();
notToMoveEObject = new HashSet<EObject>();
}
@Override
protected CommandResult doExecuteWithResult(IProgressMonitor monitor,
IAdaptable info) throws ExecutionException {
Map<IGraphicalEditPart, Collection<MessageOccurrenceSpecification>> needToMoveMessages = new HashMap<IGraphicalEditPart, Collection<MessageOccurrenceSpecification>>();
Map<IGraphicalEditPart, Integer> needToMoveBottoms = new HashMap<IGraphicalEditPart, Integer>();
Collection<InteractionFragment> fragments = getInteractionFragments(fragment);
Point realMoveDelta = getRealMoveDelta(getMoveDelta(), fragments);
for (InteractionFragment ift : fragments) {
if (notToMoveEObject.contains(ift))
continue;
if (ift instanceof ExecutionSpecification) {
IGraphicalEditPart editPart = getEditPart(ift);
Rectangle bounds = ApexSequenceUtil.getAbsoluteBounds(editPart);
// extent 하단의 이동을 방지하였을 때 (dontMoveOthers == false)
// 하단에 위치한 EditPart는 이동하지 않음
if (dontMoveOthers && extent.bottom() < bounds.y) {
continue;
}
if (extent.y <= bounds.y) { // Activation이 이동범위에 모두 포함된 경우, bounds를 이동
if (!dontMoveOthers) {
Rectangle newBounds = bounds.getCopy();
Rectangle parentBounds = editPart.getFigure().getParent().getBounds().getCopy();
editPart.getFigure().translateToRelative(newBounds);
newBounds.translate(-parentBounds.x, -parentBounds.y);
newBounds.translate(realMoveDelta);
SetBoundsCommand sbCommand = new SetBoundsCommand(getEditingDomain(), "Set Bounds", editPart, newBounds);
command.add(new ICommandProxy(sbCommand));
} else {
ChangeBoundsRequest request = new ChangeBoundsRequest(RequestConstants.REQ_MOVE);
request.setMoveDelta(realMoveDelta);
command.add(editPart.getCommand(request));
}
} else if (/*extent.y > bounds.y &&*/ extent.y < bounds.bottom()) { // Activation의 하단만 이동범위에 걸쳐있을 경우, 하단 길이 변경
ChangeBoundsRequest request = new ChangeBoundsRequest(RequestConstants.REQ_RESIZE);
request.getExtendedData().put(ApexSequenceRequestConstants.APEX_PRESERVE_ANCHOR_RELATIVE_BOUNDS, extent);
request.getExtendedData().put(SequenceRequestConstant.DO_NOT_MOVE_EDIT_PARTS, true);
request.setResizeDirection(PositionConstants.NORTH);
request.setSizeDelta(new Dimension(0, realMoveDelta.y));
command.add(editPart.getCommand(request));
}
} else if (ift instanceof CombinedFragment) {
IGraphicalEditPart editPart = getEditPart(ift);
Rectangle bounds = ApexSequenceUtil.getAbsoluteBounds(editPart);
if (dontMoveOthers && extent.bottom() < bounds.y) {
continue;
}
if (extent.y <= bounds.y) { // CF가 이동범위에 모두 포함된 경우, ChangeBoundsRequest 이용
// ChangeBoundsRequest request = new ChangeBoundsRequest(RequestConstants.REQ_MOVE);
// request.setMoveDelta(realMoveDelta);
// command.add(editPart.getCommand(request));
Rectangle newBounds = bounds.getCopy();
editPart.getFigure().translateToRelative(newBounds);
newBounds.translate(realMoveDelta);
SetBoundsCommand sbCommand = new SetBoundsCommand(getEditingDomain(), "Set Bounds", editPart, newBounds);
command.add(new ICommandProxy(sbCommand));
} else if (/*extent.y > bounds.y &&*/ extent.y < bounds.bottom()) {
// CF가 걸쳐있는 경우는 발생하지 않음
}
} else if (ift instanceof MessageOccurrenceSpecification) {
Message message = ((MessageOccurrenceSpecification)ift).getMessage();
IGraphicalEditPart editPart = getEditPart(message);
if (ift.equals(message.getSendEvent()) && editPart instanceof ConnectionNodeEditPart) {
Point edge = ApexSequenceUtil.getAbsoluteEdgeExtremity((ConnectionNodeEditPart) editPart, true);
if (edge == null) {
continue;
}
if (dontMoveOthers && extent.bottom() < edge.y) {
continue;
}
if (extent.y <= edge.y) {
EditPart source = ((ConnectionNodeEditPart)editPart).getSource();
if (source instanceof LifelineEditPart) {
LifelineEditPart lifelineEditPart = (LifelineEditPart)source;
Collection<MessageOccurrenceSpecification> occurrenceSpecifications = needToMoveMessages.get(lifelineEditPart);
if (occurrenceSpecifications == null) {
occurrenceSpecifications = new HashSet<MessageOccurrenceSpecification>();
}
occurrenceSpecifications.add((MessageOccurrenceSpecification) ift);
needToMoveMessages.put(lifelineEditPart, occurrenceSpecifications);
Integer bottom = needToMoveBottoms.get(lifelineEditPart);
if (bottom == null || bottom < edge.y + realMoveDelta.y) {
needToMoveBottoms.put(lifelineEditPart, edge.y + realMoveDelta.y);
}
}
}
}
}
}
command.add(createPreserveAnchorCommands(needToMoveMessages, needToMoveBottoms, realMoveDelta));
if (fragment instanceof InteractionOperand) {
InteractionOperandEditPart ioEditPart = (InteractionOperandEditPart)getEditPart(fragment);
ChangeBoundsRequest cbRequest = new ChangeBoundsRequest(RequestConstants.REQ_RESIZE);
cbRequest.setResizeDirection(PositionConstants.SOUTH);
cbRequest.setSizeDelta(new Dimension(0, realMoveDelta.y));
command.add(OperandBoundsComputeHelper.createIOEPResizeCommand(cbRequest, ioEditPart));
}
if (command.isEmpty() || command.canExecute()) {
command.execute();
return CommandResult.newOKCommandResult();
}
System.out.println("cannot execute!!!");
return CommandResult.newCancelledCommandResult();
}
/**
* Anchor
* @param needToMoveMessages source인 LifelineEditPart에 연결된 MOS들 중 preserve anchor가 필요한 MOS들
* @param needToMoveBottoms source인 LifelineEditPart가 bottom으로 확장이 필요하다면 최종 bottom값
* @param realMoveDelta
* @return
*/
private Command createPreserveAnchorCommands(Map<IGraphicalEditPart, Collection<MessageOccurrenceSpecification>> needToMoveMessages,
Map<IGraphicalEditPart, Integer> needToMoveBottoms, Point realMoveDelta) {
CompoundCommand compCmd = new CompoundCommand();
for (Entry<IGraphicalEditPart, Collection<MessageOccurrenceSpecification>> entry : needToMoveMessages.entrySet()) {
IGraphicalEditPart editPart = entry.getKey();
Integer bottom = needToMoveBottoms.get(editPart);
Collection<MessageOccurrenceSpecification> occurrenceSpecifications = entry.getValue();
if (bottom == null) {
continue;
}
Rectangle oldBounds = ApexSequenceUtil.getAbsoluteBounds(editPart);
EObject eObj = editPart.resolveSemanticElement();
if ( eObj instanceof Lifeline ) {
IFigure figure = ((IApexLifelineEditPart)editPart).getNodeFigure();
oldBounds = figure.getBounds().getCopy();
figure.translateToAbsolute(oldBounds);
}
/* apex replaced
if (editPart instanceof LifelineEditPart) {
LifelineEditPart lifelineEP = (LifelineEditPart)editPart;
IFigure figure = lifelineEP.getNodeFigure();
oldBounds = figure.getBounds().getCopy();
figure.translateToAbsolute(oldBounds);
}
*/
Rectangle newBounds = oldBounds.getCopy();
if (newBounds.bottom() < bottom) {
newBounds.height = bottom - newBounds.y;
}
if (!newBounds.equals(oldBounds)) {
ChangeBoundsRequest request = new ChangeBoundsRequest(RequestConstants.REQ_RESIZE);
request.getExtendedData().put(SequenceRequestConstant.DO_NOT_MOVE_EDIT_PARTS, true);
request.setResizeDirection(PositionConstants.SOUTH);
request.setSizeDelta(new Dimension(0, newBounds.bottom() - oldBounds.bottom()));
compCmd.add(editPart.getCommand(request));
}
for (MessageOccurrenceSpecification occurrenceSpecification : occurrenceSpecifications) {
Message message = occurrenceSpecification.getMessage();
EditPart part = getEditPart(message);
if (occurrenceSpecification.equals(message.getSendEvent())
&& part instanceof ConnectionNodeEditPart) {
ConnectionNodeEditPart messageEP = (ConnectionNodeEditPart)part;
Point edge = ApexSequenceUtil.getAbsoluteEdgeExtremity(messageEP, true);
List<EditPart> empty = Collections.emptyList();
LifelineEditPart lifelineEP = SequenceUtil.getParentLifelinePart(editPart);
if (lifelineEP != null) {
compCmd.add(ApexOccurrenceSpecificationMoveHelper.getMoveMessageOccurrenceSpecificationsCommand(
occurrenceSpecification, edge.y + realMoveDelta.y, newBounds, editPart, lifelineEP, empty));
}
}
}
}
return compCmd.size() > 0 ? compCmd : null;
}
public Rectangle getExtent() {
return extent;
}
public Rectangle getNewExtent() {
return extent.getCopy().translate(getMoveDelta());
}
public Point getMoveDelta() {
return moveDelta;
}
public void setMoveDelta(Point moveDelta) {
this.moveDelta = moveDelta;
}
public Point getRealMoveDelta(Point moveDelta, Collection<InteractionFragment> fragments) {
Point newDelta = moveDelta.getCopy();
for (InteractionFragment ift : fragments) {
if (ift instanceof ExecutionSpecification || ift instanceof CombinedFragment) {
IGraphicalEditPart editPart = getEditPart(ift);
Rectangle bounds = ApexSequenceUtil.getAbsoluteBounds(editPart);
// 위로 이동하는 경우
if (!dontMoveOthers) {
if (extent.y > bounds.bottom() &&
getNewExtent().y < bounds.bottom() + margin) {
newDelta.y = bounds.bottom() + margin - extent.y;
}
}
}
}
return newDelta;
}
private Collection<InteractionFragment> getInteractionFragments(InteractionFragment fragment) {
Set<InteractionFragment> allFragments = new HashSet<InteractionFragment>();
if (fragment instanceof Interaction) {
allFragments.addAll(((Interaction)fragment).getFragments());
}
else if (fragment instanceof InteractionOperand) {
allFragments.addAll(((InteractionOperand)fragment).getFragments());
}
else {
if (fragment.getEnclosingOperand() != null) {
InteractionOperand operand = fragment.getEnclosingOperand();
allFragments.addAll(operand.getFragments());
}
else if (fragment.getEnclosingInteraction() != null) {
Interaction interaction = fragment.getEnclosingInteraction();
allFragments.addAll(interaction.getFragments());
}
}
return allFragments;
}
protected IGraphicalEditPart getEditPart(EObject eObject) {
return (IGraphicalEditPart)ApexSequenceUtil.getEditPart(eObject, viewer);
}
}