WIP: ports, links

This commit is contained in:
BoykoAlex
2019-08-29 19:25:03 -04:00
parent 1a7bcf76a9
commit d1e39fef5e
10 changed files with 285 additions and 34 deletions

View File

@@ -1,5 +1,6 @@
package org.springframework.ide.vscode.commons.sprotty.scan;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
@@ -8,6 +9,7 @@ import org.eclipse.sprotty.ActionMessage;
import org.eclipse.sprotty.DefaultDiagramServer;
import org.eclipse.sprotty.IDiagramServer;
import org.eclipse.sprotty.ILayoutEngine;
import org.eclipse.sprotty.IPopupModelFactory;
import org.eclipse.sprotty.RequestModelAction;
import org.eclipse.sprotty.SGraph;
import org.slf4j.Logger;
@@ -40,6 +42,9 @@ public class DefaultDiagramServerManager implements DiagramServerManager {
@Autowired
private ILayoutEngine layoutEngine;
@Autowired
private Optional<IPopupModelFactory> popups;
private Consumer<ActionMessage> remoteEndpoint;
private IDiagramServer getDiagramServer(String clientId) {
@@ -48,6 +53,7 @@ public class DefaultDiagramServerManager implements DiagramServerManager {
DefaultDiagramServer diagramServer = new DefaultDiagramServer(clientId);
diagramServer.setRemoteEndpoint(this::sendMessageToRemoteEndpoint);
diagramServer.setLayoutEngine(layoutEngine);
diagramServer.setPopupModelFactory(popups.orElse(null));
return diagramServer;
});
} catch (ExecutionException e) {

View File

@@ -1,8 +1,12 @@
package org.springframework.ide.vscode.commons.sprotty.scan;
import java.util.Optional;
import org.eclipse.elk.alg.layered.options.LayeredMetaDataProvider;
import org.eclipse.sprotty.ILayoutEngine;
import org.eclipse.sprotty.SGraph;
import org.eclipse.sprotty.layout.ElkLayoutEngine;
import org.eclipse.sprotty.layout.SprottyLayoutConfigurator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -10,9 +14,10 @@ import org.springframework.context.annotation.Configuration;
public class DiagramServerConfiguration {
@Bean
public ILayoutEngine layoutEngine() {
public ILayoutEngine layoutEngine(Optional<SprottyLayoutConfigurator> configurator) {
ElkLayoutEngine.initialize(new LayeredMetaDataProvider());
return new ElkLayoutEngine();
final ElkLayoutEngine engine = new ElkLayoutEngine();
return (root) -> engine.layout((SGraph)root, configurator.orElse(null));
}
}

View File

@@ -20,6 +20,16 @@
stroke-width: 2;
}
.sprotty-port {
fill: #aaffaa;
stroke: #000000;
stroke-width: 1px;
}
.sprotty-port.sprotty-error-port {
fill: #ff0000;
}
.sprotty-text {
font-size: 16pt;
text-anchor: middle;

View File

@@ -40,12 +40,31 @@ import {
graphModule,
routingModule,
modelSourceModule,
edgeLayoutModule,
SLabel,
SLabelView,
SCompartment,
SCompartmentView, SEdge, RectangularNode
SCompartmentView,
SEdge,
RectangularNode,
HtmlRootView,
HtmlRoot,
PreRenderedView,
PreRenderedElement,
labelEditModule, edgeEditModule, RectangularPort
} from "sprotty";
import {IntegrationNodeView, CircleNodeView, EdgeView, ExampleGraphView, ChannelNodeView} from "./views";
import {
IntegrationNodeView,
CircleNodeView,
EdgeView,
ExampleGraphView,
ChannelNodeView,
PortView,
ErrorPortView
} from "./views";
import fadeModule from "sprotty/lib/features/fade/di.config";
import buttonModule from "sprotty/lib/features/button/di.config";
import expandModule from "sprotty/lib/features/expand/di.config";
export enum TransportMedium {
None,
@@ -56,7 +75,7 @@ export enum TransportMedium {
export default (transport: TransportMedium, clientId: string) => {
require("sprotty/css/sprotty.css");
require("../css/diagram.css");
const circlegraphModule = new ContainerModule((bind, unbind, isBound, rebind) => {
const integrationGaphModule = new ContainerModule((bind, unbind, isBound, rebind) => {
switch (transport) {
case TransportMedium.Websocket:
bind(TYPES.ModelSource).to(ExampleWebsocketDiagramServer).inSingletonScope();
@@ -77,16 +96,22 @@ export default (transport: TransportMedium, clientId: string) => {
configureModelElement(context, 'node:label', SLabel, SLabelView);
configureModelElement(context, 'compartment', SCompartment, SCompartmentView);
configureModelElement(context, 'edge:straight', /*OrthogonalEgde*/ SEdge, EdgeView);
configureModelElement(context, 'html', HtmlRoot, HtmlRootView);
configureModelElement(context, 'pre-rendered', PreRenderedElement, PreRenderedView);
configureModelElement(context, 'input-port', RectangularPort, PortView);
configureModelElement(context, 'output-port', RectangularPort, PortView);
configureModelElement(context, 'error-port', RectangularPort, ErrorPortView);
configureViewerOptions(context, {
needsClientLayout: true,
needsServerLayout: true,
baseDiv: clientId,
});
console.log('di.config.ts : ' + clientId);
});
const container = new Container();
container.load(defaultModule, selectModule, moveModule, boundsModule, undoRedoModule, viewportModule,
exportModule, updateModule, graphModule, routingModule, modelSourceModule, circlegraphModule, hoverModule);
container.load(defaultModule, selectModule, moveModule, boundsModule, undoRedoModule,
viewportModule, fadeModule, hoverModule, exportModule, expandModule, buttonModule,
updateModule, graphModule, routingModule, edgeEditModule, edgeLayoutModule, labelEditModule,
modelSourceModule, integrationGaphModule);
return container;
};

View File

@@ -123,6 +123,30 @@ export class ChannelNodeView extends RectangularNodeView {
</g>;
}
}
@injectable()
export class PortView implements IView {
render(node: Readonly<SShapeElement & Hoverable & Selectable>, context: RenderingContext): VNode {
return <g>
<rect class-sprotty-node={node instanceof SPort} class-sprotty-port={node instanceof SPort}
class-mouseover={node.hoverFeedback} class-selected={node.selected}
x="0" y="0" width={Math.max(node.size.width, 0)} height={Math.max(node.size.height, 0)}></rect>
{context.renderChildren(node)}
</g>;
}
}
@injectable()
export class ErrorPortView implements IView {
render(node: Readonly<SShapeElement & Hoverable & Selectable>, context: RenderingContext): VNode {
return <g>
<rect class-sprotty-node={node instanceof SPort} class-sprotty-port={node instanceof SPort}
class-sprotty-error-port={node instanceof SPort} class-mouseover={node.hoverFeedback} class-selected={node.selected}
x="0" y="0" width={Math.max(node.size.width, 0)} height={Math.max(node.size.height, 0)}></rect>
{context.renderChildren(node)}
</g>;
}
}
/*
<g class="shape">'+
'<rect class="border"/>' +

View File

@@ -3,5 +3,5 @@ package org.springframework.ide.si.view;
import org.springframework.ide.si.view.json.SpringIntegrationGraph;
public interface GraphDataProvider {
SpringIntegrationGraph getGraph();
SpringIntegrationGraph getGraph() throws Exception;
}

View File

@@ -0,0 +1,40 @@
package org.springframework.ide.si.view;
import org.eclipse.elk.alg.layered.options.LayeredOptions;
import org.eclipse.elk.core.options.PortConstraints;
import org.eclipse.elk.core.options.PortSide;
import org.eclipse.elk.graph.properties.IPropertyHolder;
import org.eclipse.sprotty.layout.SprottyLayoutConfigurator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class IntegrationConfiguration {
@Bean
public SprottyLayoutConfigurator integrationLayoutConfigurator() {
SprottyLayoutConfigurator configurator = new SprottyLayoutConfigurator();
IPropertyHolder holder = configurator.configureByType(SIDiagramGenerator.TYPE_INTEGRATION_GRAPH);
holder.setProperty(LayeredOptions.SPACING_COMPONENT_COMPONENT, 80.0);
holder.setProperty(LayeredOptions.SPACING_NODE_NODE_BETWEEN_LAYERS, 80.0);
holder = configurator.configureByType("output-port");
holder.setProperty(LayeredOptions.PORT_SIDE, PortSide.EAST);
holder = configurator.configureByType("input-port");
holder.setProperty(LayeredOptions.PORT_SIDE, PortSide.WEST);
holder = configurator.configureByType("error-port");
holder.setProperty(LayeredOptions.PORT_SIDE, PortSide.SOUTH);
holder = configurator.configureByType("node:channel");
holder.setProperty(LayeredOptions.PORT_CONSTRAINTS, PortConstraints.FIXED_SIDE);
holder = configurator.configureByType("node:integration_node");
holder.setProperty(LayeredOptions.PORT_CONSTRAINTS, PortConstraints.FIXED_SIDE);
return configurator;
}
}

View File

@@ -0,0 +1,35 @@
package org.springframework.ide.si.view;
import java.util.ArrayList;
import org.eclipse.sprotty.IDiagramServer;
import org.eclipse.sprotty.IPopupModelFactory;
import org.eclipse.sprotty.PreRenderedElement;
import org.eclipse.sprotty.RequestPopupModelAction;
import org.eclipse.sprotty.SGraph;
import org.eclipse.sprotty.SModelElement;
import org.eclipse.sprotty.SModelRoot;
import org.springframework.stereotype.Component;
@Component
public class IntegrationPopupFactory implements IPopupModelFactory {
@Override
public SModelRoot createPopupModel(SModelElement element, RequestPopupModelAction request, IDiagramServer server) {
SGraph popup = new SGraph();
popup.setType("html");
popup.setId("popup");
popup.setCanvasBounds(request.getBounds());
ArrayList<SModelElement> children = new ArrayList<>();
popup.setChildren(children);
PreRenderedElement content = new PreRenderedElement();
children.add(content);
content.setId("popup-title");
content.setCode("<div class=\"sprotty-popup-title\">Hello World!</div>");
return popup;
}
}

View File

@@ -1,5 +1,8 @@
package org.springframework.ide.si.view;
import java.io.IOException;
import org.apache.commons.io.IOUtils;
import org.springframework.ide.si.view.json.SpringIntegrationGraph;
import org.springframework.stereotype.Component;
@@ -323,7 +326,12 @@ public class MockGraphData implements GraphDataProvider {
"}";
@Override
public SpringIntegrationGraph getGraph() {
public SpringIntegrationGraph getGraph() throws IOException {
// Gson gson = new Gson().newBuilder().setPrettyPrinting().create();
// JsonObject json = gson.fromJson(data, JsonObject.class);
// System.out.println(gson.toJson(json));
String data = IOUtils.toString(getClass().getResource("/static/sample.json"));
return new Gson().fromJson(data, SpringIntegrationGraph.class);
}
}

View File

@@ -1,11 +1,13 @@
package org.springframework.ide.si.view;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.Map;
import org.eclipse.sprotty.Dimension;
import org.eclipse.sprotty.EdgePlacement;
import org.eclipse.sprotty.EdgePlacement.Side;
import org.eclipse.sprotty.LayoutOptions;
import org.eclipse.sprotty.Point;
import org.eclipse.sprotty.RequestModelAction;
@@ -16,6 +18,8 @@ import org.eclipse.sprotty.SLabel;
import org.eclipse.sprotty.SModelElement;
import org.eclipse.sprotty.SModelRoot;
import org.eclipse.sprotty.SNode;
import org.eclipse.sprotty.SPort;
import org.eclipse.sprotty.SShapeElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -24,15 +28,26 @@ import org.springframework.ide.si.view.json.SpringIntegrationGraph;
import org.springframework.ide.si.view.json.SpringIntegrationNode;
import org.springframework.ide.vscode.commons.sprotty.api.DiagramGenerator;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.StringUtil;
import org.springframework.stereotype.Component;
import com.google.common.collect.ImmutableSet;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
@Component
public class SIDiagramGenerator implements DiagramGenerator {
static final String TYPE_INTEGRATION_GRAPH = "graph";
private static final double CHANNEL_HEIGHT = 40D;
private static final double NODE_MIN_HEIGHT = 80D;
private static final double MIN_WIDTH = 120D;
private static final Logger log = LoggerFactory.getLogger(SIDiagramGenerator.class);
private static final ImmutableSet<String> channelTypes = ImmutableSet.of(
@@ -40,6 +55,8 @@ public class SIDiagramGenerator implements DiagramGenerator {
"publish-subscribe-channel"
);
private String labelProperty = "stats.sendCount";
@Autowired
GraphDataProvider graphDataProvider;
@@ -59,29 +76,36 @@ public class SIDiagramGenerator implements DiagramGenerator {
private SGraph toSprottyGraph(SpringIntegrationGraph json) throws Exception {
SGraph graph = new SGraph();
graph.setId("root");
graph.setType("graph");
graph.setType(TYPE_INTEGRATION_GRAPH);
List<SModelElement> children = new ArrayList<>();
graph.setChildren(children);
Set<String> nodeIds = new HashSet<>();
Map<String, SpringIntegrationNode> nodeIds = new HashMap<>();
for (SpringIntegrationNode node : json.getNodes()) {
String id = node.getNodeId()+"";
String name = node.getName();
Assert.isLegal(nodeIds.add(id));
SNode n = createNode(id, name, node.getComponentType());
children.add(n);
Assert.isLegal(!nodeIds.containsKey(id));
nodeIds.put(id, node);
String type = visualType(node.getComponentType());
switch (type) {
case "channel":
children.add(createChannelNode(id, name));
break;
default:
children.add(createIntegrationNode(id, name));
}
}
int linkId = 0;
for (SpringIntegrationEdge link : json.getLinks()) {
String id = "l"+(linkId++);
String sourceId = link.getFrom()+"";
ensureNode(nodeIds, children, sourceId);
String targetId = link.getTo()+"";
ensureNode(nodeIds, children, targetId);
SEdge e = createEdge(id, sourceId, targetId);
SEdge e = createEdge(id, sourceId, targetId, getEdgeLabel(nodeIds.get(sourceId)), link.getType());
children.add(e);
}
//
@@ -98,54 +122,106 @@ public class SIDiagramGenerator implements DiagramGenerator {
return graph;
}
private void ensureNode(Set<String> nodeIds, List<SModelElement> children, String sourceId) {
if (!nodeIds.contains(sourceId)) {
nodeIds.add(sourceId);
children.add(createNode(sourceId, "?"+sourceId, "unknown"));
}
private String getEdgeLabel(SpringIntegrationNode springIntegrationNode) {
JsonElement json = gson.toJsonTree(springIntegrationNode);
for (String prop : labelProperty.split("\\.")) {
if (json instanceof JsonObject) {
JsonObject obj = (JsonObject)json;
if (obj.has(prop)) {
json = obj.get(prop);
} else {
return null;
}
} else {
return null;
}
};
return json.getAsString();
}
private static SNode createNode(String id, String labelText, String type) {
SNode node = new SNode();
node.setId(id);
node.setType("node:"+visualType(type));
node.setType("node:"+type);
node.setLayout("vbox");
node.setPosition(new Point(Math.random() * 1024, Math.random() * 768));
node.setSize(new Dimension(80, 80));
node.setChildren(new ArrayList<>());
centerNode(node);
SCompartment compartment = new SCompartment();
compartment.setId(id + "-comp");
compartment.setType("compartment");
compartment.setLayout("hbox");
compartment.setLayout("vbox");
compartment.setChildren(new ArrayList<>());
centerNode(compartment);
SLabel label = new SLabel();
label.setId(id + "-lanbel");
label.setType("node:label");
label.setText(labelText);
centerNode(label);
compartment.getChildren().add(label);
label = new SLabel();
label.setId(id + "-lanbel1");
label.setType("node:label");
label.setText("s");
centerNode(label);
compartment.getChildren().add(label);
node.getChildren().add(compartment);
SPort outputPort = new SPort();
outputPort.setType("output-port");
outputPort.setId("output-port-" + id);
outputPort.setSize(new Dimension(10,10));
node.getChildren().add(outputPort);
SPort inputPort = new SPort();
inputPort.setType("input-port");
inputPort.setId("input-port-" + id);
inputPort.setSize(new Dimension(10,10));
node.getChildren().add(inputPort);
return node;
}
private static void centerNode(SShapeElement shape) {
LayoutOptions options = shape.getLayoutOptions();
if (options == null) {
options = new LayoutOptions();
shape.setLayoutOptions(options);
}
options.setHAlign("center");
options.setVAlign("center");
}
private static SNode createIntegrationNode(String id, String labelText) {
SNode node = createNode(id, labelText, "integration_node");
LayoutOptions layoutOptions = new LayoutOptions();
layoutOptions.setMinHeight(80D);
layoutOptions.setMinWidth(120D);
layoutOptions.setMinHeight(NODE_MIN_HEIGHT);
layoutOptions.setMinWidth(MIN_WIDTH);
node.setLayoutOptions(layoutOptions);
SPort errorPort = new SPort();
errorPort.setType("error-port");
errorPort.setId("error-port-" + id);
errorPort.setSize(new Dimension(10, 10));
node.getChildren().add(errorPort);
return node;
}
private static SNode createChannelNode(String id, String labelText) {
SNode node = createNode(id, labelText, "channel");
LayoutOptions layoutOptions = new LayoutOptions();
layoutOptions.setMinHeight(80D);
layoutOptions.setMinWidth(120D);
layoutOptions.setMinHeight(CHANNEL_HEIGHT);
layoutOptions.setMinWidth(MIN_WIDTH);
node.setLayoutOptions(layoutOptions);
return node;
}
@@ -157,12 +233,34 @@ public class SIDiagramGenerator implements DiagramGenerator {
}
}
private static SEdge createEdge(String id, String sourceId, String targetId) {
private static SEdge createEdge(String id, String sourceId, String targetId, String labelStr, String linkType) {
SEdge edge = new SEdge();
edge.setId(id);
edge.setType("edge:straight");
edge.setSourceId(sourceId);
edge.setTargetId(targetId);
if ("error".equals(linkType)) {
edge.setSourceId("error-port-" + sourceId);
} else {
edge.setSourceId("output-port-" + sourceId);
}
edge.setTargetId("input-port-" + targetId);
if (StringUtil.hasText(labelStr)) {
SLabel label = new SLabel();
label.setType("node:label");
EdgePlacement edgePlacement = new EdgePlacement();
edgePlacement.setRotate(true);
edgePlacement.setPosition(0.0);
edgePlacement.setSide(Side.right);
label.setEdgePlacement(edgePlacement);
label.setId(id + "-label1");
label.setText(labelStr);
label.setPosition(new Point(20,30));
ArrayList<SModelElement> children = new ArrayList<>();
children.add(label);
edge.setChildren(children);
}
return edge;
}