Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* http://rtsys.informatik.uni-kiel.de/kieler
*
* Copyright 2024-2025 by
* Copyright 2024-2026 by
* + Kiel University
* + Department of Computer Science
* + Real-Time and Embedded Systems Group
Expand Down Expand Up @@ -77,6 +77,12 @@ class SPVizCLI implements Runnable {

@Option(names = #["-g", "--build-generator"], defaultValue = "false", description = "Automatically build the generator projects with Maven.")
protected boolean buildGenerator

@Option(names = #["--no-model-dsl"], defaultValue = "false", description = "Skip generating the model DSL for your architecture model and skip incorporating them into the build process.")
protected boolean noModelDsl

@Option(names = #["--no-diff"], defaultValue = "false", description = "Skip generating the difference visualization and its DSL and skip incorporating them into the build process.")
protected boolean noDiff

/**
* Main entry point for this command line tool.
Expand Down Expand Up @@ -107,7 +113,7 @@ class SPVizCLI implements Runnable {
// Parse the model file.
LOGGER.info("Generating sources for {}", spvizModelFile.absolutePath.replace("\\", "/"))
val Resource resource = rs.getResource(URI.createURI("file://" + spvizModelFile.absolutePath.replace("\\", "/")), true)
SPVizModelGenerator.generate(resource, output)
SPVizModelGenerator.generate(resource, output, noModelDsl, noDiff)
}

// Prepare loading .spviz files.
Expand All @@ -118,7 +124,7 @@ class SPVizCLI implements Runnable {
LOGGER.info("Generating sources for {}", spvizFile.absolutePath.replace("\\", "/"))
val Resource resource = rs.createResource(URI.createURI("file://" + spvizFile.absolutePath.replace("\\", "/")))
resource.load(rs.getLoadOptions())
SPVizGenerator.generate(resource, output)
SPVizGenerator.generate(resource, output, noModelDsl, noDiff)

// Build the project.
val buildProject = output.toAbsolutePath.toString.replace("\\", "/") + "/" + (resource.contents.head as SPViz).package + ".build"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -499,8 +499,10 @@ class GenerateActions {
import de.cau.cs.kieler.klighd.kgraph.util.KGraphUtil
import de.cau.cs.kieler.klighd.Klighd
import «data.getBundleNamePrefix».viz.SynthesisProperties
import «data.getBundleNamePrefix».viz.SynthesisUtils
import «data.getBundleNamePrefix».model.IVisualizationContext
import «data.getBundleNamePrefix».model.«data.visualizationName»
import java.util.List
import org.eclipse.core.runtime.Status
import org.eclipse.emf.ecore.util.EcoreUtil.Copier

Expand All @@ -519,12 +521,28 @@ class GenerateActions {
abstract class AbstractVisualizationContextChangingAction implements IAction {

final override execute(ActionContext context) {
val visualizationContexts = context.viewContext.getProperty(SynthesisProperties.VISUALIZATION_CONTEXTS)
val index = context.viewContext.getProperty(SynthesisProperties.CURRENT_VISUALIZATION_CONTEXT_INDEX).intValue
var List<«data.visualizationName»> visualizationContexts = null
var index = 0

// Differentiate source/target model in diff visualization.
var visContextsProperty = SynthesisProperties.VISUALIZATION_CONTEXTS
var otherVisContextsProperty = SynthesisProperties.VISUALIZATION_CONTEXTS_OTHER
var currentVisContextIndexProperty = SynthesisProperties.CURRENT_VISUALIZATION_CONTEXT_INDEX
var otherCurrentVisContextIndexProperty = SynthesisProperties.CURRENT_VISUALIZATION_CONTEXT_INDEX_OTHER

if (SynthesisUtils.isTargetModel(context.getKNode())) {
visContextsProperty = SynthesisProperties.VISUALIZATION_CONTEXTS_OTHER
otherVisContextsProperty = SynthesisProperties.VISUALIZATION_CONTEXTS
currentVisContextIndexProperty = SynthesisProperties.CURRENT_VISUALIZATION_CONTEXT_INDEX_OTHER
otherCurrentVisContextIndexProperty = SynthesisProperties.CURRENT_VISUALIZATION_CONTEXT_INDEX
}

visualizationContexts = context.viewContext.getProperty(visContextsProperty)
index = context.viewContext.getProperty(currentVisContextIndexProperty).intValue

val currentVisualizationContext = visualizationContexts.get(index)

// Make a deep-copy of the current context and store it for the action to be the next canditate for the undo
// Make a deep-copy of the current context and store it for the action to be the next candidate for the undo
// function.

// Copy the root context and the currently shown one from the same Copier to guarantee a completely copied
Expand Down Expand Up @@ -552,10 +570,18 @@ class GenerateActions {
try {
changeVisualization(modelVisualizationContext, context)

// FIXME: this is currently a hack that changes the other visualization context, but does no copying and index updating yet, so redo/undo currently is not implemented in diff visualizations.
if (context.viewContext.viewModel.getChildren().get(0).getChildren.get(0).getProperty(SynthesisProperties.SOURCE_MODEL) !== null) {
var targetModelVisualizationContext = SynthesisUtils.getDiffContext(modelVisualizationContext, context.getViewContext().getProperty(otherVisContextsProperty).last())
if (targetModelVisualizationContext !== null) {
changeVisualization(targetModelVisualizationContext, context)
Comment thread
NiklasRentzCAU marked this conversation as resolved.
}
}

// Put the old context, that will be updated below at the at index + 1 and remember that new index as the
// current index.
visualizationContexts.add(index + 1, currentVisualizationContext)
context.viewContext.setProperty(SynthesisProperties.CURRENT_VISUALIZATION_CONTEXT_INDEX, index + 1)
context.viewContext.setProperty(currentVisContextIndexProperty, index + 1)

return getActionResult(context)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
/*
* KIELER - Kiel Integrated Environment for Layout Eclipse RichClient
*
* http://rtsys.informatik.uni-kiel.de/kieler
*
* Copyright 2025 by
* + Kiel University
* + Department of Computer Science
* + Real-Time and Embedded Systems Group
*
* This code is provided under the terms of the Eclipse Public License 2.0 (EPL-2.0).
*/
package de.cau.cs.kieler.spviz.spviz.generator

import de.cau.cs.kieler.spviz.spvizmodel.generator.FileGenerator
import java.io.File

/**
* Generates classes for the dual difference visualization.
*
* @author nre
*/
class GenerateDiffViz {
def static void generate(File sourceFolder, DataAccess data) {
val File folder = FileGenerator.createDirectory(sourceFolder, data.getBundleNamePrefix.replace('.','/') + "/diffviz")

FileGenerator.updateFile(folder, data.visualizationName.toFirstUpper + "DiffDiagramSynthesis.xtend",
generateDiffVizDiagramSynthesis(data))
FileGenerator.updateFile(folder, "KlighdSetup.xtend", generateKlighdSetup(data))
FileGenerator.updateFile(folder, "DiffSynthesisProperties.xtend", generateDiffSynthesisProperties(data))
}

/**
* Generates the content for the diagram synthesis to show two models next to each other and to compare them.
*
* @param data
* a DataAccess to easily get the information from
* @return
* the generated file content as a string
*/
def static String generateDiffVizDiagramSynthesis(DataAccess data) {
return '''
package «data.bundleNamePrefix».diffviz

import com.google.inject.Inject
import de.cau.cs.kieler.klighd.kgraph.KNode
import de.cau.cs.kieler.klighd.krendering.ViewSynthesisShared
import de.cau.cs.kieler.klighd.krendering.extensions.KEdgeExtensions
import de.cau.cs.kieler.klighd.krendering.extensions.KNodeExtensions
import de.cau.cs.kieler.klighd.krendering.extensions.KRenderingExtensions
import de.cau.cs.kieler.klighd.syntheses.AbstractDiagramSynthesis
import de.cau.cs.kieler.klighd.syntheses.DiagramSyntheses
import «data.modelBundleNamePrefix».diff.dsl.«data.spvizModel.name.toFirstLower»DiffDsl.«data.spvizModel.name.toFirstUpper»Diff
import «data.modelBundleNamePrefix».model.«data.projectName»
import «data.bundleNamePrefix».viz.«data.projectName»DiagramSynthesis
import org.eclipse.elk.alg.layered.options.LayeredOptions
import org.eclipse.elk.core.options.CoreOptions
import org.eclipse.elk.core.options.Direction
import org.eclipse.emf.common.util.URI
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.xtext.resource.XtextResourceSet

@ViewSynthesisShared
class «data.visualizationName.toFirstUpper»DiffDiagramSynthesis extends AbstractDiagramSynthesis<«data.spvizModel.name.toFirstUpper»Diff> {
@Inject extension KNodeExtensions
@Inject extension KEdgeExtensions
@Inject extension KRenderingExtensions

@Inject «data.projectName»DiagramSynthesis «data.projectName.toFirstLower»DiagramSynthesis

override getDisplayedActions() {
return «data.projectName.toFirstLower»DiagramSynthesis.displayedActions
}

override getDisplayedLayoutOptions() {
return «data.projectName.toFirstLower»DiagramSynthesis.displayedLayoutOptions
}

override getDisplayedSynthesisOptions() {
return «data.projectName.toFirstLower»DiagramSynthesis.displayedSynthesisOptions
}

override KNode transform(«data.spvizModel.name.toFirstUpper»Diff model) {
val root = model.createNode().associateWith(model)

val resource = model.eResource
// Find out where this files is located. The comparison files are interpreted relative to this path.
val fileString = resource.URI.toString
val folderString = fileString.substring(0, fileString.lastIndexOf('/'))

// In the following, exceptions can occur if the model at the given path is not loadable.
// Load the source model
val XtextResourceSet rs = new XtextResourceSet

var source«data.projectName» = usedContext.getProperty(DiffSynthesisProperties.SOURCE_«data.spvizModel.name.toUpperCase»_MODEL)
if (source«data.projectName» === null || !source«data.projectName».eResource.URI.toString.equals(folderString + "/" + model.sourceModel)) {

val Resource source = rs.createResource(URI.createURI(folderString + "/" + model.sourceModel))
source.load(rs.getLoadOptions())
source«data.projectName» = source.contents.head as «data.projectName»

usedContext.setProperty(DiffSynthesisProperties.SOURCE_«data.spvizModel.name.toUpperCase»_MODEL, source«data.projectName»)
}

var target«data.projectName» = usedContext.getProperty(DiffSynthesisProperties.TARGET_«data.spvizModel.name.toUpperCase»_MODEL)
if (target«data.projectName» === null || !target«data.projectName».eResource.URI.toString.equals(folderString + "/" + model.targetModel)) {

val Resource target = rs.createResource(URI.createURI(folderString + "/" + model.targetModel))
target.load(rs.getLoadOptions())
target«data.projectName» = target.contents.head as «data.projectName»
usedContext.setProperty(DiffSynthesisProperties.TARGET_«data.spvizModel.name.toUpperCase»_MODEL, target«data.projectName»)

}

val source«data.projectName»_ = source«data.projectName»
val target«data.projectName»_ = target«data.projectName»

root.children += createNode => [
addRectangle => [ invisible = true ]
associateWith(model)
DiagramSyntheses.setLayoutOption(it, CoreOptions::ALGORITHM, "org.eclipse.elk.layered")
setLayoutOption(LayeredOptions.DIRECTION, Direction.RIGHT)
«data.projectName.toFirstLower»DiagramSynthesis.targetModel = target«data.projectName»_
«data.projectName.toFirstLower»DiagramSynthesis.sourceModel = source«data.projectName»_

// display both «data.projectName»s and tell their synthesis which model is which
«data.projectName.toFirstLower»DiagramSynthesis.other = false

val overviewSource«data.projectName»Node = «data.projectName.toFirstLower»DiagramSynthesis.transform(source«data.projectName»_, usedContext)
children += overviewSource«data.projectName»Node

«data.projectName.toFirstLower»DiagramSynthesis.other = true

val overviewTarget«data.projectName»Node = «data.projectName.toFirstLower»DiagramSynthesis.transform(target«data.projectName»_, usedContext)
children += overviewTarget«data.projectName»Node

val edge = createEdge(source«data.projectName»_, target«data.projectName»_) => [
addPolyline => [ invisible = true ]
source = overviewSource«data.projectName»Node
target = overviewTarget«data.projectName»Node
]
overviewSource«data.projectName»Node.outgoingEdges.add(edge)

]
return root
}
}

'''
}

/**
* Generates the content for the KLighD setup.
*
* @param data
* a DataAccess to easily get the information from
* @return
* the generated file content as a string
*/
def static String generateKlighdSetup(DataAccess data) {
return '''
package «data.getBundleNamePrefix».diffviz

import de.cau.cs.kieler.klighd.IKlighdStartupHook
import de.cau.cs.kieler.klighd.KlighdDataManager

/**
* Setup registering all KLighD extensions required to run this bundle.
*/
class KlighdSetup implements IKlighdStartupHook {
override execute() {
KlighdDataManager.instance
.registerDiagramSynthesisClass(«data.visualizationName.toFirstUpper»DiffDiagramSynthesis.name, «data.visualizationName.toFirstUpper»DiffDiagramSynthesis)
}
}

'''
}

/**
* Generates the content for DiffSynthesisProperties class.
*
* @param data
* a DataAccess to easily get the information from
* @return
* the generated file content as a string
*/
def static String generateDiffSynthesisProperties(DataAccess data) {
return '''
package «data.getBundleNamePrefix».diffviz

import «data.modelBundleNamePrefix».model.«data.projectName»
import org.eclipse.elk.graph.properties.IProperty
import org.eclipse.elk.graph.properties.Property

/**
* Class to provide easy access to properties stored for the diff syntheses.
*/
class DiffSynthesisProperties {
/**
* Property that stores the view context of the source model. Used to remember already loaded synthesis.
*/
public static final IProperty<«data.projectName»> SOURCE_«data.spvizModel.name.toUpperCase»_MODEL = new Property<«data.projectName»>("«data.spvizModel.name.toFirstLower»diff.sourceModel",null)

/**
* Property that stores the view context of the target model. Used to remember already loaded synthesis.
*/

public static final IProperty<«data.projectName»> TARGET_«data.spvizModel.name.toUpperCase»_MODEL = new Property<«data.projectName»>("«data.spvizModel.name.toFirstLower»diff.targetModel",null)

}

'''
}

}
Loading