diff --git a/demo/java/.classpath b/demo/java/.classpath
new file mode 100644
index 0000000..bf77d41
--- /dev/null
+++ b/demo/java/.classpath
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/demo/java/.gitignore b/demo/java/.gitignore
new file mode 100644
index 0000000..dc7bdeb
--- /dev/null
+++ b/demo/java/.gitignore
@@ -0,0 +1,23 @@
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
\ No newline at end of file
diff --git a/demo/java/.project b/demo/java/.project
new file mode 100644
index 0000000..f1e2727
--- /dev/null
+++ b/demo/java/.project
@@ -0,0 +1,17 @@
+
+
+ java
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
\ No newline at end of file
diff --git a/demo/java/.settings/org.eclipse.jdt.core.prefs b/demo/java/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..15cf1bb
--- /dev/null
+++ b/demo/java/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,11 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=10
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=11
diff --git a/demo/java/.vscode/settings.json b/demo/java/.vscode/settings.json
new file mode 100644
index 0000000..f608fee
--- /dev/null
+++ b/demo/java/.vscode/settings.json
@@ -0,0 +1,4 @@
+{
+ "java.dependency.packagePresentation": "flat",
+ "java.dependency.syncWithFolderExplorer": true
+}
\ No newline at end of file
diff --git a/demo/java/src/app/App.java b/demo/java/src/app/App.java
new file mode 100644
index 0000000..8f4ec20
--- /dev/null
+++ b/demo/java/src/app/App.java
@@ -0,0 +1,58 @@
+package app;
+
+import app.GraphData.EdgeInfo;
+
+public class App {
+
+ public static void main(String[] args) {
+ // watch `list.visualize()`
+ LinkedList list = new LinkedList();
+ list.append("1");
+ list.append("2");
+ list.append("3");
+ list.append("4");
+ }
+}
+
+class LinkedList {
+ private Node head;
+
+ public void append(T value) {
+ if (head == null) {
+ head = new Node(value);
+ } else {
+ Node m = head;
+ while (m.next != null) {
+ m = m.next;
+ }
+ m.next = new Node(value);
+ }
+ }
+
+ public String visualize() {
+ Node list = new Node(null);
+ list.next = this.head;
+
+ return GraphData.from(list, (item, info) -> {
+ if (item != list) {
+ info.id = item.value.toString();
+ } else {
+ info.label = "list";
+ }
+ if (item.next != null) {
+ EdgeInfo> ei = info.addEdge(item.next);
+ ei.label = "next";
+ }
+ return info;
+ }).toString();
+ }
+
+ static class Node {
+ public T value;
+ public Node next;
+
+ public Node(T value) {
+ this.value = value;
+ }
+ }
+}
diff --git a/demo/java/src/app/ExtractedData.java b/demo/java/src/app/ExtractedData.java
new file mode 100644
index 0000000..6d58d3b
--- /dev/null
+++ b/demo/java/src/app/ExtractedData.java
@@ -0,0 +1,31 @@
+package app;
+
+import java.util.HashMap;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public abstract class ExtractedData {
+ public HashMap getKind() {
+ HashMap m = new HashMap<>();
+ for (String s : getTags()) {
+ m.put(s, true);
+ }
+ return m;
+ }
+
+ @JsonIgnore
+ protected abstract String[] getTags();
+
+ @Override
+ public String toString() {
+ ObjectMapper objectMapper = new ObjectMapper();
+ try {
+ String val = objectMapper.writeValueAsString(this);
+ return val;
+ } catch (JsonProcessingException e) {
+ return "JsonProcessingException";
+ }
+ }
+}
diff --git a/demo/java/src/app/GraphData.java b/demo/java/src/app/GraphData.java
new file mode 100644
index 0000000..63dc004
--- /dev/null
+++ b/demo/java/src/app/GraphData.java
@@ -0,0 +1,217 @@
+package app;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+
+public class GraphData extends ExtractedData {
+ private List nodes = new ArrayList<>();
+ private List edges = new ArrayList<>();
+
+ @Override
+ protected String[] getTags() {
+ return new String[] { "graph" };
+ }
+
+ public List getNodes() {
+ return this.nodes;
+ }
+
+ public List getEdges() {
+ return this.edges;
+ }
+
+ @JsonInclude(Include.NON_NULL)
+ public static class NodeData {
+ private String id;
+ private String label; // can be null
+ private String color; // can be null
+ private String shape; // can be null
+
+ public NodeData(String id) {
+ this.id = id;
+ }
+
+ public String getId() {
+ return this.id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getLabel() {
+ return this.label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public String getColor() {
+ return this.color;
+ }
+
+ public void setColor(String color) {
+ this.color = color;
+ }
+
+ public String getShape() {
+ return this.shape;
+ }
+
+ public void setShape(String shape) {
+ this.shape = shape;
+ }
+ }
+
+ @JsonInclude(Include.NON_NULL)
+ public static class EdgeData {
+ private String from;
+ private String to;
+ private String label; // Can be null
+ private String id; // Can be null
+ private String color; // Can be null
+ private Boolean dashes; // Can be null
+
+ public EdgeData(String from, String to) {
+ this.from = from;
+ this.to = to;
+ }
+
+ public String getFrom() {
+ return this.from;
+ }
+
+ public void setFrom(String from) {
+ this.from = from;
+ }
+
+ public String getTo() {
+ return this.to;
+ }
+
+ public void setTo(String to) {
+ this.to = to;
+ }
+
+ public String getLabel() {
+ return this.label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public String getId() {
+ return this.id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getColor() {
+ return this.color;
+ }
+
+ public void setColor(String color) {
+ this.color = color;
+ }
+
+ public Boolean getDashes() {
+ return this.dashes;
+ }
+
+ public void setDashes(Boolean dashes) {
+ this.dashes = dashes;
+ }
+ }
+
+ public interface NodeInfoProvider {
+ public NodeInfo getNodeInfo(T item, NodeInfo nodeInfo);
+ }
+
+ public static GraphData from(T item, NodeInfoProvider f) {
+ GraphData d = new GraphData();
+ ArrayDeque q = new ArrayDeque<>();
+ q.add(item);
+
+ FromState s = new FromState<>(f);
+
+ while (q.size() > 0) {
+ NodeInfo nodeInfo = s.getNodeInfo(q.removeFirst());
+ NodeData nd = new NodeData(s.getId(nodeInfo));
+ d.nodes.add(nd);
+
+ nd.setLabel(nodeInfo.label);
+ nd.setColor(nodeInfo.color);
+
+ for (EdgeInfo e : nodeInfo.edges) {
+ EdgeData ed = new EdgeData(nd.id, s.getId(s.getNodeInfo(e.to)));
+ d.edges.add(ed);
+ ed.setLabel(e.label);
+ ed.setId(e.id);
+
+ q.add(e.to);
+ }
+
+ }
+
+ return d;
+ }
+
+ static class FromState {
+ private int i = 0;
+ private NodeInfoProvider provider;
+ private HashMap> infos = new HashMap<>();
+
+ public FromState(NodeInfoProvider provider) {
+ this.provider = provider;
+ }
+
+ public String getId(NodeInfo nodeInfo) {
+ if (nodeInfo.id == null) {
+ nodeInfo.id = "hediet.de/" + (i++);
+ }
+ return nodeInfo.id;
+ }
+
+ public NodeInfo getNodeInfo(T item) {
+ if (infos.containsKey(item)) {
+ return infos.get(item);
+ }
+
+ NodeInfo info = this.provider.getNodeInfo(item, new NodeInfo<>());
+ infos.put(item, info);
+ return info;
+ }
+ }
+
+ public static class NodeInfo {
+ public ArrayList> edges = new ArrayList>();
+ public String label; // Can be null.
+ public String id; // Can be null.
+ public String color; // Can be null.
+
+ public EdgeInfo addEdge(T to) {
+ EdgeInfo i = new EdgeInfo<>(to);
+ this.edges.add(i);
+ return i;
+ }
+ }
+
+ public static class EdgeInfo {
+ public T to;
+ public String label; // Can be null.
+ public String id; // Can be null.
+
+ public EdgeInfo(T to) {
+ this.to = to;
+ }
+ }
+}
\ No newline at end of file
diff --git a/demo/java/src/app/TextData.java b/demo/java/src/app/TextData.java
new file mode 100644
index 0000000..ba7f11f
--- /dev/null
+++ b/demo/java/src/app/TextData.java
@@ -0,0 +1,18 @@
+package app;
+
+public class TextData extends ExtractedData {
+ private final String textValue;
+
+ public TextData(String text) {
+ this.textValue = text;
+ }
+
+ @Override
+ protected String[] getTags() {
+ return new String[] { "text" };
+ }
+
+ public String getText() {
+ return this.textValue;
+ }
+}
\ No newline at end of file