package org.eclipse.gmf.tooling.runtime.linklf; import java.util.Collections; import java.util.List; import org.eclipse.draw2d.AbsoluteBendpoint; import org.eclipse.draw2d.Bendpoint; import org.eclipse.draw2d.Connection; import org.eclipse.draw2d.ConnectionAnchor; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.PointList; import org.eclipse.gmf.runtime.notation.Edge; import org.eclipse.gmf.runtime.notation.RelativeBendpoints; import org.eclipse.gmf.runtime.notation.datatype.RelativeBendpoint; /** * This class defines when to enable the enhanced link anchors and bendpoints behavior, * which implementation is based on storing the fixed bendpoints coordinates instead of their relative locations to * anchors. */ public abstract class AbsoluteBendpointsConvention { private static AbsoluteBendpointsConvention ourInstance; public static AbsoluteBendpointsConvention getInstance() { if(ourInstance == null) { //ourInstance = new OnlyForNewLinks(); ourInstance = new ForAllLinks(); } return ourInstance; } public abstract RelativeBendpoint createAbsoluteBendpointStoredAsRelative(Point point); public abstract boolean isAbsoluteStoredAsRelative(RelativeBendpoint modelBP); public abstract Bendpoint d2dBendpoint(RelativeBendpoint modelBP, Connection connection, float weight); public abstract boolean hasAbsoluteStoredAsRelativeBendpoints(Edge edge); public abstract PointList getPointList(Edge edge, Object linkConstraint); private abstract static class ConventionBase extends AbsoluteBendpointsConvention { private static final int MAGIC = -643984; @Override public RelativeBendpoint createAbsoluteBendpointStoredAsRelative(Point point) { return new RelativeBendpoint(point.x, point.y, MAGIC, MAGIC); } @Override public boolean isAbsoluteStoredAsRelative(RelativeBendpoint modelBP) { return modelBP.getTargetX() == MAGIC && modelBP.getTargetY() == MAGIC; } @Override public Bendpoint d2dBendpoint(RelativeBendpoint modelBP, Connection connection, float weight) { if(isAbsoluteStoredAsRelative(modelBP)) { return new AbsoluteBendpoint(modelBP.getSourceX(), modelBP.getSourceY()); } return null; } @Override public PointList getPointList(Edge edge, Object linkConstraint) { PointList result = new PointList(); List<?> d2dBendpoints = linkConstraint instanceof List<?> ? (List<?>)linkConstraint : Collections.emptyList(); RelativeBendpoints allModelBendpoints = (RelativeBendpoints)edge.getBendpoints(); @SuppressWarnings("unchecked") List<RelativeBendpoint> modelBendpoints = allModelBendpoints.getPoints(); for(int i = 0; i < modelBendpoints.size(); i++) { RelativeBendpoint nextModel = modelBendpoints.get(i); Object nextD2d = d2dBendpoints.size() > i ? d2dBendpoints.get(i) : null; Point nextPoint = getLocation(nextModel, nextD2d); if(nextPoint == null) { throw new IllegalStateException("Can't extract location: modelBP: " + nextModel + ", d2dBP: " + nextD2d); } result.addPoint(nextPoint); } return result; } protected static org.eclipse.draw2d.RelativeBendpoint newRelativeBendpointD2d(RelativeBendpoint modelBP, Connection connection, float weight) { org.eclipse.draw2d.RelativeBendpoint rbp = new org.eclipse.draw2d.RelativeBendpoint(connection); rbp.setRelativeDimensions(new Dimension(modelBP.getSourceX(), modelBP.getSourceY()), // new Dimension(modelBP.getTargetX(), modelBP.getTargetY())); rbp.setWeight(weight); return rbp; } protected abstract Point getLocation(RelativeBendpoint modelBendpoint, Object d2dbendpoint); } protected static class OnlyForNewLinks extends ConventionBase { @Override public Bendpoint d2dBendpoint(RelativeBendpoint modelBP, Connection connection, float weight) { Bendpoint result = super.d2dBendpoint(modelBP, connection, weight); if(result == null) { result = newRelativeBendpointD2d(modelBP, connection, weight); } return result; } @Override public boolean hasAbsoluteStoredAsRelativeBendpoints(Edge edge) { List<?> bendpoints = ((RelativeBendpoints)edge.getBendpoints()).getPoints(); for(Object o : bendpoints) { if(o instanceof RelativeBendpoint && isAbsoluteStoredAsRelative((RelativeBendpoint)o)) { return true; } } return false; } @Override protected Point getLocation(RelativeBendpoint modelBendpoint, Object d2dbendpoint) { if(isAbsoluteStoredAsRelative(modelBendpoint)) { return new Point(modelBendpoint.getSourceX(), modelBendpoint.getSourceY()); } if(d2dbendpoint instanceof Bendpoint) { return ((Bendpoint)d2dbendpoint).getLocation(); } return null; } } protected static class ForAllLinks extends ConventionBase { @Override public Bendpoint d2dBendpoint(RelativeBendpoint modelBP, Connection connection, float weight) { Bendpoint result = super.d2dBendpoint(modelBP, connection, weight); if(result == null) { org.eclipse.draw2d.RelativeBendpoint rbp = newRelativeBendpointD2d(modelBP, connection, weight); //if(connection.getSourceAnchor() != null && connection.getTargetAnchor() != null) { result = new RelativeBendpointWrapper(rbp, connection); //} } return result; } @Override public boolean hasAbsoluteStoredAsRelativeBendpoints(Edge edge) { List<?> bendpoints = ((RelativeBendpoints)edge.getBendpoints()).getPoints(); return !bendpoints.isEmpty(); } @Override protected Point getLocation(RelativeBendpoint modelBendpoint, Object d2dBendpoint) { if(isAbsoluteStoredAsRelative(modelBendpoint)) { return new Point(modelBendpoint.getSourceX(), modelBendpoint.getSourceY()); } if(d2dBendpoint instanceof AbsoluteBendpoint) { AbsoluteBendpoint wrapper = (AbsoluteBendpoint)d2dBendpoint; return wrapper.getLocation(); } throw new IllegalStateException("I had to create AbsoluteBendpointWrapper for this: " + modelBendpoint + ", " + d2dBendpoint); } /** * Provides implicit migration of the diagrams created before the LinksLF. * <p/> * Idea is to create the same "absolute" bendpoints for the old relative bendpoints created with previous version, and only update the * persistence on the first modification of the link. * <p/> * However, positions of the {@link RelativeBendpoint} depends on the anchors and, more generally on the bounds of link ends, so they can't be * computed immediately at the time of creation. This class introduced the deferred replacement, that is, once the {@link RelativeBendpoint} * can compute its positions, their coordinates are saved and don't depend on the source or target anchors anymore. */ @SuppressWarnings("serial") private static class RelativeBendpointWrapper extends AbsoluteBendpoint { private Point myLocation = null; private org.eclipse.draw2d.RelativeBendpoint myRelativeBendpoint; private Connection myConnection; /** * Wraps the {@link RelativeBendpoint} and defers computation of its positions until it is ready. * * @param relativeBendpoint * @param conn */ public RelativeBendpointWrapper(org.eclipse.draw2d.RelativeBendpoint relativeBendpoint, Connection conn) { super(new Point()); myRelativeBendpoint = relativeBendpoint; myConnection = conn; } @Override public Point getLocation() { if(myLocation == null && isReadyToComputeLocation()) { myLocation = new Point(myRelativeBendpoint.getLocation()); myRelativeBendpoint = null; myConnection = null; } return myLocation != null ? myLocation : myRelativeBendpoint.getLocation(); } private boolean isReadyToComputeLocation() { if(myConnection == null) { return false; } ConnectionAnchor source = myConnection.getSourceAnchor(); ConnectionAnchor target = myConnection.getTargetAnchor(); if(source == null || target == null) { return false; } return hasLocation(source.getReferencePoint()) && hasLocation(target.getReferencePoint()); } private boolean hasLocation(Point point) { return point != null && (point.x() != 0 || point.y() != 0); } @Override public int x() { return getLocation().x(); } @Override public int y() { return getLocation().y(); } } } }