Wiki source code of Updated Java Version Example with Connections
Last modified by Niklas Rentz on 2023/09/12 13:04
Show last authors
| author | version | line-number | content |
|---|---|---|---|
| 1 | Here you'll find an updated version of the minimal examle: | ||
| 2 | |||
| 3 | [[attach:de.cau.cs.kieler.klighd.example.mydata.zip]] | ||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | Use the same procedure to get it to run as described on the [[parent page>>doc:KIELER.Discontinued Projects.KLighD old Minimal Example HowTo.Minimal Example (Java Model).WebHome]]. | ||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | The example runs with the following current (4.12.2014) version of KLighD: 0.6.0 ([[Pragmatics v. 2014-10>>url:http://rtsys.informatik.uni-kiel.de/%7Ekieler/updatesite/release_pragmatics_2014-10/||rel="nofollow" shape="rect" class="external-link"]]) | ||
| 12 | |||
| 13 | The visualized diagrams look like this: | ||
| 14 | |||
| 15 | [[image:attach:klighd-updated1.jpg]][[image:attach:klighd-updated2.jpg]] | ||
| 16 | |||
| 17 | Note that this diagram synthesis requests a specific layout algorithm for each case: | ||
| 18 | |||
| 19 | 1. de.cau.cs.kieler.graphviz.dot for splines, and | ||
| 20 | 1. de.cau.cs.kieler.klay.layered for NO splines). | ||
| 21 | |||
| 22 | == The Files == | ||
| 23 | |||
| 24 | {{code title="MyData.java" language="java"}} | ||
| 25 | public class MyData { | ||
| 26 | |||
| 27 | public String name; | ||
| 28 | public List<MyData> subData = new LinkedList<MyData>(); | ||
| 29 | |||
| 30 | public static MyData getTestData() { | ||
| 31 | MyData d1 = new MyData(); | ||
| 32 | d1.name = "all data"; | ||
| 33 | MyData d2 = new MyData(); | ||
| 34 | d2.name = "outer data"; | ||
| 35 | MyData d3 = new MyData(); | ||
| 36 | d3.name = "inner 1"; | ||
| 37 | MyData d4 = new MyData(); | ||
| 38 | d4.name = "inner 2"; | ||
| 39 | MyData d5 = new MyData(); | ||
| 40 | d5.name = "inner 5"; | ||
| 41 | MyData d6 = new MyData(); | ||
| 42 | d6.name = "most inner 6"; | ||
| 43 | d1.subData.add(d2); | ||
| 44 | d2.subData.add(d3); | ||
| 45 | d2.subData.add(d4); | ||
| 46 | d2.subData.add(d5); | ||
| 47 | d4.subData.add(d6); | ||
| 48 | return d1; | ||
| 49 | } | ||
| 50 | |||
| 51 | } | ||
| 52 | {{/code}} | ||
| 53 | |||
| 54 | {{code title="MyDataDiagrammSynthesisHandler.java" language="java"}} | ||
| 55 | /* | ||
| 56 | * KIELER - Kiel Integrated Environment for Layout Eclipse RichClient | ||
| 57 | * | ||
| 58 | * http://www.informatik.uni-kiel.de/rtsys/kieler/ | ||
| 59 | * | ||
| 60 | * Copyright 2013 by | ||
| 61 | * + Christian-Albrechts-University of Kiel | ||
| 62 | * + Department of Computer Science | ||
| 63 | * + Real-Time and Embedded Systems Group | ||
| 64 | * | ||
| 65 | * This code is provided under the terms of the Eclipse Public License (EPL). | ||
| 66 | * See the file epl-v10.html for the license text. | ||
| 67 | */ | ||
| 68 | package de.cau.cs.kieler.klighd.example.mydata; | ||
| 69 | |||
| 70 | import org.eclipse.core.commands.ExecutionEvent; | ||
| 71 | import org.eclipse.core.commands.ExecutionException; | ||
| 72 | import org.eclipse.ui.internal.handlers.WorkbenchWindowHandlerDelegate; | ||
| 73 | |||
| 74 | import de.cau.cs.kieler.klighd.kivi.effects.KlighdUpdateDiagramEffect; | ||
| 75 | import de.cau.cs.kieler.klighd.krendering.SimpleUpdateStrategy; | ||
| 76 | import de.cau.cs.kieler.klighd.util.KlighdSynthesisProperties; | ||
| 77 | |||
| 78 | /** | ||
| 79 | * This is a small mininal example action that triggers a diagram synthesis of a | ||
| 80 | * static data example. In this example the actions is connected to a tool bar | ||
| 81 | * button and can be invoked by clicking on the button. | ||
| 82 | * | ||
| 83 | * UPDATED VERSION 4. DECEMBER 2014 | ||
| 84 | * | ||
| 85 | * @author Christian Motika | ||
| 86 | */ | ||
| 87 | public class MyDataDiagrammSynthesisHandler extends | ||
| 88 | WorkbenchWindowHandlerDelegate { //IWorkbenchWindowActionDelegate { | ||
| 89 | |||
| 90 | |||
| 91 | public Object execute(ExecutionEvent event) throws ExecutionException { | ||
| 92 | // Get some static dummy test data | ||
| 93 | MyData data = MyData.getTestData(); | ||
| 94 | |||
| 95 | // Prepare the diagram synthesis: Create a new klighdUpdateDiagramEffect | ||
| 96 | KlighdUpdateDiagramEffect klighdEffect = new KlighdUpdateDiagramEffect( | ||
| 97 | "de.cau.cs.kieler.klighd.example.mydata.MyDataDiagramSynthesis", | ||
| 98 | "KLighD MyData Diagram", data); | ||
| 99 | |||
| 100 | // FIXME: Note that the incremental update currently is under re-development | ||
| 101 | // for Eclipse Kepler after large API changes to EMF Compare had been made. | ||
| 102 | // At this time we cannot use the incremental update strategy (commented out). | ||
| 103 | // As soon as it is fixed, uncomment the line below and delete the | ||
| 104 | // SimpleUpdateStrategy line: | ||
| 105 | |||
| 106 | // klighdEffect.setProperty(LightDiagramServices.REQUESTED_UPDATE_STRATEGY, | ||
| 107 | // UpdateStrategy.ID); | ||
| 108 | |||
| 109 | klighdEffect.setProperty( | ||
| 110 | KlighdSynthesisProperties.REQUESTED_UPDATE_STRATEGY, | ||
| 111 | SimpleUpdateStrategy.ID); | ||
| 112 | |||
| 113 | // Do the diagram synthesis | ||
| 114 | klighdEffect.execute(); | ||
| 115 | return null; | ||
| 116 | } | ||
| 117 | |||
| 118 | } | ||
| 119 | |||
| 120 | |||
| 121 | {{/code}} | ||
| 122 | |||
| 123 | {{code title="MyDataDiagramSynthesis.xtend"}} | ||
| 124 | /* | ||
| 125 | * KIELER - Kiel Integrated Environment for Layout Eclipse RichClient | ||
| 126 | * | ||
| 127 | * http://www.informatik.uni-kiel.de/rtsys/kieler/ | ||
| 128 | * | ||
| 129 | * Copyright 2013 by | ||
| 130 | * + Christian-Albrechts-University of Kiel | ||
| 131 | * + Department of Computer Science | ||
| 132 | * + Real-Time and Embedded Systems Group | ||
| 133 | * | ||
| 134 | * This code is provided under the terms of the Eclipse Public License (EPL). | ||
| 135 | * See the file epl-v10.html for the license text. | ||
| 136 | */ | ||
| 137 | package de.cau.cs.kieler.klighd.example.mydata | ||
| 138 | |||
| 139 | import com.google.common.collect.ImmutableList | ||
| 140 | import de.cau.cs.kieler.core.kgraph.KNode | ||
| 141 | import de.cau.cs.kieler.core.krendering.KColor | ||
| 142 | import de.cau.cs.kieler.core.krendering.KPolyline | ||
| 143 | import de.cau.cs.kieler.core.krendering.extensions.KColorExtensions | ||
| 144 | import de.cau.cs.kieler.core.krendering.extensions.KContainerRenderingExtensions | ||
| 145 | import de.cau.cs.kieler.core.krendering.extensions.KEdgeExtensions | ||
| 146 | import de.cau.cs.kieler.core.krendering.extensions.KNodeExtensions | ||
| 147 | import de.cau.cs.kieler.core.krendering.extensions.KPolylineExtensions | ||
| 148 | import de.cau.cs.kieler.core.krendering.extensions.KRenderingExtensions | ||
| 149 | import de.cau.cs.kieler.kiml.options.Direction | ||
| 150 | import de.cau.cs.kieler.kiml.options.EdgeRouting | ||
| 151 | import de.cau.cs.kieler.kiml.options.LayoutOptions | ||
| 152 | import de.cau.cs.kieler.klighd.SynthesisOption | ||
| 153 | import de.cau.cs.kieler.klighd.syntheses.AbstractDiagramSynthesis | ||
| 154 | import javax.inject.Inject | ||
| 155 | |||
| 156 | import static extension org.eclipse.emf.ecore.util.EcoreUtil.* | ||
| 157 | |||
| 158 | //import static de.cau.cs.kieler.klighd.example.mydata.MyDataDiagramSynthesis.* | ||
| 159 | /** | ||
| 160 | * This is a small mininal example that creates a hierarchical KLighD diagram | ||
| 161 | * out of very simple hierarchical data structure (MyData). | ||
| 162 | * | ||
| 163 | * @author Christian Motika | ||
| 164 | */ | ||
| 165 | class MyDataDiagramSynthesis extends AbstractDiagramSynthesis<MyData> { | ||
| 166 | |||
| 167 | @Inject extension KNodeExtensions | ||
| 168 | @Inject extension KEdgeExtensions | ||
| 169 | |||
| 170 | // @Inject extension KPortExtensions | ||
| 171 | // @Inject extension KLabelExtensions | ||
| 172 | @Inject extension KRenderingExtensions | ||
| 173 | @Inject extension KContainerRenderingExtensions | ||
| 174 | @Inject extension KPolylineExtensions | ||
| 175 | @Inject extension KColorExtensions | ||
| 176 | |||
| 177 | // Some self-defined colors | ||
| 178 | private static val KColor BLUE1 = RENDERING_FACTORY.createKColor() => | ||
| 179 | [it.red = 248; it.green = 249; it.blue = 253]; | ||
| 180 | private static val KColor BLUE2 = RENDERING_FACTORY.createKColor() => | ||
| 181 | [it.red = 205; it.green = 220; it.blue = 243]; | ||
| 182 | |||
| 183 | // Additional transformation option to hide or show a shadow | ||
| 184 | private static val SynthesisOption SHOW_SHADOW = SynthesisOption::createCheckOption("Shadow", true); | ||
| 185 | private static SynthesisOption EDGE_STYLE = SynthesisOption::createCheckOption("Spline", false); | ||
| 186 | |||
| 187 | // Add all transformation options (comma separated) | ||
| 188 | override getDisplayedSynthesisOptions() { | ||
| 189 | return ImmutableList::of( | ||
| 190 | SHOW_SHADOW, | ||
| 191 | EDGE_STYLE | ||
| 192 | ); | ||
| 193 | } | ||
| 194 | |||
| 195 | override KNode transform(MyData data) { | ||
| 196 | val rootNode = data.createNode() | ||
| 197 | |||
| 198 | if (EDGE_STYLE.booleanValue) { | ||
| 199 | rootNode.setLayoutOption(LayoutOptions::SPACING, Float::valueOf("25.0")); | ||
| 200 | rootNode.setLayoutOption(LayoutOptions::DIRECTION, Direction::DOWN); | ||
| 201 | rootNode.addLayoutParam(LayoutOptions::EDGE_ROUTING, EdgeRouting::SPLINES); | ||
| 202 | rootNode.addLayoutParam(LayoutOptions::ALGORITHM, "de.cau.cs.kieler.graphviz.dot"); | ||
| 203 | } else { | ||
| 204 | rootNode.setLayoutOption(LayoutOptions::SPACING, Float::valueOf("15.0")); | ||
| 205 | rootNode.setLayoutOption(LayoutOptions::DIRECTION, Direction::DOWN); | ||
| 206 | rootNode.addLayoutParam(LayoutOptions::EDGE_ROUTING, EdgeRouting::ORTHOGONAL); | ||
| 207 | rootNode.addLayoutParam(LayoutOptions::ALGORITHM, "de.cau.cs.kieler.klay.layered"); | ||
| 208 | } | ||
| 209 | |||
| 210 | buildNodes(data, rootNode) | ||
| 211 | buildConnections(data, rootNode) | ||
| 212 | |||
| 213 | return rootNode; | ||
| 214 | } | ||
| 215 | |||
| 216 | def void buildNodes(MyData parentData, KNode rootNode) { | ||
| 217 | val root = parentData.createNode() | ||
| 218 | root.putToLookUpWith(parentData) => [ | ||
| 219 | // Optional Layout parameters can be set | ||
| 220 | //it.addLayoutParam(LayoutOptions::ALGORITHM, "de.cau.cs.kieler.kiml.ogdf.planarization"); | ||
| 221 | //it.addLayoutParam(LayoutOptions::SPACING, 75f); | ||
| 222 | //it.addLayoutParam(LayoutOptions::DIRECTION, Direction::UP); | ||
| 223 | // A rounded rectangle is created for every MyData instance | ||
| 224 | it.addRoundedRectangle(5, 5) => [ | ||
| 225 | // Set linewith, foreground color, and a fading background color | ||
| 226 | it.lineWidth = 1; | ||
| 227 | it.setForeground("darkGray".color) | ||
| 228 | // We need a fresh copy of each color item, because it is contained by its element | ||
| 229 | it.setBackgroundGradient(BLUE1.copy, BLUE2.copy, 90) | ||
| 230 | // Here we see a how to use a boolean transformation/diagram option | ||
| 231 | if (SHOW_SHADOW.booleanValue) { | ||
| 232 | it.shadow = "black".color; | ||
| 233 | } | ||
| 234 | // Set a text | ||
| 235 | it.addText(" " + parentData.name + " ") => [ | ||
| 236 | it.setFontSize(9) | ||
| 237 | it.setForeground("black".color) | ||
| 238 | ] | ||
| 239 | // If this is a hierarchical MyData instance, then create horizontal splitter, | ||
| 240 | // a child area, and add its children | ||
| 241 | if (parentData.subData.length > 0) { | ||
| 242 | it.setGridPlacement(1); | ||
| 243 | it.addHorizontalSeperatorLine(1, 2).setForeground("darkGray".color) | ||
| 244 | it.addChildArea().setGridPlacementData() => [ | ||
| 245 | from(LEFT, 3, 0, TOP, 3, 0).to(RIGHT, 3, 0, BOTTOM, 3, 0) | ||
| 246 | minCellHeight = 5; | ||
| 247 | minCellWidth = 5; | ||
| 248 | ]; | ||
| 249 | for (subData : parentData.subData) { | ||
| 250 | |||
| 251 | // To the recursive call to transform for all children | ||
| 252 | val child = subData.transform | ||
| 253 | |||
| 254 | // It is important to add all children to the root! | ||
| 255 | root.children.add(child) | ||
| 256 | } | ||
| 257 | } | ||
| 258 | ] | ||
| 259 | ] | ||
| 260 | |||
| 261 | } | ||
| 262 | |||
| 263 | // Connect every first child to all childs (including itself!) | ||
| 264 | def void buildConnections(MyData parentData, KNode rootNode) { | ||
| 265 | |||
| 266 | // only if there is any child there | ||
| 267 | if (parentData.subData.size > 0) { | ||
| 268 | val firstChild = parentData.subData.get(0); | ||
| 269 | |||
| 270 | for (child : parentData.subData) { | ||
| 271 | |||
| 272 | // BE CAREFUL IF YOU CREATE A MAPPING LIKE THIS THERE WILL JUST BE CREATED ONE | ||
| 273 | // EDGE PER "firstchild". That's why better use the child, because we iterate | ||
| 274 | // over different child-objects (targets) but always habe the same firstChild- | ||
| 275 | // -object. | ||
| 276 | //val kEdge = firstChild.createEdge() // THIS IS MOST LIKELY WRONG | ||
| 277 | |||
| 278 | val kEdge = child.createEdge() | ||
| 279 | kEdge.source = firstChild.node; | ||
| 280 | kEdge.target = child.node; | ||
| 281 | |||
| 282 | System::out.println("Connect " + firstChild.name + " to " + child.name); | ||
| 283 | |||
| 284 | if (!EDGE_STYLE.booleanValue) { | ||
| 285 | kEdge.addPolyline(2) | ||
| 286 | } else { | ||
| 287 | kEdge.addSpline(2); | ||
| 288 | } | ||
| 289 | |||
| 290 | kEdge.KRendering as KPolyline => [ | ||
| 291 | it.addArrowDecorator() | ||
| 292 | it.setLineWidth(2) | ||
| 293 | ]; | ||
| 294 | |||
| 295 | child.buildConnections(rootNode); | ||
| 296 | } | ||
| 297 | } | ||
| 298 | |||
| 299 | } | ||
| 300 | |||
| 301 | } | ||
| 302 | |||
| 303 | |||
| 304 | {{/code}} |