Skip to content

Commit

Permalink
#738: Extend ComputedBoundsAction with route (#181)
Browse files Browse the repository at this point in the history
  • Loading branch information
martin-fleck-at authored Nov 14, 2022
1 parent fa089aa commit fbd330b
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (c) 2021 EclipseSource and others.
* Copyright (c) 2021-2022 EclipseSource and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
Expand All @@ -18,8 +18,13 @@
import java.util.Map;
import java.util.Optional;

import org.eclipse.emf.common.util.EMap;
import org.eclipse.glsp.graph.GPoint;

public final class GArguments {
public static final String KEY_EDGE_PADDING = "edgePadding";
public static final String KEY_EDGE_SOURCE_POINT = "edgeSourcePoint";
public static final String KEY_EDGE_TARGET_POINT = "edgeTargetPoint";

public static final String KEY_RADIUS_TOP_LEFT = "radiusTopLeft";
public static final String KEY_RADIUS_TOP_RIGHT = "radiusTopRight";
Expand Down Expand Up @@ -52,6 +57,14 @@ public static Optional<Double> getEdgePadding(final Map<String, Object> argument
return getDouble(arguments, KEY_EDGE_PADDING);
}

public static Optional<GPoint> getEdgeSourcePoint(final Map<String, Object> arguments) {
return get(arguments, KEY_EDGE_SOURCE_POINT, GPoint.class);
}

public static Optional<GPoint> getEdgeTargetPoint(final Map<String, Object> arguments) {
return get(arguments, KEY_EDGE_TARGET_POINT, GPoint.class);
}

public static Optional<Double> getDouble(final Map<String, Object> arguments, final String key) {
return getNumber(arguments, key).map(Number::doubleValue);
}
Expand All @@ -61,17 +74,56 @@ public static Optional<Integer> getInteger(final Map<String, Object> arguments,
}

public static Optional<Number> getNumber(final Map<String, Object> arguments, final String key) {
Object value = arguments.get(key);
return value instanceof Number ? Optional.of((Number) value) : Optional.empty();
return get(arguments, key, Number.class);
}

public static Optional<Boolean> getBoolean(final Map<String, Object> arguments, final String key) {
Object value = arguments.get(key);
return value instanceof Boolean ? Optional.of((Boolean) value) : Optional.empty();
return get(arguments, key, Boolean.class);
}

public static Optional<String> getString(final Map<String, Object> arguments, final String key) {
return get(arguments, key, String.class);
}

public static <T> Optional<T> get(final Map<String, Object> arguments, final String key, final Class<T> clazz) {
Object value = arguments.get(key);
return clazz.isInstance(value) ? Optional.of(clazz.cast(value)) : Optional.empty();
}

public static Optional<Double> getEdgePadding(final EMap<String, Object> arguments) {
return getDouble(arguments, KEY_EDGE_PADDING);
}

public static Optional<GPoint> getEdgeSourcePoint(final EMap<String, Object> arguments) {
return get(arguments, KEY_EDGE_SOURCE_POINT, GPoint.class);
}

public static Optional<GPoint> getEdgeTargetPoint(final EMap<String, Object> arguments) {
return get(arguments, KEY_EDGE_TARGET_POINT, GPoint.class);
}

public static Optional<Double> getDouble(final EMap<String, Object> arguments, final String key) {
return getNumber(arguments, key).map(Number::doubleValue);
}

public static Optional<Integer> getInteger(final EMap<String, Object> arguments, final String key) {
return getNumber(arguments, key).map(Number::intValue);
}

public static Optional<Number> getNumber(final EMap<String, Object> arguments, final String key) {
return get(arguments, key, Number.class);
}

public static Optional<Boolean> getBoolean(final EMap<String, Object> arguments, final String key) {
return get(arguments, key, Boolean.class);
}

public static Optional<String> getString(final EMap<String, Object> arguments, final String key) {
return get(arguments, key, String.class);
}

public static <T> Optional<T> get(final EMap<String, Object> arguments, final String key, final Class<T> clazz) {
Object value = arguments.get(key);
return value instanceof String ? Optional.of((String) value) : Optional.empty();
return clazz.isInstance(value) ? Optional.of(clazz.cast(value)) : Optional.empty();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (c) 2019-2021 EclipseSource and others.
* Copyright (c) 2019-2022 EclipseSource and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
Expand All @@ -22,26 +22,30 @@
import org.eclipse.glsp.server.actions.Action;
import org.eclipse.glsp.server.types.ElementAndAlignment;
import org.eclipse.glsp.server.types.ElementAndBounds;
import org.eclipse.glsp.server.types.ElementAndRoutingPoints;

public class ComputedBoundsAction extends Action {

public static final String KIND = "computedBounds";

private List<ElementAndBounds> bounds;
private List<ElementAndAlignment> alignments;
private List<ElementAndRoutingPoints> routes;
private int revision;

public ComputedBoundsAction() {
super(KIND);
this.bounds = new ArrayList<>();
this.alignments = new ArrayList<>();
this.routes = new ArrayList<>();
}

public ComputedBoundsAction(final List<ElementAndBounds> bounds, final List<ElementAndAlignment> alignments,
final int revision) {
final List<ElementAndRoutingPoints> route, final int revision) {
super(KIND);
this.bounds = bounds;
this.alignments = alignments;
this.routes = route;
this.revision = revision;
}

Expand All @@ -53,6 +57,10 @@ public ComputedBoundsAction(final List<ElementAndBounds> bounds, final List<Elem

public void setAlignments(final List<ElementAndAlignment> alignments) { this.alignments = alignments; }

public List<ElementAndRoutingPoints> getRoutes() { return routes; }

public void setRoutes(final List<ElementAndRoutingPoints> routes) { this.routes = routes; }

public Optional<Integer> getRevision() { return Optional.ofNullable(revision); }

public void setRevision(final int revision) { this.revision = revision; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,11 @@
********************************************************************************/
package org.eclipse.glsp.server.gmodel;

import static org.eclipse.glsp.server.types.GLSPServerException.getOrThrow;

import org.eclipse.emf.common.util.EList;
import org.eclipse.glsp.graph.GEdge;
import org.eclipse.glsp.graph.GModelIndex;
import org.eclipse.glsp.graph.GPoint;
import org.eclipse.glsp.server.model.GModelState;
import org.eclipse.glsp.server.operations.AbstractOperationHandler;
import org.eclipse.glsp.server.operations.ChangeRoutingPointsOperation;
import org.eclipse.glsp.server.types.ElementAndRoutingPoints;
import org.eclipse.glsp.server.utils.LayoutUtil;

import com.google.inject.Inject;

Expand All @@ -38,24 +33,11 @@ public class GModelChangeRoutingPointsHandler extends AbstractOperationHandler<C

@Override
protected void executeOperation(final ChangeRoutingPointsOperation operation) {

// check for null-values
if (operation.getNewRoutingPoints() == null) {
throw new IllegalArgumentException("Incomplete change routingPoints action");
}

// check for existence of matching elements
GModelIndex index = modelState.getIndex();

for (ElementAndRoutingPoints ear : operation.getNewRoutingPoints()) {
GEdge edge = getOrThrow(index.findElementByClass(ear.getElementId(), GEdge.class),
"Invalid edge: edge ID " + ear.getElementId());

// reroute
EList<GPoint> routingPoints = edge.getRoutingPoints();
routingPoints.clear();
routingPoints.addAll(ear.getNewRoutingPoints());
}

operation.getNewRoutingPoints().forEach(routingPoints -> LayoutUtil.applyRoutingPoints(routingPoints, index));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@

import static org.eclipse.glsp.server.types.GLSPServerException.getOrThrow;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.glsp.graph.GAlignable;
import org.eclipse.glsp.graph.GBounds;
Expand All @@ -31,11 +34,14 @@
import org.eclipse.glsp.graph.GModelRoot;
import org.eclipse.glsp.graph.GPoint;
import org.eclipse.glsp.graph.GraphFactory;
import org.eclipse.glsp.graph.builder.impl.GArguments;
import org.eclipse.glsp.graph.util.GraphUtil;
import org.eclipse.glsp.server.features.core.model.ComputedBoundsAction;
import org.eclipse.glsp.server.model.GModelState;
import org.eclipse.glsp.server.types.ElementAndAlignment;
import org.eclipse.glsp.server.types.ElementAndBounds;
import org.eclipse.glsp.server.types.ElementAndRoutingPoints;
import org.eclipse.glsp.server.types.GLSPServerException;

public final class LayoutUtil {

Expand All @@ -51,27 +57,108 @@ private LayoutUtil() {}
public static void applyBounds(final GModelRoot root, final ComputedBoundsAction action,
final GModelState modelState) {
GModelIndex index = modelState.getIndex();
for (ElementAndBounds b : action.getBounds()) {
GModelElement element = getOrThrow(index.get(b.getElementId()),
"Model element not found! ID: " + b.getElementId());
if (element instanceof GBoundsAware) {
GBoundsAware bae = (GBoundsAware) element;
if (b.getNewPosition() != null) {
bae.setPosition(GraphUtil.copy(b.getNewPosition()));
}
if (b.getNewSize() != null) {
bae.setSize(GraphUtil.copy(b.getNewSize()));
}
action.getBounds().forEach(bounds -> applyBounds(bounds, index));
action.getAlignments().forEach(alignment -> applyAlignment(alignment, index));
action.getRoutes().forEach(route -> applyRoute(route, index));
}

/**
* Applies the new bounds to the model.
*
* @param bounds The new bounds.
* @param index The model index.
* @return The changed element.
*/
public static Optional<GBoundsAware> applyBounds(final ElementAndBounds bounds, final GModelIndex index) {
GModelElement element = getOrThrow(index.get(bounds.getElementId()),
"Model element not found! ID: " + bounds.getElementId());
if (element instanceof GBoundsAware) {
GBoundsAware bae = (GBoundsAware) element;
if (bounds.getNewPosition() != null) {
bae.setPosition(GraphUtil.copy(bounds.getNewPosition()));
}
}
for (ElementAndAlignment a : action.getAlignments()) {
GModelElement element = getOrThrow(index.get(a.getElementId()),
"Model element not found! ID: " + a.getElementId());
if (element instanceof GAlignable) {
GAlignable alignable = (GAlignable) element;
alignable.setAlignment(a.getNewAlignment());
if (bounds.getNewSize() != null) {
bae.setSize(GraphUtil.copy(bounds.getNewSize()));
}
return Optional.of(bae);
}
return Optional.empty();
}

/**
* Applies the new alignment to the model.
*
* @param alignment The new alignment.
* @param index The model index.
* @return The changed element.
*/
public static Optional<GAlignable> applyAlignment(final ElementAndAlignment alignment, final GModelIndex index) {
GModelElement element = getOrThrow(index.get(alignment.getElementId()),
"Model element not found! ID: " + alignment.getElementId());
if (element instanceof GAlignable) {
GAlignable alignable = (GAlignable) element;
alignable.setAlignment(alignment.getNewAlignment());
return Optional.of(alignable);
}
return Optional.empty();
}

/**
* Applies the new route to the model.
*
* @param route The new route.
* @param index The model index.
* @return The changed element.
*/
public static GEdge applyRoute(final ElementAndRoutingPoints route, final GModelIndex index) {
List<GPoint> routingPoints = route.getNewRoutingPoints();
if (routingPoints.size() < 2) {
throw new GLSPServerException("Invalid Route!");
}
// first and last point mark the source and target point
GEdge edge = applyRoutingPoints(route, index);
EList<GPoint> edgeRoutingPoints = edge.getRoutingPoints();
edge.getArgs().put(GArguments.KEY_EDGE_SOURCE_POINT, edgeRoutingPoints.remove(0));
edge.getArgs().put(GArguments.KEY_EDGE_TARGET_POINT, edgeRoutingPoints.remove(edgeRoutingPoints.size() - 1));
return edge;
}

/**
* Returns the complete route of the given edge. The route, as opposed to the routing points, also contain the source
* and target point.
*
* @param edge The edge from which we get the route
* @return complete edge route
*/
public static ElementAndRoutingPoints getRoute(final GEdge edge) {
Optional<GPoint> sourcePoint = GArguments.getEdgeSourcePoint(edge.getArgs());
if (sourcePoint.isEmpty()) {
throw new GLSPServerException("Cannot get route without source point!");
}
Optional<GPoint> targetPoint = GArguments.getEdgeTargetPoint(edge.getArgs());
if (targetPoint.isEmpty()) {
throw new GLSPServerException("Cannot get route without target point!");
}
List<GPoint> route = new ArrayList<>(EcoreUtil.copyAll(edge.getRoutingPoints()));
route.add(0, sourcePoint.get());
route.add(targetPoint.get());
return new ElementAndRoutingPoints(edge.getId(), route);
}

/**
* Applies the new routing points to the model.
*
* @param routingPoints The new routing points.
* @param index The model index.
* @return The changed element.
*/
public static GEdge applyRoutingPoints(final ElementAndRoutingPoints routingPoints, final GModelIndex index) {
GEdge edge = getOrThrow(index.findElementByClass(routingPoints.getElementId(), GEdge.class),
"Model element not found! ID: " + routingPoints.getElementId());
EList<GPoint> edgeRoutingPoints = edge.getRoutingPoints();
edgeRoutingPoints.clear();
edgeRoutingPoints.addAll(routingPoints.getNewRoutingPoints());
return edge;
}

/**
Expand Down

0 comments on commit fbd330b

Please sign in to comment.