Updated Java Version Example with Connections
Last modified by Niklas Rentz on 2023/09/12 13:04
Here you'll find an updated version of the minimal examle:
Use the same procedure to get it to run as described on the parent page.
The example runs with the following current (4.12.2014) version of KLighD: 0.6.0 (Pragmatics v. 2014-10)
The visualized diagrams look like this:
Note that this diagram synthesis requests a specific layout algorithm for each case:
- de.cau.cs.kieler.graphviz.dot for splines, and
- de.cau.cs.kieler.klay.layered for NO splines).
The Files
MyData.java
public class MyData {
public String name;
public List<MyData> subData = new LinkedList<MyData>();
public static MyData getTestData() {
MyData d1 = new MyData();
d1.name = "all data";
MyData d2 = new MyData();
d2.name = "outer data";
MyData d3 = new MyData();
d3.name = "inner 1";
MyData d4 = new MyData();
d4.name = "inner 2";
MyData d5 = new MyData();
d5.name = "inner 5";
MyData d6 = new MyData();
d6.name = "most inner 6";
d1.subData.add(d2);
d2.subData.add(d3);
d2.subData.add(d4);
d2.subData.add(d5);
d4.subData.add(d6);
return d1;
}
}
public String name;
public List<MyData> subData = new LinkedList<MyData>();
public static MyData getTestData() {
MyData d1 = new MyData();
d1.name = "all data";
MyData d2 = new MyData();
d2.name = "outer data";
MyData d3 = new MyData();
d3.name = "inner 1";
MyData d4 = new MyData();
d4.name = "inner 2";
MyData d5 = new MyData();
d5.name = "inner 5";
MyData d6 = new MyData();
d6.name = "most inner 6";
d1.subData.add(d2);
d2.subData.add(d3);
d2.subData.add(d4);
d2.subData.add(d5);
d4.subData.add(d6);
return d1;
}
}
MyDataDiagrammSynthesisHandler.java
/*
* KIELER - Kiel Integrated Environment for Layout Eclipse RichClient
*
* http://www.informatik.uni-kiel.de/rtsys/kieler/
*
* Copyright 2013 by
* + Christian-Albrechts-University of Kiel
* + Department of Computer Science
* + Real-Time and Embedded Systems Group
*
* This code is provided under the terms of the Eclipse Public License (EPL).
* See the file epl-v10.html for the license text.
*/
package de.cau.cs.kieler.klighd.example.mydata;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.ui.internal.handlers.WorkbenchWindowHandlerDelegate;
import de.cau.cs.kieler.klighd.kivi.effects.KlighdUpdateDiagramEffect;
import de.cau.cs.kieler.klighd.krendering.SimpleUpdateStrategy;
import de.cau.cs.kieler.klighd.util.KlighdSynthesisProperties;
/**
* This is a small mininal example action that triggers a diagram synthesis of a
* static data example. In this example the actions is connected to a tool bar
* button and can be invoked by clicking on the button.
*
* UPDATED VERSION 4. DECEMBER 2014
*
* @author Christian Motika
*/
public class MyDataDiagrammSynthesisHandler extends
WorkbenchWindowHandlerDelegate { //IWorkbenchWindowActionDelegate {
public Object execute(ExecutionEvent event) throws ExecutionException {
// Get some static dummy test data
MyData data = MyData.getTestData();
// Prepare the diagram synthesis: Create a new klighdUpdateDiagramEffect
KlighdUpdateDiagramEffect klighdEffect = new KlighdUpdateDiagramEffect(
"de.cau.cs.kieler.klighd.example.mydata.MyDataDiagramSynthesis",
"KLighD MyData Diagram", data);
// FIXME: Note that the incremental update currently is under re-development
// for Eclipse Kepler after large API changes to EMF Compare had been made.
// At this time we cannot use the incremental update strategy (commented out).
// As soon as it is fixed, uncomment the line below and delete the
// SimpleUpdateStrategy line:
// klighdEffect.setProperty(LightDiagramServices.REQUESTED_UPDATE_STRATEGY,
// UpdateStrategy.ID);
klighdEffect.setProperty(
KlighdSynthesisProperties.REQUESTED_UPDATE_STRATEGY,
SimpleUpdateStrategy.ID);
// Do the diagram synthesis
klighdEffect.execute();
return null;
}
}
* KIELER - Kiel Integrated Environment for Layout Eclipse RichClient
*
* http://www.informatik.uni-kiel.de/rtsys/kieler/
*
* Copyright 2013 by
* + Christian-Albrechts-University of Kiel
* + Department of Computer Science
* + Real-Time and Embedded Systems Group
*
* This code is provided under the terms of the Eclipse Public License (EPL).
* See the file epl-v10.html for the license text.
*/
package de.cau.cs.kieler.klighd.example.mydata;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.ui.internal.handlers.WorkbenchWindowHandlerDelegate;
import de.cau.cs.kieler.klighd.kivi.effects.KlighdUpdateDiagramEffect;
import de.cau.cs.kieler.klighd.krendering.SimpleUpdateStrategy;
import de.cau.cs.kieler.klighd.util.KlighdSynthesisProperties;
/**
* This is a small mininal example action that triggers a diagram synthesis of a
* static data example. In this example the actions is connected to a tool bar
* button and can be invoked by clicking on the button.
*
* UPDATED VERSION 4. DECEMBER 2014
*
* @author Christian Motika
*/
public class MyDataDiagrammSynthesisHandler extends
WorkbenchWindowHandlerDelegate { //IWorkbenchWindowActionDelegate {
public Object execute(ExecutionEvent event) throws ExecutionException {
// Get some static dummy test data
MyData data = MyData.getTestData();
// Prepare the diagram synthesis: Create a new klighdUpdateDiagramEffect
KlighdUpdateDiagramEffect klighdEffect = new KlighdUpdateDiagramEffect(
"de.cau.cs.kieler.klighd.example.mydata.MyDataDiagramSynthesis",
"KLighD MyData Diagram", data);
// FIXME: Note that the incremental update currently is under re-development
// for Eclipse Kepler after large API changes to EMF Compare had been made.
// At this time we cannot use the incremental update strategy (commented out).
// As soon as it is fixed, uncomment the line below and delete the
// SimpleUpdateStrategy line:
// klighdEffect.setProperty(LightDiagramServices.REQUESTED_UPDATE_STRATEGY,
// UpdateStrategy.ID);
klighdEffect.setProperty(
KlighdSynthesisProperties.REQUESTED_UPDATE_STRATEGY,
SimpleUpdateStrategy.ID);
// Do the diagram synthesis
klighdEffect.execute();
return null;
}
}
MyDataDiagramSynthesis.xtend
/*
* KIELER - Kiel Integrated Environment for Layout Eclipse RichClient
*
* http://www.informatik.uni-kiel.de/rtsys/kieler/
*
* Copyright 2013 by
* + Christian-Albrechts-University of Kiel
* + Department of Computer Science
* + Real-Time and Embedded Systems Group
*
* This code is provided under the terms of the Eclipse Public License (EPL).
* See the file epl-v10.html for the license text.
*/
package de.cau.cs.kieler.klighd.example.mydata
import com.google.common.collect.ImmutableList
import de.cau.cs.kieler.core.kgraph.KNode
import de.cau.cs.kieler.core.krendering.KColor
import de.cau.cs.kieler.core.krendering.KPolyline
import de.cau.cs.kieler.core.krendering.extensions.KColorExtensions
import de.cau.cs.kieler.core.krendering.extensions.KContainerRenderingExtensions
import de.cau.cs.kieler.core.krendering.extensions.KEdgeExtensions
import de.cau.cs.kieler.core.krendering.extensions.KNodeExtensions
import de.cau.cs.kieler.core.krendering.extensions.KPolylineExtensions
import de.cau.cs.kieler.core.krendering.extensions.KRenderingExtensions
import de.cau.cs.kieler.kiml.options.Direction
import de.cau.cs.kieler.kiml.options.EdgeRouting
import de.cau.cs.kieler.kiml.options.LayoutOptions
import de.cau.cs.kieler.klighd.SynthesisOption
import de.cau.cs.kieler.klighd.syntheses.AbstractDiagramSynthesis
import javax.inject.Inject
import static extension org.eclipse.emf.ecore.util.EcoreUtil.*
//import static de.cau.cs.kieler.klighd.example.mydata.MyDataDiagramSynthesis.*
/**
* This is a small mininal example that creates a hierarchical KLighD diagram
* out of very simple hierarchical data structure (MyData).
*
* @author Christian Motika
*/
class MyDataDiagramSynthesis extends AbstractDiagramSynthesis<MyData> {
@Inject extension KNodeExtensions
@Inject extension KEdgeExtensions
// @Inject extension KPortExtensions
// @Inject extension KLabelExtensions
@Inject extension KRenderingExtensions
@Inject extension KContainerRenderingExtensions
@Inject extension KPolylineExtensions
@Inject extension KColorExtensions
// Some self-defined colors
private static val KColor BLUE1 = RENDERING_FACTORY.createKColor() =>
[it.red = 248; it.green = 249; it.blue = 253];
private static val KColor BLUE2 = RENDERING_FACTORY.createKColor() =>
[it.red = 205; it.green = 220; it.blue = 243];
// Additional transformation option to hide or show a shadow
private static val SynthesisOption SHOW_SHADOW = SynthesisOption::createCheckOption("Shadow", true);
private static SynthesisOption EDGE_STYLE = SynthesisOption::createCheckOption("Spline", false);
// Add all transformation options (comma separated)
override getDisplayedSynthesisOptions() {
return ImmutableList::of(
SHOW_SHADOW,
EDGE_STYLE
);
}
override KNode transform(MyData data) {
val rootNode = data.createNode()
if (EDGE_STYLE.booleanValue) {
rootNode.setLayoutOption(LayoutOptions::SPACING, Float::valueOf("25.0"));
rootNode.setLayoutOption(LayoutOptions::DIRECTION, Direction::DOWN);
rootNode.addLayoutParam(LayoutOptions::EDGE_ROUTING, EdgeRouting::SPLINES);
rootNode.addLayoutParam(LayoutOptions::ALGORITHM, "de.cau.cs.kieler.graphviz.dot");
} else {
rootNode.setLayoutOption(LayoutOptions::SPACING, Float::valueOf("15.0"));
rootNode.setLayoutOption(LayoutOptions::DIRECTION, Direction::DOWN);
rootNode.addLayoutParam(LayoutOptions::EDGE_ROUTING, EdgeRouting::ORTHOGONAL);
rootNode.addLayoutParam(LayoutOptions::ALGORITHM, "de.cau.cs.kieler.klay.layered");
}
buildNodes(data, rootNode)
buildConnections(data, rootNode)
return rootNode;
}
def void buildNodes(MyData parentData, KNode rootNode) {
val root = parentData.createNode()
root.putToLookUpWith(parentData) => [
// Optional Layout parameters can be set
//it.addLayoutParam(LayoutOptions::ALGORITHM, "de.cau.cs.kieler.kiml.ogdf.planarization");
//it.addLayoutParam(LayoutOptions::SPACING, 75f);
//it.addLayoutParam(LayoutOptions::DIRECTION, Direction::UP);
// A rounded rectangle is created for every MyData instance
it.addRoundedRectangle(5, 5) => [
// Set linewith, foreground color, and a fading background color
it.lineWidth = 1;
it.setForeground("darkGray".color)
// We need a fresh copy of each color item, because it is contained by its element
it.setBackgroundGradient(BLUE1.copy, BLUE2.copy, 90)
// Here we see a how to use a boolean transformation/diagram option
if (SHOW_SHADOW.booleanValue) {
it.shadow = "black".color;
}
// Set a text
it.addText(" " + parentData.name + " ") => [
it.setFontSize(9)
it.setForeground("black".color)
]
// If this is a hierarchical MyData instance, then create horizontal splitter,
// a child area, and add its children
if (parentData.subData.length > 0) {
it.setGridPlacement(1);
it.addHorizontalSeperatorLine(1, 2).setForeground("darkGray".color)
it.addChildArea().setGridPlacementData() => [
from(LEFT, 3, 0, TOP, 3, 0).to(RIGHT, 3, 0, BOTTOM, 3, 0)
minCellHeight = 5;
minCellWidth = 5;
];
for (subData : parentData.subData) {
// To the recursive call to transform for all children
val child = subData.transform
// It is important to add all children to the root!
root.children.add(child)
}
}
]
]
}
// Connect every first child to all childs (including itself!)
def void buildConnections(MyData parentData, KNode rootNode) {
// only if there is any child there
if (parentData.subData.size > 0) {
val firstChild = parentData.subData.get(0);
for (child : parentData.subData) {
// BE CAREFUL IF YOU CREATE A MAPPING LIKE THIS THERE WILL JUST BE CREATED ONE
// EDGE PER "firstchild". That's why better use the child, because we iterate
// over different child-objects (targets) but always habe the same firstChild-
// -object.
//val kEdge = firstChild.createEdge() // THIS IS MOST LIKELY WRONG
val kEdge = child.createEdge()
kEdge.source = firstChild.node;
kEdge.target = child.node;
System::out.println("Connect " + firstChild.name + " to " + child.name);
if (!EDGE_STYLE.booleanValue) {
kEdge.addPolyline(2)
} else {
kEdge.addSpline(2);
}
kEdge.KRendering as KPolyline => [
it.addArrowDecorator()
it.setLineWidth(2)
];
child.buildConnections(rootNode);
}
}
}
}
* KIELER - Kiel Integrated Environment for Layout Eclipse RichClient
*
* http://www.informatik.uni-kiel.de/rtsys/kieler/
*
* Copyright 2013 by
* + Christian-Albrechts-University of Kiel
* + Department of Computer Science
* + Real-Time and Embedded Systems Group
*
* This code is provided under the terms of the Eclipse Public License (EPL).
* See the file epl-v10.html for the license text.
*/
package de.cau.cs.kieler.klighd.example.mydata
import com.google.common.collect.ImmutableList
import de.cau.cs.kieler.core.kgraph.KNode
import de.cau.cs.kieler.core.krendering.KColor
import de.cau.cs.kieler.core.krendering.KPolyline
import de.cau.cs.kieler.core.krendering.extensions.KColorExtensions
import de.cau.cs.kieler.core.krendering.extensions.KContainerRenderingExtensions
import de.cau.cs.kieler.core.krendering.extensions.KEdgeExtensions
import de.cau.cs.kieler.core.krendering.extensions.KNodeExtensions
import de.cau.cs.kieler.core.krendering.extensions.KPolylineExtensions
import de.cau.cs.kieler.core.krendering.extensions.KRenderingExtensions
import de.cau.cs.kieler.kiml.options.Direction
import de.cau.cs.kieler.kiml.options.EdgeRouting
import de.cau.cs.kieler.kiml.options.LayoutOptions
import de.cau.cs.kieler.klighd.SynthesisOption
import de.cau.cs.kieler.klighd.syntheses.AbstractDiagramSynthesis
import javax.inject.Inject
import static extension org.eclipse.emf.ecore.util.EcoreUtil.*
//import static de.cau.cs.kieler.klighd.example.mydata.MyDataDiagramSynthesis.*
/**
* This is a small mininal example that creates a hierarchical KLighD diagram
* out of very simple hierarchical data structure (MyData).
*
* @author Christian Motika
*/
class MyDataDiagramSynthesis extends AbstractDiagramSynthesis<MyData> {
@Inject extension KNodeExtensions
@Inject extension KEdgeExtensions
// @Inject extension KPortExtensions
// @Inject extension KLabelExtensions
@Inject extension KRenderingExtensions
@Inject extension KContainerRenderingExtensions
@Inject extension KPolylineExtensions
@Inject extension KColorExtensions
// Some self-defined colors
private static val KColor BLUE1 = RENDERING_FACTORY.createKColor() =>
[it.red = 248; it.green = 249; it.blue = 253];
private static val KColor BLUE2 = RENDERING_FACTORY.createKColor() =>
[it.red = 205; it.green = 220; it.blue = 243];
// Additional transformation option to hide or show a shadow
private static val SynthesisOption SHOW_SHADOW = SynthesisOption::createCheckOption("Shadow", true);
private static SynthesisOption EDGE_STYLE = SynthesisOption::createCheckOption("Spline", false);
// Add all transformation options (comma separated)
override getDisplayedSynthesisOptions() {
return ImmutableList::of(
SHOW_SHADOW,
EDGE_STYLE
);
}
override KNode transform(MyData data) {
val rootNode = data.createNode()
if (EDGE_STYLE.booleanValue) {
rootNode.setLayoutOption(LayoutOptions::SPACING, Float::valueOf("25.0"));
rootNode.setLayoutOption(LayoutOptions::DIRECTION, Direction::DOWN);
rootNode.addLayoutParam(LayoutOptions::EDGE_ROUTING, EdgeRouting::SPLINES);
rootNode.addLayoutParam(LayoutOptions::ALGORITHM, "de.cau.cs.kieler.graphviz.dot");
} else {
rootNode.setLayoutOption(LayoutOptions::SPACING, Float::valueOf("15.0"));
rootNode.setLayoutOption(LayoutOptions::DIRECTION, Direction::DOWN);
rootNode.addLayoutParam(LayoutOptions::EDGE_ROUTING, EdgeRouting::ORTHOGONAL);
rootNode.addLayoutParam(LayoutOptions::ALGORITHM, "de.cau.cs.kieler.klay.layered");
}
buildNodes(data, rootNode)
buildConnections(data, rootNode)
return rootNode;
}
def void buildNodes(MyData parentData, KNode rootNode) {
val root = parentData.createNode()
root.putToLookUpWith(parentData) => [
// Optional Layout parameters can be set
//it.addLayoutParam(LayoutOptions::ALGORITHM, "de.cau.cs.kieler.kiml.ogdf.planarization");
//it.addLayoutParam(LayoutOptions::SPACING, 75f);
//it.addLayoutParam(LayoutOptions::DIRECTION, Direction::UP);
// A rounded rectangle is created for every MyData instance
it.addRoundedRectangle(5, 5) => [
// Set linewith, foreground color, and a fading background color
it.lineWidth = 1;
it.setForeground("darkGray".color)
// We need a fresh copy of each color item, because it is contained by its element
it.setBackgroundGradient(BLUE1.copy, BLUE2.copy, 90)
// Here we see a how to use a boolean transformation/diagram option
if (SHOW_SHADOW.booleanValue) {
it.shadow = "black".color;
}
// Set a text
it.addText(" " + parentData.name + " ") => [
it.setFontSize(9)
it.setForeground("black".color)
]
// If this is a hierarchical MyData instance, then create horizontal splitter,
// a child area, and add its children
if (parentData.subData.length > 0) {
it.setGridPlacement(1);
it.addHorizontalSeperatorLine(1, 2).setForeground("darkGray".color)
it.addChildArea().setGridPlacementData() => [
from(LEFT, 3, 0, TOP, 3, 0).to(RIGHT, 3, 0, BOTTOM, 3, 0)
minCellHeight = 5;
minCellWidth = 5;
];
for (subData : parentData.subData) {
// To the recursive call to transform for all children
val child = subData.transform
// It is important to add all children to the root!
root.children.add(child)
}
}
]
]
}
// Connect every first child to all childs (including itself!)
def void buildConnections(MyData parentData, KNode rootNode) {
// only if there is any child there
if (parentData.subData.size > 0) {
val firstChild = parentData.subData.get(0);
for (child : parentData.subData) {
// BE CAREFUL IF YOU CREATE A MAPPING LIKE THIS THERE WILL JUST BE CREATED ONE
// EDGE PER "firstchild". That's why better use the child, because we iterate
// over different child-objects (targets) but always habe the same firstChild-
// -object.
//val kEdge = firstChild.createEdge() // THIS IS MOST LIKELY WRONG
val kEdge = child.createEdge()
kEdge.source = firstChild.node;
kEdge.target = child.node;
System::out.println("Connect " + firstChild.name + " to " + child.name);
if (!EDGE_STYLE.booleanValue) {
kEdge.addPolyline(2)
} else {
kEdge.addSpline(2);
}
kEdge.KRendering as KPolyline => [
it.addArrowDecorator()
it.setLineWidth(2)
];
child.buildConnections(rootNode);
}
}
}
}