[441256] - Affixed nodes should only allow anchors at 2 of 4 sides
+ update anchor on move when changed side

Change-Id: I30af783a10e1073a9a21613aec5fa799af9e45d5
diff --git a/test-examples/linklf/xtend/org.eclipse.gmf.tooling.examples.linklf.diagram/custom-src/org/eclipse/gmf/tooling/runtime/linklf/LinksLFNodeFigure.java b/test-examples/linklf/xtend/org.eclipse.gmf.tooling.examples.linklf.diagram/custom-src/org/eclipse/gmf/tooling/runtime/linklf/LinksLFNodeFigure.java
index e5779cb..ce8d3f8 100644
--- a/test-examples/linklf/xtend/org.eclipse.gmf.tooling.examples.linklf.diagram/custom-src/org/eclipse/gmf/tooling/runtime/linklf/LinksLFNodeFigure.java
+++ b/test-examples/linklf/xtend/org.eclipse.gmf.tooling.examples.linklf.diagram/custom-src/org/eclipse/gmf/tooling/runtime/linklf/LinksLFNodeFigure.java
@@ -3,12 +3,15 @@
 import org.eclipse.draw2d.AbstractPointListShape;
 import org.eclipse.draw2d.ConnectionAnchor;
 import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.PositionConstants;
 import org.eclipse.draw2d.ScalablePolygonShape;
 import org.eclipse.draw2d.geometry.Point;
 import org.eclipse.draw2d.geometry.PointList;
 import org.eclipse.draw2d.geometry.PrecisionPoint;
 import org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart;
+import org.eclipse.gmf.runtime.diagram.ui.editparts.IBorderItemEditPart;
 import org.eclipse.gmf.runtime.draw2d.ui.figures.BaseSlidableAnchor;
+import org.eclipse.gmf.runtime.draw2d.ui.figures.IBorderItemLocator;
 import org.eclipse.gmf.runtime.gef.ui.figures.DefaultSizeNodeFigure;
 
 public class LinksLFNodeFigure extends DefaultSizeNodeFigure {
@@ -54,8 +57,24 @@
 			Point temp = p.getCopy();
 			translateToRelative(temp);
 			PrecisionPoint pt = BaseSlidableAnchor.getAnchorRelativeLocation(temp, getBounds());
-			if (isDefaultAnchorArea(pt))
+			if (isDefaultAnchorArea(pt)) {
 				return getConnectionAnchor(szAnchor);
+			}
+
+			if (myHost instanceof IBorderItemEditPart) {
+				IBorderItemLocator locator = ((IBorderItemEditPart) myHost).getBorderItemLocator();
+				switch (locator.getCurrentSideOfParent()) {
+				case PositionConstants.WEST:
+				case PositionConstants.EAST:
+					pt.setPreciseX(pt.preciseX() > 0.5 ? 1.0 : 0.0);
+					break;
+				case PositionConstants.SOUTH:
+				case PositionConstants.NORTH:
+					pt.setPreciseY(pt.preciseY() > 0.5 ? 1.0 : 0.0);
+					break;
+				}
+			}
+
 			return createAnchor(pt);
 		}
 	}
diff --git a/test-examples/linklf/xtend/org.eclipse.gmf.tooling.examples.linklf.diagram/custom-src/org/eclipse/gmf/tooling/runtime/linklf/editpolicies/AdjustBorderItemAnchorsEditPolicy.java b/test-examples/linklf/xtend/org.eclipse.gmf.tooling.examples.linklf.diagram/custom-src/org/eclipse/gmf/tooling/runtime/linklf/editpolicies/AdjustBorderItemAnchorsEditPolicy.java
new file mode 100644
index 0000000..ae09bf5
--- /dev/null
+++ b/test-examples/linklf/xtend/org.eclipse.gmf.tooling.examples.linklf.diagram/custom-src/org/eclipse/gmf/tooling/runtime/linklf/editpolicies/AdjustBorderItemAnchorsEditPolicy.java
@@ -0,0 +1,200 @@
+package org.eclipse.gmf.tooling.runtime.linklf.editpolicies;
+
+import org.eclipse.draw2d.ConnectionAnchor;
+import org.eclipse.draw2d.PositionConstants;
+import org.eclipse.draw2d.geometry.PrecisionPoint;
+import org.eclipse.draw2d.geometry.PrecisionRectangle;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.emf.transaction.TransactionalEditingDomain;
+import org.eclipse.gef.GraphicalEditPart;
+import org.eclipse.gef.commands.Command;
+import org.eclipse.gef.handles.HandleBounds;
+import org.eclipse.gef.requests.ChangeBoundsRequest;
+import org.eclipse.gmf.runtime.common.core.command.ICommand;
+import org.eclipse.gmf.runtime.diagram.core.commands.SetConnectionAnchorsCommand;
+import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy;
+import org.eclipse.gmf.runtime.diagram.ui.editparts.ConnectionEditPart;
+import org.eclipse.gmf.runtime.diagram.ui.editparts.IBorderItemEditPart;
+import org.eclipse.gmf.runtime.diagram.ui.editparts.INodeEditPart;
+import org.eclipse.gmf.runtime.diagram.ui.figures.BorderItemLocator;
+import org.eclipse.gmf.runtime.draw2d.ui.figures.BaseSlidableAnchor;
+import org.eclipse.gmf.runtime.draw2d.ui.figures.IBorderItemLocator;
+import org.eclipse.gmf.runtime.emf.core.util.EObjectAdapter;
+
+/**
+ * This edit policy adjusts anchors for links from and to host {@link IBorderItemEditPart}.
+ * Adjustment ensures that the anchors are always located on the side which is parallel to the actual side
+ * of the parent this border item is affixed to.
+ */
+public class AdjustBorderItemAnchorsEditPolicy extends AdjustAbsoluteBendpointsEditPolicyBase {
+
+	/**
+	 * Default role for registering this edit policy.
+	 * <p/>
+	 * The value is prefixed by class FQN in order to avoid conflicts, but the literal should NOT be used anywhere.
+	 */
+	public static final String ROLE = AdjustBorderItemAnchorsEditPolicy.class.getName() + ":Role";
+
+	@Override
+	protected Command getAdjustLinksCommand(ChangeBoundsRequest req) {
+		if (getHost() instanceof IBorderItemEditPart && getHost() instanceof INodeEditPart) {
+			return getAdjustAnchorsCommand(req);
+		}
+		return null;
+	}
+
+	protected Command getAdjustAnchorsCommand(ChangeBoundsRequest req) {
+		IBorderItemEditPart host = (IBorderItemEditPart) getHost();
+		final IBorderItemLocator locator = host.getBorderItemLocator();
+		if (locator == null) {
+			return null;
+		}
+
+		Rectangle bounds;
+		if (host.getFigure() instanceof HandleBounds) {
+			bounds = ((HandleBounds) host.getFigure()).getHandleBounds();
+		} else {
+			bounds = host.getFigure().getBounds();
+		}
+		PrecisionRectangle rect = new PrecisionRectangle(bounds);
+		getHostFigure().translateToAbsolute(rect);
+		rect.translate(req.getMoveDelta());
+		rect.resize(req.getSizeDelta());
+
+		getHostFigure().translateToRelative(rect);
+		Rectangle realLocation = locator.getValidLocation(rect.getCopy(), host.getFigure());
+
+		int projectedSide = BorderItemLocator.findClosestSideOfParent(realLocation, ((GraphicalEditPart) host.getParent()).getFigure().getBounds());
+		int currentSide = locator.getCurrentSideOfParent();
+
+		int curIndex = getIndexForSide(currentSide);
+		int projectedIndex = getIndexForSide(projectedSide);
+
+		if ((projectedSide & currentSide) != 0) {
+			return null;
+		}
+
+		int rotation = projectedIndex - curIndex;
+		if (rotation < 0) {
+			rotation += 4;
+		}
+		if (rotation == 0) {
+			//weird
+			return null;
+		}
+
+		ICommand result = null;
+		TransactionalEditingDomain domain = getHost().getEditingDomain();
+		for (Object next : getHost().getSourceConnections()) {
+			if (next instanceof ConnectionEditPart) {
+				ConnectionEditPart nextLink = (ConnectionEditPart) next;
+				ConnectionAnchor anchor = nextLink.getConnectionFigure().getSourceAnchor();
+				if (anchor == null) {
+					continue;
+				}
+				PrecisionPoint newRefPoint = rotateAnchorLocation(anchor, rotation);
+				String newTerminal = composeTerminalString(newRefPoint);
+
+				SetConnectionAnchorsCommand nextCommand = new SetConnectionAnchorsCommand(domain, "Adjusting source anchors");
+				nextCommand.setEdgeAdaptor(new EObjectAdapter(nextLink.getNotationView()));
+				nextCommand.setNewSourceTerminal(newTerminal);
+
+				result = result == null ? nextCommand : result.compose(nextCommand);
+			}
+		}
+
+		for (Object next : getHost().getTargetConnections()) {
+			if (next instanceof ConnectionEditPart) {
+				ConnectionEditPart nextLink = (ConnectionEditPart) next;
+				ConnectionAnchor anchor = nextLink.getConnectionFigure().getTargetAnchor();
+				if (anchor == null) {
+					continue;
+				}
+				PrecisionPoint newRefPoint = rotateAnchorLocation(anchor, rotation);
+				String newTerminal = composeTerminalString(newRefPoint);
+
+				SetConnectionAnchorsCommand nextCommand = new SetConnectionAnchorsCommand(domain, "Adjusting target anchors");
+				nextCommand.setEdgeAdaptor(new EObjectAdapter(nextLink.getNotationView()));
+				nextCommand.setNewTargetTerminal(newTerminal);
+
+				result = result == null ? nextCommand : result.compose(nextCommand);
+			}
+		}
+
+		return result == null ? null : new ICommandProxy(result);
+	}
+
+	private static int getIndexForSide(int side) {
+		if (hasBits(side, PositionConstants.NORTH)) {
+			return 0;
+		}
+		if (hasBits(side, PositionConstants.EAST)) {
+			return 1;
+		}
+		if (hasBits(side, PositionConstants.SOUTH)) {
+			return 2;
+		}
+		if (hasBits(side, PositionConstants.WEST)) {
+			return 3;
+		}
+		return 0;
+	}
+
+	protected static String position2string(int position) {
+		if (position == PositionConstants.NONE) {
+			return "NONE";
+		}
+		StringBuffer result = new StringBuffer();
+		if (hasBits(position, PositionConstants.NORTH)) {
+			result.append("N");
+		}
+		if (hasBits(position, PositionConstants.SOUTH)) {
+			result.append("S");
+		}
+		if (hasBits(position, PositionConstants.WEST)) {
+			result.append("W");
+		}
+		if (hasBits(position, PositionConstants.EAST)) {
+			result.append("E");
+		}
+
+		return result.toString();
+	}
+
+	protected static boolean hasBits(int value, int mask) {
+		return ((value & mask) != 0);
+	}
+
+	protected PrecisionPoint rotateAnchorLocation(ConnectionAnchor anchor, int quarters) {
+		String terminal = ((BaseSlidableAnchor) anchor).getTerminal();
+		PrecisionPoint result = BaseSlidableAnchor.parseTerminalString(terminal);
+		for (int i = 0; i < quarters; i++) {
+			double newX = 1. - result.preciseY();
+			double newY = result.preciseX();
+			result.setPreciseLocation(newX, newY);
+		}
+		return result;
+	}
+
+	/**
+	 * [GMFRT] make protected in {@link BaseSlidableAnchor}
+	 * <p/>
+	 * Creates a terminal string for any reference point passed in the format understandable by slidable anchors
+	 * 
+	 * @param p
+	 *        - a <Code>PrecisionPoint</Code> that must be represented as a unique <Code>String</Code>, namely as "(preciseX,preciseY)"
+	 * @return <code>String</code> terminal composed from specified <code>PrecisionPoint</code>
+	 * @deprecated copy pasted from {@link BaseSlidableAnchor}
+	 */
+	@Deprecated
+	private String composeTerminalString(PrecisionPoint p) {
+		StringBuffer s = new StringBuffer(24);
+		s.append('('); // 1 char
+		s.append(p.preciseX()); // 10 chars
+		s.append(','); // 1 char
+		s.append(p.preciseY()); // 10 chars
+		s.append(')'); // 1 char
+		return s.toString(); // 24 chars max (+1 for safety, i.e. for string termination)
+	}
+
+}
diff --git a/test-examples/linklf/xtend/org.eclipse.gmf.tooling.examples.linklf.diagram/src/org/eclipse/gmf/tooling/examples/linklf/diagram/edit/parts/PortEditPart.java b/test-examples/linklf/xtend/org.eclipse.gmf.tooling.examples.linklf.diagram/src/org/eclipse/gmf/tooling/examples/linklf/diagram/edit/parts/PortEditPart.java
index f62a265..c1160c0 100644
--- a/test-examples/linklf/xtend/org.eclipse.gmf.tooling.examples.linklf.diagram/src/org/eclipse/gmf/tooling/examples/linklf/diagram/edit/parts/PortEditPart.java
+++ b/test-examples/linklf/xtend/org.eclipse.gmf.tooling.examples.linklf.diagram/src/org/eclipse/gmf/tooling/examples/linklf/diagram/edit/parts/PortEditPart.java
@@ -20,6 +20,7 @@
 import org.eclipse.gmf.tooling.examples.linklf.diagram.edit.policies.PortItemSemanticEditPolicy;
 import org.eclipse.gmf.tooling.runtime.linklf.LinksLFNodeFigure;
 import org.eclipse.gmf.tooling.runtime.linklf.ShapeNodeAnchorDelegate;
+import org.eclipse.gmf.tooling.runtime.linklf.editpolicies.AdjustBorderItemAnchorsEditPolicy;
 import org.eclipse.gmf.tooling.runtime.linklf.editpolicies.AdjustImplicitlyMovedLinksEditPolicy;
 import org.eclipse.gmf.tooling.runtime.linklf.editpolicies.LinksLFGraphicalNodeEditPolicy;
 import org.eclipse.swt.graphics.Color;
@@ -64,6 +65,7 @@
 		
 		installEditPolicy(EditPolicy.GRAPHICAL_NODE_ROLE, new LinksLFGraphicalNodeEditPolicy());
 		installEditPolicy(AdjustImplicitlyMovedLinksEditPolicy.ROLE, new AdjustImplicitlyMovedLinksEditPolicy());
+		installEditPolicy(AdjustBorderItemAnchorsEditPolicy.ROLE, new AdjustBorderItemAnchorsEditPolicy());
 	}
 
 	/**