blob: 7ce993062232b3363e4f900aed556d14918a3c42 [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2013 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:
* RĂ©gis CHEVREL: chevrel.regis <at> gmail.com
* CEA LIST - Initial API and implementation
*
*****************************************************************************/
package org.eclipse.papyrus.sysml.diagram.parametric.policies;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.gmf.runtime.common.core.command.ICommand;
import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil;
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.DropObjectsRequest;
import org.eclipse.gmf.runtime.emf.core.util.EMFCoreUtil;
import org.eclipse.gmf.runtime.notation.Diagram;
import org.eclipse.gmf.runtime.notation.NotationPackage;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.papyrus.gmf.diagram.common.edit.policy.CommonDiagramDragDropEditPolicy;
import org.eclipse.papyrus.sysml.blocks.NestedConnectorEnd;
import org.eclipse.papyrus.sysml.diagram.internalblock.dnd.helper.CustomLinkMappingHelper;
import org.eclipse.papyrus.sysml.diagram.parametric.provider.CustomGraphicalTypeRegistry;
import org.eclipse.papyrus.sysml.service.types.utils.ConnectorUtils;
import org.eclipse.papyrus.uml.diagram.common.commands.CommonDeferredCreateConnectionViewCommand;
import org.eclipse.papyrus.uml.diagram.common.commands.SemanticAdapter;
import org.eclipse.papyrus.uml.diagram.common.utils.UMLGraphicalTypes;
import org.eclipse.uml2.uml.Connector;
import org.eclipse.uml2.uml.ConnectorEnd;
import org.eclipse.uml2.uml.EncapsulatedClassifier;
import org.eclipse.uml2.uml.Port;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.util.UMLUtil;
/** Customization of the DND edit policy for the Internal Block Diagram */
public class CustomDragDropEditPolicy extends CommonDiagramDragDropEditPolicy {
private ConnectorUtils utils = new ConnectorUtils();
/** Default constructor. */
public CustomDragDropEditPolicy() {
super(new CustomLinkMappingHelper());
registry = new CustomGraphicalTypeRegistry();
}
/**
* {@inheritDoc}
*/
@Override
protected Set<String> getSpecificDropBehaviorTypes() {
Set<String> specificDropBehaviorTypes = new HashSet<String>();
specificDropBehaviorTypes.add(UMLGraphicalTypes.LINK_UML_CONNECTOR_ID);
return specificDropBehaviorTypes;
}
/**
* {@inheritDoc}
*/
@Override
protected ICommand getSpecificDropCommand(DropObjectsRequest dropRequest, EObject droppedEObject, String nodeType, String edgeType) {
if ((UMLGraphicalTypes.LINK_UML_CONNECTOR_ID.equals(edgeType)) && (droppedEObject instanceof Connector)) {
return getConnectorDropCommand(dropRequest, (Connector) droppedEObject, edgeType);
}
return org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand.INSTANCE;
}
protected ICommand getConnectorDropCommand(DropObjectsRequest dropRequest, Connector droppedEObject, String edgeType) {
// Only manage binary link during drop
if (droppedEObject.getEnds().size() != 2) {
return org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand.INSTANCE;
}
ConnectorEnd source = droppedEObject.getEnds().get(0);
ConnectorEnd target = droppedEObject.getEnds().get(1);
// Find views in current diagram representing source and target
Collection<View> sourceViews = getViewsForConnectorEnd(source);
Collection<View> targetViews = getViewsForConnectorEnd(target);
IAdaptable sourceViewAdapter = null;
IAdaptable targetViewAdapter = null;
// If either a source or target lacks create view for these elements, abort...
if (sourceViews.isEmpty() || targetViews.isEmpty()) {
return org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand.INSTANCE;
}
View selectedSourceView = null;
View selectedTargetView = null;
// until a correct one is found, check that source and target views selected are correct given the current path for the connector...
for (View sourceView : sourceViews) {
View targetView = getFirstValidTargetViewForSource(sourceView, targetViews, droppedEObject);
if (targetView != null) {
selectedSourceView = sourceView;
selectedTargetView = targetView;
break;
}
}
// Create source adapter
if (selectedSourceView != null) { // sourceViewAdapter should still be null in this case
sourceViewAdapter = new SemanticAdapter(null, selectedSourceView);
} else {
return org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand.INSTANCE;
}
// Create target adapter
if (selectedTargetView != null) { // targetViewAdapter should still be null in this case
targetViewAdapter = new SemanticAdapter(null, selectedTargetView);
} else {
return org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand.INSTANCE;
}
// Create a view for the dropped link between the source and target view adapters
IAdaptable droppedObjectAdapter = new SemanticAdapter(droppedEObject, null);
CreateConnectionViewRequest.ConnectionViewDescriptor linkdescriptor = new CreateConnectionViewRequest.ConnectionViewDescriptor(droppedObjectAdapter, edgeType, getDiagramPreferencesHint());
CommonDeferredCreateConnectionViewCommand createConnectionViewCommand = new CommonDeferredCreateConnectionViewCommand(getEditingDomain(), edgeType, sourceViewAdapter, targetViewAdapter, getViewer(), getDiagramPreferencesHint(), linkdescriptor, null);
createConnectionViewCommand.setElement(droppedEObject);
return createConnectionViewCommand;
}
/**
* get the first valid target view for the given source. the couple source/target is valid if the path to them from the future container is valid
*
* @param sourceView
* @param targetViews
* @return
*/
private View getFirstValidTargetViewForSource(View sourceView, Collection<View> targetViews, Connector droppedConnector) {
for (View targetView : targetViews) {
if (isValidTargetViewForSource(sourceView, targetView, droppedConnector)) {
return targetView;
}
}
return null;
}
private boolean isValidTargetViewForSource(View sourceView, View targetView, Connector droppedConnector) {
if (!(utils.isCrossingEncapsulation(sourceView, targetView))) {
// no encapsulation. Computes the end paths
List<Property> sourceNestedPaths = utils.getNestedPropertyPath(sourceView, targetView);
List<Property> targetNestedPaths = utils.getNestedPropertyPath(targetView, sourceView);
// check this is compatible with current connector
ConnectorEnd sourceConnectorEnd = utils.getSourceConnectorEnd(droppedConnector);
NestedConnectorEnd nestedSourceConnectorEnd = UMLUtil.getStereotypeApplication(sourceConnectorEnd, NestedConnectorEnd.class);
if (nestedSourceConnectorEnd != null) {
List<Property> currentSourceConnectorPropertyPath = nestedSourceConnectorEnd.getPropertyPath();
// compare the 2 list
if (!sourceNestedPaths.equals(currentSourceConnectorPropertyPath)) {
return false;
}
} else { // no end for the current connector, so the sourceNestedPath should be empty
if (!sourceNestedPaths.isEmpty()) {
return false;
}
}
ConnectorEnd targetConnectorEnd = utils.getTargetConnectorEnd(droppedConnector);
NestedConnectorEnd nestedTargetConnectorEnd = UMLUtil.getStereotypeApplication(targetConnectorEnd, NestedConnectorEnd.class);
if (nestedTargetConnectorEnd != null) {
List<Property> currentTargetConnectorPropertyPath = nestedTargetConnectorEnd.getPropertyPath();
// compare the 2 list
if (!targetNestedPaths.equals(currentTargetConnectorPropertyPath)) {
return false;
}
} else { // no end for the current connector, so the targetNestedPaths should be empty
if (!targetNestedPaths.isEmpty()) {
return false;
}
}
}
return true;
}
/**
* This methods looks for views representing a given {@link ConnectorEnd} in the host diagram.
*
* @param end
* the {@link ConnectorEnd} to look for.
* @return the list of {@link View} representing the eObject.
*/
protected Set<View> getViewsForConnectorEnd(ConnectorEnd end) {
Set<View> views = new HashSet<View>();
// Retrieve host diagram
View hostView = ((IGraphicalEditPart) getHost()).getNotationView();
View hostDiagram = (hostView instanceof Diagram) ? hostView : hostView.getDiagram();
// Retrieve all views for the ConnectorEnd role
EReference[] refs = { NotationPackage.eINSTANCE.getView_Element() };
@SuppressWarnings("unchecked")
Collection<View> relatedViews = EMFCoreUtil.getReferencers(end.getRole(), refs);
// TODO: We should filter views not only for the current diagram,
// but also consider only views nested within the owner of the connector that are closest to the host view
// (in case there are several instances of a Part within which the connector could appear).
// Connector connector = (Connector)end.getOwner();
// StructuredClassifier connectorOwner = (StructuredClassifier)connector.getOwner();
// Parse and select views from host diagram only
Iterator<View> it = relatedViews.iterator();
while (it.hasNext()) {
View currentView = it.next();
if (currentView.getDiagram() == hostDiagram) { // FIXME: Cf TODO above.
boolean isInView = false;
View containerView = currentView;
while (containerView != null && !(containerView instanceof Diagram)) {
if (containerView == getReferenceViewForConnectorEnd()) {
isInView = true;
}
containerView = ViewUtil.getContainerView(containerView);
}
if (isInView) {
EObject containerElement = ViewUtil.getContainerView(currentView).getElement();
// If the ConnectorEnd partWithPort is not null, only select Views for which
// the graphical parent reference partWithPort.
if (end.getPartWithPort() != null) {
if (containerElement == end.getPartWithPort()) {
views.add(currentView);
}
} else {
// If the role is a Port, its graphical parent is a EncapsulatedClassifier
if (end.getRole() instanceof Port) {
if (containerElement instanceof EncapsulatedClassifier) {
views.add(currentView);
} else if (containerElement instanceof Property) {
Property property = (Property) containerElement;
if (property.getType() == end.getRole().getOwner()) {
views.add(currentView);
}
}
} else { // No further test needed
views.add(currentView);
}
}
}
}
}
return views;
}
protected View getReferenceViewForConnectorEnd() {
return ((IGraphicalEditPart) getHost()).getNotationView();
}
}