blob: aadb9df9a1048b0c31f5cb629bb2d91378cf46d6 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2002, 2007 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
****************************************************************************/
package org.eclipse.gmf.runtime.diagram.ui.editparts;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import org.eclipse.draw2d.Connection;
import org.eclipse.draw2d.ConnectionAnchor;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.transaction.RunnableWithResult;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.NodeEditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.requests.CreateRequest;
import org.eclipse.gef.requests.DropRequest;
import org.eclipse.gef.requests.ReconnectRequest;
import org.eclipse.gmf.runtime.common.core.util.Log;
import org.eclipse.gmf.runtime.common.core.util.Trace;
import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil;
import org.eclipse.gmf.runtime.diagram.ui.editpolicies.GraphicalNodeEditPolicy;
import org.eclipse.gmf.runtime.diagram.ui.internal.DiagramUIDebugOptions;
import org.eclipse.gmf.runtime.diagram.ui.internal.DiagramUIPlugin;
import org.eclipse.gmf.runtime.diagram.ui.internal.DiagramUIStatusCodes;
import org.eclipse.gmf.runtime.diagram.ui.internal.editpolicies.NoteAttachmentReorientEditPolicy;
import org.eclipse.gmf.runtime.draw2d.ui.figures.IAnchorableFigure;
import org.eclipse.gmf.runtime.notation.Anchor;
import org.eclipse.gmf.runtime.notation.IdentityAnchor;
import org.eclipse.gmf.runtime.notation.NotationPackage;
import org.eclipse.gmf.runtime.notation.View;
/**
* Connection Node EditPart, a specialized Connection EditPart that installs
* a <code>ditPolicy.GRAPHICAL_NODE_ROLE</code> on this edit part. it also implements
* INodeEditPart which defines the connectable edit parts
* @author mmostafa
*/
abstract public class ConnectionNodeEditPart
extends ConnectionEditPart
implements INodeEditPart {
/**
* constructor
* @param view owned view by this edit part
*/
public ConnectionNodeEditPart(View view) {
super(view);
}
/* (non-Javadoc)
* @see org.eclipse.gmf.runtime.diagram.ui.editparts.ConnectionEditPart#createDefaultEditPolicies()
*/
protected void createDefaultEditPolicies() {
// node edit policy needs to be installed before connection editpolicy from the super
// since connections of a node need to be deleted before the node
//installEditPolicy(EditPolicy.NODE_ROLE, new NodeEditPolicy());
super.createDefaultEditPolicies();
installEditPolicy(EditPolicy.GRAPHICAL_NODE_ROLE, new GraphicalNodeEditPolicy());
// Disable note attachment reorient between two shapes where neither is a note.
installEditPolicy("NoteAttachmentReorient", //$NON-NLS-1$
new NoteAttachmentReorientEditPolicy());
}
/* (non-Javadoc)
* @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#getModelSourceConnections()
*/
protected List getModelSourceConnections(){
return ViewUtil.getSourceConnections(getEdge());
}
/* (non-Javadoc)
* @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#getModelTargetConnections()
*/
protected List getModelTargetConnections(){
return ViewUtil.getTargetConnections(getEdge());
}
protected ConnectionAnchor getSourceConnectionAnchor() {
if (getSource() != null && getSource() instanceof NodeEditPart) {
NodeEditPart editPart = (NodeEditPart) getSource();
return editPart.getSourceConnectionAnchor(this);
}
return super.getSourceConnectionAnchor();
}
/*
* @see NodeEditPart#getSourceConnectionAnchor(ConnectionEditPart)
*/
public ConnectionAnchor getSourceConnectionAnchor(org.eclipse.gef.ConnectionEditPart connEditPart) {
final ConnectionNodeEditPart connection = (ConnectionNodeEditPart) connEditPart;
String t = ""; //$NON-NLS-1$
try {
t = (String) getEditingDomain().runExclusive(
new RunnableWithResult.Impl() {
public void run() {
Anchor a = connection.getEdge().getSourceAnchor();
if (a instanceof IdentityAnchor)
setResult(((IdentityAnchor) a).getId());
else
setResult(""); //$NON-NLS-1$
}
});
} catch (InterruptedException e) {
Trace.catching(DiagramUIPlugin.getInstance(),
DiagramUIDebugOptions.EXCEPTIONS_CATCHING, getClass(),
"getSourceConnectionAnchor", e); //$NON-NLS-1$
Log.error(DiagramUIPlugin.getInstance(),
DiagramUIStatusCodes.IGNORED_EXCEPTION_WARNING,
"getSourceConnectionAnchor", e); //$NON-NLS-1$
}
return ((IAnchorableFigure)getFigure()).getConnectionAnchor(t);
}
/*
* @see NodeEditPart#getSourceConnectionAnchor(Request)
*/
public ConnectionAnchor getSourceConnectionAnchor(Request request) {
Point center = getFigure().getBounds().getCenter();
getFigure().translateToAbsolute(center);
Point pt = ((DropRequest)request).getLocation()==null ?
center : new Point(((DropRequest)request).getLocation());
if (request instanceof CreateRequest) {
getFigure().translateToRelative(pt);
}
return ((IAnchorableFigure)getFigure()).getSourceConnectionAnchorAt(pt);
}
protected ConnectionAnchor getTargetConnectionAnchor() {
if (getTarget() instanceof NodeEditPart) {
NodeEditPart editPart = (NodeEditPart) getTarget();
return editPart.getTargetConnectionAnchor(this);
}
return super.getTargetConnectionAnchor();
}
/*
* @see NodeEditPart#getTargetConnectionAnchor(ConnectionEditPart)
*/
public ConnectionAnchor getTargetConnectionAnchor(org.eclipse.gef.ConnectionEditPart connEditPart) {
final ConnectionNodeEditPart connection = (ConnectionNodeEditPart) connEditPart;
String t = ""; //$NON-NLS-1$
try {
t = (String) getEditingDomain().runExclusive(
new RunnableWithResult.Impl() {
public void run() {
Anchor a = connection.getEdge().getTargetAnchor();
if (a instanceof IdentityAnchor)
setResult(((IdentityAnchor) a).getId());
else
setResult(""); //$NON-NLS-1$
}
});
} catch (InterruptedException e) {
Trace.catching(DiagramUIPlugin.getInstance(),
DiagramUIDebugOptions.EXCEPTIONS_CATCHING, getClass(),
"getTargetConnectionAnchor", e); //$NON-NLS-1$
Log.error(DiagramUIPlugin.getInstance(),
DiagramUIStatusCodes.IGNORED_EXCEPTION_WARNING,
"getTargetConnectionAnchor", e); //$NON-NLS-1$
}
return ((IAnchorableFigure)getFigure()).getConnectionAnchor(t);
}
/*
* @see NodeEditPart#getTargetConnectionAnchor(Request)
*/
public ConnectionAnchor getTargetConnectionAnchor(Request request) {
Point center = getFigure().getBounds().getCenter();
getFigure().translateToAbsolute(center);
Point pt = ((DropRequest)request).getLocation()==null ?
center : new Point(((DropRequest)request).getLocation());
if (request instanceof CreateRequest) {
getFigure().translateToRelative(pt);
}
return ((IAnchorableFigure)getFigure()).getTargetConnectionAnchorAt(pt);
}
/* (non-Javadoc)
* @see org.eclipse.gmf.runtime.diagram.ui.editparts.INodeEditPart#mapConnectionAnchorToTerminal(org.eclipse.draw2d.ConnectionAnchor)
*/
final public String mapConnectionAnchorToTerminal(ConnectionAnchor c) {
return ((IAnchorableFigure) getFigure()).getConnectionAnchorTerminal(c);
}
/**
* @see org.eclipse.gmf.runtime.diagram.ui.editparts.INodeEditPart#mapTerminalToConnectionAnchor(String)
*/
final public ConnectionAnchor mapTerminalToConnectionAnchor(String terminal) {
return ((IAnchorableFigure) getFigure()).getConnectionAnchor(terminal);
}
/* (non-Javadoc)
* @see org.eclipse.gmf.runtime.diagram.ui.internal.editparts.INoteableEditPart#canAttachNote()
*/
public boolean canAttachNote() {
return true;
}
/*
* @see org.eclipse.gmf.runtime.diagram.ui.editparts.ConnectionEditPart#handleNotificationEvent(org.eclipse.gmf.runtime.diagram.ui.internal.listener.NotificationEvent)
*/
protected void handleNotificationEvent(Notification notification) {
Object feature = notification.getFeature();
if (NotationPackage.eINSTANCE.getView_SourceEdges().equals(feature))
refreshSourceConnections();
else
if (NotationPackage.eINSTANCE.getView_TargetEdges().equals(feature))
refreshTargetConnections();
else
super.handleNotificationEvent(notification);
if ( NotationPackage.eINSTANCE.getIdentityAnchor_Id().equals(feature) ||
notification.getNewValue() instanceof IdentityAnchor ||
notification.getOldValue() instanceof IdentityAnchor) {
anchorChange();
}
}
/**
* updates identity connection anchors
*/
public void anchorChange() {
refreshSourceAnchor();
refreshTargetAnchor();
}
/**
* Retrieve the list of all source and target connections for the connection.
* @param set HashSet to add the connections to.
* @param connectionEditPart the connection edit part.
*/
private void getSourceAndTargetConnections(HashSet set,
org.eclipse.gef.ConnectionEditPart connectionEditPart) {
if (connectionEditPart == null || set == null)
return;
for (Iterator i = connectionEditPart.getSourceConnections().iterator();
i.hasNext();) {
org.eclipse.gef.ConnectionEditPart next =
(org.eclipse.gef.ConnectionEditPart) i.next();
Connection sourceConnection = (Connection) next.getFigure();
set.add(sourceConnection);
getSourceAndTargetConnections(set, next);
}
for (Iterator i = connectionEditPart.getTargetConnections().iterator();
i.hasNext();) {
org.eclipse.gef.ConnectionEditPart next =
(org.eclipse.gef.ConnectionEditPart) i.next();
Connection targetConnection = (Connection) next.getFigure();
set.add(targetConnection);
getSourceAndTargetConnections(set, next);
}
}
/**
* Figure out if a cyclic dependency will arise if target connection edit part
* is connected to the source connection edit part.
* @param targetCEP the target connection edit part
* @param sourceCEP the source connection edit part
* @param checkSourceAndTargetEditParts check both the source and taret edit parts
* for cyclic dependencies
* @param doNotCheckSourceEditPart (if checkSourceAndTargetEditParts is false) check
* only the target edit part if true, otherwise check only the source edit part
* @return true if a cyclic dependency would be create when targetCEP and
* sourceCEP were to be connected, false otherwise.
*/
private boolean isCyclicConnectionRequest(org.eclipse.gef.ConnectionEditPart targetCEP,
org.eclipse.gef.ConnectionEditPart sourceCEP,
boolean checkSourceAndTargetEditParts, boolean doNotCheckSourceEditPart) {
if (targetCEP == null || sourceCEP == null)
return false;
if (sourceCEP == targetCEP)
return true;
// first, do a cyclic check on source and target connections
// of the source connection itself.
// (as every connection is also a node).
HashSet set = new HashSet();
getSourceAndTargetConnections(set, sourceCEP);
if (set.contains(targetCEP.getFigure()))
return true;
// now do the cyclic check on the source and target of the source connection...
EditPart sourceEP = sourceCEP.getSource(),
targetEP = sourceCEP.getTarget();
if ((sourceEP == targetCEP) || (targetEP == targetCEP)) {
return true;
}
else {
if (!checkSourceAndTargetEditParts && doNotCheckSourceEditPart) {
// .
}
else
if (sourceEP instanceof org.eclipse.gef.ConnectionEditPart &&
isCyclicConnectionRequest(targetCEP,
(org.eclipse.gef.ConnectionEditPart)sourceEP,
true, doNotCheckSourceEditPart))
return true;
if (!checkSourceAndTargetEditParts && !doNotCheckSourceEditPart) {
// .
}
else
if (targetEP instanceof org.eclipse.gef.ConnectionEditPart &&
isCyclicConnectionRequest(targetCEP,
(org.eclipse.gef.ConnectionEditPart)targetEP,
true, doNotCheckSourceEditPart))
return true;
}
return false;
}
/* (non-Javadoc)
* @see org.eclipse.gef.editparts.AbstractEditPart#getTargetEditPart(org.eclipse.gef.Request)
*/
public EditPart getTargetEditPart(Request request) {
EditPart ep = super.getTargetEditPart(request);
if (ep != null && ep instanceof org.eclipse.gef.ConnectionEditPart) {
if (request instanceof ReconnectRequest) {
ReconnectRequest rRequest = (ReconnectRequest)request;
// If this is just moving an anchor point on the same target or
// source, then it is fine. See bugzilla# 208408.
if (rRequest.isMovingStartAnchor()) {
if (rRequest.getConnectionEditPart().getSource() == ep) {
return ep;
}
} else if (rRequest.getConnectionEditPart().getTarget() == ep) {
return ep;
}
// If source anchor is moved, the connection's source edit part
// should not be taken into account for a cyclic dependency
// check so as to avoid false checks. Same goes for the target
// anchor. See bugzilla# 155243 -- we do not want to target a
// connection that is already connected to us so that we do not
// introduce a cyclic connection
if (isCyclicConnectionRequest((org.eclipse.gef.ConnectionEditPart)ep,
rRequest.getConnectionEditPart(), false, rRequest.isMovingStartAnchor()))
return null;
}
}
return ep;
}
}