/*****************************************************************************
* Copyright (c) 2009 CEA LIST.
*
* 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:
* Remi Schnekenburger (CEA LIST) remi.schnekenburger@cea.fr - Initial API and implementation
* Vincent Lorenzo (CEA LIST)
*****************************************************************************/
package org.eclipse.papyrus.uml.diagram.common.service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.emf.common.notify.Notification;
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.Request;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gef.requests.CreateConnectionRequest;
import org.eclipse.gef.requests.CreateRequest;
import org.eclipse.gmf.runtime.diagram.core.edithelpers.CreateElementRequestAdapter;
import org.eclipse.gmf.runtime.diagram.core.listener.DiagramEventBroker;
import org.eclipse.gmf.runtime.diagram.core.listener.NotificationListener;
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.parts.DiagramCommandStack;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateConnectionViewAndElementRequest;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateConnectionViewRequest;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateConnectionViewRequest.ConnectionViewDescriptor;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateUnspecifiedTypeConnectionRequest;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewAndElementRequest;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewAndElementRequest.ViewAndElementDescriptor;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest.ViewDescriptor;
import org.eclipse.gmf.runtime.diagram.ui.requests.RequestConstants;
import org.eclipse.gmf.runtime.diagram.ui.tools.UnspecifiedTypeConnectionTool;
import org.eclipse.gmf.runtime.diagram.ui.util.INotationType;
import org.eclipse.gmf.runtime.emf.type.core.IElementType;
import org.eclipse.gmf.runtime.emf.type.core.IHintedType;
import org.eclipse.gmf.runtime.emf.type.core.requests.CreateElementRequest;
import org.eclipse.gmf.runtime.notation.Connector;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.papyrus.infra.core.services.ServiceException;
import org.eclipse.papyrus.infra.core.utils.EditorUtils;
import org.eclipse.papyrus.infra.gmfdiag.common.preferences.ConnectionToolPreferences;
import org.eclipse.papyrus.uml.diagram.common.Activator;
import org.eclipse.papyrus.uml.diagram.common.layout.LayoutUtils;
import org.eclipse.papyrus.uml.diagram.common.service.palette.AspectToolService;
import org.eclipse.papyrus.uml.diagram.common.service.palette.IAspectAction;
import org.eclipse.papyrus.uml.diagram.common.service.palette.IAspectActionProvider;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Connection tool that adds stereotype application after creation actions.
*/
public class AspectUnspecifiedTypeConnectionTool extends UnspecifiedTypeConnectionTool {
private static final int STATE_CONNECTION_WAITING_END = STATE_CONNECTION_STARTED + 1;
/**
* List of element types of which one will be created (of type <code>IElementType</code>).
*/
// protected List elementTypes;
/** post action list */
protected List<IAspectAction> postActions = new ArrayList<IAspectAction>();
/** List of elements to create */
private final List<IElementType> elementTypes;
/**
* Creates an AspectUnspecifiedTypeCreationTool
*
* @param elementTypes
* List of element types of which one will be created (of type <code>IElementType</code>).
*/
public AspectUnspecifiedTypeConnectionTool(List<IElementType> elementTypes) {
super(elementTypes);
this.elementTypes = elementTypes;
}
/**
* @return the elementDescriptors
*/
public List<IElementType> getElementTypes() {
return elementTypes;
}
protected boolean handleButtonUpOneClick(int button) {
return super.handleButtonUp(button);
}
protected boolean handleButtonUpTwoClicks(int button) {
setCtrlKeyDown(getCurrentInput().isControlKeyDown());
if(isInState(STATE_CONNECTION_STARTED)) {
setState(STATE_CONNECTION_WAITING_END);
return false;
} else if(isInState(STATE_CONNECTION_WAITING_END)) {
handleCreateConnection();
}
setState(STATE_TERMINAL);
if(isInState(STATE_TERMINAL | STATE_INVALID)) {
handleFinished();
}
return true;
}
/**
* @see org.eclipse.gef.tools.AbstractTool#handleButtonUp(int)
*/
@Override
protected boolean handleButtonUp(int button) {
if(ConnectionToolPreferences.instance.isInSingleClickMode()) {
return handleButtonUpOneClick(button);
} else {
return handleButtonUpTwoClicks(button);
}
}
/**
* {@inheritDoc}
*/
@Override
protected void createConnection() {
List<?> selectedEditParts = getCurrentViewer().getSelectedEditParts();
List<EObject> eobjects = new ArrayList<EObject>();
List<NotificationListener> listeners = new ArrayList<NotificationListener>();
CompoundCommand cmd = new CompoundCommand("Create Links");
DiagramEventBroker eventBroker = null;
final EditPartViewer viewer = getCurrentViewer();
// only attempt to create connection if there are two shapes selected
if(!selectedEditParts.isEmpty()) {
IGraphicalEditPart targetEditPart = (IGraphicalEditPart)selectedEditParts.get(selectedEditParts.size() - 1);
// allow add the listener only one time the target
boolean done = false;
for(int i = 0; i < selectedEditParts.size(); i++) {
IGraphicalEditPart sourceEditPart = (IGraphicalEditPart)selectedEditParts.get(i);
if(i != 0) {
if(sourceEditPart == targetEditPart) {
break;
}
}
CreateConnectionRequest connectionRequest = createTargetRequest();
// get the anchors locations
Point[] newLocation = LayoutUtils.getLinkAnchor(sourceEditPart, targetEditPart);
connectionRequest.setTargetEditPart(sourceEditPart);
connectionRequest.setType(RequestConstants.REQ_CONNECTION_START);
connectionRequest.setLocation(newLocation[0]);
// only if the connection is supported will we get a non null
// command from the sourceEditPart
if(sourceEditPart.getCommand(connectionRequest) != null) {
connectionRequest.setSourceEditPart(sourceEditPart);
connectionRequest.setTargetEditPart(targetEditPart);
connectionRequest.setType(RequestConstants.REQ_CONNECTION_END);
connectionRequest.setLocation(newLocation[1]);
// inits the listener
View eObject = (View)targetEditPart.getAdapter(View.class);
NotificationListener listener = null;
boolean requiresPostAction = requiresPostAction();
// adds the listener
if(requiresPostAction) {
// register a listener to have information about element
// creation
// retrieves editing domain
TransactionalEditingDomain domain;
try {
domain = EditorUtils.getServiceRegistry().getService(TransactionalEditingDomain.class);
eventBroker = DiagramEventBroker.getInstance(domain);
if(eventBroker == null) {
return;
}
listener = new NotificationListener() {
public void notifyChanged(Notification notification) {
Connector newValue = (Connector)notification.getNewValue();
Map<?, ?> map = viewer.getEditPartRegistry();
EditPart editPart = (EditPart)map.get(newValue);
for(IAspectAction action : postActions) {
action.run(editPart);
}
}
};
// we need to add only one time the listener of the
// target (eobject)
if(!done) {
listeners.add(listener);
eobjects.add(eObject);
eventBroker.addNotificationListener(eObject, listener);
done = true;
}
} catch (ServiceException e) {
Activator.log.error(e);
}
}
Command command = targetEditPart.getCommand(connectionRequest);
cmd.add(command);
}
}
setCurrentCommand(cmd);
executeCurrentCommand();
if(requiresPostAction()) {
if(eventBroker != null) {
for(int ii = 0; ii < eobjects.size(); ii++) {
eventBroker.removeNotificationListener(eobjects.get(ii), listeners.get(ii));
}
}
}
selectAddedObject(viewer, DiagramCommandStack.getReturnValues(cmd));
setAvoidDeactivation(false);
eraseSourceFeedback();
deactivate();
}
}
/**
* {@inheritDoc}
*/
@Override
protected boolean handleCreateConnection() {
// When a connection is to be created, a dialog box may appear which
// will cause this tool to be deactivated and the feedback to be
// erased. This behavior is overridden by setting the avoid
// deactivation flag.
setAvoidDeactivation(true);
if(getTargetEditPart() == null) {
return false;
}
// inits the listener
View eObject = (View)getTargetEditPart().getAdapter(View.class);
DiagramEventBroker eventBroker = null;
NotificationListener listener = null;
boolean requiresPostAction = requiresPostAction();
// adds the listener
if(requiresPostAction) {
// register a listener to have information about element creation
// retrieves editing domain
TransactionalEditingDomain domain;
try {
domain = EditorUtils.getServiceRegistry().getService(TransactionalEditingDomain.class);
eventBroker = DiagramEventBroker.getInstance(domain);
if(eventBroker == null) {
return false;
}
listener = new NotificationListener() {
public void notifyChanged(Notification notification) {
Connector newValue = (Connector)notification.getNewValue();
EditPart editPart = (EditPart)getCurrentViewer().getEditPartRegistry().get(newValue);
for(IAspectAction action : postActions) {
action.run(editPart);
}
}
};
eventBroker.addNotificationListener(eObject, listener);
} catch (ServiceException e) {
Activator.log.error(e);
}
}
EditPartViewer viewer = getCurrentViewer();
Command endCommand = getCommand();
setCurrentCommand(endCommand);
executeCurrentCommand();
if(requiresPostAction) {
if(eventBroker != null) {
eventBroker.removeNotificationListener(eObject, listener);
}
}
selectAddedObject(viewer, DiagramCommandStack.getReturnValues(endCommand));
setAvoidDeactivation(false);
eraseSourceFeedback();
deactivate();
return true;
}
/**
* checks if this tool realizes post actions
*
* @return <code>true</code> if post actions must be executed
*/
protected boolean requiresPostAction() {
return postActions.size() > 0;
}
/**
* {@inheritDoc}
*/
@Override
protected void applyProperty(Object key, Object value) {
if(IPapyrusPaletteConstant.ASPECT_ACTION_KEY.equals(key)) {
// initialize the pre and post actions
// the value should be a NodeList
if(value instanceof NodeList) {
NodeList nodeList = ((NodeList)value);
for(int i = 0; i < nodeList.getLength(); i++) {
Node childNode = nodeList.item(i);
String childName = childNode.getNodeName();
if(IPapyrusPaletteConstant.POST_ACTION.equals(childName)) {
// node is a post action => retrieve the id of the
// provider in charge of this configuration
IAspectActionProvider provider = AspectToolService.getInstance().getProvider(AspectToolService.getProviderId(childNode));
if(provider != null) {
IAspectAction action = provider.createAction(childNode);
postActions.add(action);
} else {
Activator.log.error("impossible to find factory with id: " + AspectToolService.getProviderId(childNode), null);
}
} else if(IPapyrusPaletteConstant.PRE_ACTION.equals(childName)) {
// no implementation yet
}
}
}
return;
}
super.applyProperty(key, value);
}
/**
* {@inheritDoc}
*/
@Override
protected CreateAspectUnspecifiedTypeConnectionRequest createTargetRequest() {
return new CreateAspectUnspecifiedTypeConnectionRequest(getElementTypes(), false, getPreferencesHint());
}
/**
* Copy of the class {@link CreateUnspecifiedTypeConnectionRequest} to use
* the Papyrus request factory instead of the gmf one.
*/
public class CreateAspectUnspecifiedTypeConnectionRequest extends CreateUnspecifiedTypeConnectionRequest {
/**
* List of relationship types of which one will be created (of type <code>IElementType</code>).
*/
// private List relationshipTypes;
/**
* A map containing the <code>CreateConnectionRequest</code> for each
* element type.
*/
private Map requests = new HashMap();
/**
* A flag to indicate if the Modeling Assistant Service should be used
* to find the types when the other end of the connection is known.
*/
private boolean useModelingAssistantService;
/**
* A flag to indicate if this request is to create a connection from
* target to source.
*/
private boolean directionReversed = false;
/**
* The hint used to find the appropriate preference store from which
* general diagramming preference values for properties of shapes,
* connections, and diagrams can be retrieved. This hint is mapped to a
* preference store in the {@link DiagramPreferencesRegistry}.
*/
private PreferencesHint preferencesHint;
/**
* Creates a new <code>CreateUnspecifiedTypeConnectionRequest</code>.
*
* @param relationshipTypes
* List of relationship types of which one will be created
* (of type <code>IElementType</code>).
* @param useModelingAssistantService
* True if the Modeling Assistant Service should be used to
* find the types when the other end of the connection is
* known.
* @param preferencesHint
* The preference hint that is to be used to find the
* appropriate preference store from which to retrieve
* diagram preference values. The preference hint is mapped
* to a preference store in the preference registry <@link
* DiagramPreferencesRegistry>.
*/
public CreateAspectUnspecifiedTypeConnectionRequest(List<IElementType> relationshipTypes, boolean useModelingAssistantService, PreferencesHint preferencesHint) {
super(relationshipTypes, useModelingAssistantService, preferencesHint);
this.useModelingAssistantService = useModelingAssistantService;
this.preferencesHint = preferencesHint;
createRequests();
}
/**
* Creates a <code>CreateConnectionRequest</code> for each relationship
* type and adds it to the map of requests.
*/
protected void createRequests() {
for(Iterator<IElementType> iter = getElementTypes().iterator(); iter.hasNext();) {
IElementType elementType = iter.next();
Request request = PapyrusCreateViewRequestFactory.getCreateConnectionRequest(elementType, getPreferencesHint());
request.setType(getType());
requests.put(elementType, request);
}
}
/**
* Returns the <code>CreateRequest</code> for the relationship type
* passed in.
*
* @param relationshipType
* @return the <code>CreateRequest</code>
*/
@Override
public CreateRequest getRequestForType(IElementType relationshipType) {
if(requests != null) {
return (CreateConnectionRequest)requests.get(relationshipType);
}
return null;
}
@Override
public void addRequest(IElementType relationshipType, Request request) {
if(requests != null) {
requests.put(relationshipType, request);
}
}
/**
* Returns a list of all the requests.
*
* @return the requests
*/
@Override
public List getAllRequests() {
if(requests != null) {
return new ArrayList(requests.values());
}
return Collections.EMPTY_LIST;
}
// /**
// * Returns the list of element types.
// *
// * @return Returns the list of element types.
// */
// public List getElementTypes() {
// return relationshipTypes;
// }
/**
* @see org.eclipse.gef.requests.CreateConnectionRequest#setSourceEditPart(org.eclipse.gef.EditPart)
*/
@Override
public void setSourceEditPart(EditPart part) {
if(requests != null) {
for(Iterator iter = requests.values().iterator(); iter.hasNext();) {
CreateConnectionRequest request = (CreateConnectionRequest)iter.next();
request.setSourceEditPart(part);
}
}
super.setSourceEditPart(part);
}
/**
* @see org.eclipse.gef.requests.TargetRequest#setTargetEditPart(org.eclipse.gef.EditPart)
*/
@Override
public void setTargetEditPart(EditPart part) {
if(requests != null) {
for(Iterator iter = requests.values().iterator(); iter.hasNext();) {
CreateConnectionRequest request = (CreateConnectionRequest)iter.next();
request.setTargetEditPart(part);
}
}
super.setTargetEditPart(part);
}
/**
* @see org.eclipse.gef.requests.CreateRequest#setLocation(org.eclipse.draw2d.geometry.Point)
*/
@Override
public void setLocation(Point location) {
if(requests != null) {
for(Iterator iter = requests.values().iterator(); iter.hasNext();) {
CreateConnectionRequest request = (CreateConnectionRequest)iter.next();
request.setLocation(location);
}
}
super.setLocation(location);
}
/**
* @see org.eclipse.gef.Request#setType(java.lang.Object)
*/
@Override
public void setType(Object type) {
if(requests != null) {
for(Iterator iter = requests.values().iterator(); iter.hasNext();) {
CreateConnectionRequest request = (CreateConnectionRequest)iter.next();
request.setType(type);
}
}
super.setType(type);
}
/**
* Returns true if this request is to create a connection from target to
* source.
*
* @return Returns the directionReversed.
*/
@Override
public boolean isDirectionReversed() {
return directionReversed;
}
/**
* Sets the directionReversed flag.
*
* @param directionReversed
* The directionReversed to set.
*/
@Override
public void setDirectionReversed(boolean directionReversed) {
this.directionReversed = directionReversed;
}
/**
* Should the Modeling Assistant Service be used?
*
* @return Returns true if the Modeling Assistant Service should be used
* to find the types when the other end of the connection is
* known.
*/
@Override
public boolean useModelingAssistantService() {
return useModelingAssistantService;
}
/**
* Gets the preferences hint that is to be used to find the appropriate
* preference store from which to retrieve diagram preference values.
* The preference hint is mapped to a preference store in the preference
* registry <@link DiagramPreferencesRegistry>.
*
* @return the preferences hint
*/
@Override
protected PreferencesHint getPreferencesHint() {
return preferencesHint;
}
}
public static class PapyrusCreateViewRequestFactory {
/**
* Creates a new <code>CreateViewRequest</code> or <code>CreateViewAndElementRequest</code> based on the <code>IElementType</code> passed in.
*
* @param type
* the <code>IElementType</code>
* @param preferencesHint
* The preference hint that is to be used to find the
* appropriate preference store from which to retrieve
* diagram preference values. The preference hint is mapped
* to a preference store in the preference registry <@link
* DiagramPreferencesRegistry>.
* @return the new request
*/
public static CreateViewRequest getCreateShapeRequest(IElementType type, PreferencesHint preferencesHint) {
if(type instanceof INotationType) {
ViewDescriptor viewDescriptor = new ViewDescriptor(null, Node.class, ((INotationType)type).getSemanticHint(), preferencesHint);
return new CreateViewRequest(viewDescriptor);
} else if(type instanceof IHintedType) {
ViewAndElementDescriptor viewDescriptor = new ViewAndElementDescriptor(new CreateElementRequestAdapter(new CreateElementRequest(type)), Node.class, ((IHintedType)type).getSemanticHint(), preferencesHint);
return new CreateViewAndElementRequest(viewDescriptor);
} else {
return new CreateViewAndElementRequest(type, preferencesHint);
}
}
/**
* Creates a new <code>CreateConnectionViewRequest</code> or <code>CreateConnectionViewAndElementRequest</code> based on the
* <code>IElementType</code> passed in.
*
* @param type
* the <code>IElementType</code>
* @param preferencesHint
* The preference hint that is to be used to find the
* appropriate preference store from which to retrieve
* diagram preference values. The preference hint is mapped
* to a preference store in the preference registry <@link
* DiagramPreferencesRegistry>.
* @return the new request
*/
public static CreateConnectionViewRequest getCreateConnectionRequest(IElementType type, PreferencesHint preferencesHint) {
if(type instanceof INotationType) {
// Pass in the type as the element adapter so that it can be
// retrieved in the cases where a popup menu is to appear with a
// list of types.
ConnectionViewDescriptor viewDescriptor = new ConnectionViewDescriptor(type, ((IHintedType)type).getSemanticHint(), preferencesHint);
return new CreateConnectionViewRequest(viewDescriptor);
} else if(type instanceof IHintedType) {
return new CreateConnectionViewAndElementRequest(type, ((IHintedType)type).getSemanticHint(), preferencesHint);
} else {
return new CreateConnectionViewAndElementRequest(type, preferencesHint);
}
}
/**
* Creates a new <code>CreateConnectionViewRequest</code> or <code>CreateConnectionViewAndElementRequest</code> based on the
* <code>IElementType</code> passed in.
*
* @param type
* the <code>IElementType</code>
* @param graphicalHint
* graphical hint for the view to create
* @param preferencesHint
* The preference hint that is to be used to find the
* appropriate preference store from which to retrieve
* diagram preference values. The preference hint is mapped
* to a preference store in the preference registry <@link
* DiagramPreferencesRegistry>.
* @return the new request
*/
public static CreateConnectionViewRequest getCreateConnectionRequest(IElementType type, String graphicalHint, PreferencesHint preferencesHint) {
if(type instanceof INotationType) {
// Pass in the type as the element adapter so that it can be
// retrieved in the cases where a popup menu is to appear with a
// list of types.
ConnectionViewDescriptor viewDescriptor = new ConnectionViewDescriptor(type, graphicalHint, preferencesHint);
return new CreateConnectionViewRequest(viewDescriptor);
} else if(type instanceof IHintedType) {
// force the graphical hint instead of the hint of the hinted
// element
return new CreateConnectionViewAndElementRequest(type, graphicalHint, preferencesHint);
} else {
return new CreateConnectionViewAndElementRequest(type, preferencesHint);
}
}
}
}