package org.eclipse.gmf.tooling.runtime.linklf.editpolicies;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.editpolicies.GraphicalEditPolicy;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gmf.runtime.common.core.command.ICommand;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.gmf.tooling.runtime.linklf.AbsoluteBendpointsConvention;
/**
* Diagrams that use {@link AbsoluteBendpointsConvention} are facing the need
* to adjust now-absolute bendpoints on different move's. This class provides boilerplate
* for edit policies that handles different aspects of this adjustment.
* <p/>
*/
public abstract class AdjustAbsoluteBendpointsEditPolicyBase extends GraphicalEditPolicy {
/**
* The same {@link ChangeBoundsRequest} is sent to all moved edit parts,
* so we can cache the info about them in request potentially improving o(N^2) performance.
*/
private static final String PARAM_CACHED_EDIT_PARTS_SET = AdjustAbsoluteBendpointsEditPolicyBase.class.getName() + ":CachedMovedEPs";
/**
* Tries to find the cached instance of {@link CachedEditPartsSet} in the request extended data map.
* If not found, initializes the new instance and caches it in request for other edit-policy instances.
* @param req
* @return never returns <code>null</code>
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected static CachedEditPartsSet getMovedEditPartsSet(ChangeBoundsRequest req) {
Map extData = req.getExtendedData();
CachedEditPartsSet set = (CachedEditPartsSet) extData.get(PARAM_CACHED_EDIT_PARTS_SET);
if (set == null) {
set = new CachedEditPartsSet(req.getEditParts());
extData.put(PARAM_CACHED_EDIT_PARTS_SET, set);
}
return set;
}
protected abstract Command getAdjustLinksCommand(ChangeBoundsRequest req);
@Override
public boolean understandsRequest(Request req) {
return req instanceof ChangeBoundsRequest && REQ_MOVE.equals(req.getType());
}
@Override
public Command getCommand(Request request) {
if (understandsRequest(request)) {
return getAdjustLinksCommand((ChangeBoundsRequest) request);
}
return null;
}
@Override
public IGraphicalEditPart getHost() {
return (IGraphicalEditPart) super.getHost();
}
protected TransactionalEditingDomain getDomain() {
return getHost().getEditingDomain();
}
protected static class CachedEditPartsSet {
private final Set<EditPart> myDirectlyMoved;
private final Set<EditPart> myKnownIndirectlyYes;
private final Set<EditPart> myKnownIndirectlyNo;
public CachedEditPartsSet(List<EditPart> directlyMoved) {
myDirectlyMoved = new HashSet<EditPart>(directlyMoved);
myKnownIndirectlyNo = new HashSet<EditPart>(directlyMoved.size() * 5 + 1);
myKnownIndirectlyYes = new HashSet<EditPart>(directlyMoved.size() * 5 + 1);
}
public MovedNodeKind isMoved(EditPart ep) {
List<EditPart> chainUp = new LinkedList<EditPart>();
EditPart cur = ep;
MovedNodeKind kind = null;
while (cur != null) {
kind = getKnownKind(cur);
if (kind != null) {
break;
}
chainUp.add(cur);
cur = cur.getParent();
}
if (cur == null || kind == null) {
kind = MovedNodeKind.NO;
} else if (kind == MovedNodeKind.DIRECTLY && cur != ep) {
kind = MovedNodeKind.INDIRECTLY;
}
Set<EditPart> forKind;
switch (kind) {
case DIRECTLY:
forKind = myDirectlyMoved;
break;
case INDIRECTLY:
forKind = myKnownIndirectlyYes;
break;
case NO:
forKind = myKnownIndirectlyNo;
break;
default:
throw new IllegalArgumentException("Wow: " + kind);
}
if (kind != MovedNodeKind.DIRECTLY) {
forKind.addAll(chainUp);
}
return kind;
}
private MovedNodeKind getKnownKind(EditPart ep) {
if (myDirectlyMoved.contains(ep)) {
return MovedNodeKind.DIRECTLY;
}
if (myKnownIndirectlyYes.contains(ep)) {
return MovedNodeKind.INDIRECTLY;
}
if (myKnownIndirectlyNo.contains(ep)) {
return MovedNodeKind.NO;
}
return null;
}
}
protected static enum MovedNodeKind {
DIRECTLY, INDIRECTLY, NO
}
protected static ICommand compose(ICommand c1, ICommand c2) {
return c1 == null ? c2 : c1.compose(c2);
}
protected static String pointList2String(PointList points) {
return points == null ? "<null>" : Arrays.toString(points.toIntArray());
}
protected static Point makeRelative(IFigure f, Point point) {
Point result = point.getCopy();
f.translateToRelative(result);
return result;
}
protected static PointList makeRelative(IFigure f, PointList points) {
PointList result = points.getCopy();
f.translateToRelative(result);
return result;
}
protected static Rectangle makeRelative(IFigure f, Rectangle rect) {
Rectangle result = rect.getCopy();
f.translateToRelative(result);
return result;
}
protected static Point makeAbsolute(IFigure f, Point point) {
Point result = point.getCopy();
f.translateToAbsolute(result);
return result;
}
protected static PointList makeAbsolute(IFigure f, PointList points) {
PointList result = points.getCopy();
f.translateToAbsolute(result);
return result;
}
protected static Rectangle makeAbsolute(IFigure f, Rectangle rect) {
Rectangle result = rect.getCopy();
f.translateToAbsolute(result);
return result;
}
}