package org.eclipse.papyrus.uml.diagram.sequence.command;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.draw2d.geometry.Point;
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.gmf.runtime.common.core.command.CommandResult;
import org.eclipse.gmf.runtime.common.core.command.ICommand;
import org.eclipse.gmf.runtime.diagram.core.preferences.PreferencesHint;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateConnectionViewRequest;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateConnectionViewRequest.ConnectionViewDescriptor;
import org.eclipse.gmf.runtime.emf.core.util.EMFCoreUtil;
import org.eclipse.gmf.runtime.emf.type.core.IHintedType;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.osgi.util.NLS;
import org.eclipse.papyrus.infra.widgets.toolbox.notification.Type;
import org.eclipse.papyrus.infra.widgets.toolbox.notification.builders.NotificationBuilder;
import org.eclipse.papyrus.uml.diagram.common.commands.CommonDeferredCreateConnectionViewCommand;
import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.LifelineEditPart;
import org.eclipse.papyrus.uml.diagram.sequence.part.Messages;
import org.eclipse.papyrus.uml.diagram.sequence.providers.UMLElementTypes;
import org.eclipse.papyrus.uml.diagram.sequence.util.SequenceUtil;
/**
* This class is used to create a connection view which source and target locations are defined. This is useful for connections linked to an
* Occurrence Specification, which is located at a particular predefined point.
*
* @author vhemery
*/
public class CreateLocatedConnectionViewCommand extends CommonDeferredCreateConnectionViewCommand {
/** Point where source must be drawn */
private Point sourceLocation;
/** Point where target must be drawn */
private Point targetLocation;
/**
* {@inheritDoc}
*/
public CreateLocatedConnectionViewCommand(TransactionalEditingDomain editingDomain, String semanticHint, IAdaptable sourceViewAdapter, IAdaptable targetViewAdapter, EditPartViewer viewer, PreferencesHint preferencesHint, ConnectionViewDescriptor viewDescriptor, ICommand command) {
super(editingDomain, semanticHint, sourceViewAdapter, targetViewAdapter, viewer, preferencesHint, viewDescriptor, command);
}
/**
* {@inheritDoc}
*/
public CreateLocatedConnectionViewCommand(TransactionalEditingDomain editingDomain, EObject element, IAdaptable sourceViewAdapter, IAdaptable targetViewAdapter, EditPartViewer viewer, PreferencesHint preferencesHint, ICommand command) {
super(editingDomain, element, sourceViewAdapter, targetViewAdapter, viewer, preferencesHint, command);
}
/**
* Creates a connection view between the source and target.
*
* @throws ExecutionException
*/
@Override
protected CommandResult doExecuteWithResult(IProgressMonitor progressMonitor, IAdaptable info, IGraphicalEditPart sourceEditPart, IGraphicalEditPart targetEditPart) throws ExecutionException {
// If these are null, then the diagram's editparts may not
// have been refreshed yet.
Assert.isNotNull(sourceEditPart);
Assert.isNotNull(targetEditPart);
// use the String semanticHint to create a view
// modification in order to fix the bug
CreateConnectionViewRequest createRequest = new CreateConnectionViewRequest(viewDescriptor);
createConnectionCmd = getCreateCommand(createRequest, sourceEditPart, targetEditPart);
if(createConnectionCmd != null && createConnectionCmd.canExecute()) {
createConnectionCmd.execute();
if(element != null) {
((View)(createRequest.getConnectionViewDescriptor().getAdapter(View.class))).setElement(element);
}
} else {
// connection can not be created
handleErrorMessage(createRequest, sourceEditPart, targetEditPart);
}
viewer = null;// for garbage collection
return CommandResult.newOKCommandResult();
}
/**
* Method getCreateCommand Gets the command given a request, source and target edit parts. (No semantic element required.)
* This method is similar to {@link CreateConnectionViewRequest#getCreateCommand(CreateConnectionViewRequest, EditPart, EditPart)}, except it
* fixes source and target locations
*
* @param request
* creation request
* @param sourceEditPart
* source edit part
* @param targetEditPart
* target edit part
* @return <code>Command</code> to create the connection at appropriate locations
*/
protected Command getCreateCommand(CreateConnectionViewRequest request, EditPart sourceEditPart, EditPart targetEditPart) {
EditPart newSourceEditPart = sourceEditPart;
// set appropriated source edit part
if(sourceEditPart instanceof LifelineEditPart && sourceLocation != null) {
newSourceEditPart = SequenceUtil.findPartToReconnectTo((LifelineEditPart)sourceEditPart, sourceLocation);
}
EditPart newTargetEditPart = targetEditPart;
// set appropriated target edit part
if(targetEditPart instanceof LifelineEditPart && targetLocation != null) {
newTargetEditPart = SequenceUtil.findPartToReconnectTo((LifelineEditPart)targetEditPart, targetLocation);
}
Assert.isNotNull(request);
Assert.isNotNull(sourceEditPart);
Assert.isNotNull(targetEditPart);
Assert.isNotNull(newSourceEditPart);
Assert.isNotNull(newTargetEditPart);
request.setSourceEditPart(sourceEditPart);
request.setTargetEditPart(targetEditPart);
request.setType(RequestConstants.REQ_CONNECTION_START);
// set source location
request.setLocation(sourceLocation);
newSourceEditPart.getCommand(request);
request.setType(RequestConstants.REQ_CONNECTION_END);
// set target location
request.setLocation(targetLocation);
return newTargetEditPart.getCommand(request);
}
/**
* Set the locations where source and target points must be drawn.
*
* @param sourcePoint
* point where to draw source or null
* @param targetPoint
* point where to draw target or null
*/
public void setLocations(Point sourcePoint, Point targetPoint) {
sourceLocation = sourcePoint;
targetLocation = targetPoint;
}
/**
* Handle the failure by reporting an adequate error message
*
* @param createRequest
* the creation request that didn't success
* @param sourceEditPart
* the link source edit part
* @param targetEditPart
* the target source edit part
*/
private void handleErrorMessage(CreateConnectionViewRequest createRequest, IGraphicalEditPart sourceEditPart, IGraphicalEditPart targetEditPart) {
String hint = createRequest.getConnectionViewDescriptor().getSemanticHint();
boolean isMessage = isMessageHint(hint);
boolean uphill = sourceLocation != null && targetLocation != null && sourceLocation.y > targetLocation.y;
if(isMessage && uphill) {
reportCanNotDropUphillMessage(sourceLocation.y - targetLocation.y, sourceEditPart, targetEditPart);
} else {
reportDefaultMessage(sourceEditPart, targetEditPart);
}
}
/**
* Test if hint is for a message creation
*
* @param hint
* hint to test
* @return true if message hint
*/
private boolean isMessageHint(String hint) {
List<String> messageHints = new ArrayList<String>(7);
String messageHint = ((IHintedType)UMLElementTypes.Message_4003).getSemanticHint();
messageHints.add(messageHint);
messageHint = ((IHintedType)UMLElementTypes.Message_4004).getSemanticHint();
messageHints.add(messageHint);
messageHint = ((IHintedType)UMLElementTypes.Message_4005).getSemanticHint();
messageHints.add(messageHint);
messageHint = ((IHintedType)UMLElementTypes.Message_4006).getSemanticHint();
messageHints.add(messageHint);
messageHint = ((IHintedType)UMLElementTypes.Message_4007).getSemanticHint();
messageHints.add(messageHint);
messageHint = ((IHintedType)UMLElementTypes.Message_4008).getSemanticHint();
messageHints.add(messageHint);
messageHint = ((IHintedType)UMLElementTypes.Message_4009).getSemanticHint();
messageHints.add(messageHint);
return messageHints.contains(hint);
}
/**
* Report a message telling that the message can not be dropped because it goes uphill.
*
* @param delta
* the missing delta between the two lifelines
* @param sourceEditPart
* the source of the message
* @param targetEditPart
* the target of the message
*/
private void reportCanNotDropUphillMessage(int delta, IGraphicalEditPart sourceEditPart, IGraphicalEditPart targetEditPart) {
EObject source = sourceEditPart.resolveSemanticElement();
String sourceText = EMFCoreUtil.getQualifiedName(source, true);
EObject target = targetEditPart.resolveSemanticElement();
String targetText = EMFCoreUtil.getQualifiedName(target, true);
String txt = NLS.bind(Messages.DropError_UphillMessageTxt, new Object[]{ sourceText, targetText, delta });
NotificationBuilder notif = NotificationBuilder.createAsyncPopup(Messages.DropError_UphillMessageTitle, txt);
notif.setType(Type.WARNING);
notif.run();
}
/**
* Report a message telling that the link drop unexpectedly failed.
*
* @param sourceEditPart
* the source of the link
* @param targetEditPart
* the target of the link
*/
private void reportDefaultMessage(IGraphicalEditPart sourceEditPart, IGraphicalEditPart targetEditPart) {
EObject source = sourceEditPart.resolveSemanticElement();
String sourceText = EMFCoreUtil.getQualifiedName(source, true);
EObject target = targetEditPart.resolveSemanticElement();
String targetText = EMFCoreUtil.getQualifiedName(target, true);
String txt = NLS.bind(Messages.DropError_DefaultTxt, sourceText, targetText);
NotificationBuilder notif = NotificationBuilder.createAsyncPopup(Messages.DropError_DefaultTitle, txt);
notif.setType(Type.ERROR);
notif.run();
}
}