Initial POC

This commit is contained in:
BoykoAlex
2019-08-21 10:40:56 -04:00
parent 0714b6a693
commit 83193e5d42
22 changed files with 34418 additions and 16 deletions

View File

@@ -120,6 +120,14 @@ public class LiveBean {
public String[] getDependencies() {
return dependencies;
}
public String getShortName() {
int idx = id.lastIndexOf('.');
if (idx >= 0) {
return id.substring(idx + 1);
}
return id;
}
@Override
public String toString() {

View File

@@ -0,0 +1,40 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.ide.vscode</groupId>
<artifactId>commons-parent</artifactId>
<version>1.11.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>commons-sprotty</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.sprotty</groupId>
<artifactId>org.eclipse.sprotty.server</artifactId>
<version>${sprotty-version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.eclipse.elk/org.eclipse.elk.core -->
<dependency>
<groupId>org.eclipse.elk</groupId>
<artifactId>org.eclipse.elk.core</artifactId>
<version>${elk-version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.elk</groupId>
<artifactId>org.eclipse.elk.alg.layered</artifactId>
<version>${elk-version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,20 @@
package org.springframework.ide.vscode.commons.sprotty.autoconf;
import org.eclipse.sprotty.IPopupModelFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.ide.vscode.commons.sprotty.scan.DiagramWebsocketServer;
@Configuration
@ComponentScan(basePackageClasses = DiagramWebsocketServer.class)
public class SprottyAutoConf {
@Bean
@ConditionalOnMissingBean(IPopupModelFactory.class)
public IPopupModelFactory popupModelFactory() {
return new IPopupModelFactory.NullImpl();
}
}

View File

@@ -0,0 +1,19 @@
package org.springframework.ide.vscode.commons.sprotty.elk;
import org.eclipse.elk.core.RecursiveGraphLayoutEngine;
import org.eclipse.elk.core.util.BasicProgressMonitor;
import org.eclipse.elk.graph.ElkNode;
import org.eclipse.sprotty.ILayoutEngine;
import org.eclipse.sprotty.SModelRoot;
public class ElkLayoutEngine implements ILayoutEngine {
@Override
public void layout(SModelRoot root) {
ElkUtils utils = new ElkUtils(root);
ElkNode graph = utils.graph;
new RecursiveGraphLayoutEngine().layout(graph, new BasicProgressMonitor());
utils.applyLayout();
}
}

View File

@@ -0,0 +1,114 @@
package org.springframework.ide.vscode.commons.sprotty.elk;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.elk.alg.layered.options.ContentAlignment;
import org.eclipse.elk.alg.layered.options.FixedAlignment;
import org.eclipse.elk.alg.layered.options.LayeredOptions;
import org.eclipse.elk.alg.layered.options.NodePlacementStrategy;
import org.eclipse.elk.core.options.Alignment;
import org.eclipse.elk.core.options.Direction;
import org.eclipse.elk.graph.ElkBendPoint;
import org.eclipse.elk.graph.ElkEdge;
import org.eclipse.elk.graph.ElkEdgeSection;
import org.eclipse.elk.graph.ElkNode;
import org.eclipse.elk.graph.properties.IProperty;
import org.eclipse.elk.graph.properties.Property;
import org.eclipse.elk.graph.util.ElkGraphUtil;
import org.eclipse.sprotty.Point;
import org.eclipse.sprotty.SEdge;
import org.eclipse.sprotty.SModelElement;
import org.eclipse.sprotty.SModelRoot;
import org.eclipse.sprotty.SNode;
import org.eclipse.sprotty.SShapeElement;
import org.springframework.util.Assert;
public class ElkUtils {
private static final IProperty<SModelElement> SPROTTY_ELEMENT = new Property<>("sprotty-element");
public final ElkNode graph;
private Map<String, ElkNode> nodes = new HashMap<>();;
public ElkUtils(SModelRoot root) {
graph = ElkGraphUtil.createGraph();
// graph.setProperty(LayeredOptions.DIRECTION, Direction.DOWN);
// graph.setProperty(LayeredOptions.CONTENT_ALIGNMENT, EnumSet.of(ContentAlignment.H_CENTER));
// graph.setProperty(LayeredOptions.ALIGNMENT, Alignment.RIGHT);
// graph.setProperty(LayeredOptions.NODE_PLACEMENT_STRATEGY, NodePlacementStrategy.SIMPLE);
graph.setProperty(LayeredOptions.NODE_PLACEMENT_BK_FIXED_ALIGNMENT, FixedAlignment.BALANCED);
List<SEdge> edges = new ArrayList<>();
for (SModelElement child : root.getChildren()) {
System.out.println(child.getClass());
if (child instanceof SNode) {
nodes.put(child.getId(), toElk((SNode)child, graph));
} else if (child instanceof SEdge) {
edges.add((SEdge)child);
} else {
throw new IllegalArgumentException("Unsupported graph layout element");
}
}
for (SEdge edge : edges) {
toElk(edge);
}
}
private ElkEdge toElk(SEdge child) {
ElkNode source = nodes.get(child.getSourceId());
ElkNode target = nodes.get(child.getTargetId());
Assert.isTrue(source != null);
Assert.isTrue(target != null);
ElkEdge edge = ElkGraphUtil.createSimpleEdge(source, target);
edge.setProperty(SPROTTY_ELEMENT, child);
return edge;
}
private ElkNode toElk(SNode child, ElkNode parent) {
ElkNode node = ElkGraphUtil.createNode(parent);
node.setX(child.getPosition().getX());
node.setY(child.getPosition().getY());
node.setWidth(child.getSize().getWidth());
node.setHeight(child.getSize().getHeight());
node.setIdentifier(child.getId());
node.setProperty(SPROTTY_ELEMENT, child);
// node.setProperty(LayeredOptions.ALIGNMENT, Alignment.CENTER);
return node;
}
public void applyLayout() {
for (Entry<String, ElkNode> entry : nodes.entrySet()) {
ElkNode elkNode = entry.getValue();
SModelElement element = elkNode.getProperty(SPROTTY_ELEMENT);
if (element instanceof SShapeElement) {
SShapeElement shape = (SShapeElement) element;
shape.setPosition(new Point(elkNode.getX(), elkNode.getY()));
}
}
for (ElkEdge elkEdge : graph.getContainedEdges()) {
SModelElement element = elkEdge.getProperty(SPROTTY_ELEMENT);
if (element instanceof SEdge) {
SEdge edge = (SEdge) element;
List<Point> bendpoints = new ArrayList<>();
for (ElkEdgeSection section : elkEdge.getSections()) {
bendpoints.add(new Point(section.getStartX(), section.getStartY()));
for (ElkBendPoint elkBend : section.getBendPoints()) {
Point bendpoint = new Point(elkBend.getX(), elkBend.getY());
bendpoints.add(bendpoint);
}
bendpoints.add(new Point(section.getEndX(), section.getEndY()));
}
edge.setRoutingPoints(bendpoints);
}
}
}
}

View File

@@ -0,0 +1,55 @@
package org.springframework.ide.vscode.commons.sprotty.scan;
import org.eclipse.elk.alg.layered.options.LayeredMetaDataProvider;
import org.eclipse.elk.alg.layered.options.LayeredOptions;
import org.eclipse.elk.core.data.LayoutMetaDataService;
import org.eclipse.sprotty.DefaultDiagramServer;
import org.eclipse.sprotty.IDiagramExpansionListener;
import org.eclipse.sprotty.IDiagramOpenListener;
import org.eclipse.sprotty.IDiagramSelectionListener;
import org.eclipse.sprotty.IDiagramServer;
import org.eclipse.sprotty.ILayoutEngine;
import org.eclipse.sprotty.IModelUpdateListener;
import org.eclipse.sprotty.IPopupModelFactory;
import org.eclipse.sprotty.SModelCloner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ide.vscode.commons.sprotty.elk.ElkLayoutEngine;
@Configuration
public class DiagramServerConfiguration {
@Bean
public IDiagramServer diagramServer() {
DefaultDiagramServer diagramServer = new DefaultDiagramServer("spring-boot");
return diagramServer;
}
@Bean public IModelUpdateListener modelUpdateListener() {
return new IModelUpdateListener.NullImpl();
}
@Bean
public ILayoutEngine layoutEngine() {
LayoutMetaDataService.getInstance().registerLayoutMetaDataProviders(new LayeredMetaDataProvider());
return new ElkLayoutEngine();
}
@Bean public IDiagramSelectionListener diagramSelectionListener() {
return new IDiagramSelectionListener.NullImpl();
}
@Bean public IDiagramExpansionListener diagramExpansionListener() {
return new IDiagramExpansionListener.NullImpl();
}
@Bean public IDiagramOpenListener diagramOpenListener() {
return new IDiagramOpenListener.NullImpl();
}
@Bean public SModelCloner modelCloner() {
return new SModelCloner();
}
}

View File

@@ -0,0 +1,10 @@
package org.springframework.ide.vscode.commons.sprotty.scan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
@Configuration
@EnableWebSocket
public class DiagramServerWebsocketConfiguration {
}

View File

@@ -0,0 +1,134 @@
package org.springframework.ide.vscode.commons.sprotty.scan;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.sprotty.ActionMessage;
import org.eclipse.sprotty.IDiagramServer;
import org.eclipse.sprotty.server.json.ActionTypeAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
@Controller
public class DiagramWebsocketServer implements WebSocketConfigurer, InitializingBean {
private static final Logger log = LoggerFactory.getLogger(DiagramWebsocketServer.class);
private Set<WebSocketSession> ws_sessions = new HashSet<>();
private Gson gson;
@Autowired
private IDiagramServer diagramServer;
private void initializeGson() {
if (gson == null) {
GsonBuilder builder = new GsonBuilder();
ActionTypeAdapter.configureGson(builder);
gson = builder.create();
}
}
@Override
public void afterPropertiesSet() throws Exception {
initializeGson();
}
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(WsMessageHandler(), "/websocket")
.setAllowedOrigins("*")
.withSockJS();
}
private final String END_MESSAGE = "@end";
/**
* WebSocketHandler which receives messages from a websocket and forwards them to a
* spring-cloud-stream.
*/
@Bean
public WebSocketHandler WsMessageHandler() {
return new TextWebSocketHandler() {
private StringBuilder buff = new StringBuilder();
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
synchronized (ws_sessions) {
ws_sessions.add(session);
}
log.info("Websocket connection OPENED in: "+this);
log.info("Number of active sessions = {}", ws_sessions.size());
diagramServer.setRemoteEndpoint(message -> {
sendMessage(gson.toJson(message, ActionMessage.class));
});
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String payload = message.getPayload();
log.info(payload);
try {
if (END_MESSAGE.equals(payload)) {
ActionMessage actionMessage = gson.fromJson(buff.toString(), ActionMessage.class);
buff = new StringBuilder();
diagramServer.accept(actionMessage);
} else {
buff.append(payload);
}
} catch (Exception e) {
log.error("", e);
}
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
log.info("Websocket connection CLOSED in: "+this);
synchronized (ws_sessions) {
ws_sessions.remove(session);
}
log.info("Number of active sessions = {}", ws_sessions.size());
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
log.error("Websocket trasnport error: ", exception);
synchronized (ws_sessions) {
ws_sessions.remove(session);
}
}
};
}
private void sendMessage(String msg) {
synchronized (ws_sessions) {
for (WebSocketSession ws : ws_sessions) {
try {
if (ws.isOpen()) {
log.info("Sent: {}", msg);
ws.sendMessage(new TextMessage(msg));
}
} catch (Exception e) {
log.error("Error forwarding message to ws session", e);
}
}
}
}
}

View File

@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.ide.vscode.commons.sprotty.autoconf.SprottyAutoConf

View File

@@ -13,8 +13,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.M2</version>
<relativePath></relativePath>
<version>2.2.0.M5</version>
</parent>
<dependencyManagement>
@@ -119,6 +118,8 @@
<reactor-netty>0.7.5.RELEASE</reactor-netty>
<commons-io-version>2.4</commons-io-version>
<commons-codec-version>1.11</commons-codec-version>
<sprotty-version>0.7.0-SNAPSHOT</sprotty-version>
<elk-version>0.6.0-SNAPSHOT</elk-version>
</properties>
<build>

View File

@@ -106,6 +106,19 @@
<artifactId>org.eclipse.lsp4j.jsonrpc</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/io.typefox.sprotty/diagram-api -->
<dependency>
<groupId>io.typefox.sprotty</groupId>
<artifactId>diagram-api</artifactId>
<version>0.4.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.typefox.sprotty/diagram-server -->
<dependency>
<groupId>io.typefox.sprotty</groupId>
<artifactId>diagram-server</artifactId>
<version>0.4.0</version>
</dependency>
<!-- Test harness -->
@@ -133,6 +146,11 @@
<version>${mockito-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.ide.vscode</groupId>
<artifactId>commons-sprotty</artifactId>
<version>${dependencies.version}</version>
</dependency>
</dependencies>
<build>
<plugins>

View File

@@ -14,10 +14,8 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
@@ -58,19 +56,22 @@ import org.springframework.ide.vscode.commons.yaml.completion.YamlAssistContext;
import org.springframework.ide.vscode.commons.yaml.completion.YamlAssistContextProvider;
import org.springframework.ide.vscode.commons.yaml.structure.YamlDocument;
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureProvider;
import org.springframework.ide.vscode.languageserver.starter.LanguageServerAutoConf;
import org.springframework.ide.vscode.languageserver.starter.LanguageServerRunnerAutoConf;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.yaml.snakeyaml.Yaml;
@SpringBootConfiguration(proxyBeanMethods = false)
@ImportAutoConfiguration({
// During development you can uncomment the below so that boot dash can detect started state properly:
// SpringApplicationAdminJmxAutoConfiguration.class,
LanguageServerAutoConf.class,
LanguageServerRunnerAutoConf.class,
ConfigurationPropertiesAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class
})
//@ImportAutoConfiguration({
// // During development you can uncomment the below so that boot dash can detect started state properly:
// // SpringApplicationAdminJmxAutoConfiguration.class,
// LanguageServerAutoConf.class,
// LanguageServerRunnerAutoConf.class,
// ConfigurationPropertiesAutoConfiguration.class,
// PropertyPlaceholderAutoConfiguration.class,
// SprottyAutoConf.class,
// WebSocketServletAutoConfiguration.class,
// WebMvcAutoConfiguration.class
//})
@EnableAutoConfiguration
@EnableConfigurationProperties(BootLsConfigProperties.class)
@ComponentScan
public class BootLanguagServerBootApp {
@@ -112,6 +113,12 @@ public class BootLanguagServerBootApp {
@Bean ValueProviderRegistry valueProviders() {
return new ValueProviderRegistry();
}
@Bean ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler t = new ThreadPoolTaskScheduler();
t.initialize();
return t;
}
@Bean InitializingBean initializeValueProviders(ValueProviderRegistry r, @Qualifier("adHocProperties") ProjectBasedPropertyIndexProvider adHocProperties, SourceLinks sourceLinks) {
return () -> {

View File

@@ -0,0 +1,120 @@
package org.springframework.ide.vscode.boot.app.diagram;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.eclipse.sprotty.Dimension;
import org.eclipse.sprotty.IDiagramServer;
import org.eclipse.sprotty.Point;
import org.eclipse.sprotty.SCompartment;
import org.eclipse.sprotty.SEdge;
import org.eclipse.sprotty.SGraph;
import org.eclipse.sprotty.SLabel;
import org.eclipse.sprotty.SModelElement;
import org.eclipse.sprotty.SModelRoot;
import org.eclipse.sprotty.SNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.stereotype.Component;
@Component
public class LiveBeanDiagramModel implements InitializingBean {
private static final Logger log = LoggerFactory.getLogger(LiveBeanDiagramModel.class);
@Autowired
private IDiagramServer diagramServer;
@Autowired
private RunningAppProvider runningAppProvider;
@Autowired
@Qualifier("taskScheduler")
private TaskScheduler taskScheduler;
@Override
public void afterPropertiesSet() throws Exception {
taskScheduler.scheduleAtFixedRate(() -> diagramServer.setModel(generateModel()), Duration.ofSeconds(15));
}
private SModelRoot generateModel() {
try {
Collection<SpringBootApp> apps = runningAppProvider.getAllRunningSpringApps();
if (!apps.isEmpty()) {
return toSprottyGraph(apps.iterator().next());
}
} catch (Exception e) {
log.error("{}", e);
}
return SGraph.EMPTY_ROOT;
}
private SModelRoot toSprottyGraph(SpringBootApp app) throws Exception {
SModelRoot graph = new SModelRoot();
graph.setId(app.getProcessName());
graph.setType("graph");
graph.setChildren(new ArrayList<>());
List<SModelElement> graphChildren = graph.getChildren();
LiveBeansModel beansModel = app.getBeans();
for (String targetBeanId : beansModel.getBeanNames()) {
for (LiveBean bean : beansModel.getBeansOfName(targetBeanId)) {
graphChildren.add(createBean(bean.getId(), bean.getShortName(), new Point(), new Dimension()));
}
for (LiveBean sourceBean : beansModel.getBeansDependingOn(targetBeanId)) {
graphChildren.add(createEdge(sourceBean.getId() + " " + targetBeanId, sourceBean.getId(), targetBeanId));
}
}
return graph;
}
private static SNode createBean(String id, String labelText, Point location, Dimension size) {
SNode node = new SNode();
node.setId(id);
node.setType("node:bean");
node.setLayout("vbox");
node.setPosition(new Point(Math.random() * 1024, Math.random() * 768));
node.setSize(new Dimension(80, 80));
node.setChildren(new ArrayList<>());
SCompartment compartment = new SCompartment();
compartment.setId(id + "-comp");
compartment.setType("compartment");
compartment.setLayout("hbox");
compartment.setChildren(new ArrayList<>());
SLabel label = new SLabel();
label.setId(id + "-lanbel");
label.setType("node:label");
label.setText(labelText);
compartment.getChildren().add(label);
node.getChildren().add(compartment);
return node;
}
private static SEdge createEdge(String id, String sourceId, String targetId) {
SEdge edge = new SEdge();
edge.setId(id);
edge.setType("edge:straight");
edge.setSourceId(sourceId);
edge.setTargetId(targetId);
return edge;
}
}

View File

@@ -0,0 +1,94 @@
package org.springframework.ide.vscode.boot.app.diagram;
import java.util.ArrayList;
import org.eclipse.sprotty.Dimension;
import org.eclipse.sprotty.IDiagramServer;
import org.eclipse.sprotty.Point;
import org.eclipse.sprotty.SCompartment;
import org.eclipse.sprotty.SEdge;
import org.eclipse.sprotty.SLabel;
import org.eclipse.sprotty.SModelRoot;
import org.eclipse.sprotty.SNode;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.stereotype.Component;
import net.bytebuddy.utility.RandomString;
@Component
public class MockDiagramServerModel implements InitializingBean {
@Autowired
IDiagramServer diagramServer;
@Autowired
@Qualifier("taskScheduler")
private TaskScheduler taskScheduler;
@Override
public void afterPropertiesSet() throws Exception {
// taskScheduler.scheduleAtFixedRate(() -> diagramServer.setModel(generateModel()), Duration.ofSeconds(5));
diagramServer.setModel(generateModel(10));
}
public static SModelRoot generateModel(int nodesNum) {
SModelRoot graph = new SModelRoot();
graph.setId("graph");
graph.setType("graph");
SNode node0 = createBean("node0", "main", new Point(100, 100), new Dimension(120, 40));
graph.setChildren(new ArrayList<>());
graph.getChildren().add(node0);
for (int i = 1; i < nodesNum; i++) {
SNode node = createBean("node" + i, RandomString.make(((int) Math.round(Math.random()* 10 + 1))), new Point(Math.random() * 1024, Math.random() * 768), new Dimension(120, 40));
SEdge edge = createEdge("edge-" + i, node0.getId(), node.getId());
graph.getChildren().add(edge);
graph.getChildren().add(node);
}
return graph;
}
private static SNode createBean(String id, String labelText, Point location, Dimension size) {
SNode node = new SNode();
node.setId(id);
node.setType("node:bean");
node.setLayout("vbox");
node.setPosition(new Point(Math.random() * 1024, Math.random() * 768));
node.setSize(new Dimension(80, 80));
node.setChildren(new ArrayList<>());
SCompartment compartment = new SCompartment();
compartment.setId(id + "-comp");
compartment.setType("compartment");
compartment.setLayout("hbox");
compartment.setChildren(new ArrayList<>());
SLabel label = new SLabel();
label.setId(id + "-lanbel");
label.setType("node:label");
label.setText(labelText);
compartment.getChildren().add(label);
node.getChildren().add(compartment);
return node;
}
private static SEdge createEdge(String id, String sourceId, String targetId) {
SEdge edge = new SEdge();
edge.setId(id);
edge.setType("edge:straight");
edge.setSourceId(sourceId);
edge.setTargetId(targetId);
return edge;
}
}

View File

@@ -0,0 +1,35 @@
package org.springframework.ide.vscode.boot.app.diagram;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.sprotty.IDiagramServer;
import org.eclipse.sprotty.IPopupModelFactory;
import org.eclipse.sprotty.RequestPopupModelAction;
import org.eclipse.sprotty.SGraph;
import org.eclipse.sprotty.SLabel;
import org.eclipse.sprotty.SModelElement;
import org.eclipse.sprotty.SModelRoot;
import org.springframework.stereotype.Component;
@Component
public class PopupModelFactory implements IPopupModelFactory {
@Override
public SModelRoot createPopupModel(SModelElement element, RequestPopupModelAction request, IDiagramServer server) {
SGraph graph = new SGraph();
graph.setId("popup");
List<SModelElement> children = new ArrayList<>();
SLabel label = new SLabel();
label.setType("node:label");
label.setText("I'm a tooltip!");
children.add(label);
graph.setChildren(children);
return graph;
}
}

View File

@@ -8,4 +8,4 @@ spring.main.banner-mode: off
#logging.level.org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer=debug
#logging.level.org.springframework.ide.vscode.boot.java.utils.SpringIndexerJava=debug
#logging.level.org.springframework.ide.vscode.boot.app.SpringSymbolIndex=debug
#logging.level.org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp=off
logging.level.org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp=off

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,46 @@
/********************************************************************************
* Copyright (c) 2017-2018 TypeFox 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
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
.sprotty-node {
fill: #aae;
stroke: #66b;
stroke-width: 3;
}
.sprotty-text {
font-size: 16pt;
text-anchor: middle;
}
.sprotty-edge {
fill: none;
stroke: #488;
stroke-width: 2;
}
.sprotty-node.selected {
stroke: #dd8;
stroke-width: 6;
}
.sprotty-missing {
stroke-width: 1;
stroke: #f00;
fill: #f00;
font-family: SansSerif;
font-size: 14pt;
text-anchor: middle;
}

View File

@@ -0,0 +1,38 @@
/********************************************************************************
* Copyright (c) 2017-2018 TypeFox 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
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
.copyright {
margin-top: 10px;
text-align: right;
font-size: 10px;
color: #888;
}
.help {
margin-top: 24px;
text-align: right;
font-size: 16px;
color: #888;
}
svg {
margin-top: 15px;
width: 100%;
height: 500px;
border-style: solid;
border-width: 1px;
border-color: #bbb;
}

View File

@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>sprotty Circles Example</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css">
<link rel="stylesheet" href="css/page.css">
<!-- support Microsoft browsers -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dom4/3.0.0/dom4.js">
</head>
<body>
<div class="container">
<div class="row" id="sprotty-app" data-app="circlegraph">
<div class="col-md-10">
<h1>sprotty Circles Example</h1>
<p>
<button id="refresh">Refresh</button>
<button id="scrambleNodes">Scramble nodes</button>
</p>
</div>
<div class="help col-md-2">
<a href='https://github.com/theia-ide/sprotty/wiki/Using-sprotty'>Help</a>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div id="spring-boot" class="sprotty"/>
</div>
<div class="copyright">
&copy; 2017 <a href="http://typefox.io">TypeFox GmbH</a>.
</div>
</div>
</div>
</div>
<script src="bundle.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
</body>
</html>

View File

@@ -0,0 +1,54 @@
package org.springframework.ide.vscode.boot.test;
import static org.junit.Assert.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.elk.alg.layered.options.LayeredMetaDataProvider;
import org.eclipse.elk.core.RecursiveGraphLayoutEngine;
import org.eclipse.elk.core.data.LayoutMetaDataService;
import org.eclipse.elk.core.util.BasicProgressMonitor;
import org.eclipse.elk.graph.ElkNode;
import org.eclipse.sprotty.Point;
import org.eclipse.sprotty.SModelRoot;
import org.junit.Test;
import org.springframework.ide.vscode.boot.app.diagram.MockDiagramServerModel;
import org.springframework.ide.vscode.commons.sprotty.elk.ElkUtils;
public class LayoutTest {
@Test
public void testCreateGraph() throws Exception {
SModelRoot modelRoot = MockDiagramServerModel.generateModel(10);
ElkNode graph = new ElkUtils(modelRoot).graph;
assertNotNull(graph);
assertEquals(10, graph.getChildren().size());
assertEquals(9, graph.getContainedEdges().size());
}
@Test
public void testLayout() throws Exception {
LayoutMetaDataService.getInstance().registerLayoutMetaDataProviders(new LayeredMetaDataProvider());
SModelRoot modelRoot = MockDiagramServerModel.generateModel(10);
ElkNode graph = new ElkUtils(modelRoot).graph;
Map<String, Point> locations = new HashMap<>();
for (ElkNode child : graph.getChildren()) {
locations.put(child.getIdentifier(), new Point(child.getX(), child.getY()));
}
new RecursiveGraphLayoutEngine().layout(graph, new BasicProgressMonitor());
for (ElkNode child : graph.getChildren()) {
assertNotEquals(locations.get(child.getIdentifier()), new Point(child.getX(), child.getY()));
}
}
}